@@ -3,6 +3,12 @@
*** Changes since GDB 13
+* Added the "next-expression" ("ne") command that source-level steps to
+ columns if the required debug info metadata was emitted by the compiler.
+
+* Added column attribute to Python type Symtab_and_line to reflect the added
+ column field on "linetable_entry" which is used in "next-expression" command.
+
* 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.
@@ -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
@@ -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,
@@ -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);
@@ -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 ()
{
@@ -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)));
}
@@ -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;
@@ -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}.
+
+
+An argument @var{count} is a repeat count, as for @code{next}. If
+repeat count is larger than amount of expressions on the current source line
+the command will default to the @code{next} command.
+
@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.
@@ -5899,6 +5899,11 @@ 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.
+@end defvar
+
A @code{gdb.Symtab_and_line} object has the following methods:
@defun Symtab_and_line.is_valid ()
@@ -18100,6 +18100,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)
@@ -18124,6 +18129,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. */
@@ -18274,9 +18280,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));
@@ -18290,7 +18295,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.
@@ -18313,7 +18318,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
@@ -18381,10 +18386,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;
@@ -18605,8 +18610,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 ();
@@ -231,8 +231,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",
@@ -833,6 +832,123 @@ step_command_fsm_prepare (struct step_command_fsm *sm,
thread->control.stepping_command = 1;
}
+// next-expression finite state machine
+struct next_expression_fsm : public thread_fsm
+{
+ int thread;
+ std::vector<breakpoint_up> bps;
+
+ next_expression_fsm (interp *cmd_interp, int thread,
+ std::vector<breakpoint_up> &&breakpoints)
+ : thread_fsm (cmd_interp), thread (thread), bps (std::move (breakpoints))
+ {
+ }
+
+ void clean_up (thread_info *thread) override;
+ bool should_stop (thread_info *thread) override;
+ enum async_reply_reason do_async_reply_reason () override;
+};
+
+bool
+next_expression_fsm::should_stop (thread_info *tp)
+{
+ const auto bp = std::find_if (
+ bps.begin (), bps.end (), [bpstat = tp->control.stop_bpstat] (auto &bkpt) {
+ return bpstat_find_breakpoint (bpstat, bkpt.get ()) != nullptr;
+ });
+
+ if (bp != std::end (bps))
+ set_finished ();
+
+ return true;
+}
+
+void
+next_expression_fsm::clean_up (thread_info *)
+{
+ bps.clear ();
+ delete_longjmp_breakpoint (thread);
+}
+
+async_reply_reason
+next_expression_fsm::do_async_reply_reason ()
+{
+ return async_reply_reason::EXEC_ASYNC_LOCATION_REACHED;
+}
+
+static gdb::optional<CORE_ADDR>
+get_next_column_pc (frame_info_ptr frame, int column_count)
+{
+ const auto sal = find_frame_sal (frame);
+ const auto range = sal.symtab->linetable_entries_for (sal.line);
+
+ for (const auto &entry : range)
+ {
+ if (entry.column > sal.col)
+ column_count--;
+ if (column_count == 0)
+ return entry.pc (sal.pspace->symfile_object_file);
+ }
+ return {};
+}
+
+static void
+next_expression (const char *count_str, int from_tty)
+{
+ int doasync;
+ gdb::unique_xmalloc_ptr<char> stripped = strip_bg_char (count_str, &doasync);
+ auto processed_count_str = stripped.get ();
+
+ prepare_execution_command (current_inferior ()->top_target (), doasync);
+
+ const auto count
+ = processed_count_str ? parse_and_eval_long (processed_count_str) : 1;
+
+ auto frame = get_selected_frame (nullptr);
+ const auto next_col_addr = get_next_column_pc (frame, count);
+
+ if (!next_col_addr.has_value ())
+ return next_command (nullptr, from_tty);
+
+ clear_proceed_status (1);
+ auto tp = inferior_thread ();
+ auto thread = tp->global_num;
+
+ std::vector<breakpoint_up> breakpoints;
+ gdb::optional<delete_longjmp_breakpoint_cleanup> lj_deleter;
+
+ const auto caller_frame_id = frame_unwind_caller_id (frame);
+ if (frame_id_p (caller_frame_id))
+ {
+ struct symtab_and_line sal2;
+ struct gdbarch *caller_gdbarch;
+
+ sal2 = find_pc_line (frame_unwind_caller_pc (frame), 0);
+ sal2.pc = frame_unwind_caller_pc (frame);
+ caller_gdbarch = frame_unwind_caller_arch (frame);
+
+ breakpoint_up caller_breakpoint = set_momentary_breakpoint (
+ caller_gdbarch, sal2, caller_frame_id, bp_until);
+ breakpoints.emplace_back (std::move (caller_breakpoint));
+ const auto stack_frame_id = get_stack_frame_id (frame);
+ set_longjmp_breakpoint (tp, stack_frame_id);
+ lj_deleter.emplace (thread);
+ }
+
+ const auto frame_gdbarch = get_frame_arch (frame);
+ auto bp_loc = set_momentary_breakpoint_at_pc (frame_gdbarch, *next_col_addr,
+ bp_breakpoint);
+ breakpoints.emplace_back (std::move (bp_loc));
+ auto fsm = std::make_unique<next_expression_fsm> (
+ command_interp (), tp->global_num, std::move (breakpoints));
+ tp->set_thread_fsm (std::move (fsm));
+
+ if (lj_deleter)
+ lj_deleter->release ();
+
+ proceed (-1, GDB_SIGNAL_DEFAULT);
+}
+
static int prepare_one_step (thread_info *, struct step_command_fsm *sm);
static void
@@ -3311,6 +3427,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\
@@ -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)));
}
@@ -286,8 +286,8 @@ salpy_str (PyObject *self)
filename = symtab_to_filename_for_display (symtab);
}
- return PyUnicode_FromFormat ("symbol and line for %s, line %d", filename,
- sal->line);
+ return PyUnicode_FromFormat ("symbol and line for %s, line %d:%d", filename,
+ sal->line, sal->col);
}
static void
@@ -343,6 +343,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 +607,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[] = {
@@ -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 ();
}
@@ -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)
@@ -1603,6 +1603,10 @@ struct linetable_entry
/* The line number for this entry. */
int line;
+ /* The column number for this entry. 0 means it was set by code that
+ doesn't understand columns. */
+ int column;
+
/* True if this PC is a good location to place a breakpoint for LINE. */
bool is_stmt : 1;
@@ -1669,6 +1673,26 @@ struct symtab
return m_linetable;
}
+ gdb::array_view<const linetable_entry>
+ linetable_entries_for (unsigned int line)
+ {
+ for (auto i = 0;
+ i < m_linetable->nitems && m_linetable->item[i].line <= line; i++)
+ {
+ if (m_linetable->item[i].line == line)
+ {
+ auto k = i + 1;
+ while (k < m_linetable->nitems
+ && m_linetable->item[k].line == line)
+ k++;
+ const linetable_entry *begin = m_linetable->item + i;
+ const linetable_entry *end = m_linetable->item + k;
+ return gdb::array_view<const linetable_entry>{ begin, end };
+ }
+ }
+ return {};
+ }
+
void set_linetable (const struct linetable *linetable)
{
m_linetable = linetable;
@@ -2318,6 +2342,9 @@ struct symtab_and_line
information is not available. */
int line = 0;
+ /* See comments on line number. */
+ int col = 0;
+
CORE_ADDR pc = 0;
CORE_ADDR end = 0;
bool explicit_pc = false;