mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-27 04:52:05 +08:00
7ffa82e122
This commit improves GDB's handling of inline functions when there are more than one inline function in a stack, so for example if we have a stack like: main -> aaa -> bbb -> ccc -> ddd And aaa, bbb, and ccc are all inline within main GDB should (when given sufficient debug information) be able to step from main through aaa, bbb, and ccc. Unfortunately, this currently doesn't work, here's an example session: (gdb) start Temporary breakpoint 1 at 0x4003b0: file test.c, line 38. Starting program: /project/gdb/tests/inline/test Temporary breakpoint 1, main () at test.c:38 38 global_var = 0; (gdb) step 39 return aaa () + 1; (gdb) step aaa () at test.c:39 39 return aaa () + 1; (gdb) step bbb () at test.c:39 39 return aaa () + 1; (gdb) step ccc () at test.c:39 39 return aaa () + 1; (gdb) step ddd () at test.c:32 32 return global_var; (gdb) bt #0 ddd () at test.c:32 #1 0x00000000004003c1 in ccc () at test.c:39 #2 bbb () at test.c:26 #3 aaa () at test.c:14 #4 main () at test.c:39 Notice that once we get to line 39 in main, GDB keeps reporting line 39 in main as the location despite understanding that the inferior is stepping through the nested inline functions with each use of step. The problem is that as soon as the inferior stops we call skip_inline_frames (from inline-frame.c) which calculates the inferiors current state in relation to inline functions - it figures out if we're in an inline function, and if we are counts how many inline frames there are at the current location. So, in our example above, when we step from line 38 in main to line 39 we stop at a location that is simultaneously in all of main, aaa, bbb, and ccc. The block structure reflects the order in which the functions would be called, with ccc being the most inner block and main being the most outer block. When we stop GDB naturally finds the block for ccc, however within skip_inline_frames we spot that bbb, aaa, and main are super-blocks of the current location and that each layer represents an inline function. The skip_inline_frames then records the depth of inline functions (3 in this case for aaa, bbb, and ccc) and also the symbol of the outermost inline function (in this case 'aaa' as main isn't an inline function, it just has things inline within it). Now GDB understands the stack to be main -> aaa -> bbb -> ccc, however, the state initialised in skip_inline_frames starts off indicating that we should hide 3 frames from the user, so we report that we're in main at line 39. The location of main, line 39 is derived by asking the inline function state for the last symbol in the stack (aaa in this case), and then asking for it's location - the location of an inlined function symbol is its call site, so main, line 39 in this case. If the user then asks GDB to step we don't actually move the inferior at all, instead we spot that we are in an inline function stack, lookup the inline state data, and reduce the skip depth by 1. We then report to the user that GDB has stopped. GDB now understands that we are in 'aaa'. In order to get the precise location we again ask GDB for the last symbol from the inline data structure, and we are again told 'aaa', we then get the location from 'aaa', and report that we are in main, line 39. Hopefully it's clear what the mistake here is, once we've reduced the inline skip depth we should not be using 'aaa' to compute the precise location, instead we should be using 'bbb'. That is what this patch does. Now, when we call skip_inline_frames instead of just recording the last skipped symbol we now record all symbols in the inline frame stack. When we ask GDB for the last skipped symbol we return a symbol based on how many frames we are skipping, not just the last know symbol. With this fix in place, the same session as above now looks much better: (gdb) start Temporary breakpoint 1 at 0x4003b0: file test.c, line 38. Starting program: /project/gdb/tests/inline/test Temporary breakpoint 1, main () at test.c:38 38 global_var = 0; (gdb) s 39 return aaa () + 1; (gdb) s aaa () at test.c:14 14 return bbb () + 1; (gdb) s bbb () at test.c:26 26 return ccc () + 1; (gdb) s ccc () at test.c:20 20 return ddd () + 1; (gdb) s ddd () at test.c:32 32 return global_var; (gdb) bt #0 ddd () at test.c:32 #1 0x00000000004003c1 in ccc () at test.c:20 #2 bbb () at test.c:26 #3 aaa () at test.c:14 #4 main () at test.c:39 gdb/ChangeLog: * frame.c (find_frame_sal): Move call to get_next_frame into more inner scope. * inline-frame.c (inilne_state) <inline_state>: Update argument types. (inilne_state) <skipped_symbol>: Rename to... (inilne_state) <skipped_symbols>: ...this, and change to a vector. (skip_inline_frames): Build vector of skipped symbols and use this to reate the inline_state. (inline_skipped_symbol): Add a comment and some assertions, fetch skipped symbol from the list. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-inline-many-frames.c: New file. * gdb.dwarf2/dw2-inline-many-frames.exp: New file. Change-Id: I99def5ffb44eb9e58cda4b449bf3d91ab0386c62
159 lines
4.4 KiB
C
159 lines
4.4 KiB
C
/* Copyright 2019-2020 Free Software Foundation, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
/* This test sets up a call stack that looks like this:
|
|
|
|
#11 #10 #9 #8 #7 #6 #5 #4 #3 #2 #1 #0
|
|
main -> aaa -> bbb -> ccc -> ddd -> eee -> fff -> ggg -> hhh -> iii -> jjj -> kkk
|
|
\_______________________/ \________/ \______________________/ \________/
|
|
Inline sequence #1 Normal Inline sequence #2 Normal
|
|
|
|
We use the 'start' command to move into main, after that we 'step'
|
|
through each function until we are in kkk. We then use the 'up' command
|
|
to look back at each from to main.
|
|
|
|
The test checks that we can handle and step through sequences of more
|
|
than one inline frame (so 'main .... ccc', and 'fff .... iii'), and also
|
|
that we can move around in a stack that contains more than one disjoint
|
|
sequence of inline frames.
|
|
|
|
The order of the functions in this file is deliberately mixed up so that
|
|
the line numbers are not "all ascending" or "all descending" in the line
|
|
table. */
|
|
|
|
#define INLINE_FUNCTION __attribute__ ((always_inline)) static inline
|
|
#define NON_INLINE_FUNCTION __attribute__ ((noinline))
|
|
|
|
volatile int global_var = 0;
|
|
|
|
INLINE_FUNCTION int aaa ();
|
|
INLINE_FUNCTION int bbb ();
|
|
INLINE_FUNCTION int ccc ();
|
|
|
|
NON_INLINE_FUNCTION int ddd ();
|
|
NON_INLINE_FUNCTION int eee ();
|
|
NON_INLINE_FUNCTION int fff ();
|
|
|
|
INLINE_FUNCTION int ggg ();
|
|
INLINE_FUNCTION int hhh ();
|
|
INLINE_FUNCTION int iii ();
|
|
|
|
NON_INLINE_FUNCTION int jjj ();
|
|
NON_INLINE_FUNCTION int kkk ();
|
|
|
|
INLINE_FUNCTION int
|
|
aaa ()
|
|
{ /* aaa prologue */
|
|
asm ("aaa_label: .globl aaa_label");
|
|
return bbb () + 1; /* aaa return */
|
|
} /* aaa end */
|
|
|
|
NON_INLINE_FUNCTION int
|
|
jjj ()
|
|
{ /* jjj prologue */
|
|
int ans;
|
|
asm ("jjj_label: .globl jjj_label");
|
|
ans = kkk () + 1; /* jjj return */
|
|
asm ("jjj_label2: .globl jjj_label2");
|
|
return ans;
|
|
} /* jjj end */
|
|
|
|
INLINE_FUNCTION int
|
|
ggg ()
|
|
{ /* ggg prologue */
|
|
asm ("ggg_label: .globl ggg_label");
|
|
return hhh () + 1; /* ggg return */
|
|
} /* ggg end */
|
|
|
|
INLINE_FUNCTION int
|
|
ccc ()
|
|
{ /* ccc prologue */
|
|
asm ("ccc_label: .globl ccc_label");
|
|
return ddd () + 1; /* ccc return */
|
|
} /* ccc end */
|
|
|
|
NON_INLINE_FUNCTION int
|
|
fff ()
|
|
{ /* fff prologue */
|
|
int ans;
|
|
asm ("fff_label: .globl fff_label");
|
|
ans = ggg () + 1; /* fff return */
|
|
asm ("fff_label2: .globl fff_label2");
|
|
return ans;
|
|
} /* fff end */
|
|
|
|
NON_INLINE_FUNCTION int
|
|
kkk ()
|
|
{ /* kkk prologue */
|
|
asm ("kkk_label: .globl kkk_label");
|
|
return global_var; /* kkk return */
|
|
} /* kkk end */
|
|
|
|
INLINE_FUNCTION int
|
|
bbb ()
|
|
{ /* bbb prologue */
|
|
asm ("bbb_label: .globl bbb_label");
|
|
return ccc () + 1; /* bbb return */
|
|
} /* bbb end */
|
|
|
|
INLINE_FUNCTION int
|
|
hhh ()
|
|
{ /* hhh prologue */
|
|
asm ("hh_label: .globl hhh_label");
|
|
return iii () + 1; /* hhh return */
|
|
} /* hhh end */
|
|
|
|
int
|
|
main ()
|
|
{ /* main prologue */
|
|
int ans;
|
|
asm ("main_label: .globl main_label");
|
|
global_var = 0; /* main set global_var */
|
|
asm ("main_label2: .globl main_label2");
|
|
ans = aaa () + 1; /* main call aaa */
|
|
asm ("main_label3: .globl main_label3");
|
|
return ans;
|
|
} /* main end */
|
|
|
|
NON_INLINE_FUNCTION int
|
|
ddd ()
|
|
{ /* ddd prologue */
|
|
int ans;
|
|
asm ("ddd_label: .globl ddd_label");
|
|
ans = eee () + 1; /* ddd return */
|
|
asm ("ddd_label2: .globl ddd_label2");
|
|
return ans;
|
|
} /* ddd end */
|
|
|
|
INLINE_FUNCTION int
|
|
iii ()
|
|
{ /* iii prologue */
|
|
int ans;
|
|
asm ("iii_label: .globl iii_label");
|
|
ans = jjj () + 1; /* iii return */
|
|
asm ("iii_label2: .globl iii_label2");
|
|
return ans;
|
|
} /* iii end */
|
|
|
|
NON_INLINE_FUNCTION int
|
|
eee ()
|
|
{ /* eee prologue */
|
|
int ans;
|
|
asm ("eee_label: .globl eee_label");
|
|
ans = fff () + 1; /* eee return */
|
|
asm ("eee_label2: .globl eee_label2");
|
|
return ans;
|
|
} /* eee end */
|