@@ -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.
@@ -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,37 @@ 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 step-expression
+@kindex se @r{(@code{step-expression})}
+@item step-expression @r{[}@var{count}@r{]}
+Step 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, describing column position of statements,
+expressions or logical breakpoint locations on a source line has been part
+of the DWARF standard for over 30 years, since DWARF2. GCC and clang both
+emit this information. Tested versions shown to emit DWARF column meta data
+are GCC-9 and Clang-9, though only Clang is fully compliant with the DWARF
+specification at the moment as GCC emits faulty DWARF debug information.
+
+Columns represent byte-level positions on a source code line.
+
+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 next-expression
+@kindex ne @r{(@code{next-expression})}
+@item next-expression @r{[}@var{count}@r{]}
+This is similar to @code{step-expression} but does not step into
+sub-expressions on the same source line, but steps over them. Examples
+of sub-expressions might be a callback, like lambdas or closures.
+
+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 +31344,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,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 its byte position
+on that line.
+@end defvar
+
A @code{gdb.Symtab_and_line} object has the following methods:
@defun Symtab_and_line.is_valid ()
@@ -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 = 0;
/* 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 ();
@@ -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'. */
@@ -471,6 +473,7 @@ class thread_info : public refcounted_object,
}
int current_line = 0;
+ int current_column = 0;
struct symtab *current_symtab = NULL;
/* Internal stepping state. */
@@ -19,6 +19,7 @@
#include "defs.h"
#include "arch-utils.h"
+#include "gdbsupport/gdb_assert.h"
#include "symtab.h"
#include "gdbtypes.h"
#include "frame.h"
@@ -61,7 +62,11 @@
static void until_next_command (int);
-static void step_1 (int, int, const char *);
+static void
+step_1 (int, int, bool, const char *);
+
+struct step_command_fsm;
+static int prepare_one_step (thread_info *, step_command_fsm *sm);
#define ERROR_NO_INFERIOR \
if (!target_has_execution ()) error (_("The program is not being run."));
@@ -231,8 +236,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 +769,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 +777,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 +785,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,44 +808,288 @@ struct step_command_fsm : public thread_fsm
/* If true, this is a stepi/nexti, otherwise a step/step. */
int single_inst;
- explicit step_command_fsm (struct interp *cmd_interp)
- : thread_fsm (cmd_interp)
+ step_command_fsm (interp *cmd_interp, int count, int skip_subroutine,
+ int single_inst, thread_info *thread)
+ : thread_fsm (cmd_interp), count (count),
+ skip_subroutines (skip_subroutine), single_inst (single_inst)
{
+ const auto frame = get_current_frame ();
+ if (!single_inst || skip_subroutines)
+ set_longjmp_breakpoint (thread, get_frame_id (frame));
+
+ thread->control.stepping_command = 1;
}
void clean_up (struct thread_info *thread) override;
bool should_stop (struct thread_info *thread) override;
enum async_reply_reason do_async_reply_reason () override;
+
+ virtual bool is_step_expression ()
+ {
+ return false;
+ }
};
-/* Prepare for a step/next/etc. command. Any target resource
- allocated here is undone in the FSM's clean_up method. */
+/* When a user says `next-expression` or `step-expression` we don't want to
+ * collect symtabs and linetable_entries over and over which for a large
+ * project might actual come at a pretty substantial cost. If a user enters
+ * this command the likelihood that she or he wants to enter it again (and
+ * soon) is high. as such, we cache that using next_expression_cmd_cache. */
+class next_expression_cmd_cache
+{
+public:
+ next_expression_cmd_cache () : line_number (-1), file_name (), cache () {}
+ gdb::optional<std::vector<linetable_entry> *>
+ get_cached (const symtab_and_line &sal, frame_info_ptr frame);
+ int line () const
+ {
+ return line_number;
+ }
+ frame_id frame () const
+ {
+ return starting_frame_id;
+ }
-static void
-step_command_fsm_prepare (struct step_command_fsm *sm,
- int skip_subroutines, int single_inst,
- int count, struct thread_info *thread)
+private:
+ frame_id starting_frame_id;
+ int line_number;
+ bool is_cached (const std::string &file_name, int line_number) const;
+ void invalidate_cache ();
+ std::string file_name;
+ std::vector<linetable_entry> cache;
+};
+
+static frame_id
+oldest_frame_at_src_and_line (const char *filename, int line,
+ frame_info_ptr begin)
{
- sm->skip_subroutines = skip_subroutines;
- sm->single_inst = single_inst;
- sm->count = count;
+ auto same = begin;
+ auto prev = get_prev_frame (begin);
+ while (prev != nullptr)
+ {
+ const auto sal = find_frame_sal (prev);
+ if (sal.symtab->filename == filename && sal.line == line)
+ same = prev;
+ prev = get_prev_frame (prev);
+ }
+ return get_frame_id (same);
+}
+
+gdb::optional<std::vector<linetable_entry> *>
+next_expression_cmd_cache::get_cached (const symtab_and_line &sal,
+ frame_info_ptr frame)
+{
+ if (sal.symtab == nullptr)
+ {
+ invalidate_cache ();
+ return {};
+ }
+
+ if (is_cached (sal.symtab->filename, sal.line))
+ return { &cache };
+ else
+ {
+ starting_frame_id
+ = oldest_frame_at_src_and_line (sal.symtab->filename, sal.line, frame);
+ const auto sals
+ = symtabs_from_filename (sal.symtab->filename, sal.pspace);
+ std::unordered_map<int, linetable_entry> ltes;
+
+ for (const auto &s : sals)
+ {
+ const auto items = s->linetable ()->items ();
+ for (const auto &entry : items)
+ if (entry.line == sal.line && entry.column != 0)
+ ltes[entry.column] = entry;
+ }
+
+ if (ltes.size () <= 1)
+ {
+ invalidate_cache ();
+ return {};
+ }
+
+ cache.clear ();
+ cache.reserve (ltes.size ());
+ for (const auto kvp : ltes)
+ cache.push_back (kvp.second);
+ line_number = sal.line;
+ file_name = sal.symtab->filename;
+ return { &cache };
+ }
+}
- /* Leave the si command alone. */
- if (!sm->single_inst || sm->skip_subroutines)
- set_longjmp_breakpoint (thread, get_frame_id (get_current_frame ()));
+void
+next_expression_cmd_cache::invalidate_cache ()
+{
+ file_name = "";
+ line_number = -1;
+ cache.clear ();
+}
- thread->control.stepping_command = 1;
+bool
+next_expression_cmd_cache::is_cached (const std::string &file_name,
+ int line_number) const
+{
+ return this->file_name == file_name && line_number == this->line_number;
}
-static int prepare_one_step (thread_info *, struct step_command_fsm *sm);
+struct step_expr_fsm : public step_command_fsm
+{
+ step_expr_fsm (interp *cmd_interp, int count, int skip_subroutine,
+ int single_inst, thread_info *thread,
+ std::vector<breakpoint_up> &&bps)
+ : step_command_fsm (cmd_interp, count, skip_subroutine, single_inst,
+ thread),
+ bps (std::move (bps)), is_step (single_inst)
+ {
+ }
+
+ static std::unique_ptr<step_command_fsm> default_to_next (interp *interp,
+ thread_info *thr);
+
+ bool should_stop (struct thread_info *thread) override;
+ void clean_up (thread_info *) override;
+
+ bool is_step_expression () override
+ {
+ return true;
+ }
+
+ static next_expression_cmd_cache *cache;
+ std::vector<breakpoint_up> bps;
+ bool is_step;
+};
+
+next_expression_cmd_cache* step_expr_fsm::cache = new next_expression_cmd_cache{};
+
+/* static */
+std::unique_ptr<step_command_fsm>
+step_expr_fsm::default_to_next (interp *interp, thread_info *ti)
+{
+ return std::make_unique<step_command_fsm> (interp, 1, 1, 0, ti);
+}
+
+void
+step_expr_fsm::clean_up (thread_info *tp)
+{
+ step_command_fsm::clean_up (tp);
+ bps.clear ();
+}
+
+bool
+step_expr_fsm::should_stop (thread_info *tp)
+{
+ auto stopped_at_bp
+ = std::find_if (bps.begin (), bps.end (), [tp] (auto &bp) {
+ return bpstat_find_breakpoint (tp->control.stop_bpstat, bp.get ())
+ != nullptr;
+ });
+
+ if (stopped_at_bp != std::end (bps))
+ {
+ const auto frame = frame_find_by_id (cache->frame ());
+ if (not is_step)
+ {
+ const auto current_frame = get_current_frame ();
+ auto fid = get_frame_id (current_frame);
+ bps.erase (stopped_at_bp);
+ if (fid != cache->frame ())
+ return prepare_one_step (tp, this);
+ }
+ if (--count > 0)
+ return prepare_one_step (tp, this);
+ else
+ {
+ set_finished ();
+ return true;
+ }
+ }
+
+ if (tp->control.stop_step)
+ {
+ const auto frame = frame_find_by_id (cache->frame ());
+
+ if (frame == nullptr)
+ {
+ set_finished ();
+ return true;
+ }
+
+ const auto sal = find_frame_sal (frame);
+ if (sal.line != cache->line ())
+ {
+ set_finished ();
+ return true;
+ }
+ return prepare_one_step (tp, this);
+ }
+ return true;
+}
+
+static std::unique_ptr<step_command_fsm>
+create_step_fsm (int count, int single_ins, int skip_subroutine,
+ bool step_expression, thread_info *thread)
+{
+ const auto interp = command_interp ();
+
+ /* n/ni, s/si fsm created here */
+ if (!step_expression)
+ return std::make_unique<step_command_fsm> (interp, count, skip_subroutine,
+ single_ins, thread);
+ else
+ {
+ const auto frame = get_current_frame ();
+ const auto sal = find_frame_sal (frame);
+ std::vector<breakpoint_up> bps;
+ const auto entries = step_expr_fsm::cache->get_cached (sal, frame);
+ if (entries.has_value ())
+ {
+ const std::vector<linetable_entry> <es = **entries;
+ if (!single_ins && ltes.size () < count)
+ return step_expr_fsm::default_to_next (interp, thread);
+
+ for (const auto &entry : ltes)
+ {
+ if (!single_ins && entry.column == sal.col)
+ continue;
+ 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_single_step);
+ bps.push_back (std::move (bp_loc));
+ }
+
+ if (!bps.empty ())
+ return std::make_unique<step_expr_fsm> (
+ interp, count, true, single_ins, thread, std::move (bps));
+ }
+ return step_expr_fsm::default_to_next (interp, thread);
+ }
+ gdb_assert (false && "This should never happen");
+ return nullptr;
+}
static void
-step_1 (int skip_subroutines, int single_inst, const char *count_string)
+next_expression (const char *count_str, int from_tty)
+{
+ step_1 (true, false, true, count_str);
+}
+
+static void
+step_expression (const char *count_str, int from_tty)
+{
+ step_1 (true, true, true, count_str);
+}
+
+static void
+step_1 (int skip_subroutines, int single_inst, bool step_expression,
+ const char *count_string)
{
int count;
int async_exec;
struct thread_info *thr;
- struct step_command_fsm *step_sm;
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
@@ -861,22 +1109,20 @@ step_1 (int skip_subroutines, int single_inst, const char *count_string)
/* Setup the execution command state machine to handle all the COUNT
steps. */
thr = inferior_thread ();
- step_sm = new step_command_fsm (command_interp ());
- thr->set_thread_fsm (std::unique_ptr<thread_fsm> (step_sm));
+ auto step_sm = create_step_fsm (count, single_inst, skip_subroutines,
+ step_expression, thr);
+ auto fsm_ptr = step_sm.get ();
- step_command_fsm_prepare (step_sm, skip_subroutines,
- single_inst, count, thr);
+ thr->set_thread_fsm (std::move (step_sm));
/* Do only one step for now, before returning control to the event
loop. Let the continuation figure out how many other steps we
need to do, and handle them one at the time, through
step_once. */
- if (!prepare_one_step (thr, step_sm))
+ if (!prepare_one_step (thr, fsm_ptr))
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
else
{
- /* Stepped into an inline frame. Pretend that we've
- stopped. */
thr->thread_fsm ()->clean_up (thr);
bool proceeded = normal_stop ();
if (!proceeded)
@@ -1033,6 +1279,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->is_step_expression();
return 0;
}
@@ -3311,6 +3558,22 @@ 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 *nexpr_cmd
+ = add_com ("next-expression", class_run, next_expression, _ ("\
+Step program by expressions, 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", nexpr_cmd, class_run, 1);
+
+ cmd_list_element *stexpr_cmd
+ = add_com ("step-expression", class_run, step_expression, _ ("\
+Step program by expressions, stepping into subroutines if they are in-line.\n\
+Usage: step-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 ("se", stexpr_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\
@@ -2887,6 +2887,7 @@ clear_proceed_status_thread (struct thread_info *tp)
tp->control.proceed_to_finish = 0;
tp->control.stepping_command = 0;
+ tp->control.step_expression = false;
/* Discard any remaining commands or status from previous stop. */
bpstat_clear (&tp->control.stop_bpstat);
@@ -4493,6 +4494,7 @@ set_step_info (thread_info *tp, frame_info_ptr frame,
tp->current_symtab = sal.symtab;
tp->current_line = sal.line;
+ tp->current_column = sal.col;
infrun_debug_printf
("symtab = %s, line = %d, step_frame_id = %s, step_stack_frame_id = %s",
@@ -7564,7 +7566,9 @@ process_event_stop_test (struct execution_control_state *ecs)
bool refresh_step_info = true;
if ((ecs->event_thread->stop_pc () == stop_pc_sal.pc)
&& (ecs->event_thread->current_line != stop_pc_sal.line
- || ecs->event_thread->current_symtab != stop_pc_sal.symtab))
+ || ecs->event_thread->current_symtab != stop_pc_sal.symtab
+ || (ecs->event_thread->control.step_expression
+ && ecs->event_thread->current_column != stop_pc_sal.col)))
{
/* We are at a different line. */
@@ -362,8 +362,6 @@ static std::vector<symtab_and_line> decode_objc (struct linespec_state *self,
linespec *ls,
const char *arg);
-static std::vector<symtab *> symtabs_from_filename
- (const char *, struct program_space *pspace);
static std::vector<block_symbol> find_label_symbols
(struct linespec_state *self,
@@ -3720,7 +3718,7 @@ collect_symtabs_from_filename (const char *file,
/* Return all the symtabs associated to the FILENAME. If SEARCH_PSPACE is
not NULL, the search is restricted to just that program space. */
-static std::vector<symtab *>
+std::vector<symtab *>
symtabs_from_filename (const char *filename,
struct program_space *search_pspace)
{
@@ -22,7 +22,7 @@ struct symtab;
#include "location.h"
/* Flags to pass to decode_line_1 and decode_line_full. */
-
+std::vector<symtab *> symtabs_from_filename (const char *filename, struct program_space *search_pspace);
enum decode_line_flags
{
/* Set this flag if you want the resulting SALs to describe the
@@ -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,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[] = {
@@ -57,6 +57,14 @@
#include "cli/cli-style.h"
#include "gdbsupport/buildargv.h"
+bool colinfo = true;
+static void
+show_debug_col_info (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ gdb_printf (file, _("Printing column position is %s.\n"), value);
+}
+
/* The possible choices of "set print frame-arguments", and the value
of this setting. */
@@ -1418,6 +1426,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 (colinfo && sal.col > 0)
+ {
+ uiout->text (":");
+ uiout->field_signed ("column", sal.col);
+ }
annotate_frame_source_end ();
}
@@ -3559,6 +3574,13 @@ source line."),
&setlist, &showlist);
disassemble_next_line = AUTO_BOOLEAN_FALSE;
+ add_setshow_boolean_cmd
+ ("colinfo", class_maintenance, &colinfo,
+ _("Set printing of column."),
+ _("Show printing of column setting."),
+ _("When non-zero, column information will be printed if it exists"),
+ nullptr, show_debug_col_info, &setdebuglist, &showdebuglist);
+
gdb::option::add_setshow_cmds_for_options
(class_stack, &user_frame_print_options,
frame_print_option_defs, &setprintlist, &showprintlist);
@@ -973,9 +973,10 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
/* Leave space for 6 digits of index and line number. After that the
tables will just not format as well. */
struct ui_out *uiout = current_uiout;
- ui_out_emit_table table_emitter (uiout, 6, -1, "line-table");
+ ui_out_emit_table table_emitter (uiout, 7, -1, "line-table");
uiout->table_header (6, ui_left, "index", _("INDEX"));
uiout->table_header (6, ui_left, "line", _("LINE"));
+ uiout->table_header (6, ui_left, "col", _("COL"));
uiout->table_header (18, ui_left, "rel-address", _("REL-ADDRESS"));
uiout->table_header (18, ui_left, "unrel-address", _("UNREL-ADDRESS"));
uiout->table_header (7, ui_left, "is-stmt", _("IS-STMT"));
@@ -993,6 +994,7 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
uiout->field_signed ("line", item->line);
else
uiout->field_string ("line", _("END"));
+ uiout->field_signed("col", item->column);
uiout->field_core_addr ("rel-address", objfile->arch (),
item->pc (objfile));
uiout->field_core_addr ("unrel-address", objfile->arch (),
@@ -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)
@@ -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;
@@ -37,7 +37,7 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {deb
clean_restart ${binfile}
-# The commands we test here produce many lines of output; disable "press
+# The commands we test here produce many lines of output; disable "press
# <return> to continue" prompts.
gdb_test_no_output "set height 0"
@@ -189,6 +189,8 @@ set run_re \
set run_re [join $run_re ""]
+send_gdb "set debug colinfo 0\n"
+
gdb_test_multiple "run" "run until main breakpoint" {
-re $run_re.*$gdb_prompt$ {
pass $gdb_test_name
@@ -36,10 +36,11 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {deb
clean_restart ${binfile}
-# The commands we test here produce many lines of output; disable "press
+# The commands we test here produce many lines of output; disable "press
# <return> to continue" prompts.
gdb_test_no_output "set height 0"
+send_gdb "set debug colinfo 0\n"
#
# break in main
#
@@ -71,7 +72,7 @@ set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
# annotate-prompt
# annotate-post-prompt (in the next block)
#
-send_gdb "set annotate 3\n"
+send_gdb "set annotate 3\n"
gdb_expect_list "annotation set at level 3" "\r\n$gdb_prompt$" {
"set annotate 3"
}
@@ -105,6 +106,7 @@ gdb_expect_list "breakpoint info" "$gdb_prompt$" [concat {
# run to a break point will test:
#
#exp_internal 1
+send_gdb "set annotate 3\n"
send_gdb "run\n"
gdb_expect_list "run until main breakpoint" "$gdb_prompt$" [concat {
"\r\n\032\032post-prompt\r\n"
@@ -106,7 +106,7 @@ proc test_dlmopen_one { ndso1 ndso2 exp_glob } {
# The actual test. We run it twice.
proc test_dlmopen {} {
global srcfile basename_lib bp_main
-
+ send_gdb "set debug colinfo 0\n"
# Note that when loading dlmopen-lib.1.so and dlmopen-lib.2.so into
# the same namespace, dlmopen-lib-dep.so is loaded only once, so in
# this case, the changes to gdb_dlmopen_glob inside test_dlmopen_one
@@ -20,6 +20,7 @@ set all_env_vars { HOME XDG_CACHE_HOME LOCALAPPDATA XDG_CONFIG_HOME }
# Record the initial value of the index-cache directory.
clean_restart
+
set index_cache_directory ""
gdb_test_multiple "show index-cache directory" "" {
-re -wrap "The directory of the index cache is \"(.*)\"\\." {
@@ -58,6 +58,7 @@ if {!$core_supported} {
# Now restart gdb and load the corefile.
clean_restart $binfile
+send_gdb "set debug colinfo 0\n"
set core_loaded [gdb_core_cmd "$corefile" "re-load generated corefile"]
if { $core_loaded == -1 } {
@@ -371,7 +371,7 @@ gdb_test_multiple "maint info breakpoints" "maint info breakpoints" {
gdb_test "maint print" \
"List.*unambiguous\\..*" \
- "maint print w/o args"
+ "maint print w/o args"
gdb_test "maint info" \
"List.*unambiguous\\..*" \
@@ -383,14 +383,15 @@ gdb_test "maint" \
# Test that "main info line-table" w/o a file name shows the symtab for
# $srcfile.
+match_max -i $gdb_spawn_id 150000
set saw_srcfile 0
gdb_test_multiple "maint info line-table" \
"maint info line-table w/o a file name" {
- -re "symtab: \[^\n\r\]+${srcfile} \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[^\r\n\]*" {
+ -re "symtab: \[^\n\r\]+${srcfile} \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+COL\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[^\r\n\]*" {
set saw_srcfile 1
exp_continue
}
- -re "symtab: \[^\n\r\]+ \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[^\r\n\]*" {
+ -re "symtab: \[^\n\r\]+ \\(\\(struct symtab \\*\\) $hex\\)\r\nlinetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+COL\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[^\r\n\]*" {
# Match each symtab to avoid overflowing expect's buffer.
exp_continue
}
@@ -398,7 +399,7 @@ gdb_test_multiple "maint info line-table" \
# For symtabs with no linetable.
exp_continue
}
- -re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\[ \t\]+$hex\[^\r\n\]*\r\n" {
+ -re "^$decimal\[ \t\]+$decimal\[ \t\]+$decimal\[ \t\]+$hex\[ \t\]+$hex\[^\r\n\]*\r\n" {
# Line table entries can be long too:
#
# INDEX LINE ADDRESS
@@ -429,6 +430,8 @@ gdb_test_multiple "maint info line-table" \
}
}
+match_max -i $gdb_spawn_id [match_max -d]
+
gdb_test "maint info line-table ${srcfile}" \
"symtab: \[^\n\r\]+${srcfile}.*INDEX.*LINE.*ADDRESS.*" \
"maint info line-table with filename of current symtab"
new file mode 100644
@@ -0,0 +1,113 @@
+/* Copyright 2023 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+struct FooBarBaz
+{
+ int a, b, c;
+};
+
+struct Builder
+{
+ using Finalizer = int (*) (int);
+ Builder () {}
+
+ Builder &set_foo (int f)
+ {
+ foo = f;
+ return *this;
+ }
+
+ Builder &set_bar (int b)
+ {
+ bar = b;
+ return *this;
+ }
+
+ Builder &set_baz (int b)
+ {
+ baz = b;
+ return *this;
+ }
+
+ Builder &set_fin (Finalizer f)
+ {
+ fn = f;
+ return *this;
+ }
+
+ template <typename Finalizer> FooBarBaz fin (Finalizer fn)
+ {
+ const auto res = FooBarBaz{ .a = fn (foo), .b = fn (bar), .c = fn (baz) };
+ return res;
+ }
+
+ FooBarBaz fin ()
+ {
+ if (fn != nullptr)
+ return FooBarBaz{ .a = fn (foo), .b = fn (bar), .c = fn (baz) };
+ else
+ return FooBarBaz{ .a = foo, .b = bar, .c = baz };
+ }
+
+private:
+ int (*fn) (int) = nullptr;
+ int foo;
+ int bar;
+ int baz;
+};
+
+static int v = 0;
+
+// next v
+static int
+n()
+{
+ return ++v;
+}
+
+static bool is_n_even() {
+ return (v & 1) == 0;
+}
+
+int
+main (int argc, const char **argv)
+{
+ const int AAA = 10, BBB = 20, CCC = 30; /* COMMA_STATEMENTS */
+ Builder b;
+ // `step-expression` between all, from foo to into fin lambda (lambda is repeated 3 times)
+ const auto se_1 = b.set_foo(AAA).set_bar(BBB).set_baz(CCC).fin([] (auto v) { return v * 2; }); /* SE-1 */
+ // clang emits col loc for n(), gcc does not yet, hopefully they fix this bug.
+ // gcc also emits faulty col locs, but usable see issue
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109902
+ const auto se_2 = b.set_foo(n()).set_bar(n()).set_baz(n()).fin([] (auto v) { return v * 3; }); /* SE-2 */
+ // `next-expression` set_fin lambda is not called immediately, should not step into
+ const auto se_3 = b.set_fin([](auto v) { return v * 3; }).fin(); /* SE-3 */
+ for (int i = 0; i < 10; i++)
+ {
+ i++;
+ }
+ // `next-expression` should step over and land on 107
+ if(is_n_even() && !is_n_even()) { /* TWO_COLS_VALID */
+ const int FOO = 10; const int BAR = 20; const int BAZ = 30;
+ }
+ const int FOO = 10; const int BAR = 20; const int BAZ = 30; /* STATEMENTS_LINE */
+
+ // do `next-expression` - step between expressions, but not into subroutines
+ const auto ne_1 = b.set_foo(AAA).set_bar(BBB).set_baz(CCC).fin([] (auto v) { return v * 2; }); /* NE-1 */
+ const auto ne_2 = b.set_foo(n()).set_bar(n()).set_baz(n()).fin([] (auto v) { return v * 3; }); /* NE-2 */
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,267 @@
+# Copyright 2023 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/>.
+
+# The intent of this test is to verify that `next-expression` command
+# behaves as expected. Note that in the future this test might
+# start failing as columns are hard coded in the test; and GCC has
+# bugs in how it emits column meta data, see
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109902
+# however, the column meta data is still useful even if not DWARF compliant
+
+set saved_gdbflags $GDBFLAGS
+
+# turn on column meta data, which is off by default during testing
+set GDBFLAGS [concat $GDBFLAGS " -ex \"set debug colinfo 1\""]
+
+set is_running 0
+
+set GCC_ISSUE_URL "https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109902"
+
+standard_testfile
+set srcfile next-expression.c
+
+set TEST_ID 1
+
+# gcc = 0, clang = 1
+set compiler 0
+if {[test_compiler_info {gcc*} {c++}]} {
+ set compiler 0
+} elseif {[test_compiler_info {clang*} {c++}]} {
+ set compiler 1
+} else {
+ set compiler 100
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+proc test_command { cmd count line columns } {
+ global srcfile
+ global GCC_ISSUE_URL
+ global TEST_ID
+ global compiler
+ set idx $count
+ set list_len [ llength $columns ]
+ for { set i [expr $count - 1] } { $i < $list_len } { incr i $count } {
+ set col [ lindex $columns $i ]
+ send_gdb "$cmd $count\n"
+ # handle testsuite flakeyness
+ sleep 0.01
+ if [ gdb_test "frame" \
+ ".*$srcfile:$line:$col.*" \
+ "verify that we $cmd $count to column $col on $line (#$TEST_ID)"] {
+ if { $compiler == 0 } {
+ puts "Test failed - GCC might have fixed bug \
+ $GCC_ISSUE_URL"
+ } else {
+ puts "Landed at unexpected column"
+ }
+ }
+ incr TEST_ID
+ }
+}
+
+proc test_next_expression { count line columns } {
+ test_command "next-expression" $count $line $columns
+}
+
+proc test_step_expression { count line columns } {
+ test_command "step-expression" $count $line $columns
+}
+
+proc goto_line_test_expression_command { count is_step line columns } {
+ global srcfile
+ global GCC_ISSUE_URL
+ global is_running
+ global TEST_ID
+ send_gdb "tbreak $line\n"
+ set firstcolumn [lindex $columns 0]
+ set test_num 1
+ if { $is_running == 0 } {
+ send_gdb "run\n"
+ # handle testsuite flakeyness
+ sleep 0.01
+ gdb_test "frame" \
+ ".*$srcfile:$line:$firstcolumn.*" \
+ "verify that we continued to column $firstcolumn on $line (#$TEST_ID)"
+ incr TEST_ID
+ set is_running 1
+ } else {
+ send_gdb "continue\n"
+ # handle testsuite flakeyness
+ sleep 0.01
+ gdb_test "frame" \
+ ".*$srcfile:$line:$firstcolumn.*" \
+ "verify that we continued to column $firstcolumn on $line (#$TEST_ID)"
+ incr TEST_ID
+ }
+ # "pop" the first element
+ set columns [lreplace $columns 0 0]
+ if { $is_step == 0 } {
+ test_next_expression $count $line $columns
+ } else {
+ test_step_expression $count $line $columns
+ }
+}
+
+proc goto_line_test_step { count line columns } {
+ goto_line_test_expression_command $count 1 $line $columns
+}
+
+proc goto_line_test_next { count line columns } {
+ goto_line_test_expression_command $count 0 $line $columns
+}
+
+# SE-FOO represents tests & columns using `step-expression`
+# NE-FOO represents tests & columns using `next-expression`
+
+# GCC emits correct col meta data (clang does as well)
+set comma_stmts_cols { 13 23 33 }
+set comma_stmts [gdb_get_line_number "COMMA_STATEMENTS"]
+goto_line_test_step 1 $comma_stmts $comma_stmts_cols
+
+set se1 [ gdb_get_line_number "SE-1" ]
+# gcc emit faulty (non DWARF-compliant) cols, but usable
+set se1_cols_gcc { 30 43 56 65 66 91 94 66 91 94 66 91 94 }
+# correct columns in order of execution, left here for documentation purposes
+set se1_cols_clang { 23 36 49 62 87 89 80 87 89 80 87 89 80}
+
+# the columns stepped to using next-expression
+set ne1 [ gdb_get_line_number "NE-1" ]
+set ne1_cols_gcc { 30 43 56 65 }
+set ne1_cols_clang { 23 36 49 62 }
+
+set se2 [ gdb_get_line_number "SE-2" ]
+# gcc emit faulty (non DWARF-compliant) cols, but usable
+set se2_cols_gcc { 30 43 56 65 66 91 94 }
+set se2_cols_clang { 31 23 44 36 57 49 62 89 80 }
+
+set ne2 [ gdb_get_line_number "NE-2" ]
+# gcc emit faulty (non DWARF-compliant) cols, but usable
+set ne2_cols_gcc { 30 43 56 65 }
+set ne2_cols_clang { 31 23 44 36 57 49 62 }
+
+set se3 [ gdb_get_line_number "SE-3" ]
+# gcc emit faulty (non DWARF-compliant) cols, but usable
+set se3_cols_gcc { 30 64 }
+# correct columns in order of execution, left here for documentation purposes
+set se3_cols_clang { 31 23 61 }
+
+
+
+
+set two_cols_valid [ gdb_get_line_number "TWO_COLS_VALID" ]
+# gcc emit faulty (non DWARF-compliant) cols, but usable
+set two_cols_gcc { 15 18 }
+# correct columns in order of execution, left here for documentation purposes
+set two_cols_clang { 6 18 }
+
+# test step-expression commands
+if { $compiler == 0 } {
+ goto_line_test_step 1 $se1 $se1_cols_gcc
+ goto_line_test_step 1 $se2 $se2_cols_gcc
+ goto_line_test_step 1 $se3 $se3_cols_gcc
+ goto_line_test_step 1 $two_cols_valid $two_cols_gcc
+} elseif { $compiler == 1 } {
+ goto_line_test_step 1 $se1 $se1_cols_clang
+ goto_line_test_step 1 $se2 $se2_cols_clang
+ goto_line_test_step 1 $se3 $se3_cols_clang
+ goto_line_test_step 1 $two_cols_valid $two_cols_clang
+}
+
+set stmt_line [ gdb_get_line_number "STATEMENTS_LINE" ]
+goto_line_test_step 1 $stmt_line { 13 33 53 }
+
+
+if { $compiler == 0 } {
+ goto_line_test_next 1 $ne1 $ne1_cols_gcc
+ goto_line_test_next 1 $ne2 $ne2_cols_gcc
+} elseif { $compiler == 1 } {
+ goto_line_test_next 1 $ne1 $ne1_cols_clang
+ goto_line_test_next 1 $ne2 $ne2_cols_clang
+}
+
+clean_restart ${testfile}
+set is_running 0
+send_gdb "set debug colinfo 1\n"
+
+# test step-expression commands with `3` as count
+if { $compiler == 0 } {
+ goto_line_test_step 3 $se1 $se1_cols_gcc
+ goto_line_test_step 3 $se2 $se2_cols_gcc
+ goto_line_test_step 3 $se3 $se3_cols_gcc
+ goto_line_test_step 3 $two_cols_valid $two_cols_gcc
+} elseif { $compiler == 1 } {
+ goto_line_test_step 3 $se1 $se1_cols_clang
+ goto_line_test_step 3 $se2 $se2_cols_clang
+ goto_line_test_step 3 $se3 $se3_cols_clang
+ goto_line_test_step 3 $two_cols_valid $two_cols_clang
+}
+
+if { $compiler == 0 } {
+ goto_line_test_next 4 $ne1 $ne1_cols_gcc
+ goto_line_test_next 4 $ne2 $ne2_cols_gcc
+} elseif { $compiler == 1 } {
+ goto_line_test_next 4 $ne1 $ne1_cols_clang
+ goto_line_test_next 4 $ne2 $ne2_cols_clang
+}
+
+# Test that caching works as expected
+# see comments on infcmd.c in function oldest_frame_at_src_and_line
+clean_restart ${testfile}
+set is_running 0
+send_gdb "set debug colinfo 1\n"
+
+send_gdb "tbreak fin\n"
+send_gdb "run\n"
+gdb_test "frame" \
+ ".*Builder::fin.*$srcfile:54:.*" \
+ "verify that landed in Builder::fin"
+send_gdb "step\n"
+# we're now inside lambda
+gdb_test "frame" \
+ ".*$srcfile:$se1:.*" \
+ "verify that we're inside lambda at $srcfile:$se1"
+# if cache'ing has worked properly, we should land at src.cpp:L+N
+# (which happens to be se-2), we should _not_ land back inside Builder::fin.
+# The `next` or `step` commands will do that, as they are instruction-level
+# commands. expression commands are meant to be source-level navigation.
+send_gdb "next-expression\n"
+gdb_test "frame" \
+ ".*$srcfile:$se2:.*" \
+ "verify that we did not step into calling frame, \
+ but to next logical source line instead"
+
+# Repeat above test, but instead of a final `next-expression` use a `next`
+# to verify that we _do_ land in Builder::fin
+clean_restart ${testfile}
+set is_running 0
+send_gdb "set debug colinfo 1\n"
+if { $compiler == 0} {
+ goto_line_test_step 1 $se1 { 30 43 56 65 66 91 }
+ # we're inside lambda - `next` should get us to Builder::fin, which is 1 frame up
+ send_gdb "next\n"
+ gdb_test "frame" \
+ ".*Builder::fin.*" \
+ "verify difference between source & instruction level commands"
+} elseif { $compiler == 1 } {
+ goto_line_test_step 1 $se1 { 23 36 49 62 87 89 }
+ # we're inside lambda - `next` should get us to Builder::fin, which is 1 frame up
+ send_gdb "next\n"
+ gdb_test "frame" \
+ ".*Builder::fin.*" \
+ "verify difference between source & instruction level commands"
+}
@@ -65,7 +65,7 @@ send_gdb "set annotate 3\n"
gdb_expect_list "annotation set at level 3" "\r\n$gdb_prompt$" {
"set annotate 3"
}
-
+send_gdb "set debug colinfo 0\n"
send_gdb "run\n"
gdb_expect_list "first run until main breakpoint" "$gdb_prompt$" {
"\r\n\032\032post-prompt\r\n"
@@ -85,10 +85,10 @@ if ![runto_main] {
set test "END with address 1 eliminated"
gdb_test_multiple "maint info line-table $srcfile$" $test {
- -re -wrap "END *0x0*1 *$hex *Y *\r\n.*" {
+ -re -wrap "END.* *0x0*1 *$hex *Y *\r\n.*" {
fail $gdb_test_name
}
- -re -wrap "END *$hex *$hex *Y *\r\n" {
+ -re -wrap "END.**$hex *$hex *Y *\r\n" {
pass $gdb_test_name
}
}
@@ -145,7 +145,7 @@ set prev -1
set seq_count 0
gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
"count END markers in line table" {
- -re "^$decimal\[ \t\]+$decimal\[ \t\]+$hex\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+ -re "^$decimal\[ \t\]+$decimal\[ \t\]+$decimal\[ \t\]+$hex\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
if { $prev != -1 } {
gdb_assert "$prev == 1" \
"prev of normal entry at $seq_count is end marker"
@@ -154,7 +154,7 @@ gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
incr seq_count
exp_continue
}
- -re "^$decimal\[ \t\]+END\[ \t\]+$hex\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
+ -re "^$decimal\[ \t\]+END\[ \t\]+$decimal\[ \t\]+$hex\[ \t\]+$hex\(\[ \t\]+Y\)? *\r\n" {
if { $prev != -1 } {
gdb_assert "$prev == 0" \
"prev of end marker at $seq_count is normal entry"
@@ -174,7 +174,7 @@ gdb_test_multiple "maint info line-table gdb.dwarf2/dw2-ranges-base.c" \
-re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
exp_continue
}
- -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END *\r\n" {
+ -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+COL\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END *\r\n" {
exp_continue
}
}
@@ -31,7 +31,7 @@ require !use_gdb_stub
# The plan is for async mode to become the default but toggle for now.
set saved_gdbflags $GDBFLAGS
-set GDBFLAGS [concat $GDBFLAGS " -ex \"set mi-async on\""]
+set GDBFLAGS [concat $GDBFLAGS " -ex \"set mi-async on\"" " -ex \"set debug colinfo 0\""]
load_lib mi-support.exp
@@ -41,7 +41,7 @@ proc do_test { non_stop cond_bp_target } {
global linenum
set saved_gdbflags $GDBFLAGS
- set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop $non_stop\""]
+ set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop $non_stop\"" " -ex \"set debug colinfo 0\""]
clean_restart $binfile
set GDBFLAGS $saved_gdbflags
@@ -26,6 +26,8 @@ proc gdb_init_commands {} {
if [target_info exists gdb_init_commands] {
set commands [concat $commands [target_info gdb_init_commands]]
}
+ # Turn off column info; irrelevant for almost all tests.
+ lappend commands "set debug colinfo 0"
return $commands
}
@@ -9028,7 +9028,7 @@ proc is_stmt_addresses { file } {
set is_stmt [list]
gdb_test_multiple "maint info line-table $file" "" {
- -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+$hex\[ \t\]+Y\[^\r\n\]*" {
+ -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+$hex\[ \t\]+Y\[^\r\n\]*" {
lappend is_stmt $expect_out(1,string)
exp_continue
}