Re: Better MI memory commands

Vladimir Prus
2010-08-13T13:23:17Z

[-- Attachment #1: Type: Text/Plain, Size: 3869 bytes --]

On Thursday 12 August 2010 21:04:36 Eli Zaretskii wrote:

> > From: Vladimir Prus <vladimir@codesourcery.com>
> > Date: Thu, 12 Aug 2010 11:25:02 +0400
> > Cc: gdb-patches@sources.redhat.com
> >
> > On Wednesday 11 August 2010 22:00:21 Eli Zaretskii wrote:
> >
> > > > From: Vladimir Prus <vladimir@codesourcery.com>
> > > > Date: Wed, 11 Aug 2010 16:37:49 +0400
> > > > Cc: gdb-patches@sources.redhat.com
> > > >
> > > > > > +The output of the command has a result record named @samp{memory},
> > > > > ^^^^^^^^^^^^^^^^^^^
> > > > > Perhaps "is a result record"?
> > > >
> > > > Nope. "result record" is actually a nonterminal in MI grammar, and output
> > > > of a command may have result records but is never a result record itself.
> > > >
> > > > > And what is the importance of naming
> > > > > this record `memory'? why is the name important here?
> > > >
> > > > Because for frontend to access a result record in a command output, it
> > > > has to know its name.
> > >
> > > Then perhaps
> > >
> > > The result record (@pxref{GDB/MI Result Records}) that is output of
> > > the command includes a field named @samp{memory} whose content is a
> > > list of tuples ...
> >
> > This is still not accurate. The output of the command is:
> >
> > - the character sequence "^done"
> > - zero, one, or more result results, separated with spaces.
> >
> > So, it's still an command output that "has" a result record named "memory"
> > as opposed to "result record .. is .. output of the command"
>
> This is a misunderstanding. In the text I suggested, "record that is
> output", the "output" part is a verb, not a noun. (I should have said
> "output by the command", though.) IOW, the text says that the command
> outputs some stuff, and part of that stuff is the result vector with a
> field named "memory".
>
> Saying "output has the result record" is not good English, because the
> result record is part of the output, not some attribute of it.
>
> I could try to be more accurate, if "that is output by the command" is
> not good enough, but please note that our current definition of what
> exactly is a "result record" is quite vague. In fact, we don't say
> what it is at all. For starters, it talks about "result indications":

Ouch. I don't think we have "result indications" anywhere else.

> In addition to a number of out-of-band notifications, the response to a
> @sc{gdb/mi} command includes one of the following result indications:
>
> @table @code
> @findex ^done
> @item "^done" [ "," @var{results} ]
> The synchronous operation was successful, @code{@var{results}} are the return
> values.
>
> @item "^running"
> @findex ^running
> This result record is equivalent to @samp{^done}. Historically, it
> was output instead of @samp{^done} if the command has resumed the
> target. This behaviour is maintained for backward compatibility, but
> all frontends should treat @samp{^done} and @samp{^running}
> identically and rely on the @samp{*running} output record to determine
> which threads are resumed.
>
> My reading of this is that ^done, ^running, etc. _are_ examples of
> result records. By contrast, you say above that a result record is
> everything _after_ these indications. This inconsistency is the main
> reason why I deliberately left the text I suggested slightly vague.

It looks like the grammar disagrees with me -- it say result record is
indeed the entire output, which has 'results'.

> I'm open to other suggestions, but if we want to be rigorous, we
> should be more consistent than we are now regarding what exactly is a
> result record.

Yeah, this is rather confusing, so I'll use your wording (with 'by')

I've checked in the below to CVS.

Thanks,

--
Vladimir Prus
CodeSourcery
vladimir@codesourcery.com
(650) 331-3385 x722

[-- Attachment #2: bytes.diff --]
[-- Type: text/x-patch, Size: 20469 bytes --]

commit c03302295114d6efffd77b6342315b4040e10cac
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Thu Jun 17 20:23:17 2010 +0400

Easier and more stubborn MI memory read commands.

gdb/
* mi/mi-cmds.c (mi_cmds): Register data-read-memory-bytes
and data-write-memory-bytes.
* mi/mi-cmds.h (mi_cmd_data_read_memory_bytes)
(mi_cmd_data_write_memory_bytes): New.
* mi/mi-main.c (mi_cmd_data_read_memory): Use regular target_read.
(mi_cmd_data_read_memory_bytes, mi_cmd_data_write_memory_bytes):
New.
(mi_cmd_list_features): Add "data-read-memory-bytes" feature.
* target.c (target_read_until_error): Remove.
(read_whatever_is_readable, free_memory_read_result_vector)
(read_memory_robust): New.
* target.h (target_read_until_error): Remove.
(struct memory_read_result, free_memory_read_result_vector)
(read_memory_robust): New.

gdb/doc
* gdb.texinfo (GDB/MI Data Manipulation): Document
-data-read-memory-raw and -data-write-memory-raw.

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index cbd636f..93008cb 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26895,6 +26895,8 @@ don't appear in the actual output):
@subheading The @code{-data-read-memory} Command
@findex -data-read-memory

+This command is deprecated, use @code{-data-read-memory-bytes} instead.
+
@subsubheading Synopsis

@smallexample
@@ -27006,6 +27008,128 @@ next-page="0x000013c0",prev-page="0x00001380",memory=[
(gdb)
@end smallexample

+@subheading The @code{-data-read-memory-bytes} Command
+@findex -data-read-memory-bytes
+
+@subsubheading Synopsis
+
+@smallexample
+ -data-read-memory-bytes [ -o @var{byte-offset} ]
+ @var{address} @var{count}
+@end smallexample
+
+@noindent
+where:
+
+@table @samp
+@item @var{address}
+An expression specifying the address of the first memory word to be
+read. Complex expressions containing embedded white space should be
+quoted using the C convention.
+
+@item @var{count}
+The number of bytes to read. This should be an integer literal.
+
+@item @var{byte-offset}
+The offsets in bytes relative to @var{address} at which to start
+reading. This should be an integer literal. This option is provided
+so that a frontend is not required to first evaluate address and then
+perform address arithmetics itself.
+
+@end table
+
+This command attempts to read all accessible memory regions in the
+specified range. First, all regions marked as unreadable in the memory
+map (if one is defined) will be skipped. @xref{Memory Region
+Attributes}. Second, @value{GDBN} will attempt to read the remaining
+regions. For each one, if reading full region results in an errors,
+@value{GDBN} will try to read a subset of the region.
+
+In general, every single byte in the region may be readable or not,
+and the only way to read every readable byte is to try a read at
+every address, which is not practical. Therefore, @value{GDBN} will
+attempt to read all accessible bytes at either beginning or the end
+of the region, using a binary division scheme. This heuristic works
+well for reading accross a memory map boundary. Note that if a region
+has a readable range that is neither at the beginning or the end,
+@value{GDBN} will not read it.
+
+The result record (@pxref{GDB/MI Result Records}) that is output of
+the command includes a field named @samp{memory} whose content is a
+list of tuples. Each tuple represent a successfully read memory block
+and has the following fields:
+
+@table @code
+@item begin
+The start address of the memory block, as hexadecimal literal.
+
+@item end
+The end address of the memory block, as hexadecimal literal.
+
+@item offset
+The offset of the memory block, as hexadecimal literal, relative to
+the start address passed to @code{-data-read-memory-bytes}.
+
+@item contents
+The contents of the memory block, in hex.
+
+@end table
+
+
+
+@subsubheading @value{GDBN} Command
+
+The corresponding @value{GDBN} command is @samp{x}.
+
+@subsubheading Example
+
+@smallexample
+(gdb)
+-data-read-memory-bytes &a 10
+^done,memory=[@{begin="0xbffff154",offset="0x00000000",
+ end="0xbffff15e",
+ contents="01000000020000000300"@}]
+(gdb)
+@end smallexample
+
+
+@subheading The @code{-data-write-memory-bytes} Command
+@findex -data-write-memory-bytes
+
+@subsubheading Synopsis
+
+@smallexample
+ -data-write-memory-bytes @var{address} @var{contents}
+@end smallexample
+
+@noindent
+where:
+
+@table @samp
+@item @var{address}
+An expression specifying the address of the first memory word to be
+read. Complex expressions containing embedded white space should be
+quoted using the C convention.
+
+@item @var{contents}
+The hex-encoded bytes to write.
+
+@end table
+
+@subsubheading @value{GDBN} Command
+
+There's no corresponding @value{GDBN} command.
+
+@subsubheading Example
+
+@smallexample
+(gdb)
+-data-write-memory-bytes &a "aabbccdd"
+^done
+(gdb)
+@end smallexample
+
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Tracepoint Commands
@section @sc{gdb/mi} Tracepoint Commands
@@ -28344,6 +28468,9 @@ pretty-printing commands, and possible presence of the
@samp{display_hint} field in the output of @code{-var-list-children}
@item thread-info
Indicates presence of the @code{-thread-info} command.
+@item data-read-memory-bytes
+Indicates presense of the @code{-data-read-memory-bytes} and the
+@code{-data-write-memory-bytes} commands.

@end table

diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 8441e17..a6a884f 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -51,7 +51,9 @@ struct mi_cmd mi_cmds[] =
{ "data-list-register-names", { NULL, 0 }, mi_cmd_data_list_register_names},
{ "data-list-register-values", { NULL, 0 }, mi_cmd_data_list_register_values},
{ "data-read-memory", { NULL, 0 }, mi_cmd_data_read_memory},
+ { "data-read-memory-bytes", { NULL, 0 }, mi_cmd_data_read_memory_bytes},
{ "data-write-memory", { NULL, 0 }, mi_cmd_data_write_memory},
+ { "data-write-memory-bytes", {NULL, 0}, mi_cmd_data_write_memory_bytes},
{ "data-write-register-values", { NULL, 0 }, mi_cmd_data_write_register_values},
{ "enable-timings", { NULL, 0 }, mi_cmd_enable_timings},
{ "enable-pretty-printing", { NULL, 0 }, mi_cmd_enable_pretty_printing},
diff --git a/gdb/mi/mi-cmds.h b/gdb/mi/mi-cmds.h
index 5954aef..b357de6 100644
--- a/gdb/mi/mi-cmds.h
+++ b/gdb/mi/mi-cmds.h
@@ -47,7 +47,9 @@ extern mi_cmd_argv_ftype mi_cmd_data_list_register_names;
extern mi_cmd_argv_ftype mi_cmd_data_list_register_values;
extern mi_cmd_argv_ftype mi_cmd_data_list_changed_registers;
extern mi_cmd_argv_ftype mi_cmd_data_read_memory;
+extern mi_cmd_argv_ftype mi_cmd_data_read_memory_bytes;
extern mi_cmd_argv_ftype mi_cmd_data_write_memory;
+extern mi_cmd_argv_ftype mi_cmd_data_write_memory_bytes;
extern mi_cmd_argv_ftype mi_cmd_data_write_register_values;
extern mi_cmd_argv_ftype mi_cmd_enable_timings;
extern mi_cmd_argv_ftype mi_cmd_env_cd;
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 85a3f99..c91c860 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -1352,9 +1352,9 @@ mi_cmd_data_read_memory (char *command, char **argv, int argc)

/* Dispatch memory reads to the topmost target, not the flattened
current_target. */
- nr_bytes = target_read_until_error (current_target.beneath,
- TARGET_OBJECT_MEMORY, NULL, mbuf,
- addr, total_bytes);
+ nr_bytes = target_read (current_target.beneath,
+ TARGET_OBJECT_MEMORY, NULL, mbuf,
+ addr, total_bytes);
if (nr_bytes <= 0)
error ("Unable to read memory.");

@@ -1437,6 +1437,88 @@ mi_cmd_data_read_memory (char *command, char **argv, int argc)
do_cleanups (cleanups);
}

+void
+mi_cmd_data_read_memory_bytes (char *command, char **argv, int argc)
+{
+ struct gdbarch *gdbarch = get_current_arch ();
+ struct cleanup *cleanups;
+ CORE_ADDR addr;
+ LONGEST length;
+ memory_read_result_s *read_result;
+ int ix;
+ VEC(memory_read_result_s) *result;
+ long offset = 0;
+ int optind = 0;
+ char *optarg;
+ enum opt
+ {
+ OFFSET_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"o", OFFSET_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ while (1)
+ {
+ int opt = mi_getopt ("mi_cmd_data_read_memory_bytes", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case OFFSET_OPT:
+ offset = atol (optarg);
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 2)
+ error ("Usage: [ -o OFFSET ] ADDR LENGTH.");
+
+ addr = parse_and_eval_address (argv[0]) + offset;
+ length = atol (argv[1]);
+
+ result = read_memory_robust (current_target.beneath, addr, length);
+
+ cleanups = make_cleanup (free_memory_read_result_vector, result);
+
+ if (VEC_length (memory_read_result_s, result) == 0)
+ error ("Unable to read memory.");
+
+ make_cleanup_ui_out_list_begin_end (uiout, "memory");
+ for (ix = 0;
+ VEC_iterate (memory_read_result_s, result, ix, read_result);
+ ++ix)
+ {
+ struct cleanup *t = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ char *data, *p;
+ int i;
+
+ ui_out_field_core_addr (uiout, "begin", gdbarch, read_result->begin);
+ ui_out_field_core_addr (uiout, "offset", gdbarch, read_result->begin
+ - addr);
+ ui_out_field_core_addr (uiout, "end", gdbarch, read_result->end);
+
+ data = xmalloc ((read_result->end - read_result->begin) * 2 + 1);
+
+ for (i = 0, p = data;
+ i < (read_result->end - read_result->begin);
+ ++i, p += 2)
+ {
+ sprintf (p, "%02x", read_result->data[i]);
+ }
+ ui_out_field_string (uiout, "contents", data);
+ xfree (data);
+ do_cleanups (t);
+ }
+ do_cleanups (cleanups);
+}
+
+
/* DATA-MEMORY-WRITE:

COLUMN_OFFSET: optional argument. Must be preceeded by '-o'. The
@@ -1523,6 +1605,44 @@ mi_cmd_data_write_memory (char *command, char **argv, int argc)
do_cleanups (old_chain);
}

+/* DATA-MEMORY-WRITE-RAW:
+
+ ADDR: start address
+ DATA: string of bytes to write at that address. */
+void
+mi_cmd_data_write_memory_bytes (char *command, char **argv, int argc)
+{
+ CORE_ADDR addr;
+ char *cdata;
+ gdb_byte *data;
+ int len, r, i;
+ struct cleanup *back_to;
+
+ if (argc != 2)
+ error ("Usage: ADDR DATA.");
+
+ addr = parse_and_eval_address (argv[0]);
+ cdata = argv[1];
+ len = strlen (cdata)/2;
+
+ data = xmalloc (len);
+ back_to = make_cleanup (xfree, data);
+
+ for (i = 0; i < len; ++i)
+ {
+ int x;
+ sscanf (cdata + i * 2, "%02x", &x);
+ data[i] = (gdb_byte)x;
+ }
+
+ r = target_write_memory (addr, data, len);
+ if (r != 0)
+ error (_("Could not write memory"));
+
+ do_cleanups (back_to);
+}
+
+
void
mi_cmd_enable_timings (char *command, char **argv, int argc)
{
@@ -1557,6 +1677,7 @@ mi_cmd_list_features (char *command, char **argv, int argc)
ui_out_field_string (uiout, NULL, "frozen-varobjs");
ui_out_field_string (uiout, NULL, "pending-breakpoints");
ui_out_field_string (uiout, NULL, "thread-info");
+ ui_out_field_string (uiout, NULL, "data-read-memory-bytes");

#if HAVE_PYTHON
ui_out_field_string (uiout, NULL, "python");
diff --git a/gdb/target.c b/gdb/target.c
index 7aa77e6..cf01b6c 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1751,73 +1751,204 @@ target_read (struct target_ops *ops,
return len;
}

-LONGEST
-target_read_until_error (struct target_ops *ops,
- enum target_object object,
- const char *annex, gdb_byte *buf,
- ULONGEST offset, LONGEST len)
+/** Assuming that the entire [begin, end) range of memory cannot be read,
+ try to read whatever subrange is possible to read.
+
+ The function results, in RESULT, either zero or one memory block.
+ If there's a readable subrange at the beginning, it is completely
+ read and returned. Any further readable subrange will not be read.
+ Otherwise, if there's a readable subrange at the end, it will be
+ completely read and returned. Any readable subranges before it (obviously,
+ not starting at the beginning), will be ignored. In other cases --
+ either no readable subrange, or readable subrange (s) that is neither
+ at the beginning, or end, nothing is returned.
+
+ The purpose of this function is to handle a read across a boundary of
+ accessible memory in a case when memory map is not available. The above
+ restrictions are fine for this case, but will give incorrect results if
+ the memory is 'patchy'. However, supporting 'patchy' memory would require
+ trying to read every single byte, and it seems unacceptable solution.
+ Explicit memory map is recommended for this case -- and
+ target_read_memory_robust will take care of reading multiple ranges then. */
+
+static void
+read_whatever_is_readable (struct target_ops *ops, ULONGEST begin, ULONGEST end,
+ VEC(memory_read_result_s) **result)
{
- LONGEST xfered = 0;
+ gdb_byte *buf = xmalloc (end-begin);
+ ULONGEST current_begin = begin;
+ ULONGEST current_end = end;
+ int forward;
+ memory_read_result_s r;
+
+ /* If we previously failed to read 1 byte, nothing can be done here. */
+ if (end - begin <= 1)
+ return;
+
+ /* Check that either first or the last byte is readable, and give up
+ if not. This heuristic is meant to permit reading accessible memory
+ at the boundary of accessible region. */
+ if (target_read_partial (ops, TARGET_OBJECT_MEMORY, NULL,
+ buf, begin, 1) == 1)
+ {
+ forward = 1;
+ ++current_begin;
+ }
+ else if (target_read_partial (ops, TARGET_OBJECT_MEMORY, NULL,
+ buf + (end-begin) - 1, end - 1, 1) == 1)
+ {
+ forward = 0;
+ --current_end;
+ }
+ else
+ {
+ return;
+ }
+
+ /* Loop invariant is that the [current_begin, current_end) was previously
+ found to be not readable as a whole.
+
+ Note loop condition -- if the range has 1 byte, we can't divide the range
+ so there's no point trying further. */
+ while (current_end - current_begin > 1)
+ {
+ ULONGEST first_half_begin, first_half_end;
+ ULONGEST second_half_begin, second_half_end;
+ LONGEST xfer;
+
+ ULONGEST middle = current_begin + (current_end - current_begin)/2;
+ if (forward)
+ {
+ first_half_begin = current_begin;
+ first_half_end = middle;
+ second_half_begin = middle;
+ second_half_end = current_end;
+ }
+ else
+ {
+ first_half_begin = middle;
+ first_half_end = current_end;
+ second_half_begin = current_begin;
+ second_half_end = middle;
+ }
+
+ xfer = target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+ buf + (first_half_begin - begin),
+ first_half_begin,
+ first_half_end - first_half_begin);
+
+ if (xfer == first_half_end - first_half_begin)
+ {
+ /* This half reads up fine. So, the error must be in the other half. */
+ current_begin = second_half_begin;
+ current_end = second_half_end;
+ }
+ else
+ {
+ /* This half is not readable. Because we've tried one byte, we
+ know some part of this half if actually redable. Go to the next
+ iteration to divide again and try to read.
+
+ We don't handle the other half, because this function only tries
+ to read a single readable subrange. */
+ current_begin = first_half_begin;
+ current_end = first_half_end;
+ }
+ }
+
+ if (forward)
+ {
+ /* The [begin, current_begin) range has been read. */
+ r.begin = begin;
+ r.end = current_begin;
+ r.data = buf;
+ }
+ else
+ {
+ /* The [current_end, end) range has been read. */
+ LONGEST rlen = end - current_end;
+ r.data = xmalloc (rlen);
+ memcpy (r.data, buf + current_end - begin, rlen);
+ r.begin = current_end;
+ r.end = end;
+ xfree (buf);
+ }
+ VEC_safe_push(memory_read_result_s, (*result), &r);
+}
+
+void
+free_memory_read_result_vector (void *x)
+{
+ VEC(memory_read_result_s) *v = x;
+ memory_read_result_s *current;
+ int ix;
+
+ for (ix = 0; VEC_iterate (memory_read_result_s, v, ix, current); ++ix)
+ {
+ xfree (current->data);
+ }
+ VEC_free (memory_read_result_s, v);
+}

+VEC(memory_read_result_s) *
+read_memory_robust (struct target_ops *ops, ULONGEST offset, LONGEST len)
+{
+ VEC(memory_read_result_s) *result = 0;
+
+ LONGEST xfered = 0;
while (xfered < len)
{
- LONGEST xfer = target_read_partial (ops, object, annex,
- (gdb_byte *) buf + xfered,
- offset + xfered, len - xfered);
+ struct mem_region *region = lookup_mem_region (offset + xfered);
+ LONGEST rlen;

- /* Call an observer, notifying them of the xfer progress? */
- if (xfer == 0)
- return xfered;
- if (xfer < 0)
+ /* If there is no explicit region, a fake one should be created. */
+ gdb_assert (region);
+
+ if (region->hi == 0)
+ rlen = len - xfered;
+ else
+ rlen = region->hi - offset;
+
+ if (region->attrib.mode == MEM_NONE || region->attrib.mode == MEM_WO)
{
- /* We've got an error. Try to read in smaller blocks. */
- ULONGEST start = offset + xfered;
- ULONGEST remaining = len - xfered;
- ULONGEST half;
-
- /* If an attempt was made to read a random memory address,
- it's likely that the very first byte is not accessible.
- Try reading the first byte, to avoid doing log N tries
- below. */
- xfer = target_read_partial (ops, object, annex,
- (gdb_byte *) buf + xfered, start, 1);
+ /* Cannot read this region. Note that we can end up here only
+ if the region is explicitly marked inaccessible, or
+ 'inaccessible-by-default' is in effect. */
+ xfered += rlen;
+ }
+ else
+ {
+ LONGEST to_read = min (len - xfered, rlen);
+ gdb_byte *buffer = (gdb_byte *)xmalloc (to_read);
+
+ LONGEST xfer = target_read (ops, TARGET_OBJECT_MEMORY, NULL,
+ (gdb_byte *) buffer,
+ offset + xfered, to_read);
+ /* Call an observer, notifying them of the xfer progress? */
if (xfer <= 0)
- return xfered;
- start += 1;
- remaining -= 1;
- half = remaining/2;
-
- while (half > 0)
{
- xfer = target_read_partial (ops, object, annex,
- (gdb_byte *) buf + xfered,
- start, half);
- if (xfer == 0)
- return xfered;
- if (xfer < 0)
- {
- remaining = half;
- }
- else
- {
- /* We have successfully read the first half. So, the
- error must be in the second half. Adjust start and
- remaining to point at the second half. */
- xfered += xfer;
- start += xfer;
- remaining -= xfer;
- }
- half = remaining/2;
+ /* Got an error reading full chunk. See if maybe we can read
+ some subrange. */
+ xfree (buffer);
+ read_whatever_is_readable (ops, offset + xfered, offset + xfered + to_read, &result);
+ xfered += to_read;
}
-
- return xfered;
+ else
+ {
+ struct memory_read_result r;
+ r.data = buffer;
+ r.begin = offset + xfered;
+ r.end = r.begin + xfer;
+ VEC_safe_push (memory_read_result_s, result, &r);
+ xfered += xfer;
+ }
+ QUIT;
}
- xfered += xfer;
- QUIT;
}
- return len;
+ return result;
}

+
/* An alternative to target_write with progress callbacks. */

LONGEST
diff --git a/gdb/target.h b/gdb/target.h
index 870a1eb..e0a63ca 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -292,10 +292,23 @@ extern LONGEST target_read (struct target_ops *ops,
const char *annex, gdb_byte *buf,
ULONGEST offset, LONGEST len);

-extern LONGEST target_read_until_error (struct target_ops *ops,
- enum target_object object,
- const char *annex, gdb_byte *buf,
- ULONGEST offset, LONGEST len);
+struct memory_read_result
+ {
+ /* First address that was read. */
+ ULONGEST begin;
+ /* Past-the-end address. */
+ ULONGEST end;
+ /* The data. */
+ gdb_byte *data;
+};
+typedef struct memory_read_result memory_read_result_s;
+DEF_VEC_O(memory_read_result_s);
+
+extern void free_memory_read_result_vector (void *);
+
+extern VEC(memory_read_result_s)* read_memory_robust (struct target_ops *ops,
+ ULONGEST offset,
+ LONGEST len);

extern LONGEST target_write (struct target_ops *ops,
enum target_object object,