diff options
author | Andrew Burgess <andrew.burgess@embecosm.com> | 2019-11-06 10:17:05 +0000 |
---|---|---|
committer | Andrew Burgess <andrew.burgess@embecosm.com> | 2020-01-24 23:43:16 +0000 |
commit | 3d92a3e313a3dfd531d5e32bbd703a8a5b03293b (patch) | |
tree | 85048807bfc58811dadd8194a718422f1337614f /gdb/buildsym.c | |
parent | 94a72be7086fa1870eca83d4d6f55cadf48f66b2 (diff) | |
download | gdb-3d92a3e313a3dfd531d5e32bbd703a8a5b03293b.zip gdb-3d92a3e313a3dfd531d5e32bbd703a8a5b03293b.tar.gz gdb-3d92a3e313a3dfd531d5e32bbd703a8a5b03293b.tar.bz2 |
gdb: Don't reorder line table entries too much when sorting.
Don't reorder line table entries for the same address when sorting the
line table, maintain the compiler given line order. Usually this will
reflect the order in which lines are conceptually encountered at a
given address.
Consider this example:
/* 1 */ volatile int global_var;
/* 2 */ int __attribute__ ((noinline))
/* 3 */ bar ()
/* 4 */ {
/* 5 */ return global_var;
/* 6 */ }
/* 7 */ static inline int __attribute__ ((always_inline))
/* 8 */ foo ()
/* 9 */ {
/* 10 */ return bar ();
/* 11 */ }
/* 12 */ int
/* 13 */ main ()
/* 14 */ {
/* 15 */ global_var = 0;
/* 16 */ return foo ();
/* 17 */ }
GCC 10 currently generates a line table like this (as shown by
objdump):
CU: ./test.c:
File name Line number Starting address
test.c 4 0x4004b0
test.c 5 0x4004b0
test.c 6 0x4004b6
test.c 6 0x4004b7
test.c 14 0x4003b0
test.c 15 0x4003b0
test.c 16 0x4003ba
test.c 10 0x4003ba
test.c 10 0x4003c1
The interesting entries are those for lines 16 and 10 at address
0x4003ba, these represent the call to foo and the inlined body of
foo.
With the current line table sorting GDB builds the line table like
this (as shown by 'maintenance info line-table'):
INDEX LINE ADDRESS
0 14 0x00000000004003b0
1 15 0x00000000004003b0
2 10 0x00000000004003ba
3 16 0x00000000004003ba
4 END 0x00000000004003c1
5 4 0x00000000004004b0
6 5 0x00000000004004b0
7 END 0x00000000004004b7
Notice that entries 2 and 3 for lines 10 and 16 are now in a different
order to the line table as given by the compiler. With this patch
applied the order is now:
INDEX LINE ADDRESS
0 14 0x00000000004003b0
1 15 0x00000000004003b0
2 16 0x00000000004003ba
3 10 0x00000000004003ba
4 END 0x00000000004003c1
5 4 0x00000000004004b0
6 5 0x00000000004004b0
7 END 0x00000000004004b7
Notice that entries 2 and 3 are now in their original order again.
The consequence of the incorrect ordering is that when stepping
through inlined functions GDB will display the wrong line for the
inner most frame. Here's a GDB session before this patch is applied:
Starting program: /home/andrew/tmp/inline/test
Temporary breakpoint 1, main () at test.c:15
15 /* 15 */ global_var = 0;
(gdb) step
16 /* 16 */ return foo ();
(gdb) step
foo () at test.c:16
16 /* 16 */ return foo ();
(gdb) step
bar () at test.c:5
5 /* 5 */ return global_var;
The step from line 15 to 16 was fine, but the next step should have
taken us to line 10, instead we are left at line 16. The final step
to line 5 is as expected.
With this patch applied the session goes better:
Starting program: /home/andrew/tmp/inline/test
Temporary breakpoint 1, main () at test.c:15
15 /* 15 */ global_var = 0;
(gdb) step
16 /* 16 */ return foo ();
(gdb) step
foo () at test.c:10
10 /* 10 */ return bar ();
(gdb) step
bar () at test.c:5
5 /* 5 */ return global_var;
We now visit the lines as 15, 16, 10, 5 as we would like.
The reason for this issue is that the inline frame unwinder is
detecting that foo is inlined in main. When we stop at the shared
address 0x4003ba the inline frame unwinder first shows us the outer
frame, this information is extracted from the DWARF's
DW_TAG_inlined_subroutine entries and passed via GDB's block data.
When we step again the inlined frame unwinder moves us up the call
stack to the inner most frame at which point the frame is displayed as
normal, with the location for the address being looked up in the line
table.
As GDB uses the last line table entry for an address as "the" line to
report for that address it is critical that GDB maintain the order of
the line table entries. In the first case, by reordering the line
table we report the wrong location.
I had to make a small adjustment in find_pc_sect_line in order to
correctly find the previous line in the line table. In some line
tables I was seeing an actual line entry and an end of sequence marker
at the same address, before this commit these would reorder to move
the end of sequence marker before the line entry (end of sequence has
line number 0). Now the end of sequence marker remains in its correct
location, and in order to find a previous line we should step backward
over any end of sequence markers.
As an example, the binary:
gdb/testsuite/outputs/gdb.dwarf2/dw2-ranges-func/dw2-ranges-func-lo-cold
Has this line table before the patch:
INDEX LINE ADDRESS
0 48 0x0000000000400487
1 END 0x000000000040048e
2 52 0x000000000040048e
3 54 0x0000000000400492
4 56 0x0000000000400497
5 END 0x000000000040049a
6 62 0x000000000040049a
7 END 0x00000000004004a1
8 66 0x00000000004004a1
9 68 0x00000000004004a5
10 70 0x00000000004004aa
11 72 0x00000000004004b9
12 END 0x00000000004004bc
13 76 0x00000000004004bc
14 78 0x00000000004004c0
15 80 0x00000000004004c5
16 END 0x00000000004004cc
And after this patch:
INDEX LINE ADDRESS
0 48 0x0000000000400487
1 52 0x000000000040048e
2 END 0x000000000040048e
3 54 0x0000000000400492
4 56 0x0000000000400497
5 END 0x000000000040049a
6 62 0x000000000040049a
7 66 0x00000000004004a1
8 END 0x00000000004004a1
9 68 0x00000000004004a5
10 70 0x00000000004004aa
11 72 0x00000000004004b9
12 END 0x00000000004004bc
13 76 0x00000000004004bc
14 78 0x00000000004004c0
15 80 0x00000000004004c5
16 END 0x00000000004004cc
When calling find_pc_sect_line with the address 0x000000000040048e, in
both cases we find entry #3, we then try to find the previous entry,
which originally found this entry '2 52 0x000000000040048e',
after the patch it finds '2 END 0x000000000040048e', which
cases the lookup to fail.
By skipping the END marker after this patch we get back to the correct
entry, which is now #1: '1 52 0x000000000040048e', and
everything works again.
gdb/ChangeLog:
* buildsym.c (lte_is_less_than): Delete.
(buildsym_compunit::end_symtab_with_blockvector): Create local
lambda function to sort line table entries, and use
std::stable_sort instead of std::sort.
* symtab.c (find_pc_sect_line): Skip backward over end of sequence
markers when looking for a previous line.
gdb/testsuite/ChangeLog:
* gdb.dwarf2/dw2-inline-stepping.c: New file.
* gdb.dwarf2/dw2-inline-stepping.exp: New file.
Change-Id: Ia0309494be4cfd9dcc554f30209477f5f040b21b
Diffstat (limited to 'gdb/buildsym.c')
-rw-r--r-- | gdb/buildsym.c | 40 |
1 files changed, 16 insertions, 24 deletions
diff --git a/gdb/buildsym.c b/gdb/buildsym.c index 5b2a92c..4965b55 100644 --- a/gdb/buildsym.c +++ b/gdb/buildsym.c @@ -719,23 +719,6 @@ buildsym_compunit::record_line (struct subfile *subfile, int line, e->pc = pc; } -/* Needed in order to sort line tables from IBM xcoff files. Sigh! */ - -static bool -lte_is_less_than (const linetable_entry &ln1, const linetable_entry &ln2) -{ - /* Note: this code does not assume that CORE_ADDRs can fit in ints. - Please keep it that way. */ - if (ln1.pc < ln2.pc) - return true; - - if (ln1.pc > ln2.pc) - return false; - - /* If pc equal, sort by line. I'm not sure whether this is optimum - behavior (see comment at struct linetable in symtab.h). */ - return ln1.line < ln2.line; -} /* Subroutine of end_symtab to simplify it. Look for a subfile that matches the main source file's basename. If there is only one, and @@ -953,14 +936,23 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block, linetablesize = sizeof (struct linetable) + subfile->line_vector->nitems * sizeof (struct linetable_entry); - /* Like the pending blocks, the line table may be - scrambled in reordered executables. Sort it if - OBJF_REORDERED is true. */ + const auto lte_is_less_than + = [] (const linetable_entry &ln1, + const linetable_entry &ln2) -> bool + { + return (ln1.pc < ln2.pc); + }; + + /* Like the pending blocks, the line table may be scrambled in + reordered executables. Sort it if OBJF_REORDERED is true. It + is important to preserve the order of lines at the same + address, as this maintains the inline function caller/callee + relationships, this is why std::stable_sort is used. */ if (m_objfile->flags & OBJF_REORDERED) - std::sort (subfile->line_vector->item, - subfile->line_vector->item - + subfile->line_vector->nitems, - lte_is_less_than); + std::stable_sort (subfile->line_vector->item, + subfile->line_vector->item + + subfile->line_vector->nitems, + lte_is_less_than); } /* Allocate a symbol table if necessary. */ |