|
|
7b26da |
commit 194cca41192efa65f710967e3149bbc813c12b22
|
|
|
7b26da |
Author: Pedro Alves <palves@redhat.com>
|
|
|
7b26da |
Date: Thu Nov 21 15:20:09 2013 +0000
|
|
|
7b26da |
|
|
|
7b26da |
Make use of the frame stash to detect wider stack cycles.
|
|
|
7b26da |
|
|
|
7b26da |
Given we already have the frame id stash, which holds the ids of all
|
|
|
7b26da |
frames in the chain, detecting corrupted stacks with wide stack cycles
|
|
|
7b26da |
with non-consecutive dup frame ids is just as cheap as just detecting
|
|
|
7b26da |
cycles in consecutive frames:
|
|
|
7b26da |
|
|
|
7b26da |
#0 frame_id1
|
|
|
7b26da |
#1 frame_id2
|
|
|
7b26da |
#2 frame_id3
|
|
|
7b26da |
#3 frame_id1
|
|
|
7b26da |
#4 frame_id2
|
|
|
7b26da |
#5 frame_id3
|
|
|
7b26da |
#6 frame_id1
|
|
|
7b26da |
... forever ...
|
|
|
7b26da |
|
|
|
7b26da |
We just need to check whether the stash already knows about a given
|
|
|
7b26da |
frame id instead of comparing the ids of the previous/this frames.
|
|
|
7b26da |
|
|
|
7b26da |
Tested on x86_64 Fedora 17.
|
|
|
7b26da |
|
|
|
7b26da |
gdb/
|
|
|
7b26da |
2013-11-22 Pedro Alves <palves@redhat.com>
|
|
|
7b26da |
Tom Tromey <tromey@redhat.com>
|
|
|
7b26da |
|
|
|
7b26da |
* frame.c (frame_stash_add): Now returns whether a frame with the
|
|
|
7b26da |
same ID was already known.
|
|
|
7b26da |
(compute_frame_id): New function, factored out from get_frame_id.
|
|
|
7b26da |
(get_frame_id): No longer lazilly compute the frame id here.
|
|
|
7b26da |
(get_prev_frame_if_no_cycle): New function. Detects wider stack
|
|
|
7b26da |
cycles.
|
|
|
7b26da |
(get_prev_frame_1): Use it instead of get_prev_frame_raw directly,
|
|
|
7b26da |
and checking for stack cycles here.
|
|
|
7b26da |
|
|
|
7b26da |
Index: gdb-7.6.1/gdb/frame.c
|
|
|
7b26da |
===================================================================
|
|
|
7b26da |
--- gdb-7.6.1.orig/gdb/frame.c
|
|
|
7b26da |
+++ gdb-7.6.1/gdb/frame.c
|
|
|
7b26da |
@@ -189,23 +189,31 @@ frame_stash_create (void)
|
|
|
7b26da |
NULL);
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
-/* Internal function to add a frame to the frame_stash hash table. Do
|
|
|
7b26da |
- not store frames below 0 as they may not have any addresses to
|
|
|
7b26da |
- calculate a hash. */
|
|
|
7b26da |
+/* Internal function to add a frame to the frame_stash hash table.
|
|
|
7b26da |
+ Returns false if a frame with the same ID was already stashed, true
|
|
|
7b26da |
+ otherwise. */
|
|
|
7b26da |
|
|
|
7b26da |
-static void
|
|
|
7b26da |
+static int
|
|
|
7b26da |
frame_stash_add (struct frame_info *frame)
|
|
|
7b26da |
{
|
|
|
7b26da |
- /* Do not stash frames below level 0. */
|
|
|
7b26da |
- if (frame->level >= 0)
|
|
|
7b26da |
- {
|
|
|
7b26da |
- struct frame_info **slot;
|
|
|
7b26da |
+ struct frame_info **slot;
|
|
|
7b26da |
|
|
|
7b26da |
- slot = (struct frame_info **) htab_find_slot (frame_stash,
|
|
|
7b26da |
- frame,
|
|
|
7b26da |
- INSERT);
|
|
|
7b26da |
- *slot = frame;
|
|
|
7b26da |
- }
|
|
|
7b26da |
+ /* Do not try to stash the sentinel frame. */
|
|
|
7b26da |
+ gdb_assert (frame->level >= 0);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ slot = (struct frame_info **) htab_find_slot (frame_stash,
|
|
|
7b26da |
+ frame,
|
|
|
7b26da |
+ INSERT);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ /* If we already have a frame in the stack with the same id, we
|
|
|
7b26da |
+ either have a stack cycle (corrupted stack?), or some bug
|
|
|
7b26da |
+ elsewhere in GDB. In any case, ignore the duplicate and return
|
|
|
7b26da |
+ an indication to the caller. */
|
|
|
7b26da |
+ if (*slot != NULL)
|
|
|
7b26da |
+ return 0;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ *slot = frame;
|
|
|
7b26da |
+ return 1;
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
/* Internal function to search the frame stash for an entry with the
|
|
|
7b26da |
@@ -397,6 +405,34 @@ skip_artificial_frames (struct frame_inf
|
|
|
7b26da |
return frame;
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
+/* Compute the frame's uniq ID that can be used to, later, re-find the
|
|
|
7b26da |
+ frame. */
|
|
|
7b26da |
+
|
|
|
7b26da |
+static void
|
|
|
7b26da |
+compute_frame_id (struct frame_info *fi)
|
|
|
7b26da |
+{
|
|
|
7b26da |
+ gdb_assert (!fi->this_id.p);
|
|
|
7b26da |
+
|
|
|
7b26da |
+ if (frame_debug)
|
|
|
7b26da |
+ fprintf_unfiltered (gdb_stdlog, "{ compute_frame_id (fi=%d) ",
|
|
|
7b26da |
+ fi->level);
|
|
|
7b26da |
+ /* Find the unwinder. */
|
|
|
7b26da |
+ if (fi->unwind == NULL)
|
|
|
7b26da |
+ frame_unwind_find_by_frame (fi, &fi->prologue_cache);
|
|
|
7b26da |
+ /* Find THIS frame's ID. */
|
|
|
7b26da |
+ /* Default to outermost if no ID is found. */
|
|
|
7b26da |
+ fi->this_id.value = outer_frame_id;
|
|
|
7b26da |
+ fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
|
|
|
7b26da |
+ gdb_assert (frame_id_p (fi->this_id.value));
|
|
|
7b26da |
+ fi->this_id.p = 1;
|
|
|
7b26da |
+ if (frame_debug)
|
|
|
7b26da |
+ {
|
|
|
7b26da |
+ fprintf_unfiltered (gdb_stdlog, "-> ");
|
|
|
7b26da |
+ fprint_frame_id (gdb_stdlog, fi->this_id.value);
|
|
|
7b26da |
+ fprintf_unfiltered (gdb_stdlog, " }\n");
|
|
|
7b26da |
+ }
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
/* Return a frame uniq ID that can be used to, later, re-find the
|
|
|
7b26da |
frame. */
|
|
|
7b26da |
|
|
|
7b26da |
@@ -406,29 +442,7 @@ get_frame_id (struct frame_info *fi)
|
|
|
7b26da |
if (fi == NULL)
|
|
|
7b26da |
return null_frame_id;
|
|
|
7b26da |
|
|
|
7b26da |
- if (!fi->this_id.p)
|
|
|
7b26da |
- {
|
|
|
7b26da |
- if (frame_debug)
|
|
|
7b26da |
- fprintf_unfiltered (gdb_stdlog, "{ get_frame_id (fi=%d) ",
|
|
|
7b26da |
- fi->level);
|
|
|
7b26da |
- /* Find the unwinder. */
|
|
|
7b26da |
- if (fi->unwind == NULL)
|
|
|
7b26da |
- frame_unwind_find_by_frame (fi, &fi->prologue_cache);
|
|
|
7b26da |
- /* Find THIS frame's ID. */
|
|
|
7b26da |
- /* Default to outermost if no ID is found. */
|
|
|
7b26da |
- fi->this_id.value = outer_frame_id;
|
|
|
7b26da |
- fi->unwind->this_id (fi, &fi->prologue_cache, &fi->this_id.value);
|
|
|
7b26da |
- gdb_assert (frame_id_p (fi->this_id.value));
|
|
|
7b26da |
- fi->this_id.p = 1;
|
|
|
7b26da |
- if (frame_debug)
|
|
|
7b26da |
- {
|
|
|
7b26da |
- fprintf_unfiltered (gdb_stdlog, "-> ");
|
|
|
7b26da |
- fprint_frame_id (gdb_stdlog, fi->this_id.value);
|
|
|
7b26da |
- fprintf_unfiltered (gdb_stdlog, " }\n");
|
|
|
7b26da |
- }
|
|
|
7b26da |
- frame_stash_add (fi);
|
|
|
7b26da |
- }
|
|
|
7b26da |
-
|
|
|
7b26da |
+ gdb_assert (fi->this_id.p);
|
|
|
7b26da |
return fi->this_id.value;
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
@@ -1678,6 +1692,42 @@ frame_register_unwind_location (struct f
|
|
|
7b26da |
}
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
+/* Get the previous raw frame, and check that it is not identical to
|
|
|
7b26da |
+ same other frame frame already in the chain. If it is, there is
|
|
|
7b26da |
+ most likely a stack cycle, so we discard it, and mark THIS_FRAME as
|
|
|
7b26da |
+ outermost, with UNWIND_SAME_ID stop reason. Unlike the other
|
|
|
7b26da |
+ validity tests, that compare THIS_FRAME and the next frame, we do
|
|
|
7b26da |
+ this right after creating the previous frame, to avoid ever ending
|
|
|
7b26da |
+ up with two frames with the same id in the frame chain. */
|
|
|
7b26da |
+
|
|
|
7b26da |
+static struct frame_info *
|
|
|
7b26da |
+get_prev_frame_if_no_cycle (struct frame_info *this_frame)
|
|
|
7b26da |
+{
|
|
|
7b26da |
+ struct frame_info *prev_frame;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ prev_frame = get_prev_frame_raw (this_frame);
|
|
|
7b26da |
+ if (prev_frame == NULL)
|
|
|
7b26da |
+ return NULL;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ compute_frame_id (prev_frame);
|
|
|
7b26da |
+ if (frame_stash_add (prev_frame))
|
|
|
7b26da |
+ return prev_frame;
|
|
|
7b26da |
+
|
|
|
7b26da |
+ /* Another frame with the same id was already in the stash. We just
|
|
|
7b26da |
+ detected a cycle. */
|
|
|
7b26da |
+ if (frame_debug)
|
|
|
7b26da |
+ {
|
|
|
7b26da |
+ fprintf_unfiltered (gdb_stdlog, "-> ");
|
|
|
7b26da |
+ fprint_frame (gdb_stdlog, NULL);
|
|
|
7b26da |
+ fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
|
|
|
7b26da |
+ }
|
|
|
7b26da |
+ this_frame->stop_reason = UNWIND_SAME_ID;
|
|
|
7b26da |
+ /* Unlink. */
|
|
|
7b26da |
+ prev_frame->next = NULL;
|
|
|
7b26da |
+ this_frame->prev = NULL;
|
|
|
7b26da |
+ return NULL;
|
|
|
7b26da |
+}
|
|
|
7b26da |
+
|
|
|
7b26da |
/* Return a "struct frame_info" corresponding to the frame that called
|
|
|
7b26da |
THIS_FRAME. Returns NULL if there is no such frame.
|
|
|
7b26da |
|
|
|
7b26da |
@@ -1689,7 +1739,6 @@ get_prev_frame_1 (struct frame_info *thi
|
|
|
7b26da |
{
|
|
|
7b26da |
struct frame_id this_id;
|
|
|
7b26da |
struct gdbarch *gdbarch;
|
|
|
7b26da |
- struct frame_info *prev_frame;
|
|
|
7b26da |
|
|
|
7b26da |
gdb_assert (this_frame != NULL);
|
|
|
7b26da |
gdbarch = get_frame_arch (this_frame);
|
|
|
7b26da |
@@ -1732,7 +1781,7 @@ get_prev_frame_1 (struct frame_info *thi
|
|
|
7b26da |
until we have unwound all the way down to the previous non-inline
|
|
|
7b26da |
frame. */
|
|
|
7b26da |
if (get_frame_type (this_frame) == INLINE_FRAME)
|
|
|
7b26da |
- return get_prev_frame_raw (this_frame);
|
|
|
7b26da |
+ return get_prev_frame_if_no_cycle (this_frame);
|
|
|
7b26da |
|
|
|
7b26da |
/* Check that this frame is unwindable. If it isn't, don't try to
|
|
|
7b26da |
unwind to the prev frame. */
|
|
|
7b26da |
@@ -1838,31 +1887,7 @@ get_prev_frame_1 (struct frame_info *thi
|
|
|
7b26da |
}
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
- prev_frame = get_prev_frame_raw (this_frame);
|
|
|
7b26da |
-
|
|
|
7b26da |
- /* Check that this and the prev frame are not identical. If they
|
|
|
7b26da |
- are, there is most likely a stack cycle. Unlike the tests above,
|
|
|
7b26da |
- we do this right after creating the prev frame, to avoid ever
|
|
|
7b26da |
- ending up with two frames with the same id in the frame
|
|
|
7b26da |
- chain. */
|
|
|
7b26da |
- if (prev_frame != NULL
|
|
|
7b26da |
- && frame_id_eq (get_frame_id (prev_frame),
|
|
|
7b26da |
- get_frame_id (this_frame)))
|
|
|
7b26da |
- {
|
|
|
7b26da |
- if (frame_debug)
|
|
|
7b26da |
- {
|
|
|
7b26da |
- fprintf_unfiltered (gdb_stdlog, "-> ");
|
|
|
7b26da |
- fprint_frame (gdb_stdlog, NULL);
|
|
|
7b26da |
- fprintf_unfiltered (gdb_stdlog, " // this frame has same ID }\n");
|
|
|
7b26da |
- }
|
|
|
7b26da |
- this_frame->stop_reason = UNWIND_SAME_ID;
|
|
|
7b26da |
- /* Unlink. */
|
|
|
7b26da |
- prev_frame->next = NULL;
|
|
|
7b26da |
- this_frame->prev = NULL;
|
|
|
7b26da |
- return NULL;
|
|
|
7b26da |
- }
|
|
|
7b26da |
-
|
|
|
7b26da |
- return prev_frame;
|
|
|
7b26da |
+ return get_prev_frame_if_no_cycle (this_frame);
|
|
|
7b26da |
}
|
|
|
7b26da |
|
|
|
7b26da |
/* Construct a new "struct frame_info" and link it previous to
|