[lldb-dev] Shared Process plugin between Linux and BSD

Kamil Rytarowski via lldb-dev lldb-dev at lists.llvm.org
Thu Dec 15 08:21:43 PST 2016


On 15.12.2016 16:08, Pavel Labath wrote:
> On 15 December 2016 at 14:14, Kamil Rytarowski <n54 at gmx.com> wrote:
>> On 15.12.2016 14:14, Pavel Labath wrote:
>>>>>
>>>>> 2. PTRACE_SETSIGINFO - equivalent currently unsupported, I will need to
>>>>> add support for it in ptrace(2).
>>> This value is currently only used in logging code, so we can just
>>> remove it from the code base. However, it's a useful thing to have for
>>> the future, so it may be worth making sure the NetBSD kernel supports
>>> it.
>>>
>>
>> I was wondering whether waitid(2) or wait6(2) could be used there, as
>> they return siginfo_t. If so, NetBSD does not need dedicated ptrace(2)
>> entry.
> 
> Note that there are two siginfo_t structures in action here. One is
> the siginfo_t the *inferior* gets as a result of some action (e.g.
> kill(), raise(), hitting an int3, etc.). The other is the siginfo_t
> corresponding to the SIGCHLD that the *debugger* gets when something
> interesting happens to the inferior. (On linux at least ), waitid(2)
> returns the second one. That one, however, generally does not contain
> any useful information apart from the process ID. PTRACE_GETSIGINFO
> returns the first one, and that one contains the interesting stuff
> (the signal the inferior recieved, and in case of a SIGTRAP, the
> information about the debug event is encoded there as well). This is
> usually very important information for the debugger.
> 
> PTRACE_SETSIGINFO allows you to overwrite the signal that the inferior
> would receive. It can be used to inject "fake" signals into the
> inferior. That can be a useful feature from time to time, but
> currently we do not support that (on linux we support injecting a
> signal via an argument to PTRACE_CONTINUE, which is a weaker
> implementation of this call, as it can only inject a signal number,
> not the full siginfo_t struct). So you may want to implement this at
> some point, but on the grand scale of things, it's not something very
> important.
> 
> 

I see, thank you for your explanation! It looks useful, but let it keep
for later.

>>
>>>>>
>>>>> 3. PTRACE_O_TRACEEXEC, PTRACE_O_TRACECLONE, PTRACE_O_TRACEEXIT -
>>>>> equivalent to be implemented.
>>> Do you mean "implemented in lldb-server" or "implemented in the
>>> kernel"? Being notified when the inferior exec()s would definitely be
>>> good. Tracing thread creation and exit is a nice-to-have but low
>>> priority. The only reason we used it on linux is because that's the
>>> only way to auto-attach to new threads, but I think that for you that
>>> will happen automatically.
>>>
>>
>> I mean implemented in the kernel.
>>
>> Thread (lwp) creation and termination is currently detectable in FreeBSD
>> with a special event, if this is useful in the NetBSD context -- like a
>> user setting a break on this event explicitly -- I will add it to TODO
>> list. On NetBSD there is no need to explicitly start tracing new
>> threads, tracing is per process and it is composed of a bulk of threads
>> (lwp). POSIX threads are a layer with extra features built upon LWP.
> BTW, are your debug registers per-process or per-thread? If they are
> per thread, then you will need to hook thread creation so you can
> inject the correct watchpoints and stuff, otherwise you will miss some
> hits. If this is handled by the kernel, then you are probably fine.
> 

Debug registers (watchpoints) are per-thread (lwp) and they are not
being inherited on thread's or process' forks. It's a valid use-case.

I just checked FreeBSD and there is PTRACE_LWP:

This event flag controls tracing of LWP kernel thread creation and
destruction. When this event is enabled, new LWPs will stop and report
an event with PL_FLAG_BORN set before executing their first instruction,
and exiting LWPs will stop and report an event with PL_FLAG_EXITED set
before completing their termination.

>>
>> There is also the the exect(3) call in our libc inherited from BSD4.2,
>> but it no longer works in a useful way. It used to singlestep execve(2)
>> call. I was thinking whether/how to make it useful again, like combine
>> in it execve(2) + PT_TRACE_ME + SIGSTOP/SIGTRAP on exec.
>>
>>>>>
>>>>> 4. No tracing of VFORK events implemented.
>>>>>
>>>>> 5. Hardware assisted watchpoints (debug registers on amd64) have their
>>>>> dedicated ptrace(2) API to set/unset watchpoints, they do not export raw
>>>>> debug registers.
>>>>>
>>>>> 6. Other ptrace(2) calls have their equivalents on NetBSD
>>>>> (PTRACE_PEEKUSER, PTRACE_POKEUSER etc).
>>> Cool, I guess that means we can share (Read|Write)Memory(WithoutTrap|).
>>>
>>
>> Functionality is the same, however the API is different.
> Ok. If the api differs by too much, then we might as well keep them as
> separate implementations. We'll see how it goes.
> 

We will see how it goes.

>>
>>>>>
>>>>> 7. SIGTRAP has currently only two si_code values (specified by POSIX).
>>> The code for decoding si_code values is already messy and in need of a
>>> rewrite. I think the act of decoding si_code values will need to be
>>> OS- (and maybe event architecture-) specific, but the actions taken
>>> upon individual events should be quite similar.
>>>
>>
>> I was wondering whether it is useful to return distinct si_code for
>> hardware assisted traps or PT_STEP. At the moment I ignored it.
> If you are able to do that, then I'd recommend you go for it. It's
> very useful to be able to figure out what caused the application to
> stop.
> 

It looks trivial to be implemented for x86, my original concern was
usefulness of this information.

My another concern was whether it's useful to keep PT_STEP and hardware
watchpoints enabled at the same time (before returning to userspace and
resuming a thread).

And third issue was whether this feature can be portable (property
retained) across capable ports (platforms).

> Imagine the following situation: The user does a single-step and the
> app stops with a SIGTRAP. What do you display to the user? Most likely
> the SIGTRAP just means the single-step completed. However, any one of
> the following things could happen:
> - the instruction happened to trigger a watchpoint (you should report
> that instead)
> - the instruction happened to be the syscall instruction corresponding
> to the raise(SIGTRAP) done by the application
> - a random other application sent a SIGTRAP to your process
> 
> The more information we get, the easier it is to display something
> meaningful to the user.
> 
> 

I makes sense. I think we could move it for TODO list for later and make
the core functional first.

>>> Agreed. The only thing that slightly worries me is the lack of
>>> execve() tracing capability, if I understood correctly. While it
>>> probably may be possible to work without it. I'd think about adding it
>>> nonetheless.
>>>
>>
>> I'm going to research it now and in case of required - as in
>> implementing a debugger much easier -- I will go for it and add support.
> 
> A debugger needs to know when the running image has changed, so that
> it can e.g. rescan the symbol table and insert new breakpoints. I
> think it would be quite hard to debug across an execve() without it.
> 

There is also PT_SYSCALL to catch each syscall... so maybe add a
dedicated filter to tap interesting system calls. It would handle thread
(lwp) events and actions like execve(2).

I don't think that performance is critical for tracee -- as breaking on
each syscall slows the things down. I'm prioritizing reusing of already
existing interfaces.

I will research how is it done with GDB.

> cheers,
> pl
> 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://lists.llvm.org/pipermail/lldb-dev/attachments/20161215/387547a0/attachment.sig>


More information about the lldb-dev mailing list