You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
16 KiB
460 lines
16 KiB
6 years ago
|
NOTE: The testcase part of this patch has been removed.
|
||
|
|
||
|
commit 5ce0145de764dc9c6e92daf2f843fa6e496c49d0
|
||
|
Author: Pedro Alves <palves@redhat.com>
|
||
|
Date: Tue Dec 17 20:47:36 2013 +0000
|
||
|
|
||
|
"tfind" across unavailable-stack frames.
|
||
|
|
||
|
Like when stepping, the current stack frame location is expected to be
|
||
|
printed as result of tfind command, if that results in moving to a
|
||
|
different function. In tfind_1 we see:
|
||
|
|
||
|
if (from_tty
|
||
|
&& (has_stack_frames () || traceframe_number >= 0))
|
||
|
{
|
||
|
enum print_what print_what;
|
||
|
|
||
|
/* NOTE: in imitation of the step command, try to determine
|
||
|
whether we have made a transition from one function to
|
||
|
another. If so, we'll print the "stack frame" (ie. the new
|
||
|
function and it's arguments) -- otherwise we'll just show the
|
||
|
new source line. */
|
||
|
|
||
|
if (frame_id_eq (old_frame_id,
|
||
|
get_frame_id (get_current_frame ())))
|
||
|
print_what = SRC_LINE;
|
||
|
else
|
||
|
print_what = SRC_AND_LOC;
|
||
|
|
||
|
print_stack_frame (get_selected_frame (NULL), 1, print_what, 1);
|
||
|
do_displays ();
|
||
|
}
|
||
|
|
||
|
However, when we haven't collected any registers in the tracepoint
|
||
|
(collect $regs), that doesn't actually work:
|
||
|
|
||
|
(gdb) tstart
|
||
|
(gdb) info tracepoints
|
||
|
Num Type Disp Enb Address What
|
||
|
1 tracepoint keep y 0x080483b7 in func0
|
||
|
at ../.././../git/gdb/testsuite/gdb.trace/circ.c:28
|
||
|
collect testload
|
||
|
installed on target
|
||
|
2 tracepoint keep y 0x080483bc in func1
|
||
|
at ../.././../git/gdb/testsuite/gdb.trace/circ.c:32
|
||
|
collect testload
|
||
|
installed on target
|
||
|
(gdb) c
|
||
|
Continuing.
|
||
|
|
||
|
Breakpoint 3, end () at ../.././../git/gdb/testsuite/gdb.trace/circ.c:72
|
||
|
72 }
|
||
|
(gdb) tstop
|
||
|
(gdb) tfind start
|
||
|
Found trace frame 0, tracepoint 1
|
||
|
#0 func0 () at ../.././../git/gdb/testsuite/gdb.trace/circ.c:28
|
||
|
28 }
|
||
|
(gdb) tfind
|
||
|
Found trace frame 1, tracepoint 2
|
||
|
32 }
|
||
|
(gdb)
|
||
|
|
||
|
When we don't have info about the stack available
|
||
|
(UNWIND_UNAVAILABLE), frames end up with outer_frame_id as frame ID.
|
||
|
And in the scenario above, the issue is that both frames before and
|
||
|
after the second tfind (the frames for func0 an func1) have the same
|
||
|
id (outer_frame_id), so the frame_id_eq check returns false, even
|
||
|
though the frames were of different functions. GDB knows that,
|
||
|
because the PC is inferred from the tracepoint's address, even if no
|
||
|
registers were collected.
|
||
|
|
||
|
To fix this, this patch adds support for frame ids with a valid code
|
||
|
address, but <unavailable> stack address, and then makes the unwinders
|
||
|
use that instead of the catch-all outer_frame_id for such frames. The
|
||
|
frame_id_eq check in tfind_1 then automatically does the right thing
|
||
|
as expected.
|
||
|
|
||
|
I tested with --directory=gdb.trace/ , before/after the patch, and
|
||
|
compared the resulting gdb.logs, then adjusted the tests to expect the
|
||
|
extra output that came out. Turns out that was only circ.exp, the
|
||
|
original test that actually brought this issue to light.
|
||
|
|
||
|
Tested on x86_64 Fedora 17, native and gdbserver.
|
||
|
|
||
|
gdb/
|
||
|
2013-12-17 Pedro Alves <palves@redhat.com>
|
||
|
|
||
|
* frame.h (enum frame_id_stack_status): New enum.
|
||
|
(struct frame_id) <stack_addr>: Adjust comment.
|
||
|
<stack_addr_p>: Delete field, replaced with ...
|
||
|
<stack_status>: ... this new field.
|
||
|
(frame_id_build_unavailable_stack): Declare.
|
||
|
* frame.c (frame_addr_hash, fprint_field, outer_frame_id)
|
||
|
(frame_id_build_special): Adjust.
|
||
|
(frame_id_build_unavailable_stack): New function.
|
||
|
(frame_id_build, frame_id_build_wild): Adjust.
|
||
|
(frame_id_p, frame_id_eq, frame_id_inner): Adjust to take into
|
||
|
account frames with unavailable stack.
|
||
|
|
||
|
* amd64-tdep.c (amd64_frame_this_id)
|
||
|
(amd64_sigtramp_frame_this_id, amd64_epilogue_frame_this_id): Use
|
||
|
frame_id_build_unavailable_stack.
|
||
|
* dwarf2-frame.c (dwarf2_frame_this_id): Likewise.
|
||
|
* i386-tdep.c (i386_frame_this_id, i386_epilogue_frame_this_id)
|
||
|
(i386_sigtramp_frame_this_id): Likewise.
|
||
|
|
||
|
gdb/testsuite/
|
||
|
2013-12-17 Pedro Alves <palves@redhat.com>
|
||
|
|
||
|
* gdb.trace/circ.exp: Expect frame info to be printed when
|
||
|
switching between frames with unavailable stack, but different
|
||
|
functions.
|
||
|
|
||
|
Index: gdb-7.6.1/gdb/amd64-tdep.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/amd64-tdep.c
|
||
|
+++ gdb-7.6.1/gdb/amd64-tdep.c
|
||
|
@@ -2403,15 +2403,16 @@ amd64_frame_this_id (struct frame_info *
|
||
|
struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (this_frame));
|
||
|
|
||
|
if (!cache->base_p)
|
||
|
- return;
|
||
|
-
|
||
|
- /* This marks the outermost frame. */
|
||
|
- if (cache->base == 0)
|
||
|
- return;
|
||
|
-
|
||
|
- /* Detect OS dependent outermost frames; such as `clone'. */
|
||
|
- if (tdep->outermost_frame_p && tdep->outermost_frame_p (this_frame))
|
||
|
- return;
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (cache->pc);
|
||
|
+ else if (cache->base == 0)
|
||
|
+ {
|
||
|
+ /* This marks the outermost frame. */
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ /* Detect OS dependent outermost frames; such as `clone'. */
|
||
|
+ if (tdep->outermost_frame_p && tdep->outermost_frame_p (this_frame))
|
||
|
+ return;
|
||
|
|
||
|
(*this_id) = frame_id_build (cache->base + 16, cache->pc);
|
||
|
}
|
||
|
@@ -2528,9 +2529,14 @@ amd64_sigtramp_frame_this_id (struct fra
|
||
|
amd64_sigtramp_frame_cache (this_frame, this_cache);
|
||
|
|
||
|
if (!cache->base_p)
|
||
|
- return;
|
||
|
-
|
||
|
- (*this_id) = frame_id_build (cache->base + 16, get_frame_pc (this_frame));
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (get_frame_pc (this_frame));
|
||
|
+ else if (cache->base == 0)
|
||
|
+ {
|
||
|
+ /* This marks the outermost frame. */
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ (*this_id) = frame_id_build (cache->base + 16, get_frame_pc (this_frame));
|
||
|
}
|
||
|
|
||
|
static struct value *
|
||
|
@@ -2699,9 +2705,9 @@ amd64_epilogue_frame_this_id (struct fra
|
||
|
this_cache);
|
||
|
|
||
|
if (!cache->base_p)
|
||
|
- return;
|
||
|
-
|
||
|
- (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (cache->pc);
|
||
|
+ else
|
||
|
+ (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
}
|
||
|
|
||
|
static const struct frame_unwind amd64_epilogue_frame_unwind =
|
||
|
Index: gdb-7.6.1/gdb/dwarf2-frame.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/dwarf2-frame.c
|
||
|
+++ gdb-7.6.1/gdb/dwarf2-frame.c
|
||
|
@@ -1265,12 +1265,11 @@ dwarf2_frame_this_id (struct frame_info
|
||
|
dwarf2_frame_cache (this_frame, this_cache);
|
||
|
|
||
|
if (cache->unavailable_retaddr)
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (get_frame_func (this_frame));
|
||
|
+ else if (cache->undefined_retaddr)
|
||
|
return;
|
||
|
-
|
||
|
- if (cache->undefined_retaddr)
|
||
|
- return;
|
||
|
-
|
||
|
- (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
|
||
|
+ else
|
||
|
+ (*this_id) = frame_id_build (cache->cfa, get_frame_func (this_frame));
|
||
|
}
|
||
|
|
||
|
static struct value *
|
||
|
Index: gdb-7.6.1/gdb/frame.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/frame.c
|
||
|
+++ gdb-7.6.1/gdb/frame.c
|
||
|
@@ -147,10 +147,11 @@ frame_addr_hash (const void *ap)
|
||
|
const struct frame_id f_id = frame->this_id.value;
|
||
|
hashval_t hash = 0;
|
||
|
|
||
|
- gdb_assert (f_id.stack_addr_p || f_id.code_addr_p
|
||
|
+ gdb_assert (f_id.stack_status != FID_STACK_INVALID
|
||
|
+ || f_id.code_addr_p
|
||
|
|| f_id.special_addr_p);
|
||
|
|
||
|
- if (f_id.stack_addr_p)
|
||
|
+ if (f_id.stack_status == FID_STACK_VALID)
|
||
|
hash = iterative_hash (&f_id.stack_addr,
|
||
|
sizeof (f_id.stack_addr), hash);
|
||
|
if (f_id.code_addr_p)
|
||
|
@@ -290,13 +291,23 @@ void
|
||
|
fprint_frame_id (struct ui_file *file, struct frame_id id)
|
||
|
{
|
||
|
fprintf_unfiltered (file, "{");
|
||
|
- fprint_field (file, "stack", id.stack_addr_p, id.stack_addr);
|
||
|
+
|
||
|
+ if (id.stack_status == FID_STACK_INVALID)
|
||
|
+ fprintf_unfiltered (file, "!stack");
|
||
|
+ else if (id.stack_status == FID_STACK_UNAVAILABLE)
|
||
|
+ fprintf_unfiltered (file, "stack=<unavailable>");
|
||
|
+ else
|
||
|
+ fprintf_unfiltered (file, "stack=%s", hex_string (id.stack_addr));
|
||
|
fprintf_unfiltered (file, ",");
|
||
|
+
|
||
|
fprint_field (file, "code", id.code_addr_p, id.code_addr);
|
||
|
fprintf_unfiltered (file, ",");
|
||
|
+
|
||
|
fprint_field (file, "special", id.special_addr_p, id.special_addr);
|
||
|
+
|
||
|
if (id.artificial_depth)
|
||
|
fprintf_unfiltered (file, ",artificial=%d", id.artificial_depth);
|
||
|
+
|
||
|
fprintf_unfiltered (file, "}");
|
||
|
}
|
||
|
|
||
|
@@ -446,7 +457,7 @@ frame_unwind_caller_id (struct frame_inf
|
||
|
}
|
||
|
|
||
|
const struct frame_id null_frame_id; /* All zeros. */
|
||
|
-const struct frame_id outer_frame_id = { 0, 0, 0, 0, 0, 1, 0 };
|
||
|
+const struct frame_id outer_frame_id = { 0, 0, 0, FID_STACK_INVALID, 0, 1, 0 };
|
||
|
|
||
|
struct frame_id
|
||
|
frame_id_build_special (CORE_ADDR stack_addr, CORE_ADDR code_addr,
|
||
|
@@ -455,7 +466,7 @@ frame_id_build_special (CORE_ADDR stack_
|
||
|
struct frame_id id = null_frame_id;
|
||
|
|
||
|
id.stack_addr = stack_addr;
|
||
|
- id.stack_addr_p = 1;
|
||
|
+ id.stack_status = FID_STACK_VALID;
|
||
|
id.code_addr = code_addr;
|
||
|
id.code_addr_p = 1;
|
||
|
id.special_addr = special_addr;
|
||
|
@@ -463,13 +474,26 @@ frame_id_build_special (CORE_ADDR stack_
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
+/* See frame.h. */
|
||
|
+
|
||
|
+struct frame_id
|
||
|
+frame_id_build_unavailable_stack (CORE_ADDR code_addr)
|
||
|
+{
|
||
|
+ struct frame_id id = null_frame_id;
|
||
|
+
|
||
|
+ id.stack_status = FID_STACK_UNAVAILABLE;
|
||
|
+ id.code_addr = code_addr;
|
||
|
+ id.code_addr_p = 1;
|
||
|
+ return id;
|
||
|
+}
|
||
|
+
|
||
|
struct frame_id
|
||
|
frame_id_build (CORE_ADDR stack_addr, CORE_ADDR code_addr)
|
||
|
{
|
||
|
struct frame_id id = null_frame_id;
|
||
|
|
||
|
id.stack_addr = stack_addr;
|
||
|
- id.stack_addr_p = 1;
|
||
|
+ id.stack_status = FID_STACK_VALID;
|
||
|
id.code_addr = code_addr;
|
||
|
id.code_addr_p = 1;
|
||
|
return id;
|
||
|
@@ -481,7 +505,7 @@ frame_id_build_wild (CORE_ADDR stack_add
|
||
|
struct frame_id id = null_frame_id;
|
||
|
|
||
|
id.stack_addr = stack_addr;
|
||
|
- id.stack_addr_p = 1;
|
||
|
+ id.stack_status = FID_STACK_VALID;
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
@@ -491,7 +515,7 @@ frame_id_p (struct frame_id l)
|
||
|
int p;
|
||
|
|
||
|
/* The frame is valid iff it has a valid stack address. */
|
||
|
- p = l.stack_addr_p;
|
||
|
+ p = l.stack_status != FID_STACK_INVALID;
|
||
|
/* outer_frame_id is also valid. */
|
||
|
if (!p && memcmp (&l, &outer_frame_id, sizeof (l)) == 0)
|
||
|
p = 1;
|
||
|
@@ -518,19 +542,20 @@ frame_id_eq (struct frame_id l, struct f
|
||
|
{
|
||
|
int eq;
|
||
|
|
||
|
- if (!l.stack_addr_p && l.special_addr_p
|
||
|
- && !r.stack_addr_p && r.special_addr_p)
|
||
|
+ if (l.stack_status == FID_STACK_INVALID && l.special_addr_p
|
||
|
+ && r.stack_status == FID_STACK_INVALID && r.special_addr_p)
|
||
|
/* The outermost frame marker is equal to itself. This is the
|
||
|
dodgy thing about outer_frame_id, since between execution steps
|
||
|
we might step into another function - from which we can't
|
||
|
unwind either. More thought required to get rid of
|
||
|
outer_frame_id. */
|
||
|
eq = 1;
|
||
|
- else if (!l.stack_addr_p || !r.stack_addr_p)
|
||
|
+ else if (l.stack_status == FID_STACK_INVALID
|
||
|
+ || r.stack_status == FID_STACK_INVALID)
|
||
|
/* Like a NaN, if either ID is invalid, the result is false.
|
||
|
Note that a frame ID is invalid iff it is the null frame ID. */
|
||
|
eq = 0;
|
||
|
- else if (l.stack_addr != r.stack_addr)
|
||
|
+ else if (l.stack_status != r.stack_status || l.stack_addr != r.stack_addr)
|
||
|
/* If .stack addresses are different, the frames are different. */
|
||
|
eq = 0;
|
||
|
else if (l.code_addr_p && r.code_addr_p && l.code_addr != r.code_addr)
|
||
|
@@ -597,8 +622,9 @@ frame_id_inner (struct gdbarch *gdbarch,
|
||
|
{
|
||
|
int inner;
|
||
|
|
||
|
- if (!l.stack_addr_p || !r.stack_addr_p)
|
||
|
- /* Like NaN, any operation involving an invalid ID always fails. */
|
||
|
+ if (l.stack_status != FID_STACK_VALID || r.stack_status != FID_STACK_VALID)
|
||
|
+ /* Like NaN, any operation involving an invalid ID always fails.
|
||
|
+ Likewise if either ID has an unavailable stack address. */
|
||
|
inner = 0;
|
||
|
else if (l.artificial_depth > r.artificial_depth
|
||
|
&& l.stack_addr == r.stack_addr
|
||
|
Index: gdb-7.6.1/gdb/frame.h
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/frame.h
|
||
|
+++ gdb-7.6.1/gdb/frame.h
|
||
|
@@ -76,6 +76,23 @@ struct block;
|
||
|
struct gdbarch;
|
||
|
struct ui_file;
|
||
|
|
||
|
+/* Status of a given frame's stack. */
|
||
|
+
|
||
|
+enum frame_id_stack_status
|
||
|
+{
|
||
|
+ /* Stack address is invalid. E.g., this frame is the outermost
|
||
|
+ (i.e., _start), and the stack hasn't been setup yet. */
|
||
|
+ FID_STACK_INVALID = 0,
|
||
|
+
|
||
|
+ /* Stack address is valid, and is found in the stack_addr field. */
|
||
|
+ FID_STACK_VALID = 1,
|
||
|
+
|
||
|
+ /* Stack address is unavailable. I.e., there's a valid stack, but
|
||
|
+ we don't know where it is (because memory or registers we'd
|
||
|
+ compute it from were not collected). */
|
||
|
+ FID_STACK_UNAVAILABLE = -1
|
||
|
+};
|
||
|
+
|
||
|
/* The frame object. */
|
||
|
|
||
|
struct frame_info;
|
||
|
@@ -97,8 +114,9 @@ struct frame_id
|
||
|
function pointer register or stack pointer register. They are
|
||
|
wrong.
|
||
|
|
||
|
- This field is valid only if stack_addr_p is true. Otherwise, this
|
||
|
- frame represents the null frame. */
|
||
|
+ This field is valid only if frame_id.stack_status is
|
||
|
+ FID_STACK_VALID. It will be 0 for other
|
||
|
+ FID_STACK_... statuses. */
|
||
|
CORE_ADDR stack_addr;
|
||
|
|
||
|
/* The frame's code address. This shall be constant through out the
|
||
|
@@ -129,7 +147,7 @@ struct frame_id
|
||
|
CORE_ADDR special_addr;
|
||
|
|
||
|
/* Flags to indicate the above fields have valid contents. */
|
||
|
- unsigned int stack_addr_p : 1;
|
||
|
+ ENUM_BITFIELD(frame_id_stack_status) stack_status : 2;
|
||
|
unsigned int code_addr_p : 1;
|
||
|
unsigned int special_addr_p : 1;
|
||
|
|
||
|
@@ -169,6 +187,12 @@ extern struct frame_id frame_id_build_sp
|
||
|
CORE_ADDR code_addr,
|
||
|
CORE_ADDR special_addr);
|
||
|
|
||
|
+/* Construct a frame ID representing a frame where the stack address
|
||
|
+ exists, but is unavailable. CODE_ADDR is the frame's constant code
|
||
|
+ address (typically the entry point). The special identifier
|
||
|
+ address is set to indicate a wild card. */
|
||
|
+extern struct frame_id frame_id_build_unavailable_stack (CORE_ADDR code_addr);
|
||
|
+
|
||
|
/* Construct a wild card frame ID. The parameter is the frame's constant
|
||
|
stack address (typically the outer-bound). The code address as well
|
||
|
as the special identifier address are set to indicate wild cards. */
|
||
|
Index: gdb-7.6.1/gdb/i386-tdep.c
|
||
|
===================================================================
|
||
|
--- gdb-7.6.1.orig/gdb/i386-tdep.c
|
||
|
+++ gdb-7.6.1/gdb/i386-tdep.c
|
||
|
@@ -1846,12 +1846,17 @@ i386_frame_this_id (struct frame_info *t
|
||
|
{
|
||
|
struct i386_frame_cache *cache = i386_frame_cache (this_frame, this_cache);
|
||
|
|
||
|
- /* This marks the outermost frame. */
|
||
|
- if (cache->base == 0)
|
||
|
- return;
|
||
|
-
|
||
|
- /* See the end of i386_push_dummy_call. */
|
||
|
- (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
+ if (!cache->base_p)
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (cache->pc);
|
||
|
+ else if (cache->base == 0)
|
||
|
+ {
|
||
|
+ /* This marks the outermost frame. */
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ /* See the end of i386_push_dummy_call. */
|
||
|
+ (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
static enum unwind_stop_reason
|
||
|
@@ -2032,9 +2037,9 @@ i386_epilogue_frame_this_id (struct fram
|
||
|
i386_epilogue_frame_cache (this_frame, this_cache);
|
||
|
|
||
|
if (!cache->base_p)
|
||
|
- return;
|
||
|
-
|
||
|
- (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (cache->pc);
|
||
|
+ else
|
||
|
+ (*this_id) = frame_id_build (cache->base + 8, cache->pc);
|
||
|
}
|
||
|
|
||
|
static struct value *
|
||
|
@@ -2226,10 +2231,12 @@ i386_sigtramp_frame_this_id (struct fram
|
||
|
i386_sigtramp_frame_cache (this_frame, this_cache);
|
||
|
|
||
|
if (!cache->base_p)
|
||
|
- return;
|
||
|
-
|
||
|
- /* See the end of i386_push_dummy_call. */
|
||
|
- (*this_id) = frame_id_build (cache->base + 8, get_frame_pc (this_frame));
|
||
|
+ (*this_id) = frame_id_build_unavailable_stack (get_frame_pc (this_frame));
|
||
|
+ else
|
||
|
+ {
|
||
|
+ /* See the end of i386_push_dummy_call. */
|
||
|
+ (*this_id) = frame_id_build (cache->base + 8, get_frame_pc (this_frame));
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
static struct value *
|