Open Quuxplusone opened 4 years ago
Attached a.out
(17872 bytes, application/x-executable): the binary
So the problem here is that the line table is:
0x000000000040118a: /home/yibiao/Debugger/small.c:13:5
0x000000000040118f: /home/yibiao/Debugger/small.c:13:16
0x0000000000401193: /home/yibiao/Debugger/small.c:13:15
But the code does:
a.out[0x40117e] <+78>: jge 0x40118f ; <+95> at
small.c:13:16
a.out[0x401184] <+84>: movl -0x10(%rbp), %eax
a.out[0x401187] <+87>: movl %eax, -0x18(%rbp)
a.out[0x40118a] <+90>: jmp 0x4011b4 ; <+132> at small.c:8:3
a.out[0x40118f] <+95>: cmpl $0x0, -0xc(%rbp)
So the code bypasses the first line table entry for line 13, and only hits the
second.
I think the line table is wrong. Unless I'm misunderstanding the assembly,
0x40118a is the last bit of the logic that handles the if branch of that
statement. After setting l = m, you need to jump past the "else if (r>0)" test
and exit the function. It is certainly odd to have code assigned to this "else
if" line that will only ever get executed if that line isn't really going to be
executed...
BTW, the reason you don't get three breakpoint locations, one for each of the
entries, is that clang's line tables are pretty noisy, particularly with
optimization on. From past experience, when lldb used to set a breakpoint on
every instance of a line in the line table, it drove users nuts. You had to
hit continue over and over without seeming to make any progress just to get
past a single breakpoint.
So lldb coalesces all the contiguous range of addresses in the line table that
have the same line number and sets a breakpoint on the lowest address.
Another solution would be to have a switch to break set that would turn off
this coalescing, which people who are debugging optimized code could use that
help in this case. We wouldn't want to make that the default in general, since
I have no reports of this causing problems, and back before the coalescing
people were coming at me with pitchforks and torches to get it changed.
But we certainly add a flag to disable it. We could even auto-trigger the
setting based on whether the code in question was built with optimization? On
Darwin, clang records whether the current CU was built with optimization
(DW_AT_APPLE_optimized or something like that).
But I think a better solution in this case would be to fix the line table.
Well, I have no reports but this one, and I think it's arguable the line table is at fault...
Another possibility is to use is_stmt (DW_LNS_negate_stmt) but despite clang is
producing it it is not helpful as is:
Address Line Column File ISA Discriminator Flags
------------------ ------ ------ ------ --- ------------- -------------
0x000000000040117e 11 8 1 0 0
0x0000000000401184 12 11 1 0 0 is_stmt
0x0000000000401187 12 9 1 0 0
0x000000000040118a 13 5 1 0 0 is_stmt
0x000000000040118f 13 16 1 0 0
> BTW, the reason you don't get three breakpoint locations, one for each of the
entries, is that clang's line tables are pretty noisy, particularly with
optimization on. From past experience, when lldb used to set a breakpoint on
every instance of a line in the line table, it drove users nuts. You had to
hit continue over and over without seeming to make any progress just to get
past a single breakpoint.
Is that the only option? Either break at the first, or break at all? What about
either checking the instructions to see if they're jump-in-able & setting extra
breakpoints in case you come into the line from different paths? And
suppressing the breakpoints (so far as the user is concerned at least -
breakpoint triggers and is silently continued without showing the user
anything) if they're reached in sequence/one after the other.
But the bug report "As showed, when setting breakpoint on Line 13, lldb exit
directly.
However, when step-i, Line 13 is hit by lldb as follow:" - essentially "step-i
can reach code on a line for a breakpoint that's never hit" seems like, at
least at the moment, that behavior is "by design". I think GDB does the same
thing (I seem to recall - yeah, when investigating
https://bugs.llvm.org/show_bug.cgi?id=19864 - "The GDB behavior of not breaking
when stepping into the middle of a line doesn't seem entirely unreasonable,").
Line table looks fairly correct/intentional to me - the 13:5 is the closing '}'
of the if. That is reached at the end of the if block and is where the jump is
attributed to (in the same way that an implicit return from a function is
attributed to the '}' of the function, etc). This is also where any destructors
for variables inside the if would be attributed to - even if it weren't for the
jump as well being attributed there.
Might just be overall "by design" here.
GCC avoids this by, I suspect, not attributing the dtor/follow-on jumps to specific lines, but letting them pick up the flow-on location from the last located instruction, which is the last statement in the body of the 'if' instead of specifically to the closing '}'. I think that's a loss in fidelity that's a bit unfortunate, really. (it creates ambiguities - is it the dtor of a statement-local object, or an object scoped to the {}, etc)
(In reply to David Blaikie from comment #4)
> > BTW, the reason you don't get three breakpoint locations, one for each of
the entries, is that clang's line tables are pretty noisy, particularly with
optimization on. From past experience, when lldb used to set a breakpoint on
every instance of a line in the line table, it drove users nuts. You had to
hit continue over and over without seeming to make any progress just to get
past a single breakpoint.
>
> Is that the only option? Either break at the first, or break at all? What
> about either checking the instructions to see if they're jump-in-able &
> setting extra breakpoints in case you come into the line from different
> paths? And suppressing the breakpoints (so far as the user is concerned at
> least - breakpoint triggers and is silently continued without showing the
> user anything) if they're reached in sequence/one after the other.
You're right, you could start getting clever to try to improve the experience
here.
Neither of your suggestions are trivial efforts, however. You'd have to be
careful about the cost of adding a full function scan for branches to every
breakpoint setting. And you don't want to add a lot of stops if you don't need
to - even if you hide them from the user - since particularly when remote
debugging stops can be expensive.
But if somebody has some time to work on improving the optimized code debugging
situation, these seem viable approaches.
Better than scanning for "jump into a lines middle" would be to have the
is_stmt express this. We'd still probably end up with some extra stops, and
might need to decide whether we want to hide them or not. But it would be
better to have the compiler figure out where the important locations are when
it has all the info on hand, rather than having the debugger do it every time
we set a breakpoint.
>
> But the bug report "As showed, when setting breakpoint on Line 13, lldb exit
> directly.
> However, when step-i, Line 13 is hit by lldb as follow:" - essentially
> "step-i can reach code on a line for a breakpoint that's never hit" seems
> like, at least at the moment, that behavior is "by design". I think GDB does
> the same thing (I seem to recall - yeah, when investigating
> https://bugs.llvm.org/show_bug.cgi?id=19864 - "The GDB behavior of not
> breaking when stepping into the middle of a line doesn't seem entirely
> unreasonable,").
Yes, this was one of the areas where we didn't copy gdb at first. After all,
we could make locations for each of the internal stops, and then you can look
and disable the ones you didn't need. Oh man did people hate that. I think it
would be better these days now that we show column information, at least you
can generally tell you are making progress.
>
> Line table looks fairly correct/intentional to me - the 13:5 is the closing
> '}' of the if. That is reached at the end of the if block and is where the
> jump is attributed to (in the same way that an implicit return from a
> function is attributed to the '}' of the function, etc). This is also where
> any destructors for variables inside the if would be attributed to - even if
> it weren't for the jump as well being attributed there.
a.out
(17872 bytes, application/x-executable)