Skip to content

Commit d19c306

Browse files
committed
gdb: assert that we don't try to get a frame's id while it is computed
I'm dealing these days with a class of bugs that involve trying to get a certain frame's id while we are in the process of computing it. In other words, compute_frame_id being called for a frame, eventually calling get_frame_id for that same frame. I don't think this is ever supposed to happen, as that creates a cyclic dependency. Usually, these problems cause some failure down the line. I'm proposing with this patch to catch them as early as possible, as soon as the situation described above happens. I think that helps because the failed assertion will be closer to the root of the problem. To do so, the patch changes the frame_info::this_id::p flag from a boolean (is the frame id computed or not) to a tri-state: - the frame id is not computed - the frame id is being computed - the frame id is computed Then, we can properly assert that get_frame_id doesn't get called for a frame whose id is being computed. To illustrate how that can help, let's imagine we apply the following change to frame_unwind_got_optimized: --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -260,8 +260,7 @@ frame_unwind_got_optimized (struct frame_info *frame, int regnum) mark_value_bytes_optimized_out (val, 0, TYPE_LENGTH (type)); VALUE_LVAL (val) = lval_register; VALUE_REGNUM (val) = regnum; - VALUE_NEXT_FRAME_ID (val) - = get_frame_id (get_next_frame_sentinel_okay (frame)); + VALUE_NEXT_FRAME_ID (val) = get_frame_id (frame); return val; } ... and run the following command, which leads to a failed assertion (you need to run the corresponding test to generate the binary first): $ ./gdb -q -nx testsuite/outputs/gdb.dwarf2/dw2-undefined-ret-addr/dw2-undefined-ret-addr -ex "b stop_frame" -ex r Without this patch applied, we catch the issue indirectly, when the top-level get_frame_id tries to stash the frame: /home/smarchi/src/binutils-gdb/gdb/frame.c:593: internal-error: frame_id get_frame_id(frame_info*): Assertion `stashed' failed. ... #9 0x0000000001af1c3a in internal_error (file=0x1cea060 "/home/smarchi/src/binutils-gdb/gdb/frame.c", line=593, fmt=0x1ce9f80 "%s: Assertion `%s' failed.") at /home/smarchi/src/binutils-gdb/gdbsupport/errors.cc:55 #10 0x0000000000e9b413 in get_frame_id (fi=0x6210005105e0) at /home/smarchi/src/binutils-gdb/gdb/frame.c:593 #11 0x0000000000e99e35 in scoped_restore_selected_frame::scoped_restore_selected_frame (this=0x7fff1d8b9760) at /home/smarchi/src/binutils-gdb/gdb/frame.c:308 #12 0x000000000149a261 in print_frame_args (fp_opts=..., func=0x6210000dd7d0, frame=0x6210005105e0, num=-1, stream=0x60300008a580) at /home/smarchi/src/binutils-gdb/gdb/stack.c:750 #13 0x000000000149d938 in print_frame (fp_opts=..., frame=0x6210005105e0, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at /home/smarchi/src/binutils-gdb/gdb/stack.c:1394 #14 0x000000000149c0c8 in print_frame_info (fp_opts=..., frame=0x6210005105e0, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at /home/smarchi/src/binutils-gdb/gdb/stack.c:1119 #15 0x0000000001498100 in print_stack_frame (frame=0x6210005105e0, print_level=0, print_what=SRC_AND_LOC, set_current_sal=1) at /home/smarchi/src/binutils-gdb/gdb/stack.c:366 #16 0x00000000010234b7 in print_stop_location (ws=0x7fff1d8ba1f0) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:8366 #17 0x000000000102362d in print_stop_event (uiout=0x607000018660, displays=true) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:8382 ... It freaks out because the frame is already in the stash: it was added by an inner call to get_frame_id, called indirectly by compute_frame_id. Debugging this failure is difficult because we have to backtrack to where this happened. With the patch applied, we catch the issue earlier, here: /home/smarchi/src/binutils-gdb/gdb/frame.c:601: internal-error: frame_id get_frame_id(frame_info*): Assertion `fi->this_id.p != frame_id_status::COMPUTING' failed ... #9 0x0000000001af22bc in internal_error (file=0x1cea6e0 "/home/smarchi/src/binutils-gdb/gdb/frame.c", line=601, fmt=0x1cea600 "%s: Assertion `%s' failed.") at /home/smarchi/src/binutils-gdb/gdbsupport/errors.cc:55 #10 0x0000000000e9b7e3 in get_frame_id (fi=0x62100050dde0) at /home/smarchi/src/binutils-gdb/gdb/frame.c:601 #11 0x0000000000e989b3 in frame_unwind_got_optimized (frame=0x62100050dde0, regnum=16) at /home/smarchi/src/binutils-gdb/gdb/frame-unwind.c:264 #12 0x0000000000cbe386 in dwarf2_frame_prev_register (this_frame=0x62100050dde0, this_cache=0x62100050ddf8, regnum=16) at /home/smarchi/src/binutils-gdb/gdb/dwarf2/frame.c:1267 #13 0x0000000000e9f569 in frame_unwind_register_value (next_frame=0x62100050dde0, regnum=16) at /home/smarchi/src/binutils-gdb/gdb/frame.c:1266 #14 0x0000000000e9eaab in frame_register_unwind (next_frame=0x62100050dde0, regnum=16, optimizedp=0x7ffca814ade0, unavailablep=0x7ffca814adf0, lvalp=0x7ffca814ae10, addrp=0x7ffca814ae20, realnump=0x7ffca814ae00, bufferp=0x7ffca814aec0 "") at /home/smarchi/src/binutils-gdb/gdb/frame.c:1169 #15 0x0000000000e9f233 in frame_unwind_register (next_frame=0x62100050dde0, regnum=16, buf=0x7ffca814aec0 "") at /home/smarchi/src/binutils-gdb/gdb/frame.c:1225 #16 0x0000000000f84262 in i386_unwind_pc (gdbarch=0x6210000eed10, next_frame=0x62100050dde0) at /home/smarchi/src/binutils-gdb/gdb/i386-tdep.c:1969 #17 0x0000000000ec95dd in gdbarch_unwind_pc (gdbarch=0x6210000eed10, next_frame=0x62100050dde0) at /home/smarchi/src/binutils-gdb/gdb/gdbarch.c:3062 #18 0x0000000000cb5e9d in dwarf2_tailcall_sniffer_first (this_frame=0x62100050dde0, tailcall_cachep=0x62100050dee0, entry_cfa_sp_offsetp=0x7ffca814b160) at /home/smarchi/src/binutils-gdb/gdb/dwarf2/frame-tailcall.c:387 #19 0x0000000000cbdd38 in dwarf2_frame_cache (this_frame=0x62100050dde0, this_cache=0x62100050ddf8) at /home/smarchi/src/binutils-gdb/gdb/dwarf2/frame.c:1198 #20 0x0000000000cbe026 in dwarf2_frame_this_id (this_frame=0x62100050dde0, this_cache=0x62100050ddf8, this_id=0x62100050de40) at /home/smarchi/src/binutils-gdb/gdb/dwarf2/frame.c:1226 #21 0x0000000000e9b447 in compute_frame_id (fi=0x62100050dde0) at /home/smarchi/src/binutils-gdb/gdb/frame.c:580 #22 0x0000000000e9b89e in get_frame_id (fi=0x62100050dde0) at /home/smarchi/src/binutils-gdb/gdb/frame.c:613 #23 0x0000000000e99e35 in scoped_restore_selected_frame::scoped_restore_selected_frame (this=0x7ffca814b610) at /home/smarchi/src/binutils-gdb/gdb/frame.c:315 #24 0x000000000149a8e3 in print_frame_args (fp_opts=..., func=0x6210000dd7d0, frame=0x62100050dde0, num=-1, stream=0x60300008a520) at /home/smarchi/src/binutils-gdb/gdb/stack.c:750 #25 0x000000000149dfba in print_frame (fp_opts=..., frame=0x62100050dde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at /home/smarchi/src/binutils-gdb/gdb/stack.c:1394 #26 0x000000000149c74a in print_frame_info (fp_opts=..., frame=0x62100050dde0, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at /home/smarchi/src/binutils-gdb/gdb/stack.c:1119 #27 0x0000000001498782 in print_stack_frame (frame=0x62100050dde0, print_level=0, print_what=SRC_AND_LOC, set_current_sal=1) at /home/smarchi/src/binutils-gdb/gdb/stack.c:366 #28 0x0000000001023b39 in print_stop_location (ws=0x7ffca814c0a0) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:8366 #29 0x0000000001023caf in print_stop_event (uiout=0x607000018660, displays=true) at /home/smarchi/src/binutils-gdb/gdb/infrun.c:8382 ... Now, we can clearly see that get_frame_id for frame `fi=0x62100050dde0` gets called while compute_frame_id is active for that frame. The backtrace is more helpful to identify the root of the problem. gdb/ChangeLog: * frame.c (enum class frame_id_status): New. (struct frame_info) <this_id::p>: Change type to frame_id_status. (fprintf_frame): Update. (compute_frame_id): Set frame id status to "computing" on entry. Set it back to "not_computed" on failure and to "computed" on success. (get_frame_id): Assert the frame id is not being computed. (create_sentinel_frame): Use frame_id_status::COMPUTED. (create_new_frame): Likewise. (frame_cleanup_after_sniffer): Update assert. Change-Id: I5f1a25fafe045f756bd75f358892720b30ed20c9
1 parent 1d4ef72 commit d19c306

File tree

2 files changed

+85
-25
lines changed

2 files changed

+85
-25
lines changed

gdb/ChangeLog

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
2020-08-22 Simon Marchi <simon.marchi@efficios.com>
2+
3+
* frame.c (enum class frame_id_status): New.
4+
(struct frame_info) <this_id::p>: Change type to frame_id_status.
5+
(fprintf_frame): Update.
6+
(compute_frame_id): Set frame id status to "computing" on entry.
7+
Set it back to "not_computed" on failure and to "computed" on
8+
success.
9+
(get_frame_id): Assert the frame id is not being computed.
10+
(create_sentinel_frame): Use frame_id_status::COMPUTED.
11+
(create_new_frame): Likewise.
12+
(frame_cleanup_after_sniffer): Update assert.
13+
114
2020-08-20 Simon Marchi <simon.marchi@polymtl.ca>
215

316
* regcache.c (pid_ptid_regcache_map): New type.

gdb/frame.c

+72-25
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ enum cached_copy_status
8787
CC_UNAVAILABLE
8888
};
8989

90+
enum class frame_id_status
91+
{
92+
/* Frame id is not computed. */
93+
NOT_COMPUTED = 0,
94+
95+
/* Frame id is being computed (compute_frame_id is active). */
96+
COMPUTING,
97+
98+
/* Frame id has been computed. */
99+
COMPUTED,
100+
};
101+
90102
/* We keep a cache of stack frames, each of which is a "struct
91103
frame_info". The innermost one gets allocated (in
92104
wait_for_inferior) each time the inferior stops; sentinel_frame
@@ -149,7 +161,7 @@ struct frame_info
149161
/* This frame's ID. */
150162
struct
151163
{
152-
bool p;
164+
frame_id_status p;
153165
struct frame_id value;
154166
} this_id;
155167

@@ -439,21 +451,25 @@ fprint_frame (struct ui_file *file, struct frame_info *fi)
439451
fprintf_unfiltered (file, "<NULL frame>");
440452
return;
441453
}
454+
442455
fprintf_unfiltered (file, "{");
443456
fprintf_unfiltered (file, "level=%d", fi->level);
444457
fprintf_unfiltered (file, ",");
458+
445459
fprintf_unfiltered (file, "type=");
446460
if (fi->unwind != NULL)
447461
fprint_frame_type (file, fi->unwind->type);
448462
else
449463
fprintf_unfiltered (file, "<unknown>");
450464
fprintf_unfiltered (file, ",");
465+
451466
fprintf_unfiltered (file, "unwind=");
452467
if (fi->unwind != NULL)
453468
gdb_print_host_address (fi->unwind, file);
454469
else
455470
fprintf_unfiltered (file, "<unknown>");
456471
fprintf_unfiltered (file, ",");
472+
457473
fprintf_unfiltered (file, "pc=");
458474
if (fi->next == NULL || fi->next->prev_pc.status == CC_UNKNOWN)
459475
fprintf_unfiltered (file, "<unknown>");
@@ -468,12 +484,16 @@ fprint_frame (struct ui_file *file, struct frame_info *fi)
468484
else if (fi->next->prev_pc.status == CC_UNAVAILABLE)
469485
val_print_unavailable (file);
470486
fprintf_unfiltered (file, ",");
487+
471488
fprintf_unfiltered (file, "id=");
472-
if (fi->this_id.p)
473-
fprint_frame_id (file, fi->this_id.value);
489+
if (fi->this_id.p == frame_id_status::NOT_COMPUTED)
490+
fprintf_unfiltered (file, "<not computed>");
491+
else if (fi->this_id.p == frame_id_status::COMPUTING)
492+
fprintf_unfiltered (file, "<computing>");
474493
else
475-
fprintf_unfiltered (file, "<unknown>");
494+
fprint_frame_id (file, fi->this_id.value);
476495
fprintf_unfiltered (file, ",");
496+
477497
fprintf_unfiltered (file, "func=");
478498
if (fi->next != NULL && fi->next->prev_func.status == CC_VALUE)
479499
fprintf_unfiltered (file, "%s", hex_string (fi->next->prev_func.addr));
@@ -544,25 +564,48 @@ skip_tailcall_frames (struct frame_info *frame)
544564
static void
545565
compute_frame_id (struct frame_info *fi)
546566
{
547-
gdb_assert (!fi->this_id.p);
567+
gdb_assert (fi->this_id.p == frame_id_status::NOT_COMPUTED);
548568

549-
if (frame_debug)
550-
fprintf_unfiltered (gdb_stdlog, "{ compute_frame_id (fi=%d) ",
551-
fi->level);
552-
/* Find the unwinder. */
553-
if (fi->unwind == NULL)
554-
frame_unwind_find_by_frame (fi, &fi->prologue_cache);
555-
/* Find THIS frame's ID. */
556-
/* Default to outermost if no ID is found. */
557-
fi->this_id.value = outer_frame_id;
558-
fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
559-
gdb_assert (frame_id_p (fi->this_id.value));
560-
fi->this_id.p = true;
561-
if (frame_debug)
569+
unsigned int entry_generation = get_frame_cache_generation ();
570+
571+
try
562572
{
563-
fprintf_unfiltered (gdb_stdlog, "-> ");
564-
fprint_frame_id (gdb_stdlog, fi->this_id.value);
565-
fprintf_unfiltered (gdb_stdlog, " }\n");
573+
/* Mark this frame's id as "being computed. */
574+
fi->this_id.p = frame_id_status::COMPUTING;
575+
576+
if (frame_debug)
577+
fprintf_unfiltered (gdb_stdlog, "{ compute_frame_id (fi=%d) ",
578+
fi->level);
579+
580+
/* Find the unwinder. */
581+
if (fi->unwind == NULL)
582+
frame_unwind_find_by_frame (fi, &fi->prologue_cache);
583+
584+
/* Find THIS frame's ID. */
585+
/* Default to outermost if no ID is found. */
586+
fi->this_id.value = outer_frame_id;
587+
fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
588+
gdb_assert (frame_id_p (fi->this_id.value));
589+
590+
/* Mark this frame's id as "computed". */
591+
fi->this_id.p = frame_id_status::COMPUTED;
592+
593+
if (frame_debug)
594+
{
595+
fprintf_unfiltered (gdb_stdlog, "-> ");
596+
fprint_frame_id (gdb_stdlog, fi->this_id.value);
597+
fprintf_unfiltered (gdb_stdlog, " }\n");
598+
}
599+
}
600+
catch (const gdb_exception &ex)
601+
{
602+
/* On error, revert the frame id status to not computed. If the frame
603+
cache generation changed, the frame object doesn't exist anymore, so
604+
don't touch it. */
605+
if (get_frame_cache_generation () == entry_generation)
606+
fi->this_id.p = frame_id_status::NOT_COMPUTED;
607+
608+
throw;
566609
}
567610
}
568611

@@ -575,7 +618,11 @@ get_frame_id (struct frame_info *fi)
575618
if (fi == NULL)
576619
return null_frame_id;
577620

578-
if (!fi->this_id.p)
621+
/* It's always invalid to try to get a frame's id while it is being
622+
computed. */
623+
gdb_assert (fi->this_id.p != frame_id_status::COMPUTING);
624+
625+
if (fi->this_id.p == frame_id_status::NOT_COMPUTED)
579626
{
580627
/* If we haven't computed the frame id yet, then it must be that
581628
this is the current frame. Compute it now, and stash the
@@ -1577,7 +1624,7 @@ create_sentinel_frame (struct program_space *pspace, struct regcache *regcache)
15771624
(the unwound PC is the same as the pc), so make it so. */
15781625
frame->next = frame;
15791626
/* The sentinel frame has a special ID. */
1580-
frame->this_id.p = true;
1627+
frame->this_id.p = frame_id_status::COMPUTED;
15811628
frame->this_id.value = sentinel_frame_id;
15821629
if (frame_debug)
15831630
{
@@ -1797,7 +1844,7 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
17971844
based on the PC. */
17981845
frame_unwind_find_by_frame (fi, &fi->prologue_cache);
17991846

1800-
fi->this_id.p = true;
1847+
fi->this_id.p = frame_id_status::COMPUTED;
18011848
fi->this_id.value = frame_id_build (addr, pc);
18021849

18031850
if (frame_debug)
@@ -2908,7 +2955,7 @@ frame_cleanup_after_sniffer (struct frame_info *frame)
29082955
gdb_assert (!frame->prev_p);
29092956

29102957
/* The sniffer should not check the frame's ID; that's circular. */
2911-
gdb_assert (!frame->this_id.p);
2958+
gdb_assert (frame->this_id.p != frame_id_status::COMPUTED);
29122959

29132960
/* Clear cached fields dependent on the unwinder.
29142961

0 commit comments

Comments
 (0)