Skip to content

Commit

Permalink
Add negative repeat count to 'x' command
Browse files Browse the repository at this point in the history
This change adds support for specifying a negative repeat count to
all the formats of the 'x' command to examine memory backward.
A new testcase 'examine-backward' is added to cover this new feature.

Here's the example output from the new feature:

<format 'i'>
(gdb) bt
#0  Func1 (n=42, p=0x40432e "hogehoge") at main.cpp:5
#1  0x00000000004041fa in main (argc=1, argv=0x7fffffffdff8) at main.cpp:19
(gdb) x/-4i 0x4041fa
  0x4041e5 <main(int, char**)+11>: mov   %rsi,-0x10(%rbp)
  0x4041e9 <main(int, char**)+15>: lea   0x13e(%rip),%rsi
  0x4041f0 <main(int, char**)+22>: mov   $0x2a,%edi
  0x4041f5 <main(int, char**)+27>: callq 0x404147

<format 'x'>
(gdb) x/-4xw 0x404200
0x4041f0 <main(int, char**)+22>: 0x00002abf 0xff4de800 0x76e8ffff 0xb8ffffff
(gdb) x/-4
0x4041e0 <main(int, char**)+6>:  0x7d8910ec 0x758948fc 0x358d48f0 0x0000013e

gdb/ChangeLog:

	* NEWS: Mention that GDB now supports a negative repeat count in
	the 'x' command.
	* printcmd.c (decode_format): Allow '-' in the parameter
	"string_ptr" to accept a negative repeat count.
	(find_instruction_backward): New function.
	(read_memory_backward): New function.
	(integer_is_zero): New function.
	(find_string_backward): New function.
	(do_examine): Use new functions to examine memory backward.
	(_initialize_printcmd): Mention that 'x' command supports a negative
	repeat count.

gdb/doc/ChangeLog:

	* gdb.texinfo (Examining Memory): Document negative repeat
	count in the 'x' command.

gdb/testsuite/ChangeLog:

	* gdb.base/examine-backward.c: New file.
	* gdb.base/examine-backward.exp: New file.
  • Loading branch information
msmania committed Jun 10, 2016
1 parent c040f3f commit bb556f1
Show file tree
Hide file tree
Showing 8 changed files with 741 additions and 3 deletions.
14 changes: 14 additions & 0 deletions gdb/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>

* NEWS: Mention that GDB now supports a negative repeat count in
the 'x' command.
* printcmd.c (decode_format): Allow '-' in the parameter
"string_ptr" to accept a negative repeat count.
(find_instruction_backward): New function.
(read_memory_backward): New function.
(integer_is_zero): New function.
(find_string_backward): New function.
(do_examine): Use new functions to examine memory backward.
(_initialize_printcmd): Mention that 'x' command supports a negative
repeat count.

2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>

* MAINTAINERS (Write After Approval): Add Toshihito Kikuchi.
Expand Down
14 changes: 14 additions & 0 deletions gdb/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@

*** Changes since GDB 7.11

* GDB now supports a negative repeat count in the 'x' command to examine
memory backward from the given address. For example:

(gdb) bt
#0 Func1 (n=42, p=0x40061c "hogehoge") at main.cpp:4
#1 0x400580 in main (argc=1, argv=0x7fffffffe5c8) at main.cpp:8
(gdb) x/-5i 0x0000000000400580
0x40056a <main(int, char**)+8>: mov %edi,-0x4(%rbp)
0x40056d <main(int, char**)+11>: mov %rsi,-0x10(%rbp)
0x400571 <main(int, char**)+15>: mov $0x40061c,%esi
0x400576 <main(int, char**)+20>: mov $0x2a,%edi
0x40057b <main(int, char**)+25>:
callq 0x400536 <Func1(int, char const*)>

* Fortran: Support structures with fields of dynamic types and
arrays of dynamic types.

Expand Down
5 changes: 5 additions & 0 deletions gdb/doc/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>

* gdb.texinfo (Examining Memory): Document negative repeat
count in the 'x' command.

2016-06-06 Simon Marchi <simon.marchi@ericsson.com>

* gdb.texinfo (GDB/MI Async Records): Document method and
Expand Down
14 changes: 13 additions & 1 deletion gdb/doc/gdb.texinfo
Original file line number Diff line number Diff line change
Expand Up @@ -9295,7 +9295,8 @@ Several commands set convenient defaults for @var{addr}.
@table @r
@item @var{n}, the repeat count
The repeat count is a decimal integer; the default is 1. It specifies
how much memory (counting by units @var{u}) to display.
how much memory (counting by units @var{u}) to display. If a negative
number is specified, memory is examined backward from @var{addr}.
@c This really is **decimal**; unaffected by 'set radix' as of GDB
@c 4.1.2.

Expand Down Expand Up @@ -9350,6 +9351,10 @@ starting at address @code{0x54320}. @samp{x/4xw $sp} prints the four
words (@samp{w}) of memory above the stack pointer (here, @samp{$sp};
@pxref{Registers, ,Registers}) in hexadecimal (@samp{x}).

You can also specify a negative repeat count to examine memory backward
from the given address. For example, @samp{x/-3uh 0x54320} prints three
halfwords (@code{h}) at @code{0x54314}, @code{0x54328}, and @code{0x5431c}.

Since the letters indicating unit sizes are all distinct from the
letters specifying output formats, you do not have to remember whether
unit size or format comes first; either order works. The output
Expand All @@ -9366,6 +9371,13 @@ follow the last instruction that is within the count. The command
@code{disassemble} gives an alternative way of inspecting machine
instructions; see @ref{Machine Code,,Source and Machine Code}.

If a negative repeat count is specified for the formats @samp{s} or @samp{i},
the command displays null-terminated strings or instructions before the given
address as many as the absolute value of the given number. For the @samp{i}
format, we use line number information in the debug info to accurately locate
instruction boundaries while disassembling backward. If line info is not
available, the command stops examining memory with an error message.

All the defaults for the arguments to @code{x} are designed to make it
easy to continue scanning memory with minimal specifications each time
you use @code{x}. For example, after you have inspected three machine
Expand Down
262 changes: 260 additions & 2 deletions gdb/printcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,13 @@ decode_format (const char **string_ptr, int oformat, int osize)
val.count = 1;
val.raw = 0;

if (*p == '-')
{
val.count = -1;
p++;
}
if (*p >= '0' && *p <= '9')
val.count = atoi (p);
val.count *= atoi (p);
while (*p >= '0' && *p <= '9')
p++;

Expand Down Expand Up @@ -785,6 +790,221 @@ print_address_demangle (const struct value_print_options *opts,
}


/* Find the address of the instruction that is INST_COUNT instructions before
the instruction at ADDR.
Since some architectures have variable-length instructions, we can't just
simply subtract INST_COUNT * INSN_LEN from ADDR. Instead, we use line
number information to locate the nearest known instruction boundary,
and disassemble forward from there. If we go out of the symbol range
during disassembling, we return the lowest address we've got so far and
set the number of instructions read to INST_READ. */

static CORE_ADDR
find_instruction_backward (struct gdbarch *gdbarch, CORE_ADDR addr,
int inst_count, int *inst_read)
{
/* The vector PCS is used to store instruction addresses within
a pc range. */
CORE_ADDR loop_start, loop_end, p;
VEC (CORE_ADDR) *pcs = NULL;
struct symtab_and_line sal;
struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs);

*inst_read = 0;
loop_start = loop_end = addr;

/* In each iteration of the outer loop, we get a pc range that ends before
LOOP_START, then we count and store every instruction address of the range
iterated in the loop.
If the number of instructions counted reaches INST_COUNT, return the
stored address that is located INST_COUNT instructions back from ADDR.
If INST_COUNT is not reached, we subtract the number of counted
instructions from INST_COUNT, and go to the next iteration. */
do
{
VEC_truncate (CORE_ADDR, pcs, 0);
sal = find_pc_sect_line (loop_start, NULL, 1);
if (sal.line <= 0)
{
/* We reach here when line info is not available. In this case,
we print a message and just exit the loop. The return value
is calculated after the loop. */
printf_filtered (_("No line number information available "
"for address "));
wrap_here (" ");
print_address (gdbarch, loop_start - 1, gdb_stdout);
printf_filtered ("\n");
break;
}

loop_end = loop_start;
loop_start = sal.pc;

/* This loop pushes instruction addresses in the range from
LOOP_START to LOOP_END. */
for (p = loop_start; p < loop_end;)
{
VEC_safe_push (CORE_ADDR, pcs, p);
p += gdb_insn_length (gdbarch, p);
}

inst_count -= VEC_length (CORE_ADDR, pcs);
*inst_read += VEC_length (CORE_ADDR, pcs);
}
while (inst_count > 0);

/* After the loop, the vector PCS has instruction addresses of the last
source line we processed, and INST_COUNT has a negative value.
We return the address at the index of -INST_COUNT in the vector for
the reason below.
Let's assume the following instruction addresses and run 'x/-4i 0x400e'.
Line X of File
0x4000
0x4001
0x4005
Line Y of File
0x4009
0x400c
=> 0x400e
0x4011
find_instruction_backward is called with INST_COUNT = 4 and expected to
return 0x4001. When we reach here, INST_COUNT is set to -1 because
it was subtracted by 2 (from Line Y) and 3 (from Line X). The value
4001 is located at the index 1 of the last iterated line (= Line X),
which is simply calculated by -INST_COUNT.
The case when the length of PCS is 0 means that we reached an area for
which line info is not available. In such case, we return LOOP_START,
which was the lowest instruction address that had line info. */
p = VEC_length (CORE_ADDR, pcs) > 0
? VEC_index (CORE_ADDR, pcs, -inst_count)
: loop_start;

/* INST_READ includes all instruction addresses in a pc range. Need to
exclude the beginning part up to the address we're returning. That
is, exclude {0x4000} in the example above. */
if (inst_count < 0)
*inst_read += inst_count;

do_cleanups (cleanup);
return p;
}

/* Backward read LEN bytes of target memory from address MEMADDR + LEN,
placing the results in GDB's memory from MYADDR + LEN. Returns
a count of the bytes actually read. */

static int
read_memory_backward (struct gdbarch *gdbarch,
CORE_ADDR memaddr, gdb_byte *myaddr, int len)
{
int errcode;
int nread; /* Number of bytes actually read. */

/* First try a complete read. */
errcode = target_read_memory (memaddr, myaddr, len);
if (errcode == 0)
{
/* Got it all. */
nread = len;
}
else
{
/* Loop, reading one byte at a time until we get as much as we can. */
memaddr += len;
myaddr += len;
for (nread = 0; nread < len; ++nread)
{
errcode = target_read_memory (--memaddr, --myaddr, 1);
if (errcode != 0)
{
/* The read was unsuccessful, so exit the loop. */
printf_filtered (_("Cannot access memory at address %s\n"),
paddress (gdbarch, memaddr));
break;
}
}
}
return nread;
}

/* Returns true if X (which is LEN bytes wide) is the number zero. */

static int
integer_is_zero (const gdb_byte *x, int len)
{
int i = 0;

while (i < len && x[i] == 0)
++i;
return (i == len);
}

/* Find the start address of a string in which ADDR is included.
Basically we search for '\0' and return the next address,
but if OPTIONS->PRINT_MAX is smaller than the length of a string,
we stop searching and return the address to print characters as many as
PRINT_MAX from the string. */

static CORE_ADDR
find_string_backward (struct gdbarch *gdbarch,
CORE_ADDR addr, int count, int char_size,
const struct value_print_options *options,
int *strings_counted)
{
const int chunk_size = 0x20;
gdb_byte *buffer = NULL;
struct cleanup *cleanup = NULL;
int read_error = 0;
int chars_read = 0;
int chars_to_read = chunk_size;
int chars_counted = 0;
int count_original = count;
CORE_ADDR string_start_addr = addr;

gdb_assert (char_size == 1 || char_size == 2 || char_size == 4);
buffer = (gdb_byte *) xmalloc (chars_to_read * char_size);
cleanup = make_cleanup (xfree, buffer);
while (count > 0 && read_error == 0)
{
int i;

addr -= chars_to_read * char_size;
chars_read = read_memory_backward (gdbarch, addr, buffer,
chars_to_read * char_size);
chars_read /= char_size;
read_error = (chars_read == chars_to_read) ? 0 : 1;
/* Searching for '\0' from the end of buffer in backward direction. */
for (i = 0; i < chars_read && count > 0 ; ++i, ++chars_counted)
{
int offset = (chars_to_read - i - 1) * char_size;

if (integer_is_zero (buffer + offset, char_size)
|| chars_counted == options->print_max)
{
/* Found '\0' or reached print_max. As OFFSET is the offset to
'\0', we add CHAR_SIZE to return the start address of
a string. */
--count;
string_start_addr = addr + offset + char_size;
chars_counted = 0;
}
}
}

/* Update STRINGS_COUNTED with the actual number of loaded strings. */
*strings_counted = count_original - count;

if (read_error != 0)
{
/* In error case, STRING_START_ADDR is pointing to the string that
was last successfully loaded. Rewind the partially loaded string. */
string_start_addr -= chars_counted * char_size;
}

do_cleanups (cleanup);
return string_start_addr;
}

/* Examine data at address ADDR in format FMT.
Fetch it from memory and print on gdb_stdout. */

Expand All @@ -798,6 +1018,8 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
int i;
int maxelts;
struct value_print_options opts;
int need_to_update_next_address = 0;
CORE_ADDR addr_rewound = 0;

format = fmt.format;
size = fmt.size;
Expand Down Expand Up @@ -868,6 +1090,38 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)

get_formatted_print_options (&opts, format);

if (count < 0)
{
/* This is the negative repeat count case.
We rewind the address based on the given repeat count and format,
then examine memory from there in forward direction. */

count = -count;
if (format == 'i')
{
next_address = find_instruction_backward (gdbarch, addr, count,
&count);
}
else if (format == 's')
{
next_address = find_string_backward (gdbarch, addr, count,
TYPE_LENGTH (val_type),
&opts, &count);
}
else
{
next_address = addr - count * TYPE_LENGTH (val_type);
}

/* The following call to print_formatted updates next_address in every
iteration. In backward case, we store the start address here
and update next_address with it before exiting the function. */
addr_rewound = (format == 's'
? next_address - TYPE_LENGTH (val_type)
: next_address);
need_to_update_next_address = 1;
}

/* Print as many objects as specified in COUNT, at most maxelts per line,
with the address of the next one at the start of each line. */

Expand Down Expand Up @@ -913,6 +1167,9 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
printf_filtered ("\n");
gdb_flush (gdb_stdout);
}

if (need_to_update_next_address)
next_address = addr_rewound;
}

static void
Expand Down Expand Up @@ -2522,7 +2779,8 @@ Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
and z(hex, zero padded on the left).\n\
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
The specified number of objects of the specified size are printed\n\
according to the format.\n\n\
according to the format. If a negative number is specified, memory is\n\
examined backward from the address.\n\n\
Defaults for format and size letters are those previously used.\n\
Default count is 1. Default address is following last thing printed\n\
with this command or \"print\"."));
Expand Down
5 changes: 5 additions & 0 deletions gdb/testsuite/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2016-06-09 Toshihito Kikuchi <k.toshihito@yahoo.de>

* gdb.base/examine-backward.c: New file.
* gdb.base/examine-backward.exp: New file.

2016-06-06 Simon Marchi <simon.marchi@ericsson.com>

* gdb.mi/mi-record-changed.exp: Adjust =record-started output
Expand Down
Loading

0 comments on commit bb556f1

Please sign in to comment.