This is an archive of the discontinued LLVM Phabricator instance.

[lldb-dev] Update LLDB test cases for 'inlineStepping'
AbandonedPublic

Authored by CarlosAlbertoEnciso on Oct 25 2017, 3:48 AM.

Details

Summary

A patch to correct the compiler issue, where instructions associated to
the function prolog are assigned line information, causing the debugger
to show incorrectly the beginning of the function body, caused some tests
in the LLDB suite to fail.

For a full description, please see:

https://reviews.llvm.org/rL313047
https://reviews.llvm.org/D37625

This patch include the required changes to the failing tests.

For example, using 'caller_trivial_1' from the test case:

void
caller_trivial_1 ()
{
    caller_trivial_2(); // In caller_trivial_1.
    inline_value += 1; 
}

The "sub $0x8,%esp" instruction, which is frame setup code is printed as
being part of the statement 'inline_value += 1

void
caller_trivial_1 ()
{
  c0:    55                       push   %ebp
  c1:    89 e5                    mov    %esp,%ebp
    inline_value += 1;  // At first increment in caller_trivial_1.
  c3:    83 ec 08                 sub    $0x8,%esp
  c6:    a1 00 00 00 00           mov    0x0,%eax
  cb:    83 c0 01                 add    $0x1,%eax
  ce:    a3 00 00 00 00           mov    %eax,0x0
    caller_trivial_2(); // In caller_trivial_1.
  d3:    e8 18 00 00 00           call   f0 <_Z16caller_trivial_2v>
    inline_value += 1;
  d8:    a1 00 00 00 00           mov    0x0,%eax
  dd:    83 c0 01                 add    $0x1,%eax
  e0:    a3 00 00 00 00           mov    %eax,0x0
}
  e5:    83 c4 08                 add    $0x8,%esp
  e8:    5d                       pop    %ebp
  e9:    c3                       ret   
  ea:    66 0f 1f 44 00 00        nopw   0x0(%eax,%eax,1)

But the instructions associated with the statement

inline_value += 1;

which start at 0xc6, are showing as starting at 0xc3, which is frame
setup code.

With the compiler patch, the prologue record is associated to the first
instruction that is not part of the frame setup code.

void
caller_trivial_1 ()
{
  c0:    55                       push   %ebp
  c1:    89 e5                    mov    %esp,%ebp
  c3:    83 ec 08                 sub    $0x8,%esp
    inline_value += 1;  // At first increment in caller_trivial_1.
  c6:    a1 00 00 00 00           mov    0x0,%eax
  cb:    83 c0 01                 add    $0x1,%eax
  ce:    a3 00 00 00 00           mov    %eax,0x0
    caller_trivial_2(); // In caller_trivial_1.
  d3:    e8 18 00 00 00           call   f0 <_Z16caller_trivial_2v>
    inline_value += 1;
  d8:    a1 00 00 00 00           mov    0x0,%eax
  dd:    83 c0 01                 add    $0x1,%eax
  e0:    a3 00 00 00 00           mov    %eax,0x0
}
  e5:    83 c4 08                 add    $0x8,%esp
  e8:    5d                       pop    %ebp
  e9:    c3                       ret   
  ea:    66 0f 1f 44 00 00        nopw   0x0(%eax,%eax,1)

Now the instructions associated with 'inline_value += 1;' are correctly
identified and are the same in 0xc6 and 0xd8.

Diff Detail

Event Timeline

tberghammer edited edge metadata.Oct 25 2017, 2:21 PM

Hi Carlos,

Sorry for not responding to your related e-mails lately. I am still not convinced that tis is the right way forward as you are changing the expected behavior from LLDB side to make it work with the new debug info emitted by clang but I think the current behavior of LLDB is correct in this context. Both your reasoning and your compiler fix makes perfect sense for the case you mentioned in the commit message (I assume you missed a "inline_value += 1;" line from the C code snippet based on the assembly) but I am still concerned by the behavior in the original case with the following source code:

void caller_trivial_1() {
    caller_trivial_2(); // In caller_trivial_1.
    inline_value += 1; 
}

I think the following debug info is emitted in 32bit mode after your change:

  • debug_info: caller_trivial_1 starts at 0x4000
  • debug_line: 0x4000 belongs to line X (declaration of caller_trivial_1) and it is prologue
  • debug_info: inlined caller_trivial_2 starts at 0x4010
  • debug_line: 0x4020 belongs to line Y (first line of function caller_trivial_1) and it is not prologue
  • debug_info: inlined caller_trivial_2 ends at 0x4080

When we step into caller_trivial_1 lldb will step to the first non-prologue line entry after the start of f what will be at 0x4020 and when we stop there that address is within g so lldb displays that we are stopped in caller_trivial_2.

I think the correct debug info would be the following:

  • debug_info: caller_trivial_1 starts at 0x4000
  • debug_line: 0x4000 belongs to line X (declaration of caller_trivial_1) and it is prologue
  • debug_line: 0x4010 belongs to line Y (first line of function caller_trivial_1) and it is not prologue
  • debug_info: inlined caller_trivial_2 starts at 0x4010
  • debug_line: 0x4020 belongs to line Y (first line of function caller_trivial_2) and it is not prologue
  • debug_info: inlined caller_trivial_2 ends at 0x4080

In case of non-optimized build (it is true for the test) I would expect from a compiler that no line entry pointing to a line inside caller_trivial_1 will have an address what is inside the address range of any other function inlined into caller_trivial_2. I think this held before your change but not after. Can you and others comment on this last expectation? If we agree that this expectation should hold then I think changing the test is the wrong and instead we should either change the line entry or the inlined function range writing code to produce debug info satisfying it.

Hi Carlos,

Sorry for not responding to your related e-mails lately.

Hi Tamas,

There is no need for apologies. You have help me a lot in setting LLDB and be able to run the test suite. (Windows and Linux).

You are correct; there is a missing line in the referenced test case. It should look like this:

void
caller_trivial_1 ()
{
    inline_value += 1;  // At first increment in caller_trivial_1.
    caller_trivial_2(); // In caller_trivial_1.
    inline_value += 1; 
}

Thanks.,
Carlos

Hi Tamas,

What I have done is to use the original code (original.cpp), modified code
(calling.cpp), created ELFs for both test cases before and after the compiler
change and debug them using LLDB, in order to show the issue while debugging
at instruction level.

I have used the -m32 option while compiling, in 'main' step into the second
call to 'caller_trivial_1' and then disassembly the function, to show the
location of the PC.

  1. Original test case before the compiler change:
(lldb) s
Process 9816 stopped
* thread #1, name = 'original_before', stop reason = step in
    frame #0: 0x08048843 original_before.out`caller_trivial_1() at original.cpp:71
   68  	void
   69  	caller_trivial_1 ()
   70  	{
-> 71  	    caller_trivial_2(); // In caller_trivial_1.
   72  	    inline_value += 1;
   73  	}
   74  	
(lldb) di -f
original_before.out`caller_trivial_1:
    0x8048840 <+0>:  pushl  %ebp
    0x8048841 <+1>:  movl   %esp, %ebp
->  0x8048843 <+3>:  subl   $0x8, %esp
    0x8048846 <+6>:  calll  0x8048860                 ; caller_trivial_2 at original.cpp:77
    0x804884b <+11>: movl   0x804a03c, %eax
    0x8048850 <+16>: addl   $0x1, %eax
    0x8048853 <+19>: movl   %eax, 0x804a03c
    0x8048858 <+24>: addl   $0x8, %esp
    0x804885b <+27>: popl   %ebp
    0x804885c <+28>: retl   
(lldb)

The disassembly shows the PC (0x8048843) pointing to the instructions that
are part of the frame setup code.

  1. Original test case after the compiler change:
(lldb) s
Process 9876 stopped
* thread #1, name = 'original_after.', stop reason = step in
    frame #0: 0x08048846 original_after.out`caller_trivial_1() at original.cpp:71
   68  	void
   69  	caller_trivial_1 ()
   70  	{
-> 71  	    caller_trivial_2(); // In caller_trivial_1.
   72  	    inline_value += 1;
   73  	}
   74  	
(lldb) di -f
original_after.out`caller_trivial_1:
    0x8048840 <+0>:  pushl  %ebp
    0x8048841 <+1>:  movl   %esp, %ebp
    0x8048843 <+3>:  subl   $0x8, %esp
->  0x8048846 <+6>:  calll  0x8048860                 ; caller_trivial_2 at original.cpp:77
    0x804884b <+11>: movl   0x804a03c, %eax
    0x8048850 <+16>: addl   $0x1, %eax
    0x8048853 <+19>: movl   %eax, 0x804a03c
    0x8048858 <+24>: addl   $0x8, %esp
    0x804885b <+27>: popl   %ebp
    0x804885c <+28>: retl   
(lldb)

The disassembly shows the PC (0x8048846) pointing to the instructions that
corresponds to the 'caller_trivial_2()' statement, which should be the correct
location and matches the source level and instruction level debugging.

  1. Modified test case before the compiler change:
(lldb) s
Process 10390 stopped
* thread #1, name = 'calling_before.', stop reason = step in
    frame #0: 0x08048843 calling_before.out`caller_trivial_1() at calling.cpp:71
   68  	void
   69  	caller_trivial_1 ()
   70  	{
-> 71  	    inline_value += 1;  // At first increment in caller_trivial_1.
   72  	    caller_trivial_2(); // In caller_trivial_1.
   73  	    inline_value += 1; 
   74  	}
   75
(lldb) di -f
calling_before.out`caller_trivial_1:
    0x8048840 <+0>:  pushl  %ebp
    0x8048841 <+1>:  movl   %esp, %ebp
->  0x8048843 <+3>:  subl   $0x8, %esp
    0x8048846 <+6>:  movl   0x804b03c, %eax
    0x804884b <+11>: addl   $0x1, %eax
    0x804884e <+14>: movl   %eax, 0x804b03c
    0x8048853 <+19>: calll  0x8048870                 ; caller_trivial_2 at calling.cpp:78
    0x8048858 <+24>: movl   0x804b03c, %eax
    0x804885d <+29>: addl   $0x1, %eax
    0x8048860 <+32>: movl   %eax, 0x804b03c
    0x8048865 <+37>: addl   $0x8, %esp
    0x8048868 <+40>: popl   %ebp
    0x8048869 <+41>: retl   
(lldb)

The disassembly shows the PC (0x8048843) pointing to the instructions that
are part of the frame setup code.

  1. Modified test case after the compiler change:
(lldb) s
Process 10496 stopped
* thread #1, name = 'calling_after.o', stop reason = step in
    frame #0: 0x08048846 calling_after.out`caller_trivial_1() at calling.cpp:71
   68  	void
   69  	caller_trivial_1 ()
   70  	{
-> 71  	    inline_value += 1;  // At first increment in caller_trivial_1.
   72  	    caller_trivial_2(); // In caller_trivial_1.
   73  	    inline_value += 1; 
   74  	}
   75
(lldb) di -f
calling_after.out`caller_trivial_1:
    0x8048840 <+0>:  pushl  %ebp
    0x8048841 <+1>:  movl   %esp, %ebp
    0x8048843 <+3>:  subl   $0x8, %esp
->  0x8048846 <+6>:  movl   0x804b03c, %eax
    0x804884b <+11>: addl   $0x1, %eax
    0x804884e <+14>: movl   %eax, 0x804b03c
    0x8048853 <+19>: calll  0x8048870                 ; caller_trivial_2 at calling.cpp:78
    0x8048858 <+24>: movl   0x804b03c, %eax
    0x804885d <+29>: addl   $0x1, %eax
    0x8048860 <+32>: movl   %eax, 0x804b03c
    0x8048865 <+37>: addl   $0x8, %esp
    0x8048868 <+40>: popl   %ebp
    0x8048869 <+41>: retl   
(lldb)

The disassembly shows the PC (0x8048846) pointing to the instructions that
corresponds to the 'inline_value += 1', statement, which should be the correct
location and matches the source level and instruction level debugging.

For both cases (2) and (4), the test case behavior is the same, as LLDB
stops at the instructions that mark the beginning of the function, as indicated
by the prologue_end line records.

I hope this gives more clarification to the intended changes (compiler and
LLDB test cases).

I have some issues running the LLDB Test Suite in 32-bit and 64-bit mode.

From the LLDB documentation:

https://lldb.llvm.org/test.html

It is possible to customize the architecture of the test binaries and compiler used by appending -A and -C options respectively to the CMake variable LLDB_TEST_USER_ARGS. For example, to test LLDB against 32-bit binaries built with a custom version of clang, do:

> cmake -DLLDB_TEST_USER_ARGS="-A i386 -C /path/to/custom/clang" -G Ninja
> ninja check-lldb

Doing

> cmake -DLLDB_TEST_USER_ARGS="-A i386" -G Ninja
> ninja check-lldb

or

> cmake -DLLDB_TEST_USER_ARGS="-A x86_64" -G Ninja
> ninja check-lldb

The LLDB Test Suite generates about 1538 errors related to missing cmake targets.

But if I used the CMake variable LLDB_TEST_ARCH as

> cmake -DLLDB_TEST_ARCH="-A i386" -G Ninja
> ninja check-lldb

or

> cmake -DLLDB_TEST_ARCH="-A x86_64" -G Ninja
> ninja check-lldb

The LLDB Test Suite pass.

My questions:

  • Am I misunderstanding the use of those CMake variables?
  • Does the LLDB Test Suite incorrectly handling those variables?

Thanks

A new patch have been submitted for review, that fix the general issue and preserve the original test cases.

https://reviews.llvm.org/D41762