From patchwork Fri Oct 18 10:45:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 99157 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 290983858C39 for ; Fri, 18 Oct 2024 10:46:24 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 805BD3858D37 for ; Fri, 18 Oct 2024 10:45:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 805BD3858D37 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 805BD3858D37 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729248356; cv=none; b=OhHh0ErBRvJ+3+Wy2ehRpyY25VEdGJ4/xjFu7Y/xAP4Qaa8W8wyNEWgK2aE8AZdMBu51hjIoJn8FbY95IkbUtlxPlwzAfm5CO8aCF/fftVXoKDMSXUIi3G3QIiTcGo7hIpDyZV7GYiVET6PF/1iIrOmgmrkDmxH4nhCdfqcO9fM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729248356; c=relaxed/simple; bh=LiwCzykb7t/z9zPVESNj4zV8MuMbFE6t+vuTXyHMQdQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=QBA4R5FfhLdgBrKvE5X1Rkuo36Np1YpOyHw/9H8Q2A504m+LjzQz7KxhH0qWyGWhWtRXYOtXFEyMt2dhmkv2Ylctx09RxFhzFS60BTcDTZ2Q//YYtmyhHWdHb7a+vRAw6wRsavaZ0xpg3EdZff1pobrW64VagyZ0B3JjjR3B6RE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729248350; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=LnmAR21C7pDxsekzlw0jAj7ntuurHnovpahU+Sx/gBg=; b=Ee7kbXtnlJg0nnQkkQmCYu5WohcZ6xSkdiNwGUPIsDUnf6bezWWcSPF6CTs1kwa2UBTDub ydhXivEZqd7rA+YdKIQ0AbK/ijk6bdg/jS/A3BAp45a7+bGbJnBxGnhQ9kkqqtGAKSEcyI 3LfFe8rNAfnY+1b4PQ006kZBgdUrws8= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-2-Sg4bVRC9OJyCNcaAa-spHQ-1; Fri, 18 Oct 2024 06:45:38 -0400 X-MC-Unique: Sg4bVRC9OJyCNcaAa-spHQ-1 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-43057c9b32bso13947015e9.0 for ; Fri, 18 Oct 2024 03:45:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729248333; x=1729853133; 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=LnmAR21C7pDxsekzlw0jAj7ntuurHnovpahU+Sx/gBg=; b=FnNMPF2X2yGZZiAAE65GnovmhBWyTCrjVP8xjAIRKVGwsTMQhn+vy+4sEV7YSutsdF L5eEET13k8/QD9vaQ1C+QzJA0VF/syg3mhshSWI4yiQ7UVnGyxEqyCgtokiyuQx1v2Gi 49cTB3/DwktBe+BGmGhlEpsuvcDif/GZHDREtAyBkBZ5J27XmoTbqWoBUjtCNjFUc23Q eZdPDO1UkTe5xDsrMTYno0h8YJ2KtVL3zHEcT34vnT11Kcn2/Y5IHarlCLeNP4TMS7Y0 2yuRKtQPXhCAKJeSQ91wyKXbfDH+tNKAadNIVsHTrRHy/mxA0Af48Ur31+qYOUmAB+wd DBsg== X-Gm-Message-State: AOJu0Yy7pfUlQiHSWAmR550YQGpwlhoMqF9FDwGfC1Nyd7agIighO6P2 qidVyFnNKz1/zgqIdijCGONOsjrPOFtS5DgM973hkHC/JSuUWcbKWQx94QizFHIi1ZFiYBKq354 xaiANybxeSnNucYW7axhhcNPr5+/In+0COrsUQwgwUFdea+gViVrZtKJXbLxb/rfq8InXqUyN2H IYzdcQ9nqNJby9MigVBmWeEBUBf+d9EtGokNNOqdUuuaw= X-Received: by 2002:a05:600c:4f92:b0:430:699b:7d22 with SMTP id 5b1f17b1804b1-4316168807cmr14747425e9.26.1729248332580; Fri, 18 Oct 2024 03:45:32 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGOC9Lji1pzmEZUn+kYrY7lOCLyszJwhTG4NaRR9bjFVerpKWcPUmx8agqtwocXDu/PwKSElg== X-Received: by 2002:a05:600c:4f92:b0:430:699b:7d22 with SMTP id 5b1f17b1804b1-4316168807cmr14747115e9.26.1729248331828; Fri, 18 Oct 2024 03:45:31 -0700 (PDT) Received: from localhost ([195.213.152.26]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-43160695cb3sm23781625e9.25.2024.10.18.03.45.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 18 Oct 2024 03:45:31 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Bernd Edlinger , Andrew Burgess Subject: [PATCH] gdb: stepping between inline functions with multiple ranges Date: Fri, 18 Oct 2024 11:45:28 +0100 Message-Id: <0e7a6b004bf671a4ccf4d63935a37aa0ee3769dd.1729248287.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org From: Bernd Edlinger I (Andrew) have split this small change from a larger patch which was posted here: https://inbox.sourceware.org/gdb-patches/AS1PR01MB9465608EBD5D62642C51C428E4922@AS1PR01MB9465.eurprd01.prod.exchangelabs.com And I have written the stand alone test for this issue. The original patch included this paragraph to explain this change (I've fixed one typo in this text replacing 'program' with 'function'): ... it may happen that the infrun machinery steps from one inline range to another inline range of the same inline function. That can look like jumping back and forth from the calling function to the inline function, while really the inline function just jumps from a hot to a cold section of the code, i.e. error handling. The important thing that happens here is that both the outer function and the inline function must both have multiple ranges. When the inferior is within the inline function and moves from one range to another it is critical that the address we stop at is the start of a range in both the outer function and the inline function. The diagram below represents how the functions are split and aligned: (A) (B) bar: |------------| |---| foo: |------------------| |--------| The inferior is stepping through 'bar' and eventually reaches point (A) at which point control passes to point (B). Currently, when the inferior stops, GDB notices that both 'foo' and 'bar' start at address (B), and so GDB uses the inline frame mechanism to skip 'bar' and tells the user that the inferior is in 'foo'. However, as we were in 'bar' before the step then it makes sense that we should be in 'bar' after the step, and this is what the patch does. There are two tests using the DWARF assembler, the first checks the above situation and ensures that GDB reports 'bar' after the step. The second test is similar, but after the step we enter a new range where a different inline function starts, something like this: (A) (B) bar: |------------| baz: |---| foo: |------------------| |--------| In this case as we step at (A) and land at (B) we leave 'bar' and expect to stop in 'foo', GDB shouldn't automatically enter 'baz' as that is a completely different inline function. And this is, indeed, what we see. Co-Authored-By: Andrew Burgess --- gdb/infrun.c | 28 +++ ...-step-between-different-inline-functions.c | 105 +++++++++ ...tep-between-different-inline-functions.exp | 215 ++++++++++++++++++ .../dw2-step-between-inline-func-blocks.c | 100 ++++++++ .../dw2-step-between-inline-func-blocks.exp | 196 ++++++++++++++++ 5 files changed, 644 insertions(+) create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.exp create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.c create mode 100644 gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.exp base-commit: efa903e0bc1db2fc13386f7fe150d9ff88ebe156 diff --git a/gdb/infrun.c b/gdb/infrun.c index 4ca15450afe..623c2d336a1 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -8114,6 +8114,34 @@ process_event_stop_test (struct execution_control_state *ecs) return; } + /* Handle the case when subroutines have multiple ranges. When we step + from one part to the next part of the same subroutine, all subroutine + levels are skipped again which begin here. Compensate for this by + removing all skipped subroutines, which were already executing from + the user's perspective. */ + + if (get_stack_frame_id (frame) + == ecs->event_thread->control.step_stack_frame_id + && inline_skipped_frames (ecs->event_thread) > 0 + && ecs->event_thread->control.step_frame_id.artificial_depth > 0 + && ecs->event_thread->control.step_frame_id.code_addr_p) + { + int depth = 0; + const struct block *prev + = block_for_pc (ecs->event_thread->control.step_frame_id.code_addr); + const struct block *curr = block_for_pc (ecs->event_thread->stop_pc ()); + while (curr != nullptr && !curr->contains (prev)) + { + if (curr->inlined_p ()) + depth++; + else if (curr->function () != nullptr) + break; + curr = curr->superblock (); + } + while (inline_skipped_frames (ecs->event_thread) > depth) + step_into_inline_frame (ecs->event_thread); + } + /* Look for "calls" to inlined functions, part one. If the inline frame machinery detected some skipped call sites, we have entered a new inline function. */ diff --git a/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.c b/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.c new file mode 100644 index 00000000000..5e4fe47b28e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.c @@ -0,0 +1,105 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +/* Used to insert labels within function foo. */ +#define LABEL(N) asm ("foo_label_" #N ": .globl foo_label_" #N) + +volatile int global_var = 0; + +/* The contents of this '#if 0' block exist so the generated debug can + point to these as the source lines. */ +#if 0 + +void +bar (void) /* bar decl line */ +{ + /* bar line 1 */ + /* bar line 2 */ +} + +void +baz (void) /* baz decl line */ +{ + /* baz line 1 */ + /* baz line 2 */ +} + +void +foo (void) /* foo decl line */ +{ + /* foo line 1 */ + /* foo line 2 */ + /* foo line 3 */ + /* foo line 4 */ +} + +#endif + +extern void *foo_label_6 (void); + +void +foo (void) +{ + /* This label is used to find the start of 'foo' when generating the + debug information. */ + asm ("foo_label: .globl foo_label"); + ++global_var; + + LABEL (1); + ++global_var; + + LABEL (2); + ++global_var; + + LABEL (3); + ++global_var; + + /* This goto will always trigger, but we make it conditional so that the + compiler doesn't optimise out the code between the goto and the + destination. + + Also 'goto *ADDR' is a GCC extension, but it is critical that the + destination address be a global label so that we can generate DWARF + that has ranges that start exactly at the destination address. */ + if (global_var > 0) + goto *(&foo_label_6); + + LABEL (4); + ++global_var; + + LABEL (5); + ++global_var; + + LABEL (6); + ++global_var; + + LABEL (7); + ++global_var; + + LABEL (8); + ++global_var; + + LABEL (9); + ++global_var; +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + foo (); +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.exp b/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.exp new file mode 100644 index 00000000000..7e7c4adcb5c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-step-between-different-inline-functions.exp @@ -0,0 +1,215 @@ +# Copyright 2024 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 . + +# This test is a follow on from the test: +# gdb.dwarf2/dw2-step-between-inline-func-blocks.exp +# It is worth reading that test before looking at this one. +# +# This test creates a function 'foo' that contains two inline +# functions 'bar' and 'baz'. The function 'foo' is split into two +# ranges. 'bar' is inline in the first range and 'baz' is inline in +# the second range, but critically, 'baz' starts at the very start of +# the second range. The functions are laid out like this: +# +# (A) (B) +# bar: |------------| +# baz: |---| +# foo: |------------------| |--------| +# +# When the inferior reaches address (A) we jump directly to point (B). +# At that point we expect GDB to tell the user that the inferior is in +# 'foo'. GDB should not automatically enter 'baz'. +# +# This tests exists because in +# dw2-step-between-inline-func-blocks.exp, the second range is another +# part of 'bar' and the jump from (A) to (B) is from one part of 'bar' +# to the next, in this case GDB does automatically reenter 'bar'. +# This test checks that GDB isn't too keen to reenter inline +# functions. + +load_lib dwarf.exp + +require dwarf2_support + +# The source program use 'goto *ADDR' which is a GCC extension. +require is_c_compiler_gcc + +standard_testfile + +# This compiles the source file and starts and stops GDB, so run it +# before calling prepare_for_testing otherwise GDB will have exited. +get_func_info foo + +# Make some DWARF for the test. +set asm_file [standard_output_file "$::testfile-dw.S"] +Dwarf::assemble $asm_file { + global srcfile + + # Create local varibles like BAR_SRC_* containing the line number + # for the souce lines of 'foo' and 'bar' and 'baz'. These will be + # referenced in the generated DWARF. + for { set i 1 } { $i <= 2 } { incr i } { + set bar_src_$i [gdb_get_line_number "bar line $i"] + set baz_src_$i [gdb_get_line_number "baz line $i"] + } + for { set i 1 } { $i <= 4 } { incr i } { + set foo_src_$i [gdb_get_line_number "foo line $i"] + } + + # More line numbers needed for the generated DWARF. + set foo_decl_line [gdb_get_line_number "foo decl line"] + set bar_decl_line [gdb_get_line_number "bar decl line"] + set baz_decl_line [gdb_get_line_number "baz decl line"] + + # Labels used to link parts of the DWARF together. + declare_labels lines_table bar_label baz_label + declare_labels ranges_label_bar ranges_label_baz ranges_label_foo + + cu { version 4 } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + bar_label: subprogram { + {external 1 flag} + {name bar} + {decl_file 1 data1} + {decl_line $bar_decl_line data1} + {decl_column 1 data1} + {inline 3 data1} + } + baz_label: subprogram { + {external 1 flag} + {name baz} + {decl_file 1 data1} + {decl_line $baz_decl_line data1} + {decl_column 1 data1} + {inline 3 data1} + } + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {ranges ${ranges_label_foo} DW_FORM_sec_offset} + {external 1 flag} + } { + inlined_subroutine { + {abstract_origin %$bar_label} + {call_file 1 data1} + {call_line $foo_src_2 data1} + {ranges ${ranges_label_bar} DW_FORM_sec_offset} + } + inlined_subroutine { + {abstract_origin %$baz_label} + {call_file 1 data1} + {call_line $foo_src_3 data1} + {ranges ${ranges_label_baz} DW_FORM_sec_offset} + } + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + program { + DW_LNE_set_address "foo_label" + line $foo_src_1 + DW_LNS_copy + DW_LNE_set_address "foo_label_1" + line $foo_src_2 + DW_LNS_copy + DW_LNE_set_address "foo_label_2" + line $bar_src_1 + DW_LNS_copy + DW_LNE_set_address "foo_label_3" + line $bar_src_2 + DW_LNS_copy + DW_LNE_set_address "foo_label_4" + DW_LNE_end_sequence + + DW_LNE_set_address "foo_label_6" + line $baz_src_1 + DW_LNS_copy + DW_LNE_set_address "foo_label_7" + line $baz_src_2 + DW_LNS_copy + DW_LNS_negate_stmt + DW_LNE_set_address "foo_label_7" + line $foo_src_3 + DW_LNS_copy + DW_LNS_negate_stmt + DW_LNE_set_address "foo_label_8" + line $foo_src_4 + DW_LNS_copy + DW_LNE_set_address $::foo_end + DW_LNE_end_sequence + } + } + + ranges { } { + ranges_label_bar: sequence { + range foo_label_2 foo_label_4 + } + ranges_label_baz: sequence { + range foo_label_6 foo_label_7 + } + ranges_label_foo: sequence { + range foo_label_1 foo_label_4 + range foo_label_6 foo_label_9 + } + } +} + +if {[prepare_for_testing "failed to prepare" "${::testfile}" \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint bar +gdb_continue_to_breakpoint "continue to bar line 1" \ + ".*bar line 1\[^\r\n\]+" + +gdb_test "step" ".*bar line 2\[^\r\n\]+" \ + "step to bar line 2" + +# This is the interesting one. This step will take us over the goto +# and place the inferior at the start of the second range of 'foo' and +# at the start of 'baz'. +# +# As we started the step in 'bar', GDB should not reenter 'baz'. +gdb_test "step" ".*foo line 3\[^\r\n\]+" \ + "step to foo line 3" + +# The next step should allow the inferior to enter 'baz'. +gdb_test "step" ".*baz line 1\[^\r\n\]+" \ + "step to baz line 1" + +# The remaining steps take the inferior through 'baz' and back into +# 'foo'. +gdb_test "step" ".*baz line 2\[^\r\n\]+" \ + "step to baz line 2" + +gdb_test "step" ".*foo line 4\[^\r\n\]+" \ + "step out of bar to foo line 4" diff --git a/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.c b/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.c new file mode 100644 index 00000000000..7fdf1bb2c86 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.c @@ -0,0 +1,100 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +/* Used to insert labels within function foo. */ +#define LABEL(N) asm ("foo_label_" #N ": .globl foo_label_" #N) + +volatile int global_var = 0; + +/* The contents of this '#if 0' block exist so the generated debug can + point to these as the source lines. */ +#if 0 + +void +bar (void) /* bar decl line */ +{ + /* bar line 1 */ + /* bar line 2 */ + /* bar line 3 */ + /* bar line 4 */ +} + +void +foo (void) /* foo decl line */ +{ + /* foo line 1 */ + /* foo line 2 */ + /* foo line 3 */ + /* foo line 4 */ +} + +#endif + +extern void *foo_label_6 (void); + +void +foo (void) +{ + /* This label is used to find the start of 'foo' when generating the + debug information. */ + asm ("foo_label: .globl foo_label"); + ++global_var; + + LABEL (1); + ++global_var; + + LABEL (2); + ++global_var; + + LABEL (3); + ++global_var; + + /* This goto will always trigger, but we make it conditional so that the + compiler doesn't optimise out the code between the goto and the + destination. + + Also 'goto *ADDR' is a GCC extension, but it is critical that the + destination address be a global label so that we can generate DWARF + that has ranges that start exactly at the destination address. */ + if (global_var > 0) + goto *(&foo_label_6); + + LABEL (4); + ++global_var; + + LABEL (5); + ++global_var; + + LABEL (6); + ++global_var; + + LABEL (7); + ++global_var; + + LABEL (8); + ++global_var; + + LABEL (9); + ++global_var; +} + +int +main (void) +{ + asm ("main_label: .globl main_label"); + foo (); +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.exp b/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.exp new file mode 100644 index 00000000000..9128f87c93d --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-step-between-inline-func-blocks.exp @@ -0,0 +1,196 @@ +# Copyright 2024 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 . + +# This test creates a function 'foo' that contains an inline function +# 'bar'. Both 'foo' and 'bar' are split into two regions. The layout +# in memory looks something like this: +# +# +# +# bar: |------------| |---| +# foo: |------------------| |--------| +# +# We see things like this in "real" code where the parts after the gap +# (part-b) are cold paths within the function that have been moved +# aside. +# +# However, in this test program we use 'goto' to jump directly from +# the first part of the function (part-a) to the second part (part-b). +# +# At the time we hit the goto we are inside 'bar'. After the goto we +# expect to still be in bar. At one point GDB would get this wrong +# and after the jump we would revert back to 'foo'. +# +# This bug will only trigger if both 'foo' and 'bar' are split, and +# both 'foo' and 'bar' must start at the same address for part-b. + +load_lib dwarf.exp + +require dwarf2_support + +# The source program use 'goto *ADDR' which is a GCC extension. +require is_c_compiler_gcc + +standard_testfile + +# This compiles the source file and starts and stops GDB, so run it +# before calling prepare_for_testing otherwise GDB will have exited. +get_func_info foo + +# Make some DWARF for the test. +set asm_file [standard_output_file "$::testfile-dw.S"] +Dwarf::assemble $asm_file { + global srcfile + + # Create local varibles BAR_SRC_* containing the line number for + # the four souce lines of 'foo' and 'bar'. These will be + # referenced in the generated DWARF. + for { set i 1 } { $i <= 4 } { incr i } { + set bar_src_$i [gdb_get_line_number "bar line $i"] + set foo_src_$i [gdb_get_line_number "foo line $i"] + } + + # More line numbers needed for the generated DWARF. + set foo_decl_line [gdb_get_line_number "foo decl line"] + set bar_decl_line [gdb_get_line_number "bar decl line"] + + # Labels used to link parts of the DWARF together. + declare_labels lines_table bar_label ranges_label_bar ranges_label_foo + + cu { version 4 } { + compile_unit { + {producer "gcc"} + {language @DW_LANG_C} + {name ${srcfile}} + {comp_dir /tmp} + {stmt_list $lines_table DW_FORM_sec_offset} + {low_pc 0 addr} + } { + bar_label: subprogram { + {external 1 flag} + {name bar} + {decl_file 1 data1} + {decl_line $bar_decl_line data1} + {decl_column 1 data1} + {inline 3 data1} + } + subprogram { + {name foo} + {decl_file 1 data1} + {decl_line $foo_decl_line data1} + {decl_column 1 data1} + {ranges ${ranges_label_foo} DW_FORM_sec_offset} + {external 1 flag} + } { + inlined_subroutine { + {abstract_origin %$bar_label} + {call_file 1 data1} + {call_line $foo_src_3 data1} + {ranges ${ranges_label_bar} DW_FORM_sec_offset} + } + } + } + } + + lines {version 2} lines_table { + include_dir "$::srcdir/$::subdir" + file_name "$srcfile" 1 + program { + DW_LNE_set_address "foo_label" + line $foo_src_1 + DW_LNS_copy + DW_LNE_set_address "foo_label_1" + line $foo_src_2 + DW_LNS_copy + DW_LNE_set_address "foo_label_2" + line $foo_src_3 + DW_LNS_copy + DW_LNE_set_address "foo_label_2" + line $bar_src_1 + DW_LNS_copy + DW_LNE_set_address "foo_label_3" + line $bar_src_2 + DW_LNS_copy + + DW_LNE_set_address "foo_label_4" + DW_LNE_end_sequence + + DW_LNE_set_address "foo_label_6" + line $bar_src_3 + DW_LNS_copy + DW_LNE_set_address "foo_label_7" + line $bar_src_4 + DW_LNS_copy + + DW_LNS_negate_stmt + DW_LNE_set_address "foo_label_7" + line $foo_src_3 + DW_LNS_copy + + DW_LNS_negate_stmt + DW_LNE_set_address "foo_label_8" + line $foo_src_4 + DW_LNS_copy + + DW_LNE_set_address $::foo_end + DW_LNE_end_sequence + } + } + + ranges { } { + ranges_label_bar: sequence { + range foo_label_2 foo_label_4 + range foo_label_6 foo_label_8 + } + ranges_label_foo: sequence { + range foo_label_1 foo_label_4 + range foo_label_6 foo_label_9 + } + } +} + +if {[prepare_for_testing "failed to prepare" "${::testfile}" \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint bar +gdb_continue_to_breakpoint "continue to bar line 1" \ + ".*bar line 1\[^\r\n\]+" + +gdb_test "step" ".*bar line 2\[^\r\n\]+" \ + "step to bar line 2" + +# This is the interesting one. This step will take us over the goto +# and into the second range of both 'foo' and 'bar'. As we started +# the step in 'bar' GDB should reselect 'bar' after the step. +# +# If this goes wrong the GDB will claim we are at foo_line_3, which is +# the DW_AT_call_line for 'bar'. +gdb_test "step" ".*bar line 3\[^\r\n\]+" \ + "step to bar line 3" + +# These following steps should be straight forward, but lets just +# check we can step out of 'bar' and back to 'foo', there shouldn't be +# anything tricky going on here though. +gdb_test "step" ".*bar line 4\[^\r\n\]+" \ + "step to bar line 4" + +gdb_test "step" ".*foo line 4\[^\r\n\]+" \ + "step out of bar to foo line 4"