From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lj1-x22e.google.com (mail-lj1-x22e.google.com [IPv6:2a00:1450:4864:20::22e]) by sourceware.org (Postfix) with ESMTPS id AB1903858CDB for ; Thu, 18 May 2023 14:46:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AB1903858CDB Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-lj1-x22e.google.com with SMTP id 38308e7fff4ca-2ad819ab8a9so23557181fa.0 for ; Thu, 18 May 2023 07:46:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1684421159; x=1687013159; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=0BPgHO0jeP/l17VBO10DFANszkFDmQwcG0CqBvNC1l8=; b=F7JDYJ9Co1XjnAFqlnkqoomSl1hxZvzxsjvdkeW4dVSC/ozq5TD0ixltPu+Xti3SL2 om0+5f/b6fCCrjkPdatiMkiU0CokF9Sr4RqMLHbK0hapkNbl5gYb8zWOQwIsBoyJXYWN Laj740U6Br3zIzpboat+Z26UQ9/p2T1g8LNydd/jkfHTRH4KSbQBtrYrrLiIKjg7Xr+2 c14IAYTl8sK3p9fo7gjy6ZhFKneXC9h25N4ipxCPV2h7h/NUEOWeMhfQ9oq3qmgSbF82 NoImJnEmsqj4dfuBJR2zKWojIasXwRgqTZsO/Qm7b9m3Nj9+7klFkq2afdabMu4khDJ7 Aoww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684421159; x=1687013159; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=0BPgHO0jeP/l17VBO10DFANszkFDmQwcG0CqBvNC1l8=; b=PfU3wbSI9SleP783FPJwOKVVwT1xt4hoh+nquISGxDvgvPjIakz+UnR7VZjMIdxiLZ QPjs62E/ufZtdxTFqOrsabVX7ETRYYWOE+ThgW+hivztphs8NNRkNsnVp7KfYyZaIB99 mfnqCYrC1/5glI7UI2v92Y0XUwK1fMJFILLphjozfJBjJTLcgImmMOLeogYWg+jOOvis DQyIYqYub6cjYkcnNU2G8StBa1qAAx81aqGcqu1sDr7wuo5+v+WKRmPwAbs5rgvyPcdP jU7i3QFk0zydudzl/h/apS0XktRa48iQnvHoIgubNzx6Jwwo4Si2aqxIJrP+jx46Ip4R +brg== X-Gm-Message-State: AC+VfDy9NnGnGJKQu0aZpoftGIfed9AFoudvC+BeW8S4KZElRaRvynUN H3PGlm4OXGqAOSRb1SoFBgl6OE2P/NA= X-Google-Smtp-Source: ACHHUZ4/MTlc7jcsofqL7nHISuPifYl4L2Un6cqXUkIBdcKLt85yB5XAK6cGYxLScxt7DtLE3cR5aA== X-Received: by 2002:a2e:2d12:0:b0:2ad:8929:e8f4 with SMTP id t18-20020a2e2d12000000b002ad8929e8f4mr10420717ljt.43.1684421158270; Thu, 18 May 2023 07:45:58 -0700 (PDT) Received: from fedora.. (78-73-77-63-no2450.tbcn.telia.com. [78.73.77.63]) by smtp.gmail.com with ESMTPSA id w11-20020a2e958b000000b002ac7a715585sm334293ljh.30.2023.05.18.07.45.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 18 May 2023 07:45:57 -0700 (PDT) From: Simon Farre To: gdb-patches@sourceware.org Cc: Simon Farre Subject: [PATCH v2] [gdb/infcmd]: Add next-expression command Date: Thu, 18 May 2023 16:45:52 +0200 Message-Id: <20230518144552.67097-1-simon.farre.cx@gmail.com> X-Mailer: git-send-email 2.40.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 (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 items () const + { + return gdb::array_view{ 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_entries_on_line (int line) + { + std::vector 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