> In short: I no longer understand why utrace->resume can be UTRACE_*STEP
> when the tracee is stopped, and in fact I think this is not right.

I think we didn't have that originally.  The rationale that I remember is
the idea that you should be able to implement a debugger interface feature
like PTRACE_SINGLESTEP without noticeably more overhead than it had in
pre-utrace ptrace.  That is, in vanilla ptrace, the tracer thread calls
user_enable_single_step itself and then just lets the tracee resume
directly to user mode.  We wanted to avoid always having an additional trip
through tracehook_notify_resume and the utrace reporting loop and then back
through the return-to-user assembly path a second time, between the tracee
waking up and actually going to user mode.

> For example. Consider two engines, E1 and E2. To simplify the discussion
> suppose that both engines have engine->data->resume and ->report_quiesce()
> just returns ->resume. So, if the debugger wants, say, single-step, it
> does
> 
>       engine->data->resume = UTRACE_SINGLESTEP;
>       utrace_control(UTRACE_SINGLESTEP);
> 
> Actually, any engine should do something like this.

Right.

> Now suppose that E1 wants UTRACE_SINGLESTEP, E2 asks for UTRACE_STOP.
> In this case utrace_resume() results in utrace_stop() which sets
> utrace->resume = UTRACE_SINGLESTEP before sleeping.
> 
> First of all - why? Yes, the theory is that we have another reporting
> loop after wake_up, but utrace->resume = UTRACE_REPORT is enough, E1
> should always acquire UTRACE_SINGLESTEP if needed or it is buggy.

If E2 uses UTRACE_RESUME rather than UTRACE_REPORT, nobody actually cared
about another callback before user mode.  So indeed the theory is that this
lets E1 get its single-step without the overhead of a reporting loop that
serves no other purpose than that.  But you are right that any engine is
buggy if it doesn't dtrt in such a reporting loop, since some other engine
might have used UTRACE_REPORT.

> But more importantly, this doesn't look right or I missed something.
> Suppose that, before E2 resumes the tracee, E1 does utrace_control(STOP)
> which immediately succeeds and allows E1 to play with the stopped tracee.
> Again, to simplify the discussion, suppose that E2 does UTRACE_RESUME
> and nothing more after that.
> 
> Now. E1 wants to resume the tracee too and it does
> 
>       engine->data->resume = UTRACE_RESUME;
>       utrace_control(UTRACE_RESUME);
> 
> utrace_control(RESUME) correctly clears TIF_SINGLESTEP, but it doesn't
> change utrace->resume. This means utrace_resume() doesn't do the
> reporting loop, it just calls user_enable_single_step() and returns
> to user-mode.

Yes, that sounds wrong.

> So. Could you explain why utrace_stop() should ever keep UTRACE_STEP
> in utrace->resume? finish_report() does the same but this looks fine.

I cannot.

> Also. I am not sure utrace_control(UTRACE_STEP) should always set
> utrace->resume = UTRACE_STEP. If the tracee is stopped but there
> is another ENGINE_STOP engine (iow, the tracee won't be resumed
> right now) we have the same problem.

Right.  Said another way, any time an engine that previously used
UTRACE_*STEP now uses UTRACE_RESUME, we must degrade utrace->resume back to
UTRACE_REPORT at most.  Since we don't keep track of which engine it was
that put UTRACE_*STEP into utrace->resume before, we'd have to just always
degrade it any time anybody uses UTRACE_RESUME.

That has almost the whole effect of punting utrace->resume back to just a
utrace->report flag.  But that's how it used to be, and then we changed it.
So it merits digging up our old discussion threads that led to doing that
and see what all we had in mind then.

> Off-topic,
> 
>       #define UTRACE_RESUME_BITS      (ilog2(UTRACE_RESUME_MAX) + 1)
> 
> why UTRACE_RESUME_MAX? it should be (ilog2(UTRACE_RESUME_MAX - 1) + 1), no?

Yes, I think you're right.


Thanks,
Roland

Reply via email to