----- Original Message ----- > The purpose of this patch is to add support for a count value in reverse > or forward mode, during disassembly: > > 'dis [-r|-f] [symbol|address] [count]' > > For example: > > crash> dis -f list_del+0x16 4 > 0xffffffff812b3346 <list_del+22>: jne 0xffffffff812b3381 > <list_del+81> > 0xffffffff812b3348 <list_del+24>: mov (%rbx),%rax > 0xffffffff812b334b <list_del+27>: mov 0x8(%rax),%r8 > 0xffffffff812b334f <list_del+31>: cmp %r8,%rbx > > crash> dis -r list_del+0x16 4 > 0xffffffff812b333c <list_del+12>: mov 0x8(%rdi),%rax > 0xffffffff812b3340 <list_del+16>: mov (%rax),%r8 > 0xffffffff812b3343 <list_del+19>: cmp %r8,%rdi > 0xffffffff812b3346 <list_del+22>: jne 0xffffffff812b3381 > <list_del+81> > > To support this feature, I have essentially incorported GDB commit > bb556f1facb ("Add negative repeat count to 'x' command"), with some > additional changes to maintain default behaviour i.e. always display the > target instruction with the examine command. Hi Aaron, The concept looks good, but the printcmd.c part of the patch doesn't apply cleanly: $ make warn TARGET: PPC64 CRASH: 7.2.6++ GDB: 7.6 ... [ cut ] ... patching file gdb-7.6/gdb/printcmd.c Hunk #2 FAILED at 800. Hunk #5 FAILED at 966. Hunk #6 succeeded at 2579 (offset 1 line). 2 out of 6 hunks FAILED ... [ cut ] ... gcc -g -O2 -m64 -fPIC -mminimal-toc -I. -I. -I./common -I./config -DLOCALEDIR="\"/usr/local/share/locale\"" -DCRASH_MERGE -DHAVE_CONFIG_H -I./../include/opcode -I./../opcodes/.. -I./../readline/.. -I../bfd -I./../bfd -I./../include -I../libdecnumber -I./../libdecnumber -I./gnulib/import -Ibuild-gnulib/import -DTUI=1 -Wall -Wdeclaration-after-statement -Wpointer-arith -Wformat-nonliteral -Wno-pointer-sign -Wno-unused -Wunused-value -Wunused-function -Wno-switch -Wno-char-subscripts -Wmissing-prototypes -Wdeclaration-after-statement -Wempty-body `echo " -Wall -Wdeclaration-after-statement -Wpointer-arith -Wformat-nonliteral -Wno-pointer-sign -Wno-unused -Wunused-value -Wunused-function -Wno-switch -Wno-char-subscripts -Wmissing-prototypes -Wdeclaration-after-statement -Wempty-body " | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"` \ -c -o printcmd.o -MT printcmd.o -MMD -MP -MF .deps/printcmd.Tpo ./printcmd.c ./printcmd.c: In function ‘do_examine’: ./printcmd.c:899:26: warning: implicit declaration of function ‘find_instruction_backward’; did you mean ‘generic_instruction_nullified’? [-Wimplicit-function-declaration] next_address = find_instruction_backward (gdbarch, addr, count, ^~~~~~~~~~~~~~~~~~~~~~~~~ generic_instruction_nullified ./printcmd.c:904:26: warning: implicit declaration of function ‘find_string_backward’ [-Wimplicit-function-declaration] next_address = find_string_backward (gdbarch, addr, count, ^~~~~~~~~~~~~~~~~~~~ Making init.c ... [ cut ] ... gcc -g -O2 -m64 -fPIC -mminimal-toc \ -o ../../crash ../../crashlib.a rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o ppc64-tdep.o solib-svr4.o solib-spu.o spu-multiarch.o glibc-tdep.o symfile-mem.o linux-tdep.o ravenscar-thread.o ppc-ravenscar-thread.o ser-base.o ser-unix.o ser-pipe.o ser-tcp.o inf-ptrace.o fork-child.o ppc-linux-nat.o proc-service.o linux-thread-db.o linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o remote-notif.o cli-dump.o cli-decode.o cli-script.o cli-cmds.o cli-setshow.o cli-logging.o cli-interp.o cli-utils.o mi-out.o mi-console.o mi-cmds.o mi-cmd-catch.o mi-cmd-env.o mi-cmd-var.o mi-cmd-break.o mi-cmd-stack.o mi-cmd-file.o mi-cmd-disas.o mi-symbol-cmds.o mi-cmd-target.o mi-cmd-info.o mi-interp.o mi-main.o mi-parse.o mi-getopt.o tui-command.o tui-data.o tui-disasm.o tui-file.o tui-hooks.o tui-interp.o tui-io.o tui-layout.o tui-out.o tui-regs.o tui-source.o tui-stack.o tui-win.o tui-windata.o tui-wingeneral.o tui-winsource.o tui.o python.o py-value.o py-prettyprint.o py-auto-load.o elfread.o stap-probe.o posix-hdep.o c-exp.o cp-name-parser.o ada-exp.o jv-exp.o f-exp.o go-exp.o m2-exp.o p-exp.o version.o annotate.o addrmap.o auto-load.o auxv.o agent.o bfd-target.o blockframe.o breakpoint.o break-catch-sig.o findvar.o regcache.o cleanups.o charset.o continuations.o corelow.o disasm.o dummy-frame.o dfp.o source.o value.o eval.o valops.o valarith.o valprint.o printcmd.o block.o symtab.o psymtab.o symfile.o symmisc.o linespec.o dictionary.o infcall.o infcmd.o infrun.o expprint.o environ.o stack.o thread.o exceptions.o filesystem.o inf-child.o interps.o minidebug.o main.o macrotab.o macrocmd.o macroexp.o macroscope.o mi-common.o event-loop.o event-top.o inf-loop.o completer.o gdbarch.o arch-utils.o gdbtypes.o gdb_bfd.o gdb_obstack.o osabi.o copying.o memattr.o mem-break.o target.o parse.o language.o buildsym.o findcmd.o std-regs.o signals.o exec.o reverse.o bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o dbxread.o coffread.o coff-pe-r ead.o dwarf2read.o mipsread.o stabsread.o corefile.o dwarf2expr.o dwarf2loc.o dwarf2-frame.o dwarf2-frame-tailcall.o ada-lang.o c-lang.o d-lang.o f-lang.o objc-lang.o ada-tasks.o ada-varobj.o ui-out.o cli-out.o varobj.o vec.o go-lang.o go-valprint.o go-typeprint.o jv-lang.o jv-valprint.o jv-typeprint.o m2-lang.o opencl-lang.o p-lang.o p-typeprint.o p-valprint.o sentinel-frame.o complaints.o typeprint.o ada-typeprint.o c-typeprint.o f-typeprint.o m2-typeprint.o ada-valprint.o c-valprint.o cp-valprint.o d-valprint.o f-valprint.o m2-valprint.o serial.o mdebugread.o top.o utils.o ui-file.o user-regs.o frame.o frame-unwind.o doublest.o frame-base.o inline-frame.o gnu-v2-abi.o gnu-v3-abi.o cp-abi.o cp-support.o cp-namespace.o reggroups.o regset.o trad-frame.o tramp-frame.o solib.o solib-target.o prologue-value.o memory-map.o memrange.o xml-support.o xml-syscall.o xml-utils.o target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o gdb_vecs.o jit.o progspace.o skip.o probe.o common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o format.o registry.o btrace.o record-btrace.o inflow.o init.o \ ../readline/libreadline.a ../opcodes/libopcodes.a ../bfd/libbfd.a ../libiberty/libiberty.a ../libdecnumber/libdecnumber.a -ldl -lncurses -lz -lm ../libiberty/libiberty.a build-gnulib/import/libgnu.a -ldl -Wl,--dynamic-list=./proc-service.list -lz -ldl -rdynamic printcmd.o: In function `do_examine': /root/crash.git/gdb-7.6/gdb/./printcmd.c:899: undefined reference to `find_instruction_backward' /root/crash.git/gdb-7.6/gdb/./printcmd.c:904: undefined reference to `find_string_backward' collect2: error: ld returned 1 exit status make[5]: *** [Makefile:1195: gdb] Error 1 make[4]: *** [Makefile:8265: all-gdb] Error 2 make[3]: *** [Makefile:835: all] Error 2 make[2]: *** [Makefile:245: rebuild] Error 2 make[1]: *** [Makefile:233: gdb_merge] Error 2 make: *** [Makefile:314: warn] Error 2 $ It will need some additions to help.c as well. Thanks, Dave > > Signed-off-by: Aaron Tomlin <atomlin@xxxxxxxxxx> > --- > gdb-7.6.patch | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++ > kernel.c | 33 ++--- > 2 files changed, 349 insertions(+), 15 deletions(-) > > diff --git a/gdb-7.6.patch b/gdb-7.6.patch > index cd75dcf..96cdef4 100644 > --- a/gdb-7.6.patch > +++ b/gdb-7.6.patch > @@ -2447,3 +2447,334 @@ diff -up gdb-7.6/opcodes/configure.orig > gdb-7.6/opcodes/configure > #else > # error "!__i386__ && !__x86_64__" > #endif > +--- gdb-7.6/gdb/printcmd.c.orig > ++++ gdb-7.6/gdb/printcmd.c > +@@ -201,8 +201,13 @@ decode_format (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++; > + > +@@ -795,6 +800,232 @@ 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, func_addr; > ++ VEC (CORE_ADDR) *pcs = NULL; > ++ struct symtab_and_line sal; > ++ struct cleanup *cleanup = make_cleanup (VEC_cleanup (CORE_ADDR), &pcs); > ++ int actual_count = 0; > ++ > ++ *inst_read = 0; > ++ inst_count--; > ++ loop_start = loop_end = addr; > ++ > ++ find_pc_partial_function (addr, NULL, &func_addr, NULL); > ++ for (p = func_addr; p != addr;) > ++ { > ++ p += gdb_insn_length (gdbarch, p); > ++ actual_count++; > ++ } > ++ if (inst_count > actual_count) > ++ inst_count = actual_count; > ++ > ++ /* 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. */ > + > +@@ -808,12 +1039,16 @@ 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; > ++ int is_backward; > + > + format = fmt.format; > + size = fmt.size; > + count = fmt.count; > + next_gdbarch = gdbarch; > + next_address = addr; > ++ is_backward = count < 0; > + > + /* Instruction format implies fetch single bytes > + regardless of the specified size. > +@@ -878,9 +1113,43 @@ do_examine (struct format_data fmt, struct gdbarch > *gdbarch, CORE_ADDR addr) > + > + get_formatted_print_options (&opts, format); > + > ++ if (is_backward) > ++ { > ++ /* 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. */ > + > ++ if (is_backward) > ++ count++; > + while (count > 0) > + { > + QUIT; > +@@ -923,6 +1192,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 > +@@ -2535,7 +2807,8 @@ Format letters are o(octal), x(hex), d(decimal), > u(unsigned decimal),\n\ > + t(binary), f(float), a(address), i(instruction), c(char) and > s(string).\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/kernel.c b/kernel.c > index 22909d2..f5960fc 100644 > --- a/kernel.c > +++ b/kernel.c > @@ -1931,16 +1931,10 @@ cmd_dis(void) > } > > if (args[++optind]) { > - if (reverse || forward) { > - error(INFO, > - "count argument ignored with -%s option\n", > - reverse ? "r" : "f"); > - } else { > - req->count = stol(args[optind], > + req->count = stol(args[optind], > FAULT_ON_ERROR, NULL); > - req->flags &= ~GNU_FUNCTION_ONLY; > - count_entered++; > - } > + req->flags &= ~GNU_FUNCTION_ONLY; > + count_entered++; > } > > if (sources) { > @@ -1992,6 +1986,10 @@ cmd_dis(void) > } > } > > + if (reverse || forward) > + if (count_entered && req->count == 1) > + reverse = forward = 0; > + > if (reverse || forward) { > target = req->addr; > if ((sp = value_search(target, NULL)) == NULL) > @@ -2006,14 +2004,19 @@ cmd_dis(void) > do_machdep_filter = machdep->dis_filter(req->addr, NULL, radix); > open_tmpfile(); > > - if (reverse) > - sprintf(buf5, "x/%ldi 0x%lx", > - (target - req->addr) ? target - req->addr : 1, > - req->addr); > - else > + if (reverse || forward) { > + if (count_entered && req->count) > + sprintf(buf5, "x/%s%ldi 0x%lx", reverse ? "-" : "", > + req->count, target); > + else > + sprintf(buf5, "x/%ldi 0x%lx", > + forward ? req->addr2 - req->addr : > + (target - req->addr) ? target - req->addr : 1, > + forward ? target : req->addr); > + } else > sprintf(buf5, "x/%ldi 0x%lx", > count_entered && req->count ? req->count : > - forward || req->flags & GNU_FUNCTION_ONLY ? > + req->flags & GNU_FUNCTION_ONLY ? > req->addr2 - req->addr : 1, > req->addr); > gdb_pass_through(buf5, NULL, GNU_RETURN_ON_ERROR); > -- > 2.20.1 > > -- > Crash-utility mailing list > Crash-utility@xxxxxxxxxx > https://www.redhat.com/mailman/listinfo/crash-utility > -- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility