aboutsummaryrefslogtreecommitdiff
path: root/gdb/inline-frame.c
diff options
context:
space:
mode:
authorAndrew Burgess <andrew.burgess@embecosm.com>2019-11-11 22:41:13 +0000
committerAndrew Burgess <andrew.burgess@embecosm.com>2020-01-24 23:44:16 +0000
commit7ffa82e1222fbe038dedf3531c655969fdda5c16 (patch)
tree6b6ec5b3b54bf2547bc6e58df726847ce9f6fe6c /gdb/inline-frame.c
parent3d92a3e313a3dfd531d5e32bbd703a8a5b03293b (diff)
downloadbinutils-7ffa82e1222fbe038dedf3531c655969fdda5c16.zip
binutils-7ffa82e1222fbe038dedf3531c655969fdda5c16.tar.gz
binutils-7ffa82e1222fbe038dedf3531c655969fdda5c16.tar.bz2
gdb: Better frame tracking for inline frames
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
Diffstat (limited to 'gdb/inline-frame.c')
-rw-r--r--gdb/inline-frame.c30
1 files changed, 19 insertions, 11 deletions
diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
index 0ee1de3..c650195 100644
--- a/gdb/inline-frame.c
+++ b/gdb/inline-frame.c
@@ -37,9 +37,9 @@
struct inline_state
{
inline_state (thread_info *thread_, int skipped_frames_, CORE_ADDR saved_pc_,
- symbol *skipped_symbol_)
+ std::vector<symbol *> &&skipped_symbols_)
: thread (thread_), skipped_frames (skipped_frames_), saved_pc (saved_pc_),
- skipped_symbol (skipped_symbol_)
+ skipped_symbols (std::move (skipped_symbols_))
{}
/* The thread this data relates to. It should be a currently
@@ -56,10 +56,10 @@ struct inline_state
any skipped frames. */
CORE_ADDR saved_pc;
- /* Only valid if SKIPPED_FRAMES is non-zero. This is the symbol
- of the outermost skipped inline function. It's used to find the
- call site of the current frame. */
- struct symbol *skipped_symbol;
+ /* Only valid if SKIPPED_FRAMES is non-zero. This is the list of all
+ function symbols that have been skipped, from inner most to outer
+ most. It is used to find the call site of the current frame. */
+ std::vector<struct symbol *> skipped_symbols;
};
static std::vector<inline_state> inline_states;
@@ -341,7 +341,7 @@ void
skip_inline_frames (thread_info *thread, bpstat stop_chain)
{
const struct block *frame_block, *cur_block;
- struct symbol *last_sym = NULL;
+ std::vector<struct symbol *> skipped_syms;
int skip_count = 0;
/* This function is called right after reinitializing the frame
@@ -369,7 +369,7 @@ skip_inline_frames (thread_info *thread, bpstat stop_chain)
break;
skip_count++;
- last_sym = BLOCK_FUNCTION (cur_block);
+ skipped_syms.push_back (BLOCK_FUNCTION (cur_block));
}
else
break;
@@ -382,7 +382,8 @@ skip_inline_frames (thread_info *thread, bpstat stop_chain)
}
gdb_assert (find_inline_frame_state (thread) == NULL);
- inline_states.emplace_back (thread, skip_count, this_pc, last_sym);
+ inline_states.emplace_back (thread, skip_count, this_pc,
+ std::move (skipped_syms));
if (skip_count != 0)
reinit_frame_cache ();
@@ -421,9 +422,16 @@ struct symbol *
inline_skipped_symbol (thread_info *thread)
{
inline_state *state = find_inline_frame_state (thread);
-
gdb_assert (state != NULL);
- return state->skipped_symbol;
+
+ /* This should only be called when we are skipping at least one frame,
+ hence SKIPPED_FRAMES will be greater than zero when we get here.
+ We initialise SKIPPED_FRAMES at the same time as we build
+ SKIPPED_SYMBOLS, hence it should be true that SKIPPED_FRAMES never
+ indexes outside of the SKIPPED_SYMBOLS vector. */
+ gdb_assert (state->skipped_frames > 0);
+ gdb_assert (state->skipped_frames <= state->skipped_symbols.size ());
+ return state->skipped_symbols[state->skipped_frames - 1];
}
/* Return the number of functions inlined into THIS_FRAME. Some of