public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2] [gdb/infcmd]: Add next-expression command
@ 2023-05-18 14:45 Simon Farre
  2023-05-18 15:19 ` Eli Zaretskii
  0 siblings, 1 reply; 2+ messages in thread
From: Simon Farre @ 2023-05-18 14:45 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Farre

v2. Fixed documentation issues as requested by Eli
Bug fixes. This version instead re-purposes the `ni` command
and uses a breakpoint for locations that are in-source-line but
out-of-frame, like for instance the following code:

setfoo().setbar().dostuff([](auto v) { doother(v); })

With this new version, stepping into doother is made possible.

Column information is printed with the `frame` command, but
work also needs to be put in for the TUI to display what column
we're at.

MI reports column when being stopped, since it re-uses the frame
print code.

v1
This commit adds the source-level "next-expression" command. It utilizes
column metadata emitted by the compiler (if any). Column metadata
has been defined in the DWARF spec since 2.0, I am currently unaware
of how other debug information types work with columns and as such
this patch only introduces support for binaries that has DWARF emitted.

Much of the code in `next_expression` (infcmd.c) was borrowed from the `until_command`
as I was thinking it really should behave like it; because we know the addresses
we want to run to, we don't want to do intermediate stepping until that point,
just execute to that position. However, it was pretty
difficult for me to understand what's really going on; so please review
this part extra carefully if you have the time (infcmd.c)

For the other debug info types, column metadata is currently set to 0, to signal that
"we don't have column metadata"; this seems to be default behavior for line information
in other places as well so I continued that tradition.

This command will also map to the DAP request "nextRequest"
with the parameter "stmt"; however, after discussion on IRC,
I was informed that statement is not the correct term here
so I named the command to "next-expression"; though it can
step across statements on the same line.

When printing frame (either when typing frame on the cli)
or "on stopped" event in MI, column information is printed,
with the format file:line:column; as I think that's the most
common way to represent file coordinates.

The column information is also exposed via the Python object
Symtab_and_line. Documentation has been added for this,
as well as the command in gdb.texinfo.

Examples

Example 1:
Say we have
foo().bar().baz() and we're currently at _foo();

next-expression 2 will land us on _baz()

NOTE ON DEFAULT BEHAVIOR:
If the user types next-expression 10 in the above example
the command will iterate over linetable_entries and find that
there is not 10 columns on this line and fall back to the "next command"
I figured this was a sane default because if a user wants to "next-expression"
they are attempting to reach at some point _on the same source line_; however
I am absolutely willing to change this if anyone has any objections to this default.

Example 2:
Say we have
for(int i = 10; i < 10; i++) and we're at
_int i = 10;

next-expression will land us at _i < 10;

So next-expression will "next to" the next statement on
a line as well.
---
 gdb/NEWS               |  7 ++++
 gdb/buildsym-legacy.c  |  4 +-
 gdb/buildsym-legacy.h  |  2 +-
 gdb/buildsym.c         |  3 +-
 gdb/buildsym.h         |  4 +-
 gdb/coffread.c         |  4 +-
 gdb/dbxread.c          |  6 +--
 gdb/doc/gdb.texinfo    | 16 +++++++-
 gdb/doc/python.texi    |  6 +++
 gdb/dwarf2/read.c      | 32 ++++++++++-----
 gdb/gdbthread.h        |  2 +
 gdb/infcmd.c           | 91 +++++++++++++++++++++++++++++++++++-------
 gdb/infrun.c           |  8 ++++
 gdb/mdebugread.c       |  2 +-
 gdb/python/py-symtab.c | 29 ++++++++++----
 gdb/stack.c            |  7 ++++
 gdb/symtab.c           |  1 +
 gdb/symtab.h           | 22 ++++++++++
 18 files changed, 201 insertions(+), 45 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b82114d80b0..38c6f54564b 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,13 @@
 
 *** Changes since GDB 13
 
+* New commmand "next-expression" ("ne") that steps by expression or statement
+  on a single source line if debug information is available.
+
+* New column attribute of Python type Symtab_and_line to reflect the added
+  column field on "linetable_entry". Describes what column on the source line
+  that is being executed. Columns are byte-level coordinates on a source line.
+
 * The AArch64 'org.gnu.gdb.aarch64.pauth' Pointer Authentication feature string
   has been deprecated in favor of the 'org.gnu.gdb.aarch64.pauth_v2' feature
   string.
diff --git a/gdb/buildsym-legacy.c b/gdb/buildsym-legacy.c
index 131d24fe9b5..5036fd2b48d 100644
--- a/gdb/buildsym-legacy.c
+++ b/gdb/buildsym-legacy.c
@@ -205,12 +205,12 @@ finish_block (struct symbol *symbol, struct pending_block *old_blocks,
 }
 
 void
-record_line (struct subfile *subfile, int line, unrelocated_addr pc)
+record_line (struct subfile *subfile, int line, int col, unrelocated_addr pc)
 {
   gdb_assert (buildsym_compunit != nullptr);
   /* Assume every line entry is a statement start, that is a good place to
      put a breakpoint for that line number.  */
-  buildsym_compunit->record_line (subfile, line, pc, LEF_IS_STMT);
+  buildsym_compunit->record_line (subfile, line, col, pc, LEF_IS_STMT);
 }
 
 /* Start a new compunit_symtab for a new source file in OBJFILE.  Called, for
diff --git a/gdb/buildsym-legacy.h b/gdb/buildsym-legacy.h
index 664d6320e54..be44d0f5783 100644
--- a/gdb/buildsym-legacy.h
+++ b/gdb/buildsym-legacy.h
@@ -76,7 +76,7 @@ extern struct context_stack *push_context (int desc, CORE_ADDR valu);
 
 extern struct context_stack pop_context ();
 
-extern void record_line (struct subfile *subfile, int line,
+extern void record_line (struct subfile *subfile, int line, int col,
 			 unrelocated_addr pc);
 
 extern struct compunit_symtab *start_compunit_symtab (struct objfile *objfile,
diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index d12ad2187ab..6b3859ea45e 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -626,7 +626,7 @@ buildsym_compunit::pop_subfile ()
    line vector for SUBFILE.  */
 
 void
-buildsym_compunit::record_line (struct subfile *subfile, int line,
+buildsym_compunit::record_line (struct subfile *subfile, int line, int col,
 				unrelocated_addr pc, linetable_entry_flags flags)
 {
   m_have_line_numbers = true;
@@ -666,6 +666,7 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
 
   subfile->line_vector_entries.emplace_back ();
   linetable_entry &e = subfile->line_vector_entries.back ();
+  e.column = col;
   e.line = line;
   e.is_stmt = (flags & LEF_IS_STMT) != 0;
   e.set_raw_pc (pc);
diff --git a/gdb/buildsym.h b/gdb/buildsym.h
index 98dc8f02874..d3328c630d8 100644
--- a/gdb/buildsym.h
+++ b/gdb/buildsym.h
@@ -239,8 +239,8 @@ struct buildsym_compunit
 
   const char *pop_subfile ();
 
-  void record_line (struct subfile *subfile, int line, unrelocated_addr pc,
-		    linetable_entry_flags flags);
+  void record_line (struct subfile *subfile, int line, int col,
+  		    unrelocated_addr pc, linetable_entry_flags flags);
 
   struct compunit_symtab *get_compunit_symtab ()
   {
diff --git a/gdb/coffread.c b/gdb/coffread.c
index ff4d4ae5313..dfb75b12799 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -1133,7 +1133,7 @@ coff_symtab_read (minimal_symbol_reader &reader,
 		 other statement-line-number.  */
 	      if (fcn_last_line == 1)
 		record_line
-		  (get_current_subfile (), fcn_first_line,
+		  (get_current_subfile (), fcn_first_line, 0,
 		   unrelocated_addr (gdbarch_addr_bits_remove (gdbarch,
 							       fcn_first_line_addr)));
 	      else
@@ -1462,7 +1462,7 @@ enter_linenos (file_ptr file_offset, int first_line,
 	{
 	  CORE_ADDR addr = lptr.l_addr.l_paddr;
 	  record_line (get_current_subfile (),
-		       first_line + L_LNNO32 (&lptr),
+		       first_line + L_LNNO32 (&lptr), 0,
 		       unrelocated_addr (gdbarch_addr_bits_remove (gdbarch,
 								   addr)));
 	}
diff --git a/gdb/dbxread.c b/gdb/dbxread.c
index 73371edd841..310ee0eef4d 100644
--- a/gdb/dbxread.c
+++ b/gdb/dbxread.c
@@ -2476,7 +2476,7 @@ process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
 	      CORE_ADDR addr = last_function_start + valu;
 
 	      record_line
-		(get_current_subfile (), 0,
+		(get_current_subfile (), 0, 0,
 		 unrelocated_addr (gdbarch_addr_bits_remove (gdbarch, addr)
 				   - objfile->text_section_offset ()));
 	    }
@@ -2686,14 +2686,14 @@ process_one_symbol (int type, int desc, CORE_ADDR valu, const char *name,
 			   last_function_start : valu;
 
 	  record_line
-	    (get_current_subfile (), desc,
+	    (get_current_subfile (), desc, 0,
 	     unrelocated_addr (gdbarch_addr_bits_remove (gdbarch, addr)
 			       - objfile->text_section_offset ()));
 	  sline_found_in_function = 1;
 	}
       else
 	record_line
-	  (get_current_subfile (), desc,
+	  (get_current_subfile (), desc, 0,
 	   unrelocated_addr (gdbarch_addr_bits_remove (gdbarch, valu)
 			     - objfile->text_section_offset ()));
       break;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 531147f6e6b..3f779154cd7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6359,6 +6359,20 @@ The @code{next} command only stops at the first instruction of a
 source line.  This prevents multiple stops that could otherwise occur in
 @code{switch} statements, @code{for} loops, etc.
 
+@kindex next-expression
+@kindex ne @r{(@code{next-expression})}
+@item next-expression @r{[}@var{count}@r{]}
+Continue to the next expression or statement on the current source line
+in the current (innermost) stack frame.  This is a source-level command
+and as such requires that debug information was emitted by the compiler.  If
+no such debug information could be found this command defaults to
+@code{next}.  Column meta data has been part of the DWARF standard since 2.0
+and GCC and clang both emit this information.
+
+An argument @var{count} is a repeat count, as for @code{next}.
+If the current source line doesn't have enough expressions to satisfy
+@var{count}, this command will do the same as @code{next}.
+
 @kindex set step-mode
 @item set step-mode
 @cindex functions without line info, and stepping
@@ -31313,7 +31327,7 @@ An -exec-until or similar CLI command was accomplished.
 @item watchpoint-scope
 A watchpoint has gone out of scope.
 @item end-stepping-range
-An -exec-next, -exec-next-instruction, -exec-step, -exec-step-instruction or 
+An -exec-next, -exec-next-instruction, -exec-step, -exec-step-instruction or
 similar CLI command was accomplished.
 @item exited-signalled 
 The inferior exited because of a signal.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 5d714ee1ca3..eb295fe2458 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -5899,6 +5899,12 @@ Indicates the current line number for this object.  This
 attribute is not writable.
 @end defvar
 
+@defvar Symtab_and_line.column
+Indicates the current column number for this object.  This
+attribute is not writeable.  A source-line column is it's byte position
+on that line.
+@end defvar
+
 A @code{gdb.Symtab_and_line} object has the following methods:
 
 @defun Symtab_and_line.is_valid ()
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4828409222c..9b56abc406e 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -18137,6 +18137,11 @@ class lnp_state_machine
     m_flags |= LEF_PROLOGUE_END;
   }
 
+  void handle_set_column (unsigned int col)
+  {
+    m_column = col;
+  }
+
 private:
   /* Advance the line by LINE_DELTA.  */
   void advance_line (int line_delta)
@@ -18161,6 +18166,7 @@ class lnp_state_machine
   /* The line table index of the current file.  */
   file_name_index m_file = 1;
   unsigned int m_line = 1;
+  unsigned int m_column = 1;
 
   /* These are initialized in the constructor.  */
 
@@ -18223,6 +18229,7 @@ lnp_state_machine::handle_special_opcode (unsigned char op_code)
   advance_line (line_delta);
   record_line (false);
   m_discriminator = 0;
+  m_column = 0;
   m_flags &= ~LEF_PROLOGUE_END;
 }
 
@@ -18311,9 +18318,8 @@ dwarf_record_line_p (struct dwarf2_cu *cu,
 
 static void
 dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
-		     unsigned int line, CORE_ADDR address,
-		     linetable_entry_flags flags,
-		     struct dwarf2_cu *cu)
+		     unsigned int line, unsigned int col, CORE_ADDR address,
+		     linetable_entry_flags flags, struct dwarf2_cu *cu)
 {
   unrelocated_addr addr
     = unrelocated_addr (gdbarch_addr_bits_remove (gdbarch, address));
@@ -18327,7 +18333,7 @@ dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
     }
 
   if (cu != nullptr)
-    cu->get_builder ()->record_line (subfile, line, addr, flags);
+    cu->get_builder ()->record_line (subfile, line, (int) col, addr, flags);
 }
 
 /* Subroutine of dwarf_decode_lines_1 to simplify it.
@@ -18350,7 +18356,7 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
 		  paddress (gdbarch, address));
     }
 
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, LEF_IS_STMT, cu);
+  dwarf_record_line_1 (gdbarch, subfile, 0, 0, address, LEF_IS_STMT, cu);
 }
 
 void
@@ -18418,10 +18424,10 @@ lnp_state_machine::record_line (bool end_sequence)
 				   m_last_subfile))
 	    {
 	      buildsym_compunit *builder = m_cu->get_builder ();
-	      dwarf_record_line_1 (m_gdbarch,
-				   builder->get_current_subfile (),
-				   m_line, m_address, lte_flags,
-				   m_currently_recording_lines ? m_cu : nullptr);
+	      dwarf_record_line_1 (m_gdbarch, builder->get_current_subfile (),
+				   m_line, m_column, m_address, lte_flags,
+				   m_currently_recording_lines ? m_cu
+							       : nullptr);
 	    }
 	  m_last_subfile = m_cu->get_builder ()->get_current_subfile ();
 	  m_last_line = m_line;
@@ -18642,8 +18648,12 @@ dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
 	      }
 	      break;
 	    case DW_LNS_set_column:
-	      (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-	      line_ptr += bytes_read;
+	      {
+		const auto col
+		  = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		state_machine.handle_set_column (col);
+		line_ptr += bytes_read;
+	      }
 	      break;
 	    case DW_LNS_negate_stmt:
 	      state_machine.handle_negate_stmt ();
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 7135515bf45..a5ad8ddddb7 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -171,6 +171,8 @@ struct thread_control_state
      command.  This is used to decide whether "set scheduler-locking
      step" behaves like "on" or "off".  */
   int stepping_command = 0;
+
+  bool step_expression = false;
 };
 
 /* Inferior thread specific part of `struct infcall_suspend_state'.  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index b12b58db9cb..bb9093012e4 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -61,7 +61,8 @@
 
 static void until_next_command (int);
 
-static void step_1 (int, int, const char *);
+static void
+step_1 (int, int, bool, const char *);
 
 #define ERROR_NO_INFERIOR \
    if (!target_has_execution ()) error (_("The program is not being run."));
@@ -231,8 +232,7 @@ strip_bg_char (const char *args, int *bg_char_p)
 void
 post_create_inferior (int from_tty)
 {
-
-  /* Be sure we own the terminal in case write operations are performed.  */ 
+  /* Be sure we own the terminal in case write operations are performed.  */
   target_terminal::ours_for_output ();
 
   infrun_debug_show_threads ("threads in the newly created inferior",
@@ -765,7 +765,7 @@ set_step_frame (thread_info *tp)
 static void
 step_command (const char *count_string, int from_tty)
 {
-  step_1 (0, 0, count_string);
+  step_1 (0, 0, false, count_string);
 }
 
 /* Likewise, but skip over subroutine calls as if single instructions.  */
@@ -773,7 +773,7 @@ step_command (const char *count_string, int from_tty)
 static void
 next_command (const char *count_string, int from_tty)
 {
-  step_1 (1, 0, count_string);
+  step_1 (1, 0, false, count_string);
 }
 
 /* Likewise, but step only one instruction.  */
@@ -781,13 +781,13 @@ next_command (const char *count_string, int from_tty)
 static void
 stepi_command (const char *count_string, int from_tty)
 {
-  step_1 (0, 1, count_string);
+  step_1 (0, 1, false, count_string);
 }
 
 static void
 nexti_command (const char *count_string, int from_tty)
 {
-  step_1 (1, 1, count_string);
+  step_1 (1, 1, false, count_string);
 }
 
 /* Data for the FSM that manages the step/next/stepi/nexti
@@ -804,6 +804,9 @@ struct step_command_fsm : public thread_fsm
   /* If true, this is a stepi/nexti, otherwise a step/step.  */
   int single_inst;
 
+  bool step_expression;
+  breakpoint_up bp = nullptr;
+
   explicit step_command_fsm (struct interp *cmd_interp)
     : thread_fsm (cmd_interp)
   {
@@ -818,25 +821,64 @@ struct step_command_fsm : public thread_fsm
    allocated here is undone in the FSM's clean_up method.  */
 
 static void
-step_command_fsm_prepare (struct step_command_fsm *sm,
-			  int skip_subroutines, int single_inst,
-			  int count, struct thread_info *thread)
+step_command_fsm_prepare (struct step_command_fsm *sm, int skip_subroutines,
+			  int single_inst, bool step_expression, int count,
+			  struct thread_info *thread)
 {
   sm->skip_subroutines = skip_subroutines;
   sm->single_inst = single_inst;
   sm->count = count;
+  sm->step_expression = step_expression;
 
   /* Leave the si command alone.  */
   if (!sm->single_inst || sm->skip_subroutines)
     set_longjmp_breakpoint (thread, get_frame_id (get_current_frame ()));
 
   thread->control.stepping_command = 1;
+
+  if (sm->step_expression)
+    {
+      /* We might need to set a breakpoint, because the next expression
+      that is on this line, might not exist in the same frame, as for instance
+      is the case with a lambda being defined and passed into a function. The
+      normal `next instruction` command would step over this, where as
+      `next-expression` does not want that behavior. A breakpoint will in that
+      case catch us.*/
+      const auto frame = get_current_frame ();
+      const auto sal = find_frame_sal (frame);
+      const auto range = sal.symtab->linetable_entries_on_line (sal.line);
+      auto column_count = count;
+      for (const auto &entry : range)
+	{
+	  if (entry.column > sal.col)
+	    column_count--;
+	  if (column_count == 0)
+	    {
+	      const auto column_rel_addr
+		= entry.pc (sal.pspace->symfile_object_file);
+	      const auto frame_gdbarch = get_frame_arch (frame);
+	      auto bp_loc = set_momentary_breakpoint_at_pc (
+		frame_gdbarch, column_rel_addr, bp_breakpoint);
+	      sm->bp = std::move (bp_loc);
+	      return;
+	    }
+	}
+      // if we did not find suitable column; default to next
+      sm->step_expression = false;
+    }
+}
+
+static void
+next_expression (const char *count_str, int from_tty)
+{
+  step_1 (true, false, true, count_str);
 }
 
 static int prepare_one_step (thread_info *, struct step_command_fsm *sm);
 
 static void
-step_1 (int skip_subroutines, int single_inst, const char *count_string)
+step_1 (int skip_subroutines, int single_inst, bool step_expression,
+	const char *count_string)
 {
   int count;
   int async_exec;
@@ -864,8 +906,8 @@ step_1 (int skip_subroutines, int single_inst, const char *count_string)
   step_sm = new step_command_fsm (command_interp ());
   thr->set_thread_fsm (std::unique_ptr<thread_fsm> (step_sm));
 
-  step_command_fsm_prepare (step_sm, skip_subroutines,
-			    single_inst, count, thr);
+  step_command_fsm_prepare (step_sm, skip_subroutines, single_inst,
+			    step_expression, count, thr);
 
   /* Do only one step for now, before returning control to the event
      loop.  Let the continuation figure out how many other steps we
@@ -899,7 +941,18 @@ step_command_fsm::should_stop (struct thread_info *tp)
       /* There are more steps to make, and we did stop due to
 	 ending a stepping range.  Do another step.  */
       if (--count > 0)
-	return prepare_one_step (tp, this);
+	{
+	  if (bp != nullptr
+	      && bpstat_find_breakpoint (tp->control.stop_bpstat, bp.get ())
+		   != nullptr)
+	    {
+	      count = 0;
+	    }
+	  else
+	    {
+	      return prepare_one_step (tp, this);
+	    }
+	}
 
       set_finished ();
     }
@@ -914,6 +967,7 @@ step_command_fsm::clean_up (struct thread_info *thread)
 {
   if (!single_inst || skip_subroutines)
     delete_longjmp_breakpoint (thread->global_num);
+  bp = nullptr;
 }
 
 /* Implementation of the 'async_reply_reason' FSM method for stepping
@@ -1033,6 +1087,7 @@ prepare_one_step (thread_info *tp, struct step_command_fsm *sm)
       if (sm->skip_subroutines)
 	tp->control.step_over_calls = STEP_OVER_ALL;
 
+      tp->control.step_expression = sm->step_expression;
       return 0;
     }
 
@@ -3311,6 +3366,14 @@ Argument N means step N times (or till program stops for another \
 reason)."));
   add_com_alias ("s", step_cmd, class_run, 1);
 
+  cmd_list_element *ne_cmd
+    = add_com ("next-expression", class_run, next_expression, _ ("\
+Step program by expressions info, proceeding through subroutine calls.\n\
+Usage: next-expression [N]\n\
+This is a source-level command and if no debug information was emitted by\n\
+the compiler this command will default to behaving like 'next'"));
+  add_com_alias ("ne", ne_cmd, class_run, 1);
+
   cmd_list_element *until_cmd
     = add_com ("until", class_run, until_command, _("\
 Execute until past the current line or past a LOCATION.\n\
diff --git a/gdb/infrun.c b/gdb/infrun.c
index efe2c00c489..6fe4230b634 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -7561,8 +7561,16 @@ process_event_stop_test (struct execution_control_state *ecs)
       return;
     }
 
+  if ((ecs->event_thread->stop_pc () == stop_pc_sal.pc)
+      && ecs->event_thread->control.step_expression)
+    {
+      infrun_debug_printf ("stepped to next expression");
+      return end_stepping_range (ecs);
+    }
+
   bool refresh_step_info = true;
   if ((ecs->event_thread->stop_pc () == stop_pc_sal.pc)
+      && (ecs->event_thread->control.step_expression == false)
       && (ecs->event_thread->current_line != stop_pc_sal.line
 	  || ecs->event_thread->current_symtab != stop_pc_sal.symtab))
     {
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index 697ce0b5b1a..aaf4878de25 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -4012,7 +4012,7 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
 		{
 		  /* Handle encoded stab line number.  */
 		  record_line
-		    (get_current_subfile (), sh.index,
+		    (get_current_subfile (), sh.index, 0,
 		     unrelocated_addr (gdbarch_addr_bits_remove (gdbarch,
 								 valu)));
 		}
diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c
index 26aa8b2fb04..89a86cc136b 100644
--- a/gdb/python/py-symtab.c
+++ b/gdb/python/py-symtab.c
@@ -286,8 +286,12 @@ salpy_str (PyObject *self)
       filename = symtab_to_filename_for_display (symtab);
     }
 
-  return PyUnicode_FromFormat ("symbol and line for %s, line %d", filename,
-			       sal->line);
+  if (sal->col > 0)
+    return PyUnicode_FromFormat ("symbol and line:col for %s, line %d:%d",
+				 filename, sal->line, sal->col);
+  else
+    return PyUnicode_FromFormat ("symbol and line for %s, line %d", filename,
+				 sal->line);
 }
 
 static void
@@ -343,6 +347,16 @@ salpy_get_line (PyObject *self, void *closure)
   return gdb_py_object_from_longest (sal->line).release ();
 }
 
+static PyObject *
+salpy_get_col (PyObject *self, void *closure)
+{
+  struct symtab_and_line *sal = NULL;
+
+  SALPY_REQUIRE_VALID (self, sal);
+
+  return gdb_py_object_from_longest (sal->col).release ();
+}
+
 static PyObject *
 salpy_get_symtab (PyObject *self, void *closure)
 {
@@ -597,11 +611,12 @@ PyTypeObject symtab_object_type = {
 static gdb_PyGetSetDef sal_object_getset[] = {
   { "symtab", salpy_get_symtab, NULL, "Symtab object.", NULL },
   { "pc", salpy_get_pc, NULL, "Return the symtab_and_line's pc.", NULL },
-  { "last", salpy_get_last, NULL,
-    "Return the symtab_and_line's last address.", NULL },
-  { "line", salpy_get_line, NULL,
-    "Return the symtab_and_line's line.", NULL },
-  {NULL}  /* Sentinel */
+  { "last", salpy_get_last, NULL, "Return the symtab_and_line's last address.",
+    NULL },
+  { "line", salpy_get_line, NULL, "Return the symtab_and_line's line.", NULL },
+  { "column", salpy_get_col, NULL, "Return the symtab_and_line's column.",
+    NULL },
+  { NULL } /* Sentinel */
 };
 
 static PyMethodDef sal_object_methods[] = {
diff --git a/gdb/stack.c b/gdb/stack.c
index b1b25aa1c7e..590862236b7 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1418,6 +1418,13 @@ print_frame (const frame_print_options &fp_opts,
 	uiout->text (":");
 	annotate_frame_source_line ();
 	uiout->field_signed ("line", sal.line);
+
+	/* Only print column if we have column meta data. */
+	if (sal.col > 0)
+	  {
+	    uiout->text (":");
+	    uiout->field_signed ("column", sal.col);
+	  }
 	annotate_frame_source_end ();
       }
 
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 4f28667b1b3..3265756a637 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3247,6 +3247,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
       val.symtab = best_symtab;
       val.line = best->line;
       val.pc = best->pc (objfile);
+      val.col = best->column;
       if (best_end && (!alt || best_end < alt->pc (objfile)))
 	val.end = best_end;
       else if (alt)
diff --git a/gdb/symtab.h b/gdb/symtab.h
index d8e3c273f85..ce6444a5a71 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1615,6 +1615,9 @@ struct linetable_entry
   /* The line number for this entry.  */
   int line;
 
+  /* The column number for this entry. 0 means it has no column data. */
+  int column;
+
   /* True if this PC is a good location to place a breakpoint for LINE.  */
   bool is_stmt : 1;
 
@@ -1651,6 +1654,10 @@ struct linetable
      `struct hack', you can shove it up your ANSI (seriously, if the
      committee tells us how to do it, we can probably go along).  */
   struct linetable_entry item[1];
+  gdb::array_view<const linetable_entry> items () const
+  {
+    return gdb::array_view<const linetable_entry>{ item, item + nitems };
+  }
 };
 
 /* How to relocate the symbols from each section in a symbol file.
@@ -1681,6 +1688,17 @@ struct symtab
     return m_linetable;
   }
 
+  std::vector<linetable_entry> linetable_entries_on_line (int line)
+  {
+    std::vector<linetable_entry> entries;
+    for (const auto &entry : m_linetable->items ())
+      {
+	if (entry.line == line)
+	  entries.push_back (entry);
+      }
+    return entries;
+  }
+
   void set_linetable (const struct linetable *linetable)
   {
     m_linetable = linetable;
@@ -2330,6 +2348,10 @@ struct symtab_and_line
      information is not available.  */
   int line = 0;
 
+  /* Column number of this particular SAL. 0 indidcates no available column
+   * information */
+  int col = 0;
+
   CORE_ADDR pc = 0;
   CORE_ADDR end = 0;
   bool explicit_pc = false;
-- 
2.40.1


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] [gdb/infcmd]: Add next-expression command
  2023-05-18 14:45 [PATCH v2] [gdb/infcmd]: Add next-expression command Simon Farre
@ 2023-05-18 15:19 ` Eli Zaretskii
  0 siblings, 0 replies; 2+ messages in thread
From: Eli Zaretskii @ 2023-05-18 15:19 UTC (permalink / raw)
  To: Simon Farre; +Cc: gdb-patches

> Cc: Simon Farre <simon.farre.cx@gmail.com>
> Date: Thu, 18 May 2023 16:45:52 +0200
> From: Simon Farre via Gdb-patches <gdb-patches@sourceware.org>
> 
>  gdb/NEWS               |  7 ++++
>  gdb/buildsym-legacy.c  |  4 +-
>  gdb/buildsym-legacy.h  |  2 +-
>  gdb/buildsym.c         |  3 +-
>  gdb/buildsym.h         |  4 +-
>  gdb/coffread.c         |  4 +-
>  gdb/dbxread.c          |  6 +--
>  gdb/doc/gdb.texinfo    | 16 +++++++-
>  gdb/doc/python.texi    |  6 +++
>  gdb/dwarf2/read.c      | 32 ++++++++++-----
>  gdb/gdbthread.h        |  2 +
>  gdb/infcmd.c           | 91 +++++++++++++++++++++++++++++++++++-------
>  gdb/infrun.c           |  8 ++++
>  gdb/mdebugread.c       |  2 +-
>  gdb/python/py-symtab.c | 29 ++++++++++----
>  gdb/stack.c            |  7 ++++
>  gdb/symtab.c           |  1 +
>  gdb/symtab.h           | 22 ++++++++++
>  18 files changed, 201 insertions(+), 45 deletions(-)

Thanks.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index b82114d80b0..38c6f54564b 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,13 @@
>  
>  *** Changes since GDB 13
>  
> +* New commmand "next-expression" ("ne") that steps by expression or statement
> +  on a single source line if debug information is available.
> +
> +* New column attribute of Python type Symtab_and_line to reflect the added
> +  column field on "linetable_entry". Describes what column on the source line
                                      ^^
Two spaces there.

> +  that is being executed. Columns are byte-level coordinates on a source line.
                           ^^
And there.

> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -5899,6 +5899,12 @@ Indicates the current line number for this object.  This
>  attribute is not writable.
>  @end defvar
>  
> +@defvar Symtab_and_line.column
> +Indicates the current column number for this object.  This
> +attribute is not writeable.  A source-line column is it's byte position
                                                        ^^^^
"its', not "it's".

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-05-18 15:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-18 14:45 [PATCH v2] [gdb/infcmd]: Add next-expression command Simon Farre
2023-05-18 15:19 ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).