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]

Re: [PATCH 2/2] gdb: Ensure disassembler covers requested address range.


Doug,

Thanks for the review.  I don't have a new revision of this patch yet,
I would like to discuss one of the issue you raised first.  Feedback
inline.

* Doug Evans <xdje42@gmail.com> [2015-09-16 21:03:46 -0700]:

> Andrew Burgess <andrew.burgess@embecosm.com> writes:
> > If gdb is asked to disassemble a particular address range, but that
> > address range spans the end of a symbol table, then currently gdb will
> > exit the disassembler early.
> >
> > This commit makes the disassembler handle passing over the boundary
> > between different symbol tables, and even over regions where there is no
> > symbol table.
> >
> > The only reason that the disassembler will now not disassemble a
> > complete address range as asked is if the inferior memory can't be
> > read within that region.
> >
> > The existing test for disassembling over compilation unit boundary is
> > not sufficient, in fact the behaviour has regressed since the test was
> > first added in 2011 (commit 9011945e46bf8b) and the test failed to
> > highlight this regression.
> >
> > This commit removes the old test in this area, and adds new, more
> > comprehensive tests that cover the following features:
> >
> >  1. Disassembly over the end of a compilation unit and into the next
> >     compilation unit should work.
> >
> >  2. When different compilation units do, or do not, have debug
> >     information, we should still get some disassembly output, even when
> >     asking for disassembly with source code.
> >
> >  3. When asking for disassembly with source code, we should, where
> >     possible provide that source code, even when earlier compilation
> >     units within the region being disassembled don't have debug
> >     information.
> >
> > gdb/ChangeLog:
> >
> > 	* disasm.c (do_mixed_source_and_assembly_deprecated): Return
> > 	count, and take extra parameter.  Fill in final_pc parameter.
> > 	(do_mixed_source_and_assembly): Likewise.
> > 	(do_assembly_only): Likewise, also return from function when we've
> > 	displayed enough instructions.
> > 	(gdb_disassembly): Add loop, looking up new symtabs, or displaying
> > 	raw instructions as required.
> >
> > gdb/testsuite/ChangeLog:
> >
> > 	* gdb.base/disasm-end-cu-1.c: Deleted.
> > 	* gdb.base/disasm-end-cu-2.c: Deleted.
> > 	* gdb.base/disasm-end-cu.exp: Deleted.
> > 	* gdb.base/disasm-multi-cu-1.c: New file.
> > 	* gdb.base/disasm-multi-cu-2.c: New file.
> > 	* gdb.base/disasm-multi-cu-3.c: New file.
> > 	* gdb.base/disasm-multi-cu.exp: New file.
> > 	* gdb.base/disasm-multi-cu.h: New file.
> 
> Hi. Some comments inline.
> 
> > diff --git a/gdb/ChangeLog b/gdb/ChangeLog
> > index 463b1b9..7ff35a2 100644
> > --- a/gdb/ChangeLog
> > +++ b/gdb/ChangeLog
> > @@ -1,5 +1,15 @@
> >  2015-08-30  Andrew Burgess  <andrew.burgess@embecosm.com>
> >  
> > +	* disasm.c (do_mixed_source_and_assembly_deprecated): Return
> > +	count, and take extra parameter.  Fill in final_pc parameter.
> > +	(do_mixed_source_and_assembly): Likewise.
> > +	(do_assembly_only): Likewise, also return from function when we've
> > +	displayed enough instructions.
> > +	(gdb_disassembly): Add loop, looking up new symtabs, or displaying
> > +	raw instructions as required.
> > +
> > +2015-08-30  Andrew Burgess  <andrew.burgess@embecosm.com>
> > +
> >  	* disasm.c (do_mixed_source_and_assembly_deprecated): Remove
> >  	asm_insns list.
> >  	(do_mixed_source_and_assembly): Likewise.
> > diff --git a/gdb/disasm.c b/gdb/disasm.c
> > index 3032090..9199eb2 100644
> > --- a/gdb/disasm.c
> > +++ b/gdb/disasm.c
> > @@ -274,12 +274,12 @@ dump_insns (struct gdbarch *gdbarch, struct ui_out *uiout,
> >  
> >     N.B. This view is deprecated.  */
> >  
> > -static void
> > +static int
> >  do_mixed_source_and_assembly_deprecated
> >    (struct gdbarch *gdbarch, struct ui_out *uiout,
> >     struct disassemble_info *di, struct symtab *symtab,
> >     CORE_ADDR low, CORE_ADDR high,
> > -   int how_many, int flags, struct ui_file *stb)
> > +   int how_many, int flags, struct ui_file *stb, CORE_ADDR *final_pc)
> >  {
> >    int newlines = 0;
> >    int nlines;
> > @@ -293,6 +293,7 @@ do_mixed_source_and_assembly_deprecated
> >    enum print_source_lines_flags psl_flags = 0;
> >    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;
> >  
> >    gdb_assert (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL);
> >  
> > @@ -411,7 +412,7 @@ do_mixed_source_and_assembly_deprecated
> >  
> >        num_displayed += dump_insns (gdbarch, uiout, di,
> >  				   mle[i].start_pc, mle[i].end_pc,
> > -				   how_many, flags, stb, NULL);
> > +				   how_many, flags, stb, &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.  */
> > @@ -426,6 +427,11 @@ do_mixed_source_and_assembly_deprecated
> >        if (how_many >= 0 && num_displayed >= how_many)
> >  	break;
> >      }
> > +
> > +  if (final_pc != NULL)
> > +    *final_pc = next_pc;
> > +
> > +  return num_displayed;
> >  }
> >  
> >  /* The idea here is to present a source-O-centric view of a
> > @@ -433,12 +439,13 @@ do_mixed_source_and_assembly_deprecated
> >     in source order, with (possibly) out of order assembly
> >     immediately following.  */
> 
> Bleah. This function comment needs updating. Mea culpa.
> I'll get to it after this patch (to not interfere with it).

If you're going to do that, do you think you could rename the
function to deprecated_do_mixed_source_and_assembly.  All the other
deprecated functions in gdb, and almost all of the deprecated
variables use a prefix rather than suffix approach - it just makes
finding deprecated stuff easier :)

> 
> >  
> > -static void
> > +static int
> >  do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
> >  			      struct disassemble_info *di,
> >  			      struct symtab *main_symtab,
> >  			      CORE_ADDR low, CORE_ADDR high,
> > -			      int how_many, int flags, struct ui_file *stb)
> > +			      int how_many, int flags, struct ui_file *stb,
> > +			      CORE_ADDR *final_pc)
> >  {
> >    int newlines = 0;
> >    const struct linetable_entry *le, *first_le;
> > @@ -455,6 +462,7 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
> >    struct symtab *last_symtab;
> >    int last_line;
> >    htab_t dis_line_table;
> > +  CORE_ADDR end_pc = low;
> >  
> >    gdb_assert (main_symtab != NULL && SYMTAB_LINETABLE (main_symtab) != NULL);
> >  
> > @@ -537,7 +545,6 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
> >      {
> >        struct linetable_entry *le = NULL;
> >        struct symtab_and_line sal;
> > -      CORE_ADDR end_pc;
> >        int start_preceding_line_to_display = 0;
> >        int end_preceding_line_to_display = 0;
> >        int new_source_line = 0;
> > @@ -676,18 +683,39 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch, struct ui_out *uiout,
> >      }
> >  
> >    do_cleanups (cleanups);
> > +
> > +  if (final_pc != NULL)
> > +    *final_pc = end_pc;
> > +
> > +  return num_displayed;
> >  }
> >  
> > -static void
> > +static int
> >  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_file *stb)
> > +		  int how_many, int flags, struct ui_file *stb,
> > +		  CORE_ADDR *final_pc)
> >  {
> > -  int num_displayed = 0;
> > +  CORE_ADDR npc;
> > +  int total = 0;
> > +
> > +  do
> > +    {
> > +      total += dump_insns (gdbarch, uiout, di, low, high,
> > +			   how_many, flags, stb, &npc);
> > +      if (npc >= high || total >= how_many)
> > +        break;
> > +      if (low == npc)
> > +	low = npc + 1;
> 
> npc + 1?
> Perhaps it's correct, but at a glance I don't see how.

No you're not.  This should have more comments.  I think on revision
I'll remove this change for now.

These disassembler changes were pulled out of an even larger
disassembler patch that I have had say around for ages.

The addition of +1 both here, and further down are part of addressing
disassembling over unreadable memory.  The +1 was part of ensuring
that the disassembler continued to make progress.

With out the supporting changes though the +1 alone does not make a
lot of sense, my only defence for leaving it in was that it makes the
gdb side of the disassembler super-paranoid: if for any reason the
disassembler fails to make progress gdb will still nudge the address
on.  However, in general gdb is not written that way so I should
probably drop this change.

>
> > +      else
> > +	low = npc;
> > +    } while (1);
> 
> I think(!) convention requires the "while (1);" to go on the next line.
> 
> The higher order question, though, is why is this loop needed?
> IOW, what does the loop do that dump_insns doesn't already do?
> Am I missing something?

This relates to the above point and should be removed (for now), the
assumption will be that dump_insns will always cover the requested
range.

> > +
> > +  if (final_pc != NULL)
> > +    *final_pc = npc;
> >  
> > -  num_displayed = dump_insns (gdbarch, uiout, di, low, high, how_many,
> > -                              flags, stb, NULL);
> > +  return total;
> >  }
> >  
> >  /* Initialize the disassemble info struct ready for the specified
> > @@ -740,31 +768,72 @@ gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
> >    struct ui_file *stb = mem_fileopen ();
> >    struct cleanup *cleanups = make_cleanup_ui_file_delete (stb);
> >    struct disassemble_info di = gdb_disassemble_info (gdbarch, stb);
> > -  struct symtab *symtab;
> > -  struct linetable_entry *le = NULL;
> > -  int nlines = -1;
> > -
> > -  /* Assume symtab is valid for whole PC range.  */
> > -  symtab = find_pc_line_symtab (low);
> > -
> > -  if (symtab != NULL && SYMTAB_LINETABLE (symtab) != NULL)
> > -    nlines = SYMTAB_LINETABLE (symtab)->nitems;
> >  
> >    /* This cleanup is inner to CLEANUPS, so we don't need a separate
> >       variable to track it.  */
> >    (void) make_cleanup_ui_out_list_begin_end (uiout, "asm_insns");
> >  
> > -  if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
> > -      || nlines <= 0)
> > -    do_assembly_only (gdbarch, uiout, &di, low, high, how_many, flags, stb);
> > +  if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE)))
> > +    (void) do_assembly_only (gdbarch, uiout, &di, low, high,
> > +			     how_many, flags, stb, NULL);
> > +  else if (flags & (DISASSEMBLY_SOURCE | DISASSEMBLY_SOURCE_DEPRECATED))
> > +    {
> > +      /* We want to disassemble with source information, however, if such
> > +	 information is not available then we'd rather have raw assembly
> > +	 than nothing.  Here we loop until the entire range is
> > +	 disassembled, giving source information where possible.  */
> > +      while (low < high)
> > +	{
> > +	  int nlines = -1;
> 
> No need to init nlines to -1 twice.
> I'd say delete this init.

My mistake.  Will fix.

> 
> > +	  CORE_ADDR prev_low = low;
> > +	  struct symtab *symtab;
> > +	  struct linetable_entry *le = NULL;
> > +	  int num_displayed = 0;
> >  
> > -  else if (flags & DISASSEMBLY_SOURCE)
> > -    do_mixed_source_and_assembly (gdbarch, uiout, &di, symtab, low, high,
> > -				  how_many, flags, stb);
> > +	  /* Attempt to find a symtab for the PC range.  */
> > +	  nlines = -1;
> > +	  symtab = find_pc_line_symtab (low);
> >  
> > -  else if (flags & DISASSEMBLY_SOURCE_DEPRECATED)
> > -    do_mixed_source_and_assembly_deprecated (gdbarch, uiout, &di, symtab,
> > -					     low, high, how_many, flags, stb);
> > +	  if (symtab != NULL && symtab->linetable != NULL)
> > +	    {
> > +	      /* Convert the linetable to a bunch of my_line_entry's.  */
> > +	      le = SYMTAB_LINETABLE (symtab)->item;
> > +	      nlines = SYMTAB_LINETABLE (symtab)->nitems;
> > +
> > +	      if (flags & DISASSEMBLY_SOURCE)
> > +		num_displayed +=
> > +		  do_mixed_source_and_assembly (gdbarch, uiout, &di, symtab,
> > +						low, high, how_many, flags,
> > +						stb, &low);
> 
> Storing final_pc in low here is awkward.
> Can you store it in end_pc or some such first?

OK, will rename some of the variable around this area.

> 
> > +	      else
> > +		num_displayed +=
> > +		  do_mixed_source_and_assembly_deprecated (gdbarch, uiout,
> > +							   &di, symtab,
> > +							   low, high,
> > +							   how_many,
> > +							   flags, stb,
> > +							   &low);
> 
> Ditto.
> 
> > +	    }
> > +
> > +	  /* Disassemble a single instruction if we couldn't find a
> > +	     suitable symtab, or if, for any reason, the above disassembly
> > +	     didn't move the LOW address on at all.  */
> 
> I think a reasonable case can be made that this is the wrong thing to
> do for the deprecated case (based on my observations of how it works,
> and does not work). Consider that it doesn't print inlined function calls
> that come from another file. If it doesn't do that, why go to any effort
> to print non-source-based disassembly in the gaps between source
> files.

I guess I see the question the other way around; why should we
maintain a separate way of doing things just to preserve (what I see
as) a bug?

By placing the choice between the old and new right into the middle of
my new disassembly control loop both old and new pick up the fix.  To
do what you suggest I see two choices (I'm open to alternative
suggestions though):

  1. Move the call to the old, deprecated, function outside of the new
     disassembly control loop.  We'd probably end up with code
     duplication, so extra maintenance overhead, just to support a
     deprecated method.

  2. Add a 'break;' immediately after the call to the deprecated
     disassembly function (within the else block).  This would ensure
     we only get once chance to disassemble.  Sure, if that's what it
     takes to get my patch in I can do that, but it feels kind of
     petty to me, like we're leaving a bug in place just to force
     people to move to the new method.

> [Over time I can imagine attempts to try to morph the deprecated
> case into the new non-deprecated case, i.e., changing /m to be more like /s.
> How about leaving /m the way it is, and just encourage everyone to
> use /s.]

Is that really so bad?  I'm going from memory here so I may have the
history wrong, but isn't the story that you found the inline-functions
issue with the /m case, looked into fixing it and discovered that
you'd be better off writing a brand new function, hence /s.

But surely, if someone put forward a patch that "fixed" /m (without
making it exactly the same as /s) then would that really be so bad?

I'm in no way proposing to make any changes to /m, I guess I'm just
arguing that the inline-functions issue with /m is orthogonal to this
cross-symtab issue.  Fixing the first was hard for /m, but it feels
like more work to _not_ fix the second issue.

> 
> > +	  if (how_many != 0
> > +	      && (nlines <= 0
> > +		  || symtab == NULL
> > +		  || symtab->linetable == NULL
> > +		  || low == prev_low))
> > +	    num_displayed += do_assembly_only (gdbarch, uiout, &di, low,
> > +					       high, 1, flags, stb, &low);
> > +
> > +	  if (how_many >=0 && num_displayed >= how_many)
> > +	    break;
> > +
> > +	  /* For sanity, if we've not moved then nudge onward.  */
> > +	  if (low <= prev_low)
> > +	    low = prev_low + 1;
> 
> This doesn't feel right, e.g. for ISAs with instruction widths always > 1.
> Am I missing something?

As for the +1 at the very top this was part of the unreadable memory
changes; I'll remove these in a revised patch.

Thanks again for your time,

Andrew


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