From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) by sourceware.org (Postfix) with ESMTPS id 855DA386EC79 for ; Tue, 21 Jul 2020 15:37:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 855DA386EC79 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=alves.ped@gmail.com Received: by mail-wm1-f54.google.com with SMTP id o8so3265170wmh.4 for ; Tue, 21 Jul 2020 08:37:47 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=f87QeZonXIQUk3xR+Ih5MtMAlziJLT6DklF5hDPl0v0=; b=Fi7bb/6mBhHV/cKsPHizzY3OHPt2JLYZGGupsb+QDQ8cKaPdA+KVu2/MNSTkI1lcf3 SOS30C+zWj4Wq2qHohW/tEEPfUQc1eLKaZi3Pu15RYAsG7g8vr5dmInkc3YdowWXpIb/ h51A6qpac30y26/vntzWKyJUFf79CkonwV2SPHOqfU/KcQ4y5pVLKMf74IQQbBwpi5dH XQo11n9a7W/KJMFU7mtfGDqfy4pLJs3v+K551W3jb1pvz36W177y7lUS8Pdxld8itDsD DtZtmqTwUMs4YKJWw7cRlzmuPP1OFHoXPjJBLA07cE8DQ9bGLZ7XXPtRxczU5K1GBOcc 91Aw== X-Gm-Message-State: AOAM531gIVr17GDgDPs2AFOhWxvmyyeYAjX9KrD/z8bX87xyLoMmqaVX 1Jxqrokt0BPqnLCuiWx3M7v4bVOZm0E= X-Google-Smtp-Source: ABdhPJytHgDjk3BDUa0UVjjpU7eBfhs/6fW55E1ivc9FSppIaLmACCPeZJii4M30XI9+NfLovnsyEA== X-Received: by 2002:a1c:9e4c:: with SMTP id h73mr4908295wme.177.1595345865032; Tue, 21 Jul 2020 08:37:45 -0700 (PDT) Received: from localhost ([2001:8a0:f91a:c400:56ee:75ff:fe8d:232b]) by smtp.gmail.com with ESMTPSA id r10sm38378470wrm.17.2020.07.21.08.37.43 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Tue, 21 Jul 2020 08:37:44 -0700 (PDT) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 1/2] Keep stepping over "line 0" ranges (PR 26243) Date: Tue, 21 Jul 2020 16:37:36 +0100 Message-Id: <20200721153737.11262-2-pedro@palves.net> X-Mailer: git-send-email 2.14.5 In-Reply-To: <20200721153737.11262-1-pedro@palves.net> References: <20200721153737.11262-1-pedro@palves.net> X-Spam-Status: No, score=-8.5 required=5.0 tests=BAYES_00, FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 21 Jul 2020 15:37:50 -0000 The DWARF standard states for the line register in the line number information state machine the following: "An unsigned integer indicating a source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line." So, it's possible to have a zero line number in the DWARF line table. For some reason, Clang associates some instructions in the middle of functions to line 0 using that form. See PR 26243. This is currently not handled well by GDB when using the next/step commands. The problem was masked until the recent line info reading changes. Let's use some simple dummy instruction / line mapping as an example: insn1 <-> line 10 insn2 <-> line 0 insn3 <-> line 0 insn4 <-> line 11 - If an instruction is mapped to line 0, we should not hide the fact that the instruction maps to no source line. We should not merge it with the preceding instruction's line, or something like that. So if you stepi until insn2, or put a breakpoint on it and run, it should not appear as if you stopped at line 10. It should appear that you stopped somewhere without source. I.e., present to the user the truth as the compiler produced it. If the instructions should really be associated with a line, then this GDB behavior should be a good motivation for compilers to emit better debug info, where all instructions are covered, where possible. - Getting from a region with line info to a region without line info. You are on insn1 and you do "next". The current behavior is: the program stops at insn2, where you have no source available. On one hand, it's "correct" to stop, because we went from an instruction belonging to line 10 to an instruction not belonging to line 10. That's the conservative thing to do. But that's also very confusing to users who want to debug at source level. There's a quite unknown setting, "set step-mode" that I think is relevant here: (gdb) help set step-mode Set mode of the step operation. When set, doing a step over a function without debug line information will stop at the first instruction of that function. Otherwise, the function is skipped and the step command stops at a different source line. It talks about "stepping over a function", but I think it should apply here as well. Basically, if you have step-mode off, then you want to skip undebuggable code. If "on", you want to stop stepping in undebuggable code. If it did (apply here as well), then when "off" (the default), GDB would behave as it did historically, skipping those regions. When "on", a step would stop at the instruction with line 0. That's what this patch does -- it makes the step/next commands keep stepping when they go from an instruction with line info to an instruction without line info. In our example, doing a "next" while stopped at insn1 would step until stopped at insn4. The testcase added by this commit uses the DWARF assembler to write a line table with the instructions that should be assigned to line 31 assigned to line 0 instead. I.e., there's no line 31 in the line info. Note that with Clang-produced binaries the "holes" are seemingly smaller than the whole set of instructions for a given line, so the user doesn't notice anything missing, similarly to the example above with lines 10 and 11. With the testcase, and before the GDB fix, we see: (gdb) b bar1 Breakpoint 2 at 0x55555555513f: file /net/cascais.nfs/gdb/binutils-gdb/src/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c, line 27. (gdb) c Continuing. Breakpoint 2, bar1 () at /net/cascais.nfs/gdb/binutils-gdb/src/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c:27 27 foo (1); (gdb) list 22 23 void 24 bar1 (void) 25 { 26 asm ("bar1_label: .globl bar1_label"); 27 foo (1); 28 asm ("bar1_label_2: .globl bar1_label_2"); 29 foo (2); 30 asm ("bar1_label_3: .globl bar1_label_3"); 31 foo (3); 32 asm ("bar1_label_4: .globl bar1_label_4"); 33 foo (4); 34 asm ("bar1_label_5: .globl bar1_label_5"); 35 } (gdb) n 29 foo (2); (gdb) n 0x0000555555555153 in bar1 () (gdb) n Single stepping until exit from function bar1, which has no line number information. 0x00005555555551aa in main () (gdb) Note how the step started at line 29 stopped at an address without showing a line number. If we instead kept single-stepping at line 29, we would eventually reach line 33. (gdb) n 29 foo (2); (gdb) ni 0x000055555555514e 29 foo (2); (gdb) 0x0000555555555153 in bar1 () (gdb) 0x0000555555555158 in bar1 () (gdb) 33 foo (4); That is what this patch does. With it, we get instead: 27 foo (1); (gdb) n 29 foo (2); (gdb) n 33 foo (4); (gdb) The testcase exercises various interesting aspects: 1. Next through a zero-line instruction, is_stmt == 1 2. Next through a zero-line instruction, is_stmt == 0 3. Step through a zero-line instruction, is_stmt == 1 4. Step through a zero-line instruction, is_stmt == 0 5. Show source location at zero-line instruction, is_stmt == 1 6. Show source location at zero-line instruction, is_stmt == 0 All the above with "set step-mode" either "on" or "off". gdb/doc/ChangeLog: 2020-07-16 Pedro Alves PR symtab/26243 * gdb.texinfo (Continuing and Stepping) : Describe behavior in function address ranges with no line info. gdb/ChangeLog: 2020-07-16 Pedro Alves PR symtab/26243 * infrun.c (process_event_stop_test): If we have no line number information for the current PC, but have debug info for the function we started stepping, and are still in the same function, then continue stepping. gdb/testsuite/ChangeLog: 2020-07-20 Tom de Vries Pedro Alves PR symtab/26243 * gdb.dwarf2/dw2-line-number-zero.c: New test. * gdb.dwarf2/dw2-line-number-zero.exp: New file. --- gdb/doc/gdb.texinfo | 13 +- gdb/infrun.c | 47 +++-- gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c | 61 +++++++ gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.exp | 210 ++++++++++++++++++++++ 4 files changed, 314 insertions(+), 17 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.exp diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index a002084d5b9..99d5383f009 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -6037,14 +6037,19 @@ source line. This prevents multiple stops that could otherwise occur in The @code{set step-mode on} command causes the @code{step} command to stop at the first instruction of a function which contains no debug line information rather than stepping over it. +Similarly, causes the @code{step} and @code{next} commands to stop at +the first instruction of a function address range if it contains no +debug line information rather than stepping over it. This is useful in cases where you may be interested in inspecting the -machine instructions of a function which has no symbolic info and do not -want @value{GDBN} to automatically skip over this function. +machine instructions of a function or function address range which has +no symbolic info and do not want @value{GDBN} to automatically skip +over this function or range. @item set step-mode off -Causes the @code{step} command to step over any functions which contains no -debug information. This is the default. +Causes the @code{step} and @code{next} commands to automatically step +over any functions or function address ranges that have no line number +information. This is the default. @item show step-mode Show whether @value{GDBN} will stop in or step over functions without diff --git a/gdb/infrun.c b/gdb/infrun.c index 31266109a6d..28a42676e07 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -127,9 +127,8 @@ mark_infrun_async_event_handler (void) mark_async_event_handler (infrun_async_inferior_event_token); } -/* When set, stop the 'step' command if we enter a function which has - no line number information. The normal behavior is that we step - over such function. */ +/* For "set step-mode". */ + bool step_stop_if_no_debug = false; static void show_step_stop_if_no_debug (struct ui_file *file, int from_tty, @@ -7193,13 +7192,31 @@ process_event_stop_test (struct execution_control_state *ecs) if (stop_pc_sal.line == 0) { - /* We have no line number information. That means to stop - stepping (does this always happen right after one instruction, - when we do "s" in a function with no line numbers, - or can this happen as a result of a return or longjmp?). */ - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: no line number info\n"); - end_stepping_range (ecs); + /* We have no line number information. */ + + if (!step_stop_if_no_debug + && ecs->event_thread->control.step_start_function != nullptr + && (ecs->event_thread->control.step_start_function + == find_pc_function (ecs->event_thread->suspend.stop_pc))) + { + /* "set step-mode" is "off", and we're still in the same + function. Continue stepping until we're out of the + no-line-info range. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: no line number info, " + "but still in same function\n"); + ecs->event_thread->control.may_range_step = 0; + keep_going (ecs); + } + else + { + /* Otherwise, stop stepping and let the user decide how to + proceed. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: no line number info\n"); + end_stepping_range (ecs); + } return; } @@ -9691,9 +9708,13 @@ mode (see help set scheduler-locking)."), add_setshow_boolean_cmd ("step-mode", class_run, &step_stop_if_no_debug, _("\ Set mode of the step operation."), _("\ Show mode of the step operation."), _("\ -When set, doing a step over a function without debug line information\n\ -will stop at the first instruction of that function. Otherwise, the\n\ -function is skipped and the step command stops at a different source line."), +When on, stop the 'step' command if we enter a function that has no\n\ +line number information, and stop the 'step' and 'next' commands if\n\ +stepping within a function steps into an address range with no line\n\ +number information.\n\ +\n\ +When off (the default), stepping automatically skips functions and\n\ +function address ranges with no line number information."), NULL, show_step_stop_if_no_debug, &setlist, &showlist); diff --git a/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c b/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c new file mode 100644 index 00000000000..e6f99ad7e0c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.c @@ -0,0 +1,61 @@ +/* + Copyright 2020 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 . */ + +void +foo (int x) +{ + +} + +void +bar1 (void) +{ + asm ("bar1_label: .globl bar1_label"); + foo (1); + asm ("bar1_label_2: .globl bar1_label_2"); + foo (2); + asm ("bar1_label_3: .globl bar1_label_3"); + foo (3); + asm ("bar1_label_4: .globl bar1_label_4"); + foo (4); + asm ("bar1_label_5: .globl bar1_label_5"); +} + +void +bar2 (void) +{ + asm ("bar2_label: .globl bar2_label"); + foo (1); + asm ("bar2_label_2: .globl bar2_label_2"); + foo (2); + asm ("bar2_label_3: .globl bar2_label_3"); + foo (3); + asm ("bar2_label_4: .globl bar2_label_4"); + foo (4); + asm ("bar2_label_5: .globl bar2_label_5"); +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + + bar1 (); + + bar2 (); + + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.exp b/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.exp new file mode 100644 index 00000000000..91a23237759 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-line-number-zero.exp @@ -0,0 +1,210 @@ +# Copyright 2020 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 . + +# Test GDB's support for line 0 entries in the line table. Per the +# DWARF standard (v2 onward), "State Machine Registers" / "line": "The +# compiler may emit the value 0 in cases where an instruction cannot +# be attributed to any source line.". + +# With "set step-mode off" (which is the default), stepping does not +# stop in such undebuggable code regions. With "set step-mode on", it +# does stop. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +if {![dwarf2_support]} { + verbose "Skipping dw2-line-number-zero test." + return 0 +} + +# The .c files use __attribute__. +if [get_compiler_info] { + return -1 +} +if !$gcc_compiled { + verbose "Skipping dw2-line-number-zero test." + return 0 +} + +standard_testfile .c dw2-line-number-zero-dw.S + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + declare_labels Llines + + cu {} { + compile_unit { + {language @DW_LANG_C} + {name dw2-line-number-zero.c} + {stmt_list $Llines DW_FORM_sec_offset} + } { + subprogram { + {external 1 flag} + {MACRO_AT_func {main}} + } + subprogram { + {external 1 flag} + {MACRO_AT_func {bar1}} + } + subprogram { + {external 1 flag} + {MACRO_AT_func {bar2}} + } + } + } + + lines {version 2} Llines { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + program { + {DW_LNE_set_address bar1_label} + {line 27} + {DW_LNS_copy} + + {DW_LNE_set_address bar1_label_2} + {line 29} + {DW_LNS_copy} + + {DW_LNE_set_address bar1_label_3} + {line 0} + {DW_LNS_copy} + + {DW_LNE_set_address bar1_label_4} + {line 33} + {DW_LNS_copy} + + {DW_LNE_set_address bar1_label_5} + {DW_LNE_end_sequence} + + + {DW_LNE_set_address bar2_label} + {line 41} + {DW_LNS_copy} + + {DW_LNE_set_address bar2_label_2} + {line 43} + {DW_LNS_copy} + + {DW_LNE_set_address bar2_label_3} + {line 0} + {DW_LNS_negate_stmt} + {DW_LNS_copy} + {DW_LNS_negate_stmt} + + {DW_LNE_set_address bar2_label_4} + {line 47} + {DW_LNS_copy} + + {DW_LNE_set_address bar2_label_5} + {DW_LNE_end_sequence} + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +# Test that "next" automatically steps over the line 0 instructions, +# stopping at the next source line. + +proc_with_prefix test_next_step_mode_off {} { + if ![runto_main] { + return -1 + } + + foreach {function start_line} { + "bar1" 27 + "bar2" 41 + } { + global hex gdb_prompt + + with_test_prefix $function { + gdb_breakpoint $function + gdb_continue_to_breakpoint $function "\[^\r\n\]*:$start_line\r\n.*" + + gdb_test "n" "foo \\(2\\);" "1st next" + gdb_test "n" "foo \\(4\\);" "2nd next" + } + } +} + +# Test that with "set step-mode on", "next" stops at line 0 +# instructions. + +proc_with_prefix test_next_step_mode_on {} { + global hex gdb_prompt + + if ![runto_main] { + return -1 + } + + gdb_test "set step-mode on" + + foreach {function start_line} { + "bar1" 27 + "bar2" 41 + } { + global hex gdb_prompt + + with_test_prefix $function { + gdb_breakpoint $function + gdb_continue_to_breakpoint $function "\[^\r\n\]*:$start_line\r\n.*" + + gdb_test "n" "foo \\(2\\);" "next over normal line region" + + set saw_no_line 0 + gdb_test_multiple "n" "next over line0 region" { + -re "$hex in $function \\(\\)\r\n$gdb_prompt $" { + incr saw_no_line + + # Avoid nexting forever if the testcase goes wild. + if {$saw_no_line > 100} { + fail $gdb_test_name + } + + send_gdb "n\n" + exp_continue + } + -re "foo \\(4\\);\r\n$gdb_prompt $" { + gdb_assert $saw_no_line $gdb_test_name + } + } + } + } +} + +# Test running to a breakpoint at an address associated with line 0. +# GDB should not show any line info for the stop location. + +proc_with_prefix test_bkpt {} { + if ![runto_main] { + return -1 + } + + gdb_breakpoint "bar1_label_3" + gdb_continue_to_breakpoint "bar1_label_3" "bar1 \\(\\)" + + gdb_breakpoint "bar2_label_3" + gdb_continue_to_breakpoint "bar2_label_3" "bar2 \\(\\)" +} + +test_next_step_mode_off +test_next_step_mode_on +test_bkpt -- 2.14.5