This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Changes to disassemble command


The patch below addresses a number of issues relating to the disassemble
command. These are,

(1) When disassembling using address ranges over a compilation unit
boundary and asking for source code information the disassembly will fail.

(2) If (1) is fixed then the disassemble command will stop short of the
requested end address, and will stop giving source information once the
address being disassembled moves beyond the line-table for the start
address.

(3) The MI/MI2 interface does not support dumping of the raw opcodes.

Tested on i686-pc-linux-gnu with no regressions.

I've included a test for issue (1), and will be happy to have a go at
some more for 2 & 3 if the patch gets reviewed.

Happy to make changes/fixes as needed.

Thanks,
Andrew

gdb/

2010-12-10 Andrew Burgess <aburgess@broadcom.com>

         * disasm.c (compare_lines): Sort by PC for entries where
           the line number is 0, these are the end of sequence markers.
           (dump_insns): Return the number of instructions dumped, and
           the next PC at which dumping should resume. Build the opcodes
           into a stream so that we can dump them as a field for MI.
           (do_mixed_source_and_assembly): Don't include the end of
           sequence marker in the list of lines to dump. Return the next
           address to disassemble from. Move the asm_insns list to
           gdb_disassembly.
           (do_assembly_only): Also move the asm_insns list to
           gdb_disassembly. Return the next address to disassemble from.
           (gdb_disassembly): Create the asm_insns list here. When doing
           mixed source and assembly disassembly keep going until we
           reach the high address, dump raw instructions when no symbol
           information is available.
         * gdb.texinfo: Update to reflect changes in mi/mi-cmd-disas.c


gdb/mi/


2010-12-10 Andrew Burgess <aburgess@broadcom.com>

         * mi-cmd-disas.c (mi_cmd_disassemble): Allow mode to control
           dumping of instruction opcodes.

gdb/testsuite/

2010-12-10 Andrew Burgess <aburgess@broadcom.com>

         * gdb.disasm/disasm-end-cu-1.c, gdb.disasm/disasm-end-cu-2.c,
           gdb.disasm/disasm-end-cu.exp: New test for disassembling over
           the boundary between two compilation units.
         * gdb.mi/mi-disassemble.exp, gdb.mi/mi2-disassemble.exp: Update
           expected output to reflect changes in gdb/mi/mi-cmd-disas.c



diff --git a/gdb/disasm.c b/gdb/disasm.c
index c51f0bf..491560f 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -43,6 +43,15 @@ struct dis_line_entry
    CORE_ADDR end_pc;
  };

+/* This structure holds the result of calling dump_insns. It contains
+   the number of instructions dumped, and the next address to start
+   dumping at if you want a continuous dump of memory. */
+struct dump_insns
+{
+  int how_many;
+  CORE_ADDR next_pc;
+};
+
  /* Like target_read_memory, but slightly different parameters.  */
  static int
  dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr, unsigned int len,
@@ -77,22 +86,37 @@ compare_lines (const void *mle1p, const void *mle2p)
    mle1 = (struct dis_line_entry *) mle1p;
    mle2 = (struct dis_line_entry *) mle2p;

-  val = mle1->line - mle2->line;
+  /* Work with end of sequence markers
+     where the line number is set to 0 */
+  if ( mle1->line == 0 || mle2->line == 0 )
+    {
+      val = mle1->start_pc - mle2->start_pc;

-  if (val != 0)
-    return val;
+      if ( val == 0 )
+        val = mle1->line - mle2->line;
+    }
+  else
+    {
+      val = mle1->line - mle2->line;

-  return mle1->start_pc - mle2->start_pc;
+      if (val == 0)
+        val = mle1->start_pc - mle2->start_pc;
+    }
+
+  return val;
  }

-static int
+static struct dump_insns
  dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
            struct disassemble_info * di,
            CORE_ADDR low, CORE_ADDR high,
            int how_many, int flags, struct ui_stream *stb)
  {
+  struct dump_insns result;
    int num_displayed = 0;
-  CORE_ADDR pc;
+  CORE_ADDR pc = low;
+  /* Just initialise prev_pc to anything that is not pc */
+  CORE_ADDR prev_pc = pc - 1;

    /* parts of the symbolic representation of the address */
    int unmapped;
@@ -100,12 +124,18 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out
*uiout,
    int line;
    struct cleanup *ui_out_chain;

-  for (pc = low; pc < high;)
+  while (pc < high)
      {
        char *filename = NULL;
        char *name = NULL;

        QUIT;
+
+      /* Ensure we don't get stuck */
+      if ( prev_pc == pc )
+        break;
+      prev_pc = pc;
+
        if (how_many >= 0)
        {
          if (num_displayed >= how_many)
@@ -143,6 +173,12 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out
*uiout,
            CORE_ADDR old_pc = pc;
            bfd_byte data;
            int status;
+          char *spacer = "";
+
+          /* Temporary stream for building up the opcodes */
+          struct ui_stream *opcode_stream = ui_out_stream_new (uiout);
+          struct cleanup *cleanups =
+            make_cleanup_ui_out_stream_delete (opcode_stream);

            pc += gdbarch_print_insn (gdbarch, pc, di);
            for (;old_pc < pc; old_pc++)
@@ -150,9 +186,15 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out
*uiout,
                status = (*di->read_memory_func) (old_pc, &data, 1, di);
                if (status != 0)
                  (*di->memory_error_func) (status, old_pc, di);
-              ui_out_message (uiout, 0, " %02x", (unsigned)data);
+              fprintf_filtered(opcode_stream->stream, "%s%02x",
+                               spacer, (unsigned)data);
+              spacer = " ";
              }
+
+          ui_out_field_stream (uiout, "opcodes", opcode_stream);
            ui_out_text (uiout, "\t");
+
+          do_cleanups (cleanups);
          }
        else
          pc += gdbarch_print_insn (gdbarch, pc, di);
@@ -161,14 +203,18 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out
*uiout,
        do_cleanups (ui_out_chain);
        ui_out_text (uiout, "\n");
      }
-  return num_displayed;
+
+  result.how_many = num_displayed;
+  result.next_pc = pc;
+
+  return result;
  }

  /* The idea here is to present a source-O-centric view of a
     function to the user.  This means that things are presented
     in source order, with (possibly) out of order assembly
     immediately following.  */
-static void
+static CORE_ADDR
  do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out
*uiout,
                              struct disassemble_info *di, int nlines,
                              struct linetable_entry *le,
@@ -186,6 +232,8 @@ do_mixed_source_and_assembly (struct gdbarch
*gdbarch, struct ui_out *uiout,
    struct cleanup *ui_out_chain;
    struct cleanup *ui_out_tuple_chain = make_cleanup (null_cleanup, 0);
    struct cleanup *ui_out_list_chain = make_cleanup (null_cleanup, 0);
+  CORE_ADDR next_pc = low;
+  struct dump_insns dumped;

mle = (struct dis_line_entry *) alloca (nlines
* sizeof (struct dis_line_entry));
@@ -220,7 +268,7 @@ do_mixed_source_and_assembly (struct gdbarch
*gdbarch, struct ui_out *uiout,
/* If we're on the last line, and it's part of the function,
then we need to get the end pc in a special way. */


-  if (i == nlines - 1 && le[i].pc < high)
+  if (i == nlines - 1 && le[i].pc < high && le[i].line != 0)
      {
        mle[newlines].line = le[i].line;
        mle[newlines].start_pc = le[i].pc;
@@ -239,7 +287,6 @@ do_mixed_source_and_assembly (struct gdbarch
*gdbarch, struct ui_out *uiout,
       they have been emitted before), followed by the assembly code
       for that line.  */

- ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");

    for (i = 0; i < newlines; i++)
      {
@@ -295,9 +342,11 @@ do_mixed_source_and_assembly (struct gdbarch
*gdbarch, struct ui_out *uiout,
            = make_cleanup_ui_out_list_begin_end (uiout, "line_asm_insn");
        }

-      num_displayed += dump_insns (gdbarch, uiout, di,
-                                  mle[i].start_pc, mle[i].end_pc,
-                                  how_many, flags, stb);
+      dumped = dump_insns (gdbarch, uiout, di,
+                           mle[i].start_pc, mle[i].end_pc,
+                           how_many, flags, stb);
+      num_displayed += dumped.how_many;
+      next_pc = dumped.next_pc;

        /* When we've reached the end of the mle array, or we've seen
the last
           assembly range for this source line, close out the
list/tuple.  */
@@ -310,27 +359,25 @@ do_mixed_source_and_assembly (struct gdbarch
*gdbarch, struct ui_out *uiout,
          ui_out_text (uiout, "\n");
        }
        if (how_many >= 0 && num_displayed >= how_many)
-       break;
+        {
+          /* We've dumped enough now so push on next_pc */
+          next_pc = high;
+          break;
+        }
      }
-  do_cleanups (ui_out_chain);
+  return next_pc;
  }


-static void +static CORE_ADDR do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout, struct disassemble_info * di, CORE_ADDR low, CORE_ADDR high, int how_many, int flags, struct ui_stream *stb) { - int num_displayed = 0; - struct cleanup *ui_out_chain; - - ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns"); - - num_displayed = dump_insns (gdbarch, uiout, di, low, high, how_many, - flags, stb); - - do_cleanups (ui_out_chain); + struct dump_insns result = + dump_insns (gdbarch, uiout, di, low, high, how_many, flags, stb); + return result.next_pc; }

  /* Initialize the disassemble info struct ready for the specified
@@ -387,6 +434,7 @@ gdb_disassembly (struct gdbarch *gdbarch, struct
ui_out *uiout,
    struct symtab *symtab = NULL;
    struct linetable_entry *le = NULL;
    int nlines = -1;
+  struct cleanup *ui_out_chain;

    /* Assume symtab is valid for whole PC range */
    symtab = find_pc_symtab (low);
@@ -398,14 +446,47 @@ gdb_disassembly (struct gdbarch *gdbarch, struct
ui_out *uiout,
        nlines = symtab->linetable->nitems;
      }

+  ui_out_chain = make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
+
    if (!(flags & DISASSEMBLY_SOURCE) || nlines <= 0
        || symtab == NULL || symtab->linetable == NULL)
      do_assembly_only (gdbarch, uiout, &di, low, high, how_many, flags,
stb);

    else if (flags & DISASSEMBLY_SOURCE)
-    do_mixed_source_and_assembly (gdbarch, uiout, &di, nlines, le, low,
-                                 high, symtab, how_many, flags, stb);
+  {
+    low = do_mixed_source_and_assembly (gdbarch, uiout, &di, nlines,
le, low,
+                                        high, symtab, how_many, flags,
stb);

+    /* If we've not covered the requested range then keep going */
+    while ( low < high )
+    {
+      CORE_ADDR prev_low = low;
+
+      /* Attempt to find a new symtab entry for the next address */
+      nlines = -1;
+      symtab = find_pc_symtab (low);
+
+      if (symtab != NULL && symtab->linetable != NULL)
+      {
+        /* Convert the linetable to a bunch of my_line_entry's.  */
+        le = symtab->linetable->item;
+        nlines = symtab->linetable->nitems;
+
+        low = do_mixed_source_and_assembly (gdbarch, uiout, &di,
nlines, le, low,
+                                            high, symtab, how_many,
flags, stb);
+      }
+
+      /* Disassemble a single instruction as we couldn't find a symtab */
+      if (nlines <= 0 || symtab == NULL || symtab->linetable == NULL ||
low == prev_low)
+        low = do_assembly_only (gdbarch, uiout, &di, low, high, 1,
flags, stb);
+
+      /* For sanity, if we've not moved on then bail */
+      if ( low == prev_low )
+        break;
+    }
+  }
+
+  do_cleanups (ui_out_chain);
    do_cleanups (cleanups);
    gdb_flush (gdb_stdout);
  }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ddc711b..57c89a8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27567,8 +27567,9 @@ displayed; if @var{lines} is higher than the
number of lines between
  @var{start-addr} and @var{end-addr}, only the lines up to @var{end-addr}
  are displayed.
  @item @var{mode}
-is either 0 (meaning only disassembly) or 1 (meaning mixed source and
-disassembly).
+is either 0 (meaning only disassembly), 1 (meaning mixed source and
+disassembly), 2 (meaning disassembly with raw opcodes), or 3 (meaning
+mixed source and disassembly with raw opcodes).
  @end table

  @subsubheading Result
diff --git a/gdb/mi/mi-cmd-disas.c b/gdb/mi/mi-cmd-disas.c
index b543acc..afaaf4d 100644
--- a/gdb/mi/mi-cmd-disas.c
+++ b/gdb/mi/mi-cmd-disas.c
@@ -46,15 +46,18 @@

always required:

-   MODE: 0 or 1 for disassembly only, or mixed source and disassembly,
-   respectively. */
+   MODE: 0 -- disassembly.
+         1 -- disassembly and source.
+         2 -- disassembly            and opcodes.
+         3 -- disassembly and source and opcodes.
+*/
  void
  mi_cmd_disassemble (char *command, char **argv, int argc)
  {
    struct gdbarch *gdbarch = get_current_arch ();
    CORE_ADDR start;

-  int mixed_source_and_assembly;
+  int mode;
    struct symtab *s;

    /* Which options have we processed ... */
@@ -135,10 +138,9 @@ mi_cmd_disassemble (char *command, char **argv, int
argc)
      error
        ("mi_cmd_disassemble: Usage: [-f filename -l linenum [-n
howmany]] [-s startaddr -e endaddr] [--] mixed_mode.");

-  mixed_source_and_assembly = atoi (argv[0]);
-  if ((mixed_source_and_assembly != 0) && (mixed_source_and_assembly != 1))
-    error (_("mi_cmd_disassemble: Mixed_mode argument must be 0 or 1."));
-
+  mode = atoi (argv[0]);
+  if (mode < 0 || mode > 3)
+    error (_("mi_cmd_disassemble: Mode argument must be 0, 1, 2, or 3."));

    /* We must get the function beginning and end where line_num is
       contained. */
@@ -156,6 +158,6 @@ mi_cmd_disassemble (char *command, char **argv, int
argc)

    gdb_disassembly (gdbarch, uiout,
                   file_string,
-                  mixed_source_and_assembly? DISASSEMBLY_SOURCE : 0,
+                  mode,
                   how_many, low, high);
  }
diff --git a/gdb/testsuite/gdb.disasm/disasm-end-cu-1.c
b/gdb/testsuite/gdb.disasm/disasm-end-cu-1.c
new file mode 100644
index 0000000..c031778
--- /dev/null
+++ b/gdb/testsuite/gdb.disasm/disasm-end-cu-1.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+void
+dummy_1 ()
+{
+  printf("In dummy_1 ()\n");
+}
+
+int
+main ()
+{
+  printf("Hello World!\n");
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.disasm/disasm-end-cu-2.c
b/gdb/testsuite/gdb.disasm/disasm-end-cu-2.c
new file mode 100644
index 0000000..7fddb87
--- /dev/null
+++ b/gdb/testsuite/gdb.disasm/disasm-end-cu-2.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+void
+dummy_2 ()
+{
+  printf("In dummy_2 ()\n");
+}
+
+void
+dummy_3 ()
+{
+  printf("In dummy_3 ()\n");
+}
diff --git a/gdb/testsuite/gdb.disasm/disasm-end-cu.exp
b/gdb/testsuite/gdb.disasm/disasm-end-cu.exp
new file mode 100644
index 0000000..fdbd179
--- /dev/null
+++ b/gdb/testsuite/gdb.disasm/disasm-end-cu.exp
@@ -0,0 +1,70 @@
+# Copyright 2010 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 <http://www.gnu.org/licenses/>.
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+# For now pick a sampling of likely targets.
+if {![istarget *-*-linux*]
+    && ![istarget *-*-gnu*]
+    && ![istarget *-*-elf*]
+    && ![istarget *-*-openbsd*]
+    && ![istarget arm-*-eabi*]
+    && ![istarget powerpc-*-eabi*]} {
+    return 0
+}
+
+# This test tries to disassemble over the boundary between two compilation
+# units displaying source lines. This checks that the disassemble routine
+# can handle our use of line number 0 to mark the end of sequence.
+
+if { [prepare_for_testing disasm-end-cu.exp "disasm-end-cu"
{disasm-end-cu-1.c disasm-end-cu-2.c} {debug}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return -1
+}
+
+set main_addr [get_hexadecimal_valueof "&main" "0"]
+set dummy_3_addr [get_hexadecimal_valueof "&dummy_3" "0"]
+
+if {$main_addr == 0 || $dummy_3_addr == 0 || $dummy_3_addr <= $main_addr} {
+    fail "Unable to extract required addresses, or addresses out of order"
+    return -1
+}
+
+send_gdb "disassemble /m ${main_addr},${dummy_3_addr}\n"
+gdb_expect {
+    -re "Dump of assembler code from ${main_addr} to
${dummy_3_addr}:\r\nEnd of assembler dump\." {
+        fail "No output from the disassemble command"
+    }
+
+    -re "Line number 0 out of range;.* has $decimal lines\." {
+        fail "The disassemble command failed"
+    }
+
+    -re "Dump of assembler code from ${main_addr} to
${dummy_3_addr}:\r\n.*main.*End of assembler dump\." {
+        pass "disassemble command returned some output"
+    }
+
+    -re ".*$gdb_prompt $" {
+        fail "Unexpected output from disassemble command"
+    }
+
+    timeout {
+        fail "(timeout) waiting for output of disassemble command"
+    }
+}
+
+gdb_stop_suppressing_tests;
diff --git a/gdb/testsuite/gdb.mi/mi-disassemble.exp
b/gdb/testsuite/gdb.mi/mi-disassemble.exp
index f5aa784..fdc4789 100644
--- a/gdb/testsuite/gdb.mi/mi-disassemble.exp
+++ b/gdb/testsuite/gdb.mi/mi-disassemble.exp
@@ -172,7 +172,7 @@ proc test_disassembly_bogus_args {} {
               "data-disassemble mix different args"

      mi_gdb_test "789-data-disassemble -f basics.c -l $line_main_body
-- 9" \
-             "789\\^error,msg=\"mi_cmd_disassemble: Mixed_mode argument
must be 0 or 1.\"" \
+             "789\\^error,msg=\"mi_cmd_disassemble: Mode argument must
be 0, 1, 2, or 3.\"" \
               "data-disassemble wrong mode arg"

  }
diff --git a/gdb/testsuite/gdb.mi/mi2-disassemble.exp
b/gdb/testsuite/gdb.mi/mi2-disassemble.exp
index 8ccbc9f..fd73654 100644
--- a/gdb/testsuite/gdb.mi/mi2-disassemble.exp
+++ b/gdb/testsuite/gdb.mi/mi2-disassemble.exp
@@ -172,7 +172,7 @@ proc test_disassembly_bogus_args {} {
               "data-disassemble mix different args"

      mi_gdb_test "789-data-disassemble -f basics.c -l $line_main_body
-- 9" \
-             "789\\^error,msg=\"mi_cmd_disassemble: Mixed_mode argument
must be 0 or 1.\"" \
+             "789\\^error,msg=\"mi_cmd_disassemble: Mode argument must
be 0, 1, 2, or 3.\"" \
               "data-disassemble wrong mode arg"

}





Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]