[lldb-dev] Problem unwinding from inside of a CRT function

jingham at apple.com jingham at apple.com
Fri Jan 16 12:44:58 PST 2015


> On Jan 16, 2015, at 12:14 PM, Zachary Turner <zturner at google.com> wrote:
> 
> I'm still trying to wrap my head around the way LLDB does things.
> 
> If I understand correctly, when you run thread step-over, it enters single step mode (by setting the trap flag on the CPU).  Every time the CPU traps, LLDB picks this up and asks the ThreadPlan "should i stop now?"  "should i stop now?" until the ThreadPlan returns true.  And part of this "should i stop now" involves generating an unwind.
> 

That's close, but not quite correct.  

1) In the original implementation, (and this is how gdb does it, BTW) lldb single-stepped till "something interesting happened."  As an optimization, when you are doing any kind of step through source range, I changed lldb so it runs from "instruction that could branch" to "instruction that could branch" using breakpoints.  Then when it hits an instruction that could branch it single steps that instruction, and then figures out from where that went what to do next.

BTW, if it were helpful to figure out what to do next, we could store some info (the old stack frame or whatever) when we hit a branch instruction, and then use it when the single-step completed.  I haven't needed to do that yet, however; Jason's always been able to get the unwinder work reliably enough not to require this.

2) If the single step pushes a frame, and we are "stepping over", lldb sets a breakpoint on the return address and continues.  When the return address is hit (for the current frame of course since it could be hit recursively) then we continue stepping as above.

3) And of course, if the single step over the branch pops a frame, then we stop.

> If my understanding is correct, then I have some questions:
> 
> 1) Isn't this extremely slow?  What if I'm in main(), and the program I'm debugging is, say, clang, and I say "step over the entire compilation"?  It seems like this would take a decade to return.

Right, if we were to keep single stepping after we push a frame it would be very slow.  You would definitely have noticed that.  But as you see, we don't.  

Note, sometimes not doing single-stepping through gobs of library code requires some tricky footwork.  For instance, if you are stepping into and hit a cross-library call, you first step to the inter-library procedural stub.  The particular stub you are stepping through might not have been bound up yet, so if you can't tell by some other means where that call would go you would have to single stepping through lots of loader code to get to the  real function call.  lldb has a whole section of code to predict the trampoline endpoint, some of this coming from the dynamic linker plugin, and in the case of ObjC some of it coming from the ObjCRuntime plugin.

> 
> 2) What if one of the instructions is a pushf / popf?   You step over a pushf, then later you try to continue, and it continues over the popf, which restores the trap flag.  Now LLDB is confused because it doesn't think it's single stepping, but the CPU does.  How does this work?

Interesting.  gdb does the same thing that lldb does except it does single step through all the instructions in the current source range.  In all the years of supporting gdb I never had this come up.  Maybe these instructions don't get used all that often in code you're likely to be source stepping through?

Anyway, this isn't an issue for lldb, since pushf/popf don't count as branch instructions, so we would continue over them rather than single stepping.

Hope that helps,

Jim


> 
> On Fri Jan 16 2015 at 11:50:21 AM Greg Clayton <gclayton at apple.com> wrote:
> One important thing to get right before proceeding is getting the correct address bounds of all functions so that the disassembly unwinder can do its job. You said you are stopped at a function, but don't know the function bounds. You will want to modify your object file reader (COFF?) to create a viable symbol table that can be used. On MacOSX we use the actual symbol table from the object file and supplement it with all sorts of goodies:
> 1 - LC_FUNCTION_STARTS load command which tells us all function bounds even if their symbols have been stripped
> 2 - the PLT entries are made into symbols
> 3 - more data from the __LINKEDIT is used to create other symbols
> 
> Can you modify your COFF plug-in to get the symbols bounds for every function somehow? Then we can rely on the unwind plan that manually disassembles the functions and makes its own unwind info.
> 
> Greg
> 
> > On Jan 15, 2015, at 5:01 PM, Zachary Turner <zturner at google.com> wrote:
> >
> > Btw, I'm still a little uncomfortable that not having unwind/ symbol info at any point no matter how deep in a function call chain, has the possibility to mess up a step over. In my original example, i had symbols for main but not printf. Is that not sufficient to step over a call to printf? It should be able to know from that a) the bounds of main(), b) the pc corresponding to the next line of source after printf, and c) the value of esp. Aren't those 3 pieces of information enough to step over any line of source, regardless of whether you have unwind information for the code inside the function you're stepping over?
> > On Thu, Jan 15, 2015 at 4:36 PM <jingham at apple.com> wrote:
> >
> > > On Jan 15, 2015, at 4:18 PM, Zachary Turner <zturner at google.com> wrote:
> > >
> > > Which is unfortunate, because it seems to be needed even for basic stepping to work, like step over.  Originally I was just trying to implement stepping, and that's how I ran into this issue.  So that brings me to a related question.  Why is step over as complicated as it is?  It seems to me like step over can be implemented by disassembling 1 opcode, adding the size of the opcode to the current pc, and having the ThreadPlan::ShouldStop always return false unless the pc is equal to old_pc + size_of_opcode.
> > >
> >
> > You are describing "thread step-inst".  That should pretty much always work regardless of unwinder, etc.
> >
> > Source step over, as Jason said, is much more complicated.
> >
> > Jim
> > _______________________________________________
> > lldb-dev mailing list
> > lldb-dev at cs.uiuc.edu
> > http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev
> 





More information about the lldb-dev mailing list