Skip to content

Commit ddfe970

Browse files
committed
Don't elide all inlined frames
This patch essentially causes GDB to treat inlined frames like "normal" frames from the user's perspective. This means, for example, that when a user sets a breakpoint in an inlined function, GDB will now actually stop "in" that function. Using the test case from breakpoints/17534, 3 static inline void NVIC_EnableIRQ(int IRQn) 4 { 5 volatile int y; 6 y = IRQn; 7 } 8 9 __attribute__( ( always_inline ) ) static inline void __WFI(void) 10 { 11 __asm volatile ("nop"); 12 } 13 14 int main(void) { 15 16 x= 42; 17 18 if (x) 19 NVIC_EnableIRQ(16); 20 else 21 NVIC_EnableIRQ(18); (gdb) b NVIC_EnableIRQ Breakpoint 1 at 0x4003e4: NVIC_EnableIRQ. (2 locations) (gdb) r Starting program: 17534 Breakpoint 1, main () at 17534.c:19 19 NVIC_EnableIRQ(16); Because skip_inline_frames currently skips every inlined frame, GDB "stops" in the caller. This patch adds a new parameter to skip_inline_frames that allows us to pass in a bpstat stop chain. The breakpoint locations on the stop chain can be used to determine if we've stopped inside an inline function (due to a user breakpoint). If we have, we do not elide the frame. With this patch, GDB now reports that the inferior has stopped inside the inlined function: (gdb) r Starting program: 17534 Breakpoint 1, NVIC_EnableIRQ (IRQn=16) at 17534.c:6 6 y = IRQn; Many thanks to Jan and Pedro for guidance on this. gdb/ChangeLog: * breakpoint.c (build_bpstat_chain): New function, moved from bpstat_stop_status. (bpstat_stop_status): Add optional parameter, `stop_chain'. If no stop chain is passed, call build_bpstat_chain to build it. * breakpoint.h (build_bpstat_chain): Declare. (bpstat_stop_status): Move documentation here from breakpoint.c. * infrun.c (handle_signal_stop): Before eliding inlined frames, build the stop chain and pass it to skip_inline_frames. Pass this stop chain to bpstat_stop_status. * inline-frame.c: Include breakpoint.h. (stopped_by_user_bp_inline_frame): New function. (skip_inline_frames): Add parameter `stop_chain'. Move documention to inline-frame.h. If non-NULL, use stopped_by_user_bp_inline_frame to determine whether the frame should be elided. * inline-frame.h (skip_inline_frames): Add parameter `stop_chain'. Add moved documentation and update for new parameter. gdb/testsuite/ChangeLog: * gdb.ada/bp_inlined_func.exp: Update inlined frame locations in expected breakpoint stop locations. * gdb.dwarf2/implptr.exp (implptr_test_baz): Use up/down to move to proper scope to test variable values. * gdb.opt/inline-break.c (inline_func1, not_inline_func1) (inline_func2, not_inline_func2, inline_func3, not_inline_func3): New functions. (main): Call not_inline_func3. * gdb.opt/inline-break.exp: Start inferior and set breakpoints at inline_func1, inline_func2, and inline_func3. Test that when each breakpoint is hit, GDB properly reports both the stop location and the backtrace. Repeat tests for temporary breakpoints.
1 parent b17992c commit ddfe970

File tree

11 files changed

+259
-69
lines changed

11 files changed

+259
-69
lines changed

gdb/ChangeLog

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
2018-05-17 Keith Seitz <keiths@redhat.com>
2+
3+
* breakpoint.c (build_bpstat_chain): New function, moved from
4+
bpstat_stop_status.
5+
(bpstat_stop_status): Add optional parameter, `stop_chain'.
6+
If no stop chain is passed, call build_bpstat_chain to build it.
7+
* breakpoint.h (build_bpstat_chain): Declare.
8+
(bpstat_stop_status): Move documentation here from breakpoint.c.
9+
* infrun.c (handle_signal_stop): Before eliding inlined frames,
10+
build the stop chain and pass it to skip_inline_frames.
11+
Pass this stop chain to bpstat_stop_status.
12+
* inline-frame.c: Include breakpoint.h.
13+
(stopped_by_user_bp_inline_frame): New function.
14+
(skip_inline_frames): Add parameter `stop_chain'.
15+
Move documention to inline-frame.h.
16+
If non-NULL, use stopped_by_user_bp_inline_frame to determine
17+
whether the frame should be elided.
18+
* inline-frame.h (skip_inline_frames): Add parameter `stop_chain'.
19+
Add moved documentation and update for new parameter.
20+
121
2018-05-17 Simon Marchi <simon.marchi@ericsson.com>
222

323
PR cli/14975

gdb/breakpoint.c

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5308,54 +5308,21 @@ need_moribund_for_location_type (struct bp_location *loc)
53085308
&& !target_supports_stopped_by_hw_breakpoint ()));
53095309
}
53105310

5311-
5312-
/* Get a bpstat associated with having just stopped at address
5313-
BP_ADDR in thread PTID.
5314-
5315-
Determine whether we stopped at a breakpoint, etc, or whether we
5316-
don't understand this stop. Result is a chain of bpstat's such
5317-
that:
5318-
5319-
if we don't understand the stop, the result is a null pointer.
5320-
5321-
if we understand why we stopped, the result is not null.
5322-
5323-
Each element of the chain refers to a particular breakpoint or
5324-
watchpoint at which we have stopped. (We may have stopped for
5325-
several reasons concurrently.)
5326-
5327-
Each element of the chain has valid next, breakpoint_at,
5328-
commands, FIXME??? fields. */
5311+
/* See breakpoint.h. */
53295312

53305313
bpstat
5331-
bpstat_stop_status (const address_space *aspace,
5332-
CORE_ADDR bp_addr, ptid_t ptid,
5314+
build_bpstat_chain (const address_space *aspace, CORE_ADDR bp_addr,
53335315
const struct target_waitstatus *ws)
53345316
{
5335-
struct breakpoint *b = NULL;
5336-
struct bp_location *bl;
5337-
struct bp_location *loc;
5338-
/* First item of allocated bpstat's. */
5317+
struct breakpoint *b;
53395318
bpstat bs_head = NULL, *bs_link = &bs_head;
5340-
/* Pointer to the last thing in the chain currently. */
5341-
bpstat bs;
5342-
int ix;
5343-
int need_remove_insert;
5344-
int removed_any;
5345-
5346-
/* First, build the bpstat chain with locations that explain a
5347-
target stop, while being careful to not set the target running,
5348-
as that may invalidate locations (in particular watchpoint
5349-
locations are recreated). Resuming will happen here with
5350-
breakpoint conditions or watchpoint expressions that include
5351-
inferior function calls. */
53525319

53535320
ALL_BREAKPOINTS (b)
53545321
{
53555322
if (!breakpoint_enabled (b))
53565323
continue;
53575324

5358-
for (bl = b->loc; bl != NULL; bl = bl->next)
5325+
for (bp_location *bl = b->loc; bl != NULL; bl = bl->next)
53595326
{
53605327
/* For hardware watchpoints, we look only at the first
53615328
location. The watchpoint_check function will work on the
@@ -5374,8 +5341,8 @@ bpstat_stop_status (const address_space *aspace,
53745341
/* Come here if it's a watchpoint, or if the break address
53755342
matches. */
53765343

5377-
bs = new bpstats (bl, &bs_link); /* Alloc a bpstat to
5378-
explain stop. */
5344+
bpstat bs = new bpstats (bl, &bs_link); /* Alloc a bpstat to
5345+
explain stop. */
53795346

53805347
/* Assume we stop. Should we find a watchpoint that is not
53815348
actually triggered, or if the condition of the breakpoint
@@ -5400,12 +5367,15 @@ bpstat_stop_status (const address_space *aspace,
54005367
if (!target_supports_stopped_by_sw_breakpoint ()
54015368
|| !target_supports_stopped_by_hw_breakpoint ())
54025369
{
5403-
for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
5370+
bp_location *loc;
5371+
5372+
for (int ix = 0;
5373+
VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
54045374
{
54055375
if (breakpoint_location_address_match (loc, aspace, bp_addr)
54065376
&& need_moribund_for_location_type (loc))
54075377
{
5408-
bs = new bpstats (loc, &bs_link);
5378+
bpstat bs = new bpstats (loc, &bs_link);
54095379
/* For hits of moribund locations, we should just proceed. */
54105380
bs->stop = 0;
54115381
bs->print = 0;
@@ -5414,6 +5384,33 @@ bpstat_stop_status (const address_space *aspace,
54145384
}
54155385
}
54165386

5387+
return bs_head;
5388+
}
5389+
5390+
/* See breakpoint.h. */
5391+
5392+
bpstat
5393+
bpstat_stop_status (const address_space *aspace,
5394+
CORE_ADDR bp_addr, ptid_t ptid,
5395+
const struct target_waitstatus *ws,
5396+
bpstat stop_chain)
5397+
{
5398+
struct breakpoint *b = NULL;
5399+
/* First item of allocated bpstat's. */
5400+
bpstat bs_head = stop_chain;
5401+
bpstat bs;
5402+
int need_remove_insert;
5403+
int removed_any;
5404+
5405+
/* First, build the bpstat chain with locations that explain a
5406+
target stop, while being careful to not set the target running,
5407+
as that may invalidate locations (in particular watchpoint
5408+
locations are recreated). Resuming will happen here with
5409+
breakpoint conditions or watchpoint expressions that include
5410+
inferior function calls. */
5411+
if (bs_head == NULL)
5412+
bs_head = build_bpstat_chain (aspace, bp_addr, ws);
5413+
54175414
/* A bit of special processing for shlib breakpoints. We need to
54185415
process solib loading here, so that the lists of loaded and
54195416
unloaded libraries are correct before we handle "catch load" and

gdb/breakpoint.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,9 +920,37 @@ extern void bpstat_clear (bpstat *);
920920
is part of the bpstat is copied as well. */
921921
extern bpstat bpstat_copy (bpstat);
922922

923+
/* Build the (raw) bpstat chain for the stop information given by ASPACE,
924+
BP_ADDR, and WS. Returns the head of the bpstat chain. */
925+
926+
extern bpstat build_bpstat_chain (const address_space *aspace,
927+
CORE_ADDR bp_addr,
928+
const struct target_waitstatus *ws);
929+
930+
/* Get a bpstat associated with having just stopped at address
931+
BP_ADDR in thread PTID. STOP_CHAIN may be supplied as a previously
932+
computed stop chain or NULL, in which case the stop chain will be
933+
computed using build_bpstat_chain.
934+
935+
Determine whether we stopped at a breakpoint, etc, or whether we
936+
don't understand this stop. Result is a chain of bpstat's such
937+
that:
938+
939+
if we don't understand the stop, the result is a null pointer.
940+
941+
if we understand why we stopped, the result is not null.
942+
943+
Each element of the chain refers to a particular breakpoint or
944+
watchpoint at which we have stopped. (We may have stopped for
945+
several reasons concurrently.)
946+
947+
Each element of the chain has valid next, breakpoint_at,
948+
commands, FIXME??? fields. */
949+
923950
extern bpstat bpstat_stop_status (const address_space *aspace,
924951
CORE_ADDR pc, ptid_t ptid,
925-
const struct target_waitstatus *ws);
952+
const struct target_waitstatus *ws,
953+
bpstat stop_chain = NULL);
926954

927955
/* This bpstat_what stuff tells wait_for_inferior what to do with a
928956
breakpoint (a challenging task).

gdb/infrun.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5862,6 +5862,7 @@ handle_signal_stop (struct execution_control_state *ecs)
58625862
ecs->event_thread->control.stop_step = 0;
58635863
stop_print_frame = 1;
58645864
stopped_by_random_signal = 0;
5865+
bpstat stop_chain = NULL;
58655866

58665867
/* Hide inlined functions starting here, unless we just performed stepi or
58675868
nexti. After stepi and nexti, always show the innermost frame (not any
@@ -5893,7 +5894,8 @@ handle_signal_stop (struct execution_control_state *ecs)
58935894
ecs->event_thread->prev_pc,
58945895
&ecs->ws)))
58955896
{
5896-
skip_inline_frames (ecs->ptid);
5897+
stop_chain = build_bpstat_chain (aspace, stop_pc, &ecs->ws);
5898+
skip_inline_frames (ecs->ptid, stop_chain);
58975899

58985900
/* Re-fetch current thread's frame in case that invalidated
58995901
the frame cache. */
@@ -5942,7 +5944,7 @@ handle_signal_stop (struct execution_control_state *ecs)
59425944
handles this event. */
59435945
ecs->event_thread->control.stop_bpstat
59445946
= bpstat_stop_status (get_current_regcache ()->aspace (),
5945-
stop_pc, ecs->ptid, &ecs->ws);
5947+
stop_pc, ecs->ptid, &ecs->ws, stop_chain);
59465948

59475949
/* Following in case break condition called a
59485950
function. */

gdb/inline-frame.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
along with this program. If not, see <http://www.gnu.org/licenses/>. */
1919

2020
#include "defs.h"
21+
#include "breakpoint.h"
2122
#include "inline-frame.h"
2223
#include "addrmap.h"
2324
#include "block.h"
@@ -284,12 +285,36 @@ block_starting_point_at (CORE_ADDR pc, const struct block *block)
284285
return 1;
285286
}
286287

287-
/* Skip all inlined functions whose call sites are at the current PC.
288-
Frames for the hidden functions will not appear in the backtrace until the
289-
user steps into them. */
288+
/* Loop over the stop chain and determine if execution stopped in an
289+
inlined frame because of a user breakpoint. THIS_PC is the current
290+
frame's PC. */
291+
292+
static bool
293+
stopped_by_user_bp_inline_frame (CORE_ADDR this_pc, bpstat stop_chain)
294+
{
295+
for (bpstat s = stop_chain; s != NULL; s = s->next)
296+
{
297+
struct breakpoint *bpt = s->breakpoint_at;
298+
299+
if (bpt != NULL && user_breakpoint_p (bpt))
300+
{
301+
bp_location *loc = s->bp_location_at;
302+
enum bp_loc_type t = loc->loc_type;
303+
304+
if (loc->address == this_pc
305+
&& (t == bp_loc_software_breakpoint
306+
|| t == bp_loc_hardware_breakpoint))
307+
return true;
308+
}
309+
}
310+
311+
return false;
312+
}
313+
314+
/* See inline-frame.h. */
290315

291316
void
292-
skip_inline_frames (ptid_t ptid)
317+
skip_inline_frames (ptid_t ptid, bpstat stop_chain)
293318
{
294319
const struct block *frame_block, *cur_block;
295320
struct symbol *last_sym = NULL;
@@ -313,8 +338,14 @@ skip_inline_frames (ptid_t ptid)
313338
if (BLOCK_START (cur_block) == this_pc
314339
|| block_starting_point_at (this_pc, cur_block))
315340
{
316-
skip_count++;
317-
last_sym = BLOCK_FUNCTION (cur_block);
341+
/* Do not skip the inlined frame if execution
342+
stopped in an inlined frame because of a user
343+
breakpoint. */
344+
if (!stopped_by_user_bp_inline_frame (this_pc, stop_chain))
345+
{
346+
skip_count++;
347+
last_sym = BLOCK_FUNCTION (cur_block);
348+
}
318349
}
319350
else
320351
break;

gdb/inline-frame.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,21 @@
2222

2323
struct frame_info;
2424
struct frame_unwind;
25+
struct bpstats;
2526

2627
/* The inline frame unwinder. */
2728

2829
extern const struct frame_unwind inline_frame_unwind;
2930

3031
/* Skip all inlined functions whose call sites are at the current PC.
31-
Frames for the hidden functions will not appear in the backtrace until the
32-
user steps into them. */
3332
34-
void skip_inline_frames (ptid_t ptid);
33+
If non-NULL, STOP_CHAIN is used to determine whether a stop was caused by
34+
a user breakpoint. In that case, do not skip that inlined frame. This
35+
allows the inlined frame to be treated as if it were non-inlined from the
36+
user's perspective. GDB will stop "in" the inlined frame instead of
37+
the caller. */
38+
39+
void skip_inline_frames (ptid_t ptid, struct bpstats *stop_chain);
3540

3641
/* Forget about any hidden inlined functions in PTID, which is new or
3742
about to be resumed. If PTID is minus_one_ptid, forget about all

gdb/testsuite/ChangeLog

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
2018-05-17 Keith Seitz <keiths@redhat.com>
2+
3+
* gdb.ada/bp_inlined_func.exp: Update inlined frame locations
4+
in expected breakpoint stop locations.
5+
* gdb.dwarf2/implptr.exp (implptr_test_baz): Use up/down to
6+
move to proper scope to test variable values.
7+
* gdb.opt/inline-break.c (inline_func1, not_inline_func1)
8+
(inline_func2, not_inline_func2, inline_func3, not_inline_func3):
9+
New functions.
10+
(main): Call not_inline_func3.
11+
* gdb.opt/inline-break.exp: Start inferior and set breakpoints at
12+
inline_func1, inline_func2, and inline_func3. Test that when each
13+
breakpoint is hit, GDB properly reports both the stop location
14+
and the backtrace. Repeat tests for temporary breakpoints.
15+
116
2018-05-15 Maciej W. Rozycki <macro@mips.com>
217

318
* gdb.server/server-kill.exp: Verify whether `server_pid' exists

gdb/testsuite/gdb.ada/bp_inlined_func.exp

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,13 @@ gdb_test "break read_small" \
3838
# We do not verify each breakpoint info, but use continue commands instead
3939
# to verify that we properly stop on each expected breakpoint.
4040

41-
gdb_test "continue" \
42-
"Breakpoint $decimal, b\\.doit \\(\\).*" \
43-
"Hitting first call of read_small"
44-
45-
gdb_test "continue" \
46-
"Breakpoint $decimal, foo \\(\\).*" \
47-
"Hitting second call of read_small"
48-
49-
gdb_test "continue" \
50-
"Breakpoint $decimal, c\\.c_doit \\(\\).*" \
51-
"Hitting third call of read_small"
52-
53-
gdb_test "continue" \
54-
"Breakpoint $decimal, c\\.c_doit2 \\(\\).*" \
55-
"Hitting fourth call of read_small"
41+
for {set i 0} {$i < 4} {incr i} {
42+
with_test_prefix "iteration $i" {
43+
gdb_test "continue" \
44+
"Breakpoint $decimal, b\\.read_small \\(\\).*" \
45+
"stopped in read_small"
46+
}
47+
}
5648

5749
gdb_test "continue" \
5850
"Continuing\..*$inferior_exited_re.*" \

gdb/testsuite/gdb.dwarf2/implptr.exp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,13 @@ proc implptr_test_baz {} {
6666
gdb_test "break implptr.c:$line" "Breakpoint 3.*" \
6767
"set baz breakpoint for implptr"
6868
gdb_continue_to_breakpoint "continue to baz breakpoint for implptr"
69+
70+
# We are breaking in an inlined function. GDB should appear to
71+
# have stopped "in" the inlined function.
72+
gdb_test "up" "#1 foo .*"
6973
gdb_test {p p[0].y} " = 92" "sanity check element 0"
7074
gdb_test {p p[1].y} " = 46" "sanity check element 1"
71-
gdb_test "step" "\r\nadd \\(.*" "enter the inlined function"
75+
gdb_test "down" "#0 add .*"
7276
gdb_test "p a->y" " = 92" "check element 0 for the offset"
7377
gdb_test "p b->y" " = 46" "check element 1 for the offset"
7478
gdb_continue_to_breakpoint "ignore the second baz breakpoint"

0 commit comments

Comments
 (0)