From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 81474 invoked by alias); 8 Aug 2016 14:45:57 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 81435 invoked by uid 89); 8 Aug 2016 14:45:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.1 required=5.0 tests=BAYES_00,KAM_LAZY_DOMAIN_SECURITY,KAM_LOTSOFHASH,RP_MATCHES_RCVD autolearn=no version=3.3.2 spammy=PCS, reaches, Examine, giant X-HELO: foss.arm.com Received: from foss.arm.com (HELO foss.arm.com) (217.140.101.70) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 08 Aug 2016 14:45:46 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 8805028; Mon, 8 Aug 2016 07:47:11 -0700 (PDT) Received: from [10.2.206.52] (usa-sjc-imap-foss1.foss.arm.com [10.72.51.249]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 88A223F487; Mon, 8 Aug 2016 07:45:44 -0700 (PDT) Subject: Re: [PATCH v5] Add negative repeat count to 'x' command To: Toshihito Kikuchi , gdb-patches@sourceware.org References: <1464857355-29883-1-git-send-email-k.toshihito@yahoo.de> Cc: Simon Marchi , Pedro Alves From: Thomas Preudhomme Message-ID: <4e8feff5-935e-668e-6c68-87603173a953@foss.arm.com> Date: Mon, 08 Aug 2016 14:45:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-Version: 1.0 In-Reply-To: <1464857355-29883-1-git-send-email-k.toshihito@yahoo.de> Content-Type: text/plain; charset=windows-1252; format=flowed Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2016-08/txt/msg00102.txt.bz2 Sorry for the late reply but I noticed an issue in the tests added by this patch for arm-none-eabi: FAIL: gdb.base/examine-backward.exp: address zero boundary: examine 3 bytes forward from 0x0 NA->FAIL: gdb.base/examine-backward.exp char-width=1, print-max=20 take 1 string backward (6/6) NA->FAIL: gdb.base/examine-backward.exp char-width=1, print-max=20 take 6 strings backward (pattern 1) The first one is a testism: we match for: 0x\[0-9a-f\]+00.*:${byte}${byte}${byte} but we get: 0x0: 0x7f 0x45 0x4c I'd thus suggest to match 0x\(\[0-9a-f\]+0\)?0 instead, with the right amount of escaping (I don't know whether ? needs escaping and why + does not need escaping in current pattern). The other two seems like genuine errors. take 6 strings backward (pattern 1) gives: gdb_expect_list pattern: /"ABCDEFGHIJKLMNOPQRST".../ x/-6s^M 0x9aff <_fini+2>: "\277\370\274\b\274\236FpGABCDEFGHIJK"...^M 0x9b13 : "LMNOPQRSTUVWXYZ"^M 0x9b23 : ""^M 0x9b24 : ""^M 0x9b25 : "\343\201\273\343\201\222\343\201\273\343\201\222"^M 0x9b32 : "01234567890123456789"...^M (gdb) FAIL: gdb.base/examine-backward.exp: char-width=1, print-max=20: take 6 strings backward (pattern 1) take 1 string backward (6/6) gives: 0x9aff <_fini+2>: "\277\370\274\b\274\236FpGABCDEFGHIJK"... Please let me know what I can do to help diagnose this error. Best regards, Thomas On 02/06/16 09:49, Toshihito Kikuchi wrote: > 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: > > > (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 : mov %rsi,-0x10(%rbp) > 0x4041e9 : lea 0x13e(%rip),%rsi > 0x4041f0 : mov $0x2a,%edi > 0x4041f5 : callq 0x404147 > > > (gdb) x/-4xw 0x404200 > 0x4041f0 : 0x00002abf 0xff4de800 0x76e8ffff 0xb8ffffff > (gdb) x/-4 > 0x4041e0 : 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. > --- > gdb/NEWS | 14 ++ > gdb/doc/gdb.texinfo | 14 +- > gdb/printcmd.c | 262 +++++++++++++++++++++- > gdb/testsuite/gdb.base/examine-backward.c | 106 +++++++++ > gdb/testsuite/gdb.base/examine-backward.exp | 324 ++++++++++++++++++++++++++++ > 5 files changed, 717 insertions(+), 3 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/examine-backward.c > create mode 100644 gdb/testsuite/gdb.base/examine-backward.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index dce79a2..6b6cc4f 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -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 : mov %edi,-0x4(%rbp) > + 0x40056d : mov %rsi,-0x10(%rbp) > + 0x400571 : mov $0x40061c,%esi > + 0x400576 : mov $0x2a,%edi > + 0x40057b : > + callq 0x400536 > + > * Fortran: Support structures with fields of dynamic types and > arrays of dynamic types. > > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index 7e89003..12d0140 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -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. > > @@ -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 > @@ -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 > diff --git a/gdb/printcmd.c b/gdb/printcmd.c > index f5c4211..d4a4b9e 100644 > --- a/gdb/printcmd.c > +++ b/gdb/printcmd.c > @@ -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++; > > @@ -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. */ > > @@ -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; > @@ -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. */ > > @@ -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 > @@ -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\".")); > diff --git a/gdb/testsuite/gdb.base/examine-backward.c b/gdb/testsuite/gdb.base/examine-backward.c > new file mode 100644 > index 0000000..b338503 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/examine-backward.c > @@ -0,0 +1,106 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2015-2016 Free Software Foundation, Inc. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +/* > +Define TestStrings, TestStringsH, and TestStringsW to test utf8, utf16, > +and utf32 strings respectively. > +To avoid compile errors due to old compiler mode, we don't use string > +literals. The content of each array is the same as followings: > + > + const char TestStrings[] = { > + "ABCD" > + "EFGHIJKLMNOPQRSTUVWXYZ\0" > + "\0" > + "\0" > + "\u307B\u3052\u307B\u3052\0" > + "012345678901234567890123456789\0" > + "!!!!!!\0" > + }; > +*/ > + > +const char TestStrings[] = { > + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, > + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, > + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, > + 0x59, 0x5a, 0x00, 0x00, 0x00, 0xe3, 0x81, 0xbb, > + 0xe3, 0x81, 0x92, 0xe3, 0x81, 0xbb, 0xe3, 0x81, > + 0x92, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, > + 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, > + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, > + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, > + 0x00, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x00, > + 0x00 > +}; > + > +const short TestStringsH[] = { > + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, > + 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, > + 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, > + 0x0059, 0x005a, 0x0000, 0x0000, 0x0000, 0x307b, 0x3052, 0x307b, > + 0x3052, 0x0000, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, > + 0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031, 0x0032, 0x0033, > + 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0030, 0x0031, > + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, > + 0x0000, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0021, 0x0000, > + 0x0000 > +}; > + > +const int TestStringsW[] = { > + 0x00000041, 0x00000042, 0x00000043, 0x00000044, > + 0x00000045, 0x00000046, 0x00000047, 0x00000048, > + 0x00000049, 0x0000004a, 0x0000004b, 0x0000004c, > + 0x0000004d, 0x0000004e, 0x0000004f, 0x00000050, > + 0x00000051, 0x00000052, 0x00000053, 0x00000054, > + 0x00000055, 0x00000056, 0x00000057, 0x00000058, > + 0x00000059, 0x0000005a, 0x00000000, 0x00000000, > + 0x00000000, 0x0000307b, 0x00003052, 0x0000307b, > + 0x00003052, 0x00000000, 0x00000030, 0x00000031, > + 0x00000032, 0x00000033, 0x00000034, 0x00000035, > + 0x00000036, 0x00000037, 0x00000038, 0x00000039, > + 0x00000030, 0x00000031, 0x00000032, 0x00000033, > + 0x00000034, 0x00000035, 0x00000036, 0x00000037, > + 0x00000038, 0x00000039, 0x00000030, 0x00000031, > + 0x00000032, 0x00000033, 0x00000034, 0x00000035, > + 0x00000036, 0x00000037, 0x00000038, 0x00000039, > + 0x00000000, 0x00000021, 0x00000021, 0x00000021, > + 0x00000021, 0x00000021, 0x00000021, 0x00000000, > + 0x00000000 > +}; > + > +int > +main (void) > +{ > + /* Backward disassemble test requires at least 20 instructions in > + this function. Adding a simple bubble sort. */ > + int i, j; > + int n[] = {3, 1, 4, 1, 5, 9}; > + int len = sizeof (n) / sizeof (n[0]); > + > + for (i = 0; i < len - 1; ++i) > + { > + for (j = i; j < len; ++j) > + { > + if (n[j] < n[i]) > + { > + int tmp = n[i]; > + n[i] = n[j]; > + n[j] = tmp; > + } > + } > + } > + return 42; > +} > diff --git a/gdb/testsuite/gdb.base/examine-backward.exp b/gdb/testsuite/gdb.base/examine-backward.exp > new file mode 100644 > index 0000000..e03cbfd > --- /dev/null > +++ b/gdb/testsuite/gdb.base/examine-backward.exp > @@ -0,0 +1,324 @@ > +# Copyright 2015-2016 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > + > +# This testsuite is to test examining memory backward by specifying a negative > +# number in the 'x' command. > + > +standard_testfile > +if { [prepare_for_testing "failed to prepare for examine-backward" \ > + ${testfile} ${srcfile}] } { > + return -1 > +} > + > +if ![runto_main] { > + untested "could not run to main" > + return -1 > +} > + > +proc get_first_mapped_address {} { > + global gdb_prompt > + > + set addr "0" > + gdb_test_multiple "info proc mappings" "info proc mappings" { > + -re "objfile\[\r\n\t \]+(0x\[0-9a-fA-F\]+).*\[\r\n\]*$gdb_prompt $" { > + set addr $expect_out(1,string) > + } > + -re "$gdb_prompt $" { > + unsupported "Current target does not support 'info proc mappings'" > + } > + } > + return ${addr} > +} > + > +with_test_prefix "invalid format" { > + gdb_test "x/- 10xb main" "Invalid number \"10xb\"\." \ > + "a whitespace after a leading hyphen" > + gdb_test "x/--10xb main" "Invalid number \"10xb\"\." \ > + "double hyphen" > + gdb_test "x/-a10xb main" "Invalid number \"10xb\"\." \ > + "an alphabet after a leading hyphen" > + gdb_test_no_output "x/-0i main" "zero with backward disassemble" > + gdb_test_no_output "x/-0sh main" "zero with backward examine string" > +} > + > +with_test_prefix "memory page boundary" { > + set boundary [get_first_mapped_address] > + if {![is_address_zero_readable] && $boundary != 0} { > + gdb_test_no_output "set print elements 0" > + gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward" { > + "0x" > + "0x" > + "0x" > + } > + gdb_test_sequence "x/-4s" "take 4 strings backward" { > + "Cannot access memory at address 0x" > + "0x" > + "0x" > + "0x" > + } > + gdb_test_sequence "x/3s ${boundary}" "take 3 strings forward again" { > + "0x" > + "0x" > + "0x" > + } > + gdb_test_sequence "x/-3s" "take 3 strings backward" { > + "Cannot access memory at address 0x" > + "0x" > + "0x" > + "0x" > + } > + } > +} > + > +with_test_prefix "address zero boundary" { > + if {[is_address_zero_readable]} { > + set address_zero "0x0" > + set byte "\t0x\[0-9a-f\]+" > + gdb_test "x/3xb ${address_zero}" \ > + "0x\[0-9a-f\]+00.*:${byte}${byte}${byte}" \ > + "examine 3 bytes forward from ${address_zero}" > + gdb_test "x/-6x" \ > + "0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}${byte}${byte}${byte}" \ > + "examine 6 bytes backward" > + gdb_test "x/-3x ${address_zero}" \ > + "0x\[0-9a-f\]+fd.*:${byte}${byte}${byte}" \ > + "examine 3 bytes backward from ${address_zero}" > + } > +} > + > +gdb_test_no_output "set charset ASCII" > + > +with_test_prefix "char-width=1, print-max=20" { > + gdb_test_no_output "set print elements 20" > + gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward" { > + "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "\"UVWXYZ\"" > + "\"\"" > + "\"\"" > + "\"[^\"]+\"" > + "\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-1xb" "0x39" "take 1 char backward" > + gdb_test_sequence "x/-6s" "take 6 strings backward" { > + "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "\"UVWXYZ\"" > + "\"\"" > + "\"\"" > + "\"[^\"]+\"" > + "\"01234567890123456789\"\.\.\." > + } > + gdb_test_sequence "x/6s &TestStrings" "take 6 strings forward again" { > + "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "\"UVWXYZ\"" > + "\"\"" > + "\"\"" > + "\"[^\"]+\"" > + "\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-xb" "0x39" "take 1 char backward again" > + gdb_test "x/-s" "\"01234567890123456789\"\.\.\." \ > + "take 1 string backward (1/6)" > + gdb_test "x/-s" "\".+\"" \ > + "take 1 string backward (2/6)" > + gdb_test "x/-s" "\"\"" \ > + "take 1 string backward (3/6)" > + gdb_test "x/-s" "\"\"" \ > + "take 1 string backward (4/6)" > + gdb_test "x/-s" "\"GHIJKLMNOPQRSTUVWXYZ\"" \ > + "take 1 string backward (5/6)" > + gdb_test "x/-s" "\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \ > + "take 1 string backward (6/6)" > +} > + > +with_test_prefix "char-width=2, print-max=20" { > + gdb_test_no_output "set print elements 20" > + gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" { > + "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "u\"UVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"[^\"]+\"" > + "u\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-1xh" "0x0039" "take 1 char backward" > + gdb_test_sequence "x/-6sh" "take 6 strings backward" { > + "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "u\"UVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"[^\"]+\"" > + "u\"01234567890123456789\"\.\.\." > + } > + gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" { > + "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "u\"UVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"[^\"]+\"" > + "u\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-xh" "0x0039" "take 1 char backward again" > + gdb_test "x/-sh" "u\"01234567890123456789\"\.\.\." \ > + "take 1 string backward (1/6)" > + gdb_test "x/-sh" "u\".+\"" \ > + "take 1 string backward (2/6)" > + gdb_test "x/-sh" "u\"\"" \ > + "take 1 string backward (3/6)" > + gdb_test "x/-sh" "u\"\"" \ > + "take 1 string backward (4/6)" > + gdb_test "x/-sh" "u\"GHIJKLMNOPQRSTUVWXYZ\"" \ > + "take 1 string backward (5/6)" > + gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \ > + "take 1 string backward (6/6)" > +} > + > +with_test_prefix "char-width=4, print-max=20" { > + gdb_test_no_output "set print elements 20" > + gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward" { > + "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "U\"UVWXYZ\"" > + "U\"\"" > + "U\"\"" > + "U\"[^\"]+\"" > + "U\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-1xw" "0x00000039" "take 1 char backward" > + gdb_test_sequence "x/-6sw" "take 6 strings backward" { > + "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "U\"UVWXYZ\"" > + "U\"\"" > + "U\"\"" > + "U\"[^\"]+\"" > + "U\"01234567890123456789\"\.\.\." > + } > + gdb_test_sequence "x/6sw &TestStringsW" "take 6 strings forward again" { > + "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." > + "U\"UVWXYZ\"" > + "U\"\"" > + "U\"\"" > + "U\"[^\"]+\"" > + "U\"01234567890123456789\"\.\.\." > + } > + gdb_test "x/-xw" "0x00000039" "take 1 char backward again" > + gdb_test "x/-sw" "U\"01234567890123456789\"\.\.\." \ > + "take 1 string backward (1/6)" > + gdb_test "x/-sw" "U\".+\"" \ > + "take 1 string backward (2/6)" > + gdb_test "x/-sw" "U\"\"" \ > + "take 1 string backward (3/6)" > + gdb_test "x/-sw" "U\"\"" \ > + "take 1 string backward (4/6)" > + gdb_test "x/-sw" "U\"GHIJKLMNOPQRSTUVWXYZ\"" \ > + "take 1 string backward (5/6)" > + gdb_test "x/-sw" "U\"ABCDEFGHIJKLMNOPQRST\"\.\.\." \ > + "take 1 string backward (6/6)" > +} > + > +with_test_prefix "char-width=2, print-max=0" { > + gdb_test_no_output "set print elements 0" > + gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward" { > + "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\"" > + "u\"012345678901234567890123456789\"" > + "u\"!!!!!!\"" > + } > + gdb_test "x/-4xh" "0x0021\[\t \]+0x0021\[\t \]+0x0021\[\t \]+0x0000" \ > + "take 4 characters backward" > + gdb_test_sequence "x/-6sh" "take 6 strings backward" { > + "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"[^\"]+\"" > + "u\"012345678901234567890123456789\"" > + "u\"!!!!!!\"" > + } > + gdb_test_sequence "x/6sh &TestStringsH" "take 6 strings forward again" { > + "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" > + "u\"\"" > + "u\"\"" > + "u\"\\\\x307b\\\\x3052\\\\x307b\\\\x3052\"" > + "u\"012345678901234567890123456789\"" > + "u\"!!!!!!\"" > + } > + gdb_test "x/-xh" "0x0000" "take 1 char backward" > + gdb_test "x/-sh" "u\"!!!!!!\"" \ > + "take 1 string backward (1/6)" > + gdb_test "x/-sh" "u\"012345678901234567890123456789\"" \ > + "take 1 string backward (2/6)" > + gdb_test "x/-sh" "u\".+\"" \ > + "take 1 string backward (3/6)" > + gdb_test "x/-sh" "u\"\"" \ > + "take 1 string backward (4/6)" > + gdb_test "x/-sh" "u\"\"" \ > + "take 1 string backward (5/6)" > + gdb_test "x/-sh" "u\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"" \ > + "take 1 string backward (6/6)" > +} > + > +with_test_prefix "char-width=1, print-max=4" { > + gdb_test_no_output "set print elements 4" > + gdb_test_sequence "x/9s &TestStrings" "take 9 strings forward" { > + "\"ABCD\"\.\.\." > + "\"EFGH\"\.\.\." > + "\"IJKL\"\.\.\." > + "\"MNOP\"\.\.\." > + "\"QRST\"\.\.\." > + "\"UVWX\"\.\.\." > + "\"YZ\"" > + "\"\"" > + "\"\"" > + } > + gdb_test "x/-xb" "0x00" "take 1 byte backward" > + gdb_test_sequence "x/-4s" "take 4 strings backward (1/2)" { > + "\"TUVW\"\.\.\." > + "\"XYZ\"" > + "\"\"" > + "\"\"" > + } > + gdb_test_sequence "x/-4s" "take 4 strings backward (2/2)" { > + "\"CDEF\"\.\.\." > + "\"GHIJ\"\.\.\." > + "\"KLMN\"\.\.\." > + "\"OPQR\"\.\.\." > + } > +} > + > +with_test_prefix "backward disassemble general" { > + set length_to_examine {1 2 3 4 10} > + set disassmbly {} > + > + gdb_test "x/i main" "0x\[0-9a-fA-F\]+
:\t.*" \ > + "move the current position to main (x/i)" > + gdb_test "x/-i" "0x\[0-9a-fA-F\]+
:\t.*" \ > + "move the current position to main (x/-i)" > + for {set i 0} {$i < [llength $length_to_examine]} {incr i} { > + set len [lindex $length_to_examine $i] > + set instructions [capture_command_output "x/${len}i" ""] > + lappend disassmbly $instructions > + } > + for {set i 0} {$i < [llength $length_to_examine]} {incr i} { > + set idx [expr [llength $length_to_examine] - $i - 1] > + set len [lindex $length_to_examine $idx] > + set actual [capture_command_output "x/-${len}i" ""] > + set expected [lindex $disassmbly $idx] > + if {$actual == $expected} { > + pass "inst:$idx" > + } else { > + fail "inst:$idx" > + } > + } > +}