This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[unavailable regs/locals, 03/11] glue unavailable regcache registers / lval_register values / frame registers
- From: Pedro Alves <pedro at codesourcery dot com>
- To: gdb-patches at sourceware dot org
- Date: Tue, 22 Feb 2011 13:29:06 +0000
- Subject: [unavailable regs/locals, 03/11] glue unavailable regcache registers / lval_register values / frame registers
This patch glues the regcache registers unavailableness
with frame unwinding, and lval_register values.
* sentinel-frame.c can now make use of the changes done in
the previous patch to mark register values as unvailable:
- regcache_cooked_read (cache->regcache, regnum, value_contents_raw (value));
+ if (regcache_cooked_read (cache->regcache,
+ regnum,
+ value_contents_raw (value)) == REG_UNAVAILABLE)
+ mark_value_bytes_unavailable (value, 0, register_size (gdbarch, regnum));
* frame_register_unwind and frame_register gain a new `unavailablep' output
boolean parameter, that is set to true if the register read or unwound is
unavailable. This is obviously modeled on the `optimizedp' output
parameter. Then all callers are updated.
* Adds a few basic tests around "info registers", and "print $regfoo".
"info frame" behaves better after this patch (no longer throws a "value
not available" error), but I'm saving adding a test for that for later.
As for the mandatory screenshots, here's Before Patch:
This is inspecting the traceframe of a tracepoint that
collected nothing.
(gdb) info registers
rax 0x0 0
rbx 0x0 0
rcx 0x0 0
rdx 0x0 0
rsi 0x0 0
rdi 0x0 0
rbp 0x0 0x0
rsp 0x0 0x0
r8 0x0 0
r9 0x0 0
r10 0x0 0
r11 0x0 0
r12 0x0 0
r13 0x0 0
r14 0x0 0
r15 0x0 0
rip 0x4007e4 0x4007e4 <globals_test_func()+4>
eflags 0x0 [ ]
cs 0x0 0
ss 0x0 0
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) info registers $rsp
rsp 0x0 0x0
(gdb) print /x $rbp
$1 = 0x0
(gdb) print /x $rsp
$2 = 0x0
And After Patch:
(gdb) info registers
rax *value not available*
rbx *value not available*
rcx *value not available*
rdx *value not available*
rsi *value not available*
rdi *value not available*
rbp *value not available*
rsp *value not available*
r8 *value not available*
r9 *value not available*
r10 *value not available*
r11 *value not available*
r12 *value not available*
r13 *value not available*
r14 *value not available*
r15 *value not available*
rip 0x4007d8 0x4007d8 <begin()+4>
eflags *value not available*
cs *value not available*
ss *value not available*
ds *value not available*
es *value not available*
fs *value not available*
gs *value not available*
(gdb) p $rax
$1 = <unavailable>
(gdb) p *$rax
value is not available
The "*value not available*" output of "info registers"
is not new (and it wasn't me who added it). Only the glue
was missing.
--
Pedro Alves
2011-02-22 Pedro Alves <pedro@codesourcery.com>
gdb/
* findvar.c (value_of_register): Mark the value as unavailable, if
the register is unavailable.
* frame.h (frame_register_unwind): New `unavailablep' parameter.
(frame_register): New `unavailablep' parameter.
(frame_register_read): Update comment.
* frame.c (frame_register_unwind): New `unavailablep' parameter.
Set it if the register is unavailable. If the register is
unavailable, clear the output buffer.
(frame_register): New `unavailablep' parameter. Pass it down.
(frame_unwind_register): Adjust.
(put_frame_register): Adjust.
(frame_register_read): Adjust. Also return false if the register
is not available.
(frame_register_unwind_location): Adjust.
* sentinel-frame.c (sentinel_frame_prev_register): If the register
is unavailable, mark the value accordingly.
* stack.c (frame_info): Handle unavailable registers.
gdb/testsuite/
* gdb.trace/unavailable.exp (fpreg, spreg, pcreg): Define.
(test_register, test_register_unavailable): New procedures.
(gdb_unavailable_registers_test): New procedure.
(gdb_trace_collection_test): Call it.
---
gdb/findvar.c | 15 ++++-
gdb/frame.c | 44 +++++++++-----
gdb/frame.h | 20 ++++--
gdb/sentinel-frame.c | 5 +
gdb/stack.c | 17 +++--
gdb/testsuite/gdb.trace/unavailable.exp | 96 ++++++++++++++++++++++++++++++++
6 files changed, 162 insertions(+), 35 deletions(-)
Index: src/gdb/findvar.c
===================================================================
--- src.orig/gdb/findvar.c 2011-02-21 20:30:31.979344010 +0000
+++ src/gdb/findvar.c 2011-02-22 11:39:35.994707993 +0000
@@ -265,6 +265,7 @@ value_of_register (int regnum, struct fr
struct gdbarch *gdbarch = get_frame_arch (frame);
CORE_ADDR addr;
int optim;
+ int unavail;
struct value *reg_val;
int realnum;
gdb_byte raw_buffer[MAX_REGISTER_SIZE];
@@ -276,16 +277,24 @@ value_of_register (int regnum, struct fr
+ gdbarch_num_pseudo_regs (gdbarch))
return value_of_user_reg (regnum, frame);
- frame_register (frame, regnum, &optim, &lval, &addr, &realnum, raw_buffer);
+ frame_register (frame, regnum, &optim, &unavail,
+ &lval, &addr, &realnum, raw_buffer);
reg_val = allocate_value (register_type (gdbarch, regnum));
- memcpy (value_contents_raw (reg_val), raw_buffer,
- register_size (gdbarch, regnum));
+ if (!optim && !unavail)
+ memcpy (value_contents_raw (reg_val), raw_buffer,
+ register_size (gdbarch, regnum));
+ else
+ memset (value_contents_raw (reg_val), 0,
+ register_size (gdbarch, regnum));
+
VALUE_LVAL (reg_val) = lval;
set_value_address (reg_val, addr);
VALUE_REGNUM (reg_val) = regnum;
set_value_optimized_out (reg_val, optim);
+ if (unavail)
+ mark_value_bytes_unavailable (reg_val, 0, register_size (gdbarch, regnum));
VALUE_FRAME_ID (reg_val) = get_frame_id (frame);
return reg_val;
}
Index: src/gdb/frame.c
===================================================================
--- src.orig/gdb/frame.c 2011-02-21 21:09:45.449344005 +0000
+++ src/gdb/frame.c 2011-02-22 11:39:36.004707990 +0000
@@ -757,8 +757,9 @@ frame_pop (struct frame_info *this_frame
void
frame_register_unwind (struct frame_info *frame, int regnum,
- int *optimizedp, enum lval_type *lvalp,
- CORE_ADDR *addrp, int *realnump, gdb_byte *bufferp)
+ int *optimizedp, int *unavailablep,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, gdb_byte *bufferp)
{
struct value *value;
@@ -775,13 +776,19 @@ frame_register_unwind (struct frame_info
gdb_assert (value != NULL);
*optimizedp = value_optimized_out (value);
+ *unavailablep = !value_entirely_available (value);
*lvalp = VALUE_LVAL (value);
*addrp = value_address (value);
*realnump = VALUE_REGNUM (value);
- if (bufferp && !*optimizedp)
- memcpy (bufferp, value_contents_all (value),
- TYPE_LENGTH (value_type (value)));
+ if (bufferp)
+ {
+ if (!*optimizedp && !*unavailablep)
+ memcpy (bufferp, value_contents_all (value),
+ TYPE_LENGTH (value_type (value)));
+ else
+ memset (bufferp, 0, TYPE_LENGTH (value_type (value)));
+ }
/* Dispose of the new value. This prevents watchpoints from
trying to watch the saved frame pointer. */
@@ -791,7 +798,7 @@ frame_register_unwind (struct frame_info
void
frame_register (struct frame_info *frame, int regnum,
- int *optimizedp, enum lval_type *lvalp,
+ int *optimizedp, int *unavailablep, enum lval_type *lvalp,
CORE_ADDR *addrp, int *realnump, gdb_byte *bufferp)
{
/* Require all but BUFFERP to be valid. A NULL BUFFERP indicates
@@ -805,20 +812,21 @@ frame_register (struct frame_info *frame
/* Obtain the register value by unwinding the register from the next
(more inner frame). */
gdb_assert (frame != NULL && frame->next != NULL);
- frame_register_unwind (frame->next, regnum, optimizedp, lvalp, addrp,
- realnump, bufferp);
+ frame_register_unwind (frame->next, regnum, optimizedp, unavailablep,
+ lvalp, addrp, realnump, bufferp);
}
void
frame_unwind_register (struct frame_info *frame, int regnum, gdb_byte *buf)
{
int optimized;
+ int unavailable;
CORE_ADDR addr;
int realnum;
enum lval_type lval;
- frame_register_unwind (frame, regnum, &optimized, &lval, &addr,
- &realnum, buf);
+ frame_register_unwind (frame, regnum, &optimized, &unavailable,
+ &lval, &addr, &realnum, buf);
}
void
@@ -940,10 +948,12 @@ put_frame_register (struct frame_info *f
struct gdbarch *gdbarch = get_frame_arch (frame);
int realnum;
int optim;
+ int unavail;
enum lval_type lval;
CORE_ADDR addr;
- frame_register (frame, regnum, &optim, &lval, &addr, &realnum, NULL);
+ frame_register (frame, regnum, &optim, &unavail,
+ &lval, &addr, &realnum, NULL);
if (optim)
error (_("Attempt to assign to a value that was optimized out."));
switch (lval)
@@ -978,13 +988,15 @@ frame_register_read (struct frame_info *
gdb_byte *myaddr)
{
int optimized;
+ int unavailable;
enum lval_type lval;
CORE_ADDR addr;
int realnum;
- frame_register (frame, regnum, &optimized, &lval, &addr, &realnum, myaddr);
+ frame_register (frame, regnum, &optimized, &unavailable,
+ &lval, &addr, &realnum, myaddr);
- return !optimized;
+ return !optimized && !unavailable;
}
int
@@ -1417,8 +1429,10 @@ frame_register_unwind_location (struct f
while (this_frame != NULL)
{
- frame_register_unwind (this_frame, regnum, optimizedp, lvalp,
- addrp, realnump, NULL);
+ int unavailable;
+
+ frame_register_unwind (this_frame, regnum, optimizedp, &unavailable,
+ lvalp, addrp, realnump, NULL);
if (*optimizedp)
break;
Index: src/gdb/frame.h
===================================================================
--- src.orig/gdb/frame.h 2011-02-21 20:30:31.979344010 +0000
+++ src/gdb/frame.h 2011-02-22 11:43:39.464708012 +0000
@@ -468,7 +468,8 @@ const char *frame_stop_reason_string (en
fetch/compute the value. Instead just return the location of the
value. */
extern void frame_register_unwind (struct frame_info *frame, int regnum,
- int *optimizedp, enum lval_type *lvalp,
+ int *optimizedp, int *unavailablep,
+ enum lval_type *lvalp,
CORE_ADDR *addrp, int *realnump,
gdb_byte *valuep);
@@ -504,7 +505,8 @@ extern ULONGEST get_frame_register_unsig
VALUEP is NULL, the registers value is not fetched/computed. */
extern void frame_register (struct frame_info *frame, int regnum,
- int *optimizedp, enum lval_type *lvalp,
+ int *optimizedp, int *unavailablep,
+ enum lval_type *lvalp,
CORE_ADDR *addrp, int *realnump,
gdb_byte *valuep);
@@ -651,12 +653,14 @@ extern int deprecated_pc_in_call_dummy (
/* FIXME: cagney/2003-02-02: Should be deprecated or replaced with a
function called get_frame_register_p(). This slightly weird (and
older) variant of get_frame_register() returns zero (indicating the
- register is unavailable) if either: the register isn't cached; or
- the register has been optimized out. Problem is, neither check is
- exactly correct. A register can't be optimized out (it may not
- have been saved as part of a function call); The fact that a
- register isn't in the register cache doesn't mean that the register
- isn't available (it could have been fetched from memory). */
+ register value is unavailable/invalid) if either: the register
+ isn't cached; or the register has been optimized out; or the
+ register contents are unavailable (because they haven't been
+ collected in a traceframe). Problem is, neither check is exactly
+ correct. A register can't be optimized out (it may not have been
+ saved as part of a function call); The fact that a register isn't
+ in the register cache doesn't mean that the register isn't
+ available (it could have been fetched from memory). */
extern int frame_register_read (struct frame_info *frame, int regnum,
gdb_byte *buf);
Index: src/gdb/sentinel-frame.c
===================================================================
--- src.orig/gdb/sentinel-frame.c 2011-02-21 20:30:31.979344010 +0000
+++ src/gdb/sentinel-frame.c 2011-02-22 11:39:36.064707994 +0000
@@ -61,7 +61,10 @@ sentinel_frame_prev_register (struct fra
/* Use the regcache_cooked_read() method so that it, on the fly,
constructs either a raw or pseudo register from the raw
register cache. */
- regcache_cooked_read (cache->regcache, regnum, value_contents_raw (value));
+ if (regcache_cooked_read (cache->regcache,
+ regnum,
+ value_contents_raw (value)) == REG_UNAVAILABLE)
+ mark_value_bytes_unavailable (value, 0, register_size (gdbarch, regnum));
return value;
}
Index: src/gdb/stack.c
===================================================================
--- src.orig/gdb/stack.c 2011-02-21 20:30:31.979344010 +0000
+++ src/gdb/stack.c 2011-02-22 11:39:36.064707994 +0000
@@ -1186,6 +1186,7 @@ frame_info (char *addr_exp, int from_tty
{
enum lval_type lval;
int optimized;
+ int unavailable;
CORE_ADDR addr;
int realnum;
int count;
@@ -1202,9 +1203,9 @@ frame_info (char *addr_exp, int from_tty
/* Find out the location of the saved stack pointer with out
actually evaluating it. */
frame_register_unwind (fi, gdbarch_sp_regnum (gdbarch),
- &optimized, &lval, &addr,
+ &optimized, &unavailable, &lval, &addr,
&realnum, NULL);
- if (!optimized && lval == not_lval)
+ if (!optimized && !unavailable && lval == not_lval)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int sp_size = register_size (gdbarch, gdbarch_sp_regnum (gdbarch));
@@ -1212,7 +1213,7 @@ frame_info (char *addr_exp, int from_tty
CORE_ADDR sp;
frame_register_unwind (fi, gdbarch_sp_regnum (gdbarch),
- &optimized, &lval, &addr,
+ &optimized, &unavailable, &lval, &addr,
&realnum, value);
/* NOTE: cagney/2003-05-22: This is assuming that the
stack pointer was packed as an unsigned integer. That
@@ -1223,14 +1224,14 @@ frame_info (char *addr_exp, int from_tty
printf_filtered ("\n");
need_nl = 0;
}
- else if (!optimized && lval == lval_memory)
+ else if (!optimized && !unavailable && lval == lval_memory)
{
printf_filtered (" Previous frame's sp at ");
fputs_filtered (paddress (gdbarch, addr), gdb_stdout);
printf_filtered ("\n");
need_nl = 0;
}
- else if (!optimized && lval == lval_register)
+ else if (!optimized && !unavailable && lval == lval_register)
{
printf_filtered (" Previous frame's sp in %s\n",
gdbarch_register_name (gdbarch, realnum));
@@ -1248,11 +1249,11 @@ frame_info (char *addr_exp, int from_tty
{
/* Find out the location of the saved register without
fetching the corresponding value. */
- frame_register_unwind (fi, i, &optimized, &lval, &addr, &realnum,
- NULL);
+ frame_register_unwind (fi, i, &optimized, &unavailable,
+ &lval, &addr, &realnum, NULL);
/* For moment, only display registers that were saved on the
stack. */
- if (!optimized && lval == lval_memory)
+ if (!optimized && !unavailable && lval == lval_memory)
{
if (count == 0)
puts_filtered (" Saved registers:\n ");
Index: src/gdb/testsuite/gdb.trace/unavailable.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.trace/unavailable.exp 2011-02-21 20:30:31.979344010 +0000
+++ src/gdb/testsuite/gdb.trace/unavailable.exp 2011-02-22 11:39:36.064707994 +0000
@@ -30,10 +30,48 @@ if { [gdb_compile "$srcdir/$subdir/$srcf
set ws "\[\r\n\t \]+"
set cr "\[\r\n\]+"
+if [istarget "x86_64-*"] then {
+ set fpreg "rbp"
+ set spreg "rsp"
+ set pcreg "rip"
+} elseif [istarget "i?86-*"] then {
+ set fpreg "ebp"
+ set spreg "esp"
+ set pcreg "eip"
+} else {
+ set fpreg "fp"
+ set spreg "sp"
+ set pcreg "pc"
+}
+
#
# Utility procs
#
+proc test_register { reg } {
+ global gdb_prompt
+ global hex
+ global cr
+
+ gdb_test_multiple "print /x $reg" "collected $reg" {
+ -re "\\$\[0-9\]+ = \[x0\]+$cr$gdb_prompt $" {
+ fail "collected $reg (zero)"
+ }
+ -re "\\$\[0-9\]+ = $hex$cr$gdb_prompt $" {
+ pass "collected $reg"
+ }
+ -re "\[Ee\]rror.*$gdb_prompt $" {
+ fail "collected $reg (error)"
+ }
+ }
+}
+
+proc test_register_unavailable { reg } {
+ gdb_test "print /x $reg" \
+ "<unavailable>" \
+ "correctly report $reg as unavailable"
+}
+
proc prepare_for_trace_test {} {
global executable
@@ -69,6 +107,63 @@ proc run_trace_experiment { test_func }
# Test procs
#
+proc gdb_unavailable_registers_test { } {
+ global gdb_prompt
+ global spreg
+ global pcreg
+ global pf_prefix
+
+ set old_pf_prefix $pf_prefix
+ set pf_prefix "$pf_prefix unavailable registers:"
+
+ prepare_for_trace_test
+
+ # We'll simply re-use the globals_test_function for this test
+ gdb_test "trace globals_test_func" \
+ "Tracepoint \[0-9\]+ at .*" \
+ "set tracepoint"
+
+ # Collect nothing.
+
+ # Begin the test.
+ run_trace_experiment globals_test_func
+
+ # On some archs, the $sp/$pc are a real raw registers. On others,
+ # like x86, they're user registers. Test both variants.
+ test_register_unavailable "\$$spreg"
+ test_register_unavailable "\$sp"
+
+ # Test reading uncollected pseudo-registers. The set of which
+ # depends on target.
+ if [istarget "x86_64-*"] then {
+ # Check the raw register first.
+ test_register_unavailable "\$rax"
+ test_register_unavailable "\$eax"
+ test_register_unavailable "\$ax"
+ } elseif [istarget "i?86-*"] then {
+ # Check the raw register first.
+ test_register_unavailable "\$eax"
+ test_register_unavailable "\$ax"
+ }
+
+ # GDBserver always provides the PC value of regular tracepoint
+ # hits, since it's the same as the tracepoint's address.
+ test_register "\$$pcreg"
+ test_register "\$pc"
+
+ gdb_test "info registers" \
+ "\\*value not available\\*.*\\*value not available\\*" \
+ "info registers, multiple registers not available"
+
+ gdb_test "info registers \$$spreg" \
+ "\\*value not available\\*" \
+ "info registers \$$spreg reports not available"
+
+ gdb_test "tfind none" "#0 end .*" "cease trace debugging"
+
+ set pf_prefix $old_pf_prefix
+}
+
proc gdb_collect_globals_test { } {
global ws
global cr
@@ -321,6 +416,7 @@ proc gdb_collect_globals_test { } {
proc gdb_trace_collection_test {} {
gdb_collect_globals_test
+ gdb_unavailable_registers_test
}
clean_restart $executable