[PATCH v2 3/9] add target method delegation

Tom Tromey
2013-07-31T18:28:19Z

This patch replaces some code in the record targets with target method
delegation.

Right now there are two latent problems in the record target.

First, record-full.c stores pointers to many target methods when the
record target is pushed. Then it later delegates some calls via
these. This is wrong because it violates the target stack contract.
In particular it is ok to unpush a target at any stratum, but
record-full does not keep track of this, so it could potentially call
into an unpushed target.

Second, RECORD_IS_USED and some other spots look at
current_target.to_stratum to determine whether a record target is in
use. This is bad because arch_stratum is greater than record_stratum.

To fix the first problem, this patch introduces a handful of
target_delegate_* functions, which forward calls further down the
target stack.

To fix the second problem, this patch adds find_target_at to determine
whether a target appears at a given stratum. This may seem like
overkill somehow, but I have a subsequent patch series (see archer.git
tromey/multi-target) that uses it more heavily.

* record-full.c (record_full_beneath_to_resume_ops)
(record_full_beneath_to_resume, record_full_beneath_to_wait_ops)
(record_full_beneath_to_wait)
(record_full_beneath_to_store_registers_ops)
(record_full_beneath_to_store_registers)
(record_full_beneath_to_xfer_partial_ops)
(record_full_beneath_to_xfer_partial)
(record_full_beneath_to_insert_breakpoint)
(record_full_beneath_to_remove_breakpoint)
(record_full_beneath_to_stopped_by_watchpoint)
(record_full_beneath_to_stopped_data_address)
(record_full_beneath_to_async, tmp_to_resume_ops, tmp_to_resume)
(tmp_to_wait_ops, tmp_to_wait, tmp_to_store_registers_ops)
(tmp_to_store_registers, tmp_to_xfer_partial_ops)
(tmp_to_xfer_partial, tmp_to_insert_breakpoint)
(tmp_to_remove_breakpoint, tmp_to_stopped_by_watchpoint)
(tmp_to_stopped_data_address, tmp_to_async): Remove.
(record_full_open_1, record_full_open): Update. Use RECORD_IS_USED.
(record_full_resume, record_full_wait_1)
(record_full_stopped_by_watchpoint, record_full_stopped_data_address)
(record_full_store_registers, record_full_xfer_partial)
(record_full_insert_breakpoint, record_full_remove_breakpoint)
(record_full_async, record_full_can_async_p, record_full_is_async_p)
(record_full_core_xfer_partial): Use target delegation.
* record.c (find_record_target): Use find_target_at.
* record.h (RECORD_IS_USED): Use find_target_at.
* target.c (update_current_target): Use target_delegate_xfer_partial.
(target_delegate_xfer_partial): Now public. Renamed from...
(current_xfer_partial): ...this. Remove.
(target_delegate_async, target_delegate_is_async_p)
(target_delegate_can_async_p, target_delegate_insert_breakpoint)
(target_delegate_remove_breakpoint, target_delegate_wait)
(target_delegate_resume, find_target_at)
(target_delegate_store_registers)
(target_delegate_stopped_by_watchpoint)
(target_delegate_stopped_data_address): New functions.
* target.h (target_delegate_async, target_delegate_is_async_p)
(target_delegate_can_async_p, target_delegate_insert_breakpoint)
(target_delegate_remove_breakpoint, target_delegate_wait)
(target_delegate_resume, find_target_at)
(target_delegate_store_registers)
(target_delegate_stopped_by_watchpoint)
(target_delegate_stopped_data_address)
(target_delegate_xfer_partial): Declare.
---
gdb/record-full.c | 209 ++++++------------------------------------------------
gdb/record.c | 8 +--
gdb/record.h | 2 +-
gdb/target.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++----
gdb/target.h | 76 ++++++++++++++++++--
5 files changed, 287 insertions(+), 214 deletions(-)

diff --git a/gdb/record-full.c b/gdb/record-full.c
index 4904225..e8f46a6 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -215,43 +215,6 @@ static struct cmd_list_element *show_record_full_cmdlist;
/* Command list for "record full". */
static struct cmd_list_element *record_full_cmdlist;

-/* The beneath function pointers. */
-static struct target_ops *record_full_beneath_to_resume_ops;
-static void (*record_full_beneath_to_resume) (struct target_ops *, ptid_t, int,
- enum gdb_signal);
-static struct target_ops *record_full_beneath_to_wait_ops;
-static ptid_t (*record_full_beneath_to_wait) (struct target_ops *, ptid_t,
- struct target_waitstatus *,
- int);
-static struct target_ops *record_full_beneath_to_store_registers_ops;
-static void (*record_full_beneath_to_store_registers) (struct target_ops *,
- struct regcache *,
- int regno);
-static struct target_ops *record_full_beneath_to_xfer_partial_ops;
-static LONGEST
- (*record_full_beneath_to_xfer_partial) (struct target_ops *ops,
- enum target_object object,
- const char *annex,
- gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset,
- LONGEST len);
-static int
- (*record_full_beneath_to_insert_breakpoint) (struct target_ops *,
- struct gdbarch *,
- struct bp_target_info *);
-static int
- (*record_full_beneath_to_remove_breakpoint) (struct target_ops *,
- struct gdbarch *,
- struct bp_target_info *);
-static int (*record_full_beneath_to_stopped_by_watchpoint) (struct target_ops *);
-static int (*record_full_beneath_to_stopped_data_address) (struct target_ops *,
- CORE_ADDR *);
-static void
- (*record_full_beneath_to_async) (struct target_ops *,
- void (*) (enum inferior_event_type, void *),
- void *);
-
static void record_full_goto_insn (struct record_full_entry *entry,
enum exec_direction_kind dir);
static void record_full_save (const char *recfilename);
@@ -798,36 +761,6 @@ record_full_exec_insn (struct regcache *regcache,
}
}

-static struct target_ops *tmp_to_resume_ops;
-static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
- enum gdb_signal);
-static struct target_ops *tmp_to_wait_ops;
-static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
- struct target_waitstatus *,
- int);
-static struct target_ops *tmp_to_store_registers_ops;
-static void (*tmp_to_store_registers) (struct target_ops *,
- struct regcache *,
- int regno);
-static struct target_ops *tmp_to_xfer_partial_ops;
-static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
- enum target_object object,
- const char *annex,
- gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset,
- LONGEST len);
-static int (*tmp_to_insert_breakpoint) (struct target_ops *,
- struct gdbarch *,
- struct bp_target_info *);
-static int (*tmp_to_remove_breakpoint) (struct target_ops *ops,
- struct gdbarch *,
- struct bp_target_info *);
-static int (*tmp_to_stopped_by_watchpoint) (struct target_ops *);
-static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
-static void (*tmp_to_async) (struct target_ops *,
- void (*) (enum inferior_event_type, void *), void *);
-
static void record_full_restore (void);

/* Asynchronous signal handle registered as event loop source for when
@@ -890,26 +823,6 @@ record_full_open_1 (char *name, int from_tty)
error (_("Process record: the current architecture doesn't support "
"record function."));

- if (!tmp_to_resume)
- error (_("Could not find 'to_resume' method on the target stack."));
- if (!tmp_to_wait)
- error (_("Could not find 'to_wait' method on the target stack."));
- if (!tmp_to_store_registers)
- error (_("Could not find 'to_store_registers' "
- "method on the target stack."));
- if (!tmp_to_insert_breakpoint)
- error (_("Could not find 'to_insert_breakpoint' "
- "method on the target stack."));
- if (!tmp_to_remove_breakpoint)
- error (_("Could not find 'to_remove_breakpoint' "
- "method on the target stack."));
- if (!tmp_to_stopped_by_watchpoint)
- error (_("Could not find 'to_stopped_by_watchpoint' "
- "method on the target stack."));
- if (!tmp_to_stopped_data_address)
- error (_("Could not find 'to_stopped_data_address' "
- "method on the target stack."));
-
push_target (&record_full_ops);
}

@@ -926,83 +839,16 @@ record_full_open (char *name, int from_tty)
fprintf_unfiltered (gdb_stdlog, "Process record: record_full_open\n");

/* Check if record target is already running. */
- if (current_target.to_stratum == record_stratum)
+ if (RECORD_IS_USED)
error (_("Process record target already running. Use \"record stop\" to "
"stop record target first."));

- /* Reset the tmp beneath pointers. */
- tmp_to_resume_ops = NULL;
- tmp_to_resume = NULL;
- tmp_to_wait_ops = NULL;
- tmp_to_wait = NULL;
- tmp_to_store_registers_ops = NULL;
- tmp_to_store_registers = NULL;
- tmp_to_xfer_partial_ops = NULL;
- tmp_to_xfer_partial = NULL;
- tmp_to_insert_breakpoint = NULL;
- tmp_to_remove_breakpoint = NULL;
- tmp_to_stopped_by_watchpoint = NULL;
- tmp_to_stopped_data_address = NULL;
- tmp_to_async = NULL;
-
- /* Set the beneath function pointers. */
- for (t = current_target.beneath; t != NULL; t = t->beneath)
- {
- if (!tmp_to_resume)
- {
- tmp_to_resume = t->to_resume;
- tmp_to_resume_ops = t;
- }
- if (!tmp_to_wait)
- {
- tmp_to_wait = t->to_wait;
- tmp_to_wait_ops = t;
- }
- if (!tmp_to_store_registers)
- {
- tmp_to_store_registers = t->to_store_registers;
- tmp_to_store_registers_ops = t;
- }
- if (!tmp_to_xfer_partial)
- {
- tmp_to_xfer_partial = t->to_xfer_partial;
- tmp_to_xfer_partial_ops = t;
- }
- if (!tmp_to_insert_breakpoint)
- tmp_to_insert_breakpoint = t->to_insert_breakpoint;
- if (!tmp_to_remove_breakpoint)
- tmp_to_remove_breakpoint = t->to_remove_breakpoint;
- if (!tmp_to_stopped_by_watchpoint)
- tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
- if (!tmp_to_stopped_data_address)
- tmp_to_stopped_data_address = t->to_stopped_data_address;
- if (!tmp_to_async)
- tmp_to_async = t->to_async;
- }
- if (!tmp_to_xfer_partial)
- error (_("Could not find 'to_xfer_partial' method on the target stack."));
-
/* Reset */
record_full_insn_num = 0;
record_full_insn_count = 0;
record_full_list = &record_full_first;
record_full_list->next = NULL;

- /* Set the tmp beneath pointers to beneath pointers. */
- record_full_beneath_to_resume_ops = tmp_to_resume_ops;
- record_full_beneath_to_resume = tmp_to_resume;
- record_full_beneath_to_wait_ops = tmp_to_wait_ops;
- record_full_beneath_to_wait = tmp_to_wait;
- record_full_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
- record_full_beneath_to_store_registers = tmp_to_store_registers;
- record_full_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops;
- record_full_beneath_to_xfer_partial = tmp_to_xfer_partial;
- record_full_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
- record_full_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
- record_full_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
- record_full_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
- record_full_beneath_to_async = tmp_to_async;
-
if (core_bfd)
record_full_core_open_1 (name, from_tty);
else
@@ -1126,8 +972,7 @@ record_full_resume (struct target_ops *ops, ptid_t ptid, int step,
/* Make sure the target beneath reports all signals. */
target_pass_signals (0, NULL);

- record_full_beneath_to_resume (record_full_beneath_to_resume_ops,
- ptid, step, signal);
+ target_delegate_resume (ops, ptid, step, signal);
}

/* We are about to start executing the inferior (or simulate it),
@@ -1217,8 +1062,7 @@ record_full_wait_1 (struct target_ops *ops,
if (record_full_resume_step)
{
/* This is a single step. */
- return record_full_beneath_to_wait (record_full_beneath_to_wait_ops,
- ptid, status, options);
+ return target_delegate_wait (ops, ptid, status, options);
}
else
{
@@ -1229,8 +1073,7 @@ record_full_wait_1 (struct target_ops *ops,

while (1)
{
- ret = record_full_beneath_to_wait
- (record_full_beneath_to_wait_ops, ptid, status, options);
+ ret = target_delegate_wait (ops, ptid, status, options);
if (status->kind == TARGET_WAITKIND_IGNORE)
{
if (record_debug)
@@ -1314,9 +1157,7 @@ record_full_wait_1 (struct target_ops *ops,
"Process record: record_full_wait "
"issuing one more step in the "
"target beneath\n");
- record_full_beneath_to_resume
- (record_full_beneath_to_resume_ops, ptid, step,
- GDB_SIGNAL_0);
+ target_delegate_resume (ops, ptid, step, GDB_SIGNAL_0);
continue;
}
}
@@ -1520,7 +1361,7 @@ record_full_stopped_by_watchpoint (struct target_ops *ops)
if (RECORD_FULL_IS_REPLAY)
return record_full_hw_watchpoint;
else
- return record_full_beneath_to_stopped_by_watchpoint (find_target_beneath (ops));
+ return target_delegate_stopped_by_watchpoint (ops);
}

static int
@@ -1529,7 +1370,7 @@ record_full_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
if (RECORD_FULL_IS_REPLAY)
return 0;
else
- return record_full_beneath_to_stopped_data_address (ops, addr_p);
+ return target_delegate_stopped_data_address (ops, addr_p);
}

/* Record registers change (by user or by GDB) to list as an instruction. */
@@ -1632,8 +1473,7 @@ record_full_store_registers (struct target_ops *ops,

record_full_registers_change (regcache, regno);
}
- record_full_beneath_to_store_registers
- (record_full_beneath_to_store_registers_ops, regcache, regno);
+ target_delegate_store_registers (ops, regcache, regno);
}

/* "to_xfer_partial" method. Behavior is conditional on
@@ -1698,9 +1538,8 @@ record_full_xfer_partial (struct target_ops *ops, enum target_object object,
record_full_insn_num++;
}

- return record_full_beneath_to_xfer_partial
- (record_full_beneath_to_xfer_partial_ops, object, annex,
- readbuf, writebuf, offset, len);
+ return target_delegate_xfer_partial (ops, object, annex,
+ readbuf, writebuf, offset, len);
}

/* This structure represents a breakpoint inserted while the record
@@ -1781,8 +1620,7 @@ record_full_insert_breakpoint (struct target_ops *ops,
int ret;

old_cleanups = record_full_gdb_operation_disable_set ();
- ret = record_full_beneath_to_insert_breakpoint (find_target_beneath (ops),
- gdbarch, bp_tgt);
+ ret = target_delegate_insert_breakpoint (ops, gdbarch, bp_tgt);
do_cleanups (old_cleanups);

if (ret != 0)
@@ -1823,8 +1661,7 @@ record_full_remove_breakpoint (struct target_ops *ops,
int ret;

old_cleanups = record_full_gdb_operation_disable_set ();
- ret = record_full_beneath_to_remove_breakpoint (find_target_beneath (ops),
- gdbarch, bp_tgt);
+ ret = target_delegate_remove_breakpoint (ops, gdbarch, bp_tgt);
do_cleanups (old_cleanups);

if (ret != 0)
@@ -1906,22 +1743,19 @@ record_full_async (struct target_ops *ops,
/* If we're on top of a line target (e.g., linux-nat, remote), then
set it to async mode as well. Will be NULL if we're sitting on
top of the core target, for "record restore". */
- if (record_full_beneath_to_async != NULL)
- record_full_beneath_to_async (find_target_beneath (ops), callback, context);
+ target_delegate_async (ops, callback, context);
}

static int
record_full_can_async_p (struct target_ops *ops)
{
- /* We only enable async when the user specifically asks for it. */
- return target_async_permitted;
+ return target_delegate_can_async_p (ops);
}

static int
record_full_is_async_p (struct target_ops *ops)
{
- /* We only enable async when the user specifically asks for it. */
- return target_async_permitted;
+ return target_delegate_is_async_p (ops);
}

static enum exec_direction_kind
@@ -2247,10 +2081,10 @@ record_full_core_xfer_partial (struct target_ops *ops,
else
{
if (!entry)
- return record_full_beneath_to_xfer_partial
- (record_full_beneath_to_xfer_partial_ops,
- object, annex, readbuf, writebuf,
- offset, len);
+ return target_delegate_xfer_partial (ops,
+ object, annex,
+ readbuf, writebuf,
+ offset, len);

memcpy (readbuf, entry->buf + sec_offset,
(size_t) len);
@@ -2266,9 +2100,8 @@ record_full_core_xfer_partial (struct target_ops *ops,
error (_("You can't do that without a process to debug."));
}

- return record_full_beneath_to_xfer_partial
- (record_full_beneath_to_xfer_partial_ops, object, annex,
- readbuf, writebuf, offset, len);
+ return target_delegate_xfer_partial (ops, object, annex,
+ readbuf, writebuf, offset, len);
}

/* "to_insert_breakpoint" method for prec over corefile. */
diff --git a/gdb/record.c b/gdb/record.c
index cbbe365..8a199f9 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -62,13 +62,7 @@ struct cmd_list_element *info_record_cmdlist = NULL;
static struct target_ops *
find_record_target (void)
{
- struct target_ops *t;
-
- for (t = current_target.beneath; t != NULL; t = t->beneath)
- if (t->to_stratum == record_stratum)
- return t;
-
- return NULL;
+ return find_target_at (record_stratum);
}

/* Check that recording is active. Throw an error, if it isn't. */
diff --git a/gdb/record.h b/gdb/record.h
index 86e6bc6..00fbd3f 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -22,7 +22,7 @@

struct cmd_list_element;

-#define RECORD_IS_USED (current_target.to_stratum == record_stratum)
+#define RECORD_IS_USED (find_target_at (record_stratum) != NULL)

extern unsigned int record_debug;

diff --git a/gdb/target.c b/gdb/target.c
index b6b54cf..097e15f 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -75,12 +75,6 @@ static LONGEST default_xfer_partial (struct target_ops *ops,
const gdb_byte *writebuf,
ULONGEST offset, LONGEST len);

-static LONGEST current_xfer_partial (struct target_ops *ops,
- enum target_object object,
- const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset, LONGEST len);
-
static LONGEST target_xfer_partial (struct target_ops *ops,
enum target_object object,
const char *annex,
@@ -858,7 +852,7 @@ update_current_target (void)
de_fault (to_stop,
(void (*) (ptid_t))
target_ignore);
- current_target.to_xfer_partial = current_xfer_partial;
+ current_target.to_xfer_partial = target_delegate_xfer_partial;
de_fault (to_rcmd,
(void (*) (char *, struct ui_file *))
tcomplain);
@@ -1986,14 +1980,14 @@ default_xfer_partial (struct target_ops *ops, enum target_object object,
return -1;
}

-/* The xfer_partial handler for the topmost target. Unlike the default,
- it does not need to handle memory specially; it just passes all
- requests down the stack. */
+/* See target.h. */

-static LONGEST
-current_xfer_partial (struct target_ops *ops, enum target_object object,
- const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+LONGEST
+target_delegate_xfer_partial (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
{
if (ops->beneath != NULL)
return ops->beneath->to_xfer_partial (ops->beneath, object, annex,
@@ -2002,6 +1996,53 @@ current_xfer_partial (struct target_ops *ops, enum target_object object,
return -1;
}

+/* See target.h. */
+
+void
+target_delegate_async (struct target_ops *self,
+ void (*callback) (enum inferior_event_type, void *),
+ void *datum)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_async)
+ {
+ t->to_async (t, callback, datum);
+ break;
+ }
+ }
+}
+
+/* See target.h. */
+
+int
+target_delegate_is_async_p (struct target_ops *self)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ if (t->to_is_async_p != NULL)
+ return t->to_is_async_p (t);
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
+/* See target.h. */
+
+int
+target_delegate_can_async_p (struct target_ops *self)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ if (t->to_can_async_p != NULL)
+ return t->to_can_async_p (t);
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
/* Target vector read/write partial wrapper functions. */

static LONGEST
@@ -2453,6 +2494,24 @@ target_insert_breakpoint (struct gdbarch *gdbarch,
gdbarch, bp_tgt);
}

+/* See target.h. */
+
+int
+target_delegate_insert_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_insert_breakpoint)
+ return t->to_insert_breakpoint (t, gdbarch, bp_tgt);
+ }
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
int
target_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
@@ -2471,6 +2530,24 @@ target_remove_breakpoint (struct gdbarch *gdbarch,
gdbarch, bp_tgt);
}

+/* See target.h. */
+
+int
+target_delegate_remove_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_remove_breakpoint)
+ return t->to_remove_breakpoint (t, gdbarch, bp_tgt);
+ }
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
static void
target_info (char *args, int from_tty)
{
@@ -2677,6 +2754,24 @@ target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
noprocess ();
}

+/* See target.h. */
+
+ptid_t
+target_delegate_wait (struct target_ops *self,
+ ptid_t ptid, struct target_waitstatus *status,
+ int options)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_wait)
+ return t->to_wait (t, ptid, status, options);
+ }
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
char *
target_pid_to_str (ptid_t ptid)
{
@@ -2734,6 +2829,24 @@ target_resume (ptid_t ptid, int step, enum gdb_signal signal)
noprocess ();
}

+/* See target.h. */
+
+void
+target_delegate_resume (struct target_ops *self,
+ ptid_t ptid, int step, enum gdb_signal signal)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_resume)
+ {
+ t->to_resume (t, ptid, step, signal);
+ break;
+ }
+ }
+}
+
void
target_pass_signals (int numsigs, unsigned char *pass_signals)
{
@@ -3629,6 +3742,20 @@ find_target_beneath (struct target_ops *t)
return t->beneath;
}

+/* See target.h. */
+
+struct target_ops *
+find_target_at (enum strata stratum)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_stratum == stratum)
+ return t;
+
+ return NULL;
+}
+
\f
/* The inferior process has died. Long live the inferior! */

@@ -3982,6 +4109,24 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}

+/* See target.h. */
+
+void
+target_delegate_store_registers (struct target_ops *self,
+ struct regcache *regcache, int regno)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_store_registers)
+ {
+ t->to_store_registers (t, regcache, regno);
+ break;
+ }
+ }
+}
+
int
target_core_of_thread (ptid_t ptid)
{
@@ -4115,6 +4260,39 @@ target_ranged_break_num_registers (void)
/* See target.h. */

int
+target_delegate_stopped_by_watchpoint (struct target_ops *self)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_stopped_by_watchpoint)
+ return t->to_stopped_by_watchpoint (t);
+ }
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
+/* See target.h. */
+
+int
+target_delegate_stopped_data_address (struct target_ops *self,
+ CORE_ADDR *addr_p)
+{
+ struct target_ops *t;
+
+ for (t = self->beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_stopped_data_address)
+ return t->to_stopped_data_address (t, addr_p);
+ }
+
+ gdb_assert_not_reached (_("reached end of target stack during delegation"));
+}
+
+/* See target.h. */
+
+int
target_supports_btrace (void)
{
struct target_ops *t;
diff --git a/gdb/target.h b/gdb/target.h
index b03d33d..dae31f2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -940,6 +940,12 @@ extern void target_disconnect (char *, int);

extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);

+/* Delegate "target_resume" to a target beneath SELF. */
+
+extern void target_delegate_resume (struct target_ops *self,
+ ptid_t ptid, int step,
+ enum gdb_signal signal);
+
/* Wait for process pid to do something. PTID = -1 to wait for any
pid to do something. Return pid of child, or -1 in case of error;
store status through argument pointer STATUS. Note that it is
@@ -952,6 +958,13 @@ extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);
extern ptid_t target_wait (ptid_t ptid, struct target_waitstatus *status,
int options);

+/* Delegate "target_wait" to a target beneath SELF. */
+
+extern ptid_t target_delegate_wait (struct target_ops *self,
+ ptid_t ptid,
+ struct target_waitstatus *status,
+ int options);
+
/* Fetch at least register REGNO, or all regs if regno == -1. No result. */

extern void target_fetch_registers (struct regcache *regcache, int regno);
@@ -962,6 +975,12 @@ extern void target_fetch_registers (struct regcache *regcache, int regno);

extern void target_store_registers (struct regcache *regcache, int regs);

+/* Delegate "target_store_registers" to a target beneath SELF. */
+
+extern void target_delegate_store_registers (struct target_ops *self,
+ struct regcache *regcache,
+ int regno);
+
/* Get ready to modify the registers array. On machines which store
individual registers, this doesn't need to do anything. On machines
which store all the registers in one fell swoop, this makes sure
@@ -1094,12 +1113,24 @@ int target_write_memory_blocks (VEC(memory_write_request_s) *requests,
extern int target_insert_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt);

+/* Delegate "target_insert_breakpoint" to a target beneath SELF. */
+
+extern int target_delegate_insert_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt);
+
/* Remove a breakpoint at address BP_TGT->placed_address in the target
machine. Result is 0 for success, or an errno value. */

extern int target_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt);

+/* Delegate "target_remove_breakpoint" to a target beneath SELF. */
+
+extern int target_delegate_remove_breakpoint (struct target_ops *self,
+ struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt);
+
/* Initialize the terminal settings we record for the inferior,
before we actually run the inferior. */

@@ -1376,16 +1407,30 @@ extern int default_child_has_execution (struct target_ops *ops,
extern int target_async_permitted;

/* Can the target support asynchronous execution? */
-#define target_can_async_p() (current_target.to_can_async_p (&current_target))
+#define target_can_async_p() (target_delegate_can_async_p (&current_target))
+
+/* Delegate "target_can_async_p" to a target beneath SELF. */
+
+extern int target_delegate_can_async_p (struct target_ops *self);

/* Is the target in asynchronous execution mode? */
-#define target_is_async_p() (current_target.to_is_async_p (&current_target))
+#define target_is_async_p() (target_delegate_is_async_p (&current_target))
+
+/* Delegate "target_is_async_p" to a target beneath SELF. */
+
+extern int target_delegate_is_async_p (struct target_ops *self);

int target_supports_non_stop (void);

/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
- (current_target.to_async (&current_target, (CALLBACK), (CONTEXT)))
+ (target_delegate_async (&current_target, (CALLBACK), (CONTEXT)))
+
+/* Delegate "target_async" to a target beneath SELF. */
+
+extern void target_delegate_async (struct target_ops *,
+ void (*) (enum inferior_event_type, void *),
+ void *);

#define target_execution_direction() \
(current_target.to_execution_direction ())
@@ -1460,7 +1505,11 @@ extern char *target_thread_name (struct thread_info *);
write). Only the INFERIOR_PTID task is being queried. */

#define target_stopped_by_watchpoint() \
- ((*current_target.to_stopped_by_watchpoint) (&current_target))
+ (target_delegate_stopped_by_watchpoint (&current_target))
+
+/* Delegate "target_stopped_by_watchpoint" to a target beneath SELF. */
+
+extern int target_delegate_stopped_by_watchpoint (struct target_ops *self);

/* Non-zero if we have steppable watchpoints */

@@ -1535,6 +1584,11 @@ extern int target_ranged_break_num_registers (void);
#define target_stopped_data_address(target, addr_p) \
(*target.to_stopped_data_address) (target, addr_p)

+/* Delegate "target_stopped_data_address" to a target beneath SELF. */
+
+extern int target_delegate_stopped_data_address (struct target_ops *self,
+ CORE_ADDR *addr_p);
+
/* Return non-zero if ADDR is within the range of a watchpoint spanning
LENGTH bytes beginning at START. */
#define target_watchpoint_addr_within_range(target, addr, start, length) \
@@ -1865,6 +1919,11 @@ extern void find_default_create_inferior (struct target_ops *,

extern struct target_ops *find_target_beneath (struct target_ops *);

+/* Find the target at STRATUM. If no target is at that stratum,
+ return NULL. */
+
+struct target_ops *find_target_at (enum strata stratum);
+
/* Read OS data object of type TYPE from the target, and return it in
XML format. The result is NUL-terminated and returned as a string,
allocated using xmalloc. If an error occurs or the transfer is
@@ -1967,4 +2026,13 @@ extern void target_call_history_from (ULONGEST begin, int size, int flags);
/* See to_call_history_range. */
extern void target_call_history_range (ULONGEST begin, ULONGEST end, int flags);

+/* Delegate "target_xfer_partial" to a target beneath SELF. */
+
+extern LONGEST target_delegate_xfer_partial (struct target_ops *ops,
+ enum target_object object,
+ const char *annex,
+ gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len);
+
#endif /* !defined (TARGET_H) */
--
1.8.1.4