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.
459 lines
16 KiB
459 lines
16 KiB
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 *
|
|
|