[PATCH 1/8] gdb: Add supply_pseudo_pc hook to gdbarch.

Marcin Kościelnicki
2016-02-07T13:59:54Z

When we're looking at a tracefile trace frame where registers are not
available, and the tracepoint has only one location, we supply
the location's address as the PC register. However, this only works
if PC is not a pseudo register. Add a gdbarch hook that will handle
that for pseudo registers.

gdb/ChangeLog:

* gdbarch.c: Regenerate.
* gdbarch.h: Regenerate.
* gdbarch.sh: Add supply_pseudo_pc hook.
* tracefile.c (tracefile_fetch_registers): When PC is a pseudo,
ask gdbarch to handle guessed PC via the new hook.
---
I've changed the name of the hook to supply_pseudo_pc, added missing
comments, and fixed the indent issue.

I don't think generalizing that function to arbitrary pseudo registers
is a good idea - there's already a pseudo_register_write, which covers
writing them. The only reason I'm not using it in tracepoint.c is
that there's no "current register state" to write over, I'm making
a new one from scratch.

Perhaps a better generalization would be to instead make a hook that's
supposed to provide a whole regcache based on the tracepoint location,
not just PC? I can imagine a few uses for that:

- on machines like ARM where you have several instruction encodings,
you could also supply whatever registers determine the encoding
(arm vs thumb) based on the location.
- if you can determine from the debug information that the function
keeps GOT pointer / literal pool pointer / whatever in some register
at the tracepoint location, you can also guess the contents of these
(although that's probably not all that useful, and can be a lie
if something is horribly buggy).

gdb/ChangeLog | 8 ++++++++
gdb/gdbarch.c | 32 ++++++++++++++++++++++++++++++++
gdb/gdbarch.h | 11 +++++++++++
gdb/gdbarch.sh | 5 +++++
gdb/tracefile.c | 40 +++++++++++++++++++++++++++-------------
5 files changed, 83 insertions(+), 13 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index a2b0d39..e0bfd21 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,11 @@
+2016-02-07 Marcin KoÅ›cielnicki <koriakin@0x04.net>
+
+ * gdbarch.c: Regenerate.
+ * gdbarch.h: Regenerate.
+ * gdbarch.sh: Add supply_pseudo_pc hook.
+ * tracefile.c (tracefile_fetch_registers): When PC is a pseudo,
+ ask gdbarch to handle guessed PC via the new hook.
+
2016-02-04 Yao Qi <yao.qi@linaro.org>

* remote.c (remote_wait_as): Set rs->waiting_for_stop_reply to
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 4143744..b323d44 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -181,6 +181,7 @@ struct gdbarch
int char_signed;
gdbarch_read_pc_ftype *read_pc;
gdbarch_write_pc_ftype *write_pc;
+ gdbarch_supply_pseudo_pc_ftype *supply_pseudo_pc;
gdbarch_virtual_frame_pointer_ftype *virtual_frame_pointer;
gdbarch_pseudo_register_read_ftype *pseudo_register_read;
gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value;
@@ -523,6 +524,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
gdbarch->char_signed = 1;
/* Skip verify of read_pc, has predicate. */
/* Skip verify of write_pc, has predicate. */
+ /* Skip verify of supply_pseudo_pc, has predicate. */
/* Skip verify of virtual_frame_pointer, invalid_p == 0 */
/* Skip verify of pseudo_register_read, has predicate. */
/* Skip verify of pseudo_register_read_value, has predicate. */
@@ -1366,6 +1368,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
"gdbarch_dump: static_transform_name = <%s>\n",
host_address_to_string (gdbarch->static_transform_name));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_supply_pseudo_pc_p() = %d\n",
+ gdbarch_supply_pseudo_pc_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: supply_pseudo_pc = <%s>\n",
+ host_address_to_string (gdbarch->supply_pseudo_pc));
+ fprintf_unfiltered (file,
"gdbarch_dump: syscalls_info = %s\n",
host_address_to_string (gdbarch->syscalls_info));
fprintf_unfiltered (file,
@@ -1821,6 +1829,30 @@ set_gdbarch_write_pc (struct gdbarch *gdbarch,
gdbarch->write_pc = write_pc;
}

+int
+gdbarch_supply_pseudo_pc_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->supply_pseudo_pc != NULL;
+}
+
+void
+gdbarch_supply_pseudo_pc (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR val)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->supply_pseudo_pc != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_supply_pseudo_pc called\n");
+ gdbarch->supply_pseudo_pc (regcache, val);
+}
+
+void
+set_gdbarch_supply_pseudo_pc (struct gdbarch *gdbarch,
+ gdbarch_supply_pseudo_pc_ftype supply_pseudo_pc)
+{
+ gdbarch->supply_pseudo_pc = supply_pseudo_pc;
+}
+
void
gdbarch_virtual_frame_pointer (struct gdbarch *gdbarch, CORE_ADDR pc, int *frame_regnum, LONGEST *frame_offset)
{
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 3fadcd1..bbb0ba3 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -239,6 +239,17 @@ typedef void (gdbarch_write_pc_ftype) (struct regcache *regcache, CORE_ADDR val)
extern void gdbarch_write_pc (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR val);
extern void set_gdbarch_write_pc (struct gdbarch *gdbarch, gdbarch_write_pc_ftype *write_pc);

+/* Supply given PC into an empty regcache. Used for tracepoints where
+ no registers have been collected, but there's only one location,
+ allowing us to guess the PC value. Only used on targets where PC
+ is a pseudo-register - otherwise PC is supplied directly. */
+
+extern int gdbarch_supply_pseudo_pc_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_supply_pseudo_pc_ftype) (struct regcache *regcache, CORE_ADDR val);
+extern void gdbarch_supply_pseudo_pc (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR val);
+extern void set_gdbarch_supply_pseudo_pc (struct gdbarch *gdbarch, gdbarch_supply_pseudo_pc_ftype *supply_pseudo_pc);
+
/* Function for getting target's idea of a frame pointer. FIXME: GDB's
whole scheme for dealing with "frames" and "frame pointers" needs a
serious shakedown. */
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 4ac6b90..346dc3a 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -417,6 +417,11 @@ v:int:char_signed:::1:-1:1
#
F:CORE_ADDR:read_pc:struct regcache *regcache:regcache
F:void:write_pc:struct regcache *regcache, CORE_ADDR val:regcache, val
+# Supply given PC into an empty regcache. Used for tracepoints where
+# no registers have been collected, but there's only one location,
+# allowing us to guess the PC value. Only used on targets where PC
+# is a pseudo-register - otherwise PC is supplied directly.
+F:void:supply_pseudo_pc:struct regcache *regcache, CORE_ADDR val:regcache, val
# Function for getting target's idea of a frame pointer. FIXME: GDB's
# whole scheme for dealing with "frames" and "frame pointers" needs a
# serious shakedown.
diff --git a/gdb/tracefile.c b/gdb/tracefile.c
index fef4ed9..e3d7b0e 100644
--- a/gdb/tracefile.c
+++ b/gdb/tracefile.c
@@ -396,16 +396,18 @@ tracefile_fetch_registers (struct regcache *regcache, int regno)
as the address of the tracepoint. */
pc_regno = gdbarch_pc_regnum (gdbarch);

- /* XXX This guessing code below only works if the PC register isn't
- a pseudo-register. The value of a pseudo-register isn't stored
- in the (non-readonly) regcache -- instead it's recomputed
- (probably from some other cached raw register) whenever the
- register is read. This guesswork should probably move to some
- higher layer. */
- if (pc_regno < 0 || pc_regno >= gdbarch_num_regs (gdbarch))
+ if (pc_regno < 0)
return;

- if (regno == -1 || regno == pc_regno)
+ /* We try to guess PC if:
+
+ 1) We want all registers, or
+ 2) PC is a real register, and we want exactly it, or
+ 3) PC is a pseudo register (we don't know which real register it
+ corresponds to, so let's try to play safe). */
+
+ if (regno == -1 || regno == pc_regno ||
+ pc_regno >= gdbarch_num_regs (gdbarch))
{
struct tracepoint *tp = get_tracepoint (get_tracepoint_number ());
gdb_byte *regs;
@@ -429,11 +431,23 @@ tracefile_fetch_registers (struct regcache *regcache, int regno)
return;
}

- regs = (gdb_byte *) alloca (register_size (gdbarch, pc_regno));
- store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
- gdbarch_byte_order (gdbarch),
- tp->base.loc->address);
- regcache_raw_supply (regcache, pc_regno, regs);
+ if (pc_regno >= gdbarch_num_regs (gdbarch))
+ {
+ /* PC is a pseudo, let gdbarch deal with that. If it doesn't
+ know how, just bail. */
+ if (gdbarch_supply_pseudo_pc_p (gdbarch))
+ gdbarch_supply_pseudo_pc (gdbarch, regcache,
+ tp->base.loc->address);
+ }
+ else
+ {
+ /* PC is a real register. */
+ regs = (gdb_byte *) alloca (register_size (gdbarch, pc_regno));
+ store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
+ gdbarch_byte_order (gdbarch),
+ tp->base.loc->address);
+ regcache_raw_supply (regcache, pc_regno, regs);
+ }
}
}
}
--
2.7.0