From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) by sourceware.org (Postfix) with ESMTPS id 351153939C21 for ; Wed, 13 Jan 2021 01:16:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 351153939C21 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-wr1-f50.google.com with SMTP id r7so357256wrc.5 for ; Tue, 12 Jan 2021 17:16:15 -0800 (PST) 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:mime-version:content-transfer-encoding; bh=Pxc9bhwU/16OqNeROcwHIJ/1bcETIgYiJErlytyeYAU=; b=n4TmFSvHAsKnAet/a1+0tJdR8nPPU6c6Zu4TMHxfdhf2TgknaFSGBjXZwnnGe+WUfJ 7a0kjcXcncrwfCFRc4Mbf6iGX35u/3ZVnZGAQyki72maVoWC+INP3UKPvb8vUkNxC8Hi cQHLkBOJNyqVN+5tFTKzcBnWAlXZRgoqqpjkq17bQ7mUWX/2NmfRLBZv2k8m6sPRQ6u7 awqNdLDkNKkU9aLBFDORU2c+WIRG08ZdXSm46eANbEgNPmY/ExqCtNvDKuUz1rWwudMp qPaX0mww/Uv5COf4Ipp88HPUPx0bocglWh6htxrFS+biO5Y3kX0VIAub0mQLygJRSjJI 8jJQ== X-Gm-Message-State: AOAM531GkontpzDSdTtPx7CacgV6hKJFeq/AGXkWf5hvOJo/dfftxwS4 CfOKkrT6nl28QKtV9wLywvY3jontABB5kg== X-Google-Smtp-Source: ABdhPJyIu+WfrkYmcu/c5gjmEz+gdQNCshEmVAkrxMApKxrl3Qj4t6i3qn+hxZx1cGwvtcTZoXphXQ== X-Received: by 2002:adf:b343:: with SMTP id k3mr1359727wrd.202.1610500573300; Tue, 12 Jan 2021 17:16:13 -0800 (PST) Received: from localhost ([2001:8a0:f91f:e900:1d90:d745:3c32:c159]) by smtp.gmail.com with ESMTPSA id b19sm386630wmj.37.2021.01.12.17.16.11 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 12 Jan 2021 17:16:12 -0800 (PST) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 12/12] Testcase for detaching while stepping over breakpoint Date: Wed, 13 Jan 2021 01:15:43 +0000 Message-Id: <20210113011543.2047449-13-pedro@palves.net> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20210113011543.2047449-1-pedro@palves.net> References: <20210113011543.2047449-1-pedro@palves.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-10.1 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: Wed, 13 Jan 2021 01:16:17 -0000 This adds a testcase that exercises detaching while GDB is stepping over a breakpoint, in all combinations of: - maint target non-stop off/on - set non-stop on/off - displaced stepping on/off This exercises the bugs fixed in the previous 8 patches. gdb/testsuite/ChangeLog: * gdb.threads/detach-step-over.c: New file. * gdb.threads/detach-step-over.exp: New file. --- gdb/testsuite/gdb.threads/detach-step-over.c | 112 +++++++ .../gdb.threads/detach-step-over.exp | 290 ++++++++++++++++++ 2 files changed, 402 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/detach-step-over.c create mode 100644 gdb/testsuite/gdb.threads/detach-step-over.exp diff --git a/gdb/testsuite/gdb.threads/detach-step-over.c b/gdb/testsuite/gdb.threads/detach-step-over.c new file mode 100644 index 00000000000..13db9f60b04 --- /dev/null +++ b/gdb/testsuite/gdb.threads/detach-step-over.c @@ -0,0 +1,112 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2021 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 . */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +/* Number of threads we'll create. */ +int n_threads = 10; + +int mypid; + +static void +setup_done (void) +{ +} + +/* Entry point for threads. Loops forever. */ + +void * +thread_func (void *arg) +{ + /* Avoid setting the breakpoint at an instruction that wouldn't + require a fixup phase, like a branch/jump. In such a case, even + if GDB manages to detach the inferior with an incomplete + displaced step, GDB inferior may still not crash. A breakpoint + at a line that increments a variable is good bet that we end up + setting a breakpoint at an instruction that will require a fixup + phase to move the PC from the scratch pad to the instruction + after the breakpoint. */ + volatile unsigned counter = 0; + + while (1) + { + counter++; /* Set breakpoint here. */ + counter++; + counter++; + } + + return NULL; +} + +/* Allow for as much timeout as DejaGnu wants, plus a bit of + slack. */ +#define SECONDS (TIMEOUT + 20) + +/* We'll exit after this many seconds. */ +unsigned int seconds_left = SECONDS; + +/* GDB sets this whenever it's about to start a new detach/attach + sequence. We react by resetting the seconds-left counter. */ +volatile int again = 0; + +int +main (int argc, char **argv) +{ + int i; + + signal (SIGUSR1, SIG_IGN); + + mypid = getpid (); + setup_done (); + + if (argc > 1) + n_threads = atoi (argv[1]); + + /* Spawn the test threads. */ + for (i = 0; i < n_threads; ++i) + { + pthread_t child; + int rc; + + rc = pthread_create (&child, NULL, thread_func, NULL); + assert (rc == 0); + } + + /* Exit after a while if GDB is gone/crashes. But wait long enough + for one attach/detach sequence done by the .exp file. */ + while (--seconds_left > 0) + { + sleep (1); + + if (again) + { + /* GDB should be reattaching soon. Restart the timer. */ + again = 0; + seconds_left = SECONDS; + } + } + + printf ("timeout, exiting\n"); + return 0; +} diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp new file mode 100644 index 00000000000..3298c5b61a3 --- /dev/null +++ b/gdb/testsuite/gdb.threads/detach-step-over.exp @@ -0,0 +1,290 @@ +# Copyright 2021 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 detaching from a process that is running and has threads +# constantly hitting a breakpoint and stepping over it, in all +# combinations of: +# +# - maint target non-stop off/on +# - set non-stop on/off +# - displaced stepping on/off +# +# This stresses the edge cases of detaching while a displaced step or +# an in-line step over are in progress. +# +# A fail mode is that the inferior process dies after being detached. +# This can happen because e.g.: +# +# - GDB leaves a breakpoint installed behind, or +# +# - GDB leaves a thread running in the displaced step scratch buffer. +# With no debugger around to run the finish step, the thread runs +# off of the scratch buffer, with undefined results. +# +# To exercise this, the testcase reattaches to the process shortly +# after detaching, ensuring the process is still alive and well. +# +# In addition, since GDB may pause threads of all processes for +# stepping over a breakpoint, it needs to re-resume all threads if it +# detaches from the process that was just stepping over the +# breakpoint. To ensure that, the testcase actually runs a second +# process at the same time as the one that is used to test detaching. +# After the first process is detached, the testcase sends a SIGUSR1 to +# the second process. If threads failed to be resumed, then the +# SIGUSR1 is never reported to the user, resulting in timeout. The +# threads of this second process will also be constantly stepping over +# a breakpoint, which has helped with exposing further corner case +# bugs. + +if {![can_spawn_for_attach]} { + return 0 +} + +standard_testfile + +set bp_lineno [gdb_get_line_number "Set breakpoint here"] + +# The test proper. See description above. +proc test {condition_eval target_non_stop non_stop displaced} { + global binfile srcfile + global gdb_prompt + global decimal + global bp_lineno + global GDBFLAGS + + # Number of threads started by the program. + set n_threads 10 + + save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\"" + append GDBFLAGS " -ex \"set non-stop $non_stop\"" + append GDBFLAGS " -ex \"set displaced $displaced\"" + append GDBFLAGS " -ex \"set schedule-multiple on\"" + clean_restart $binfile + } + + set test_spawn_id [spawn_wait_for_attach $binfile] + set testpid [spawn_id_get_pid $test_spawn_id] + + set any "\[^\r\n\]*" + + gdb_test "add-inferior" "Added inferior 2.*" + gdb_test "inferior 2" "Switching to .*" + + gdb_load $binfile + if ![runto setup_done] then { + fail "can't run to setup_done" + kill_wait_spawned_process $test_spawn_id + return + } + + gdb_test_no_output "set breakpoint condition-evaluation $condition_eval" + + # Get the PID of the test process. + set pid_inf2 "" + gdb_test_multiple "p mypid" "get pid of inferior 2" { + -re " = ($decimal)\r\n$gdb_prompt $" { + set pid_inf2 $expect_out(1,string) + pass $gdb_test_name + } + } + + set attempts 3 + for {set attempt 1} { $attempt <= $attempts } { incr attempt } { + with_test_prefix "iter $attempt" { + gdb_test "inferior 1" "Switching to .*" + + set attached 0 + set eperm 0 + set test "attach" + gdb_test_multiple "attach $testpid" $test { + -re "new threads in iteration" { + # Seen when "set debug libthread_db" is on. + exp_continue + } + -re "is a zombie - the process has already terminated.*$gdb_prompt " { + fail $gdb_test_name + } + -re "Unable to attach: .*$gdb_prompt " { + fail $gdb_test_name + } + -re "Attaching to program.*process $testpid.*$gdb_prompt " { + pass $test + set attached 1 + } + } + + if {!$attached} { + kill_wait_spawned_process $test_spawn_id + return + } + + if {$non_stop} { + # In non-stop, we will see one stop per thread after + # the prompt. + set stops 0 + set tid_re "$::decimal\.$::decimal" + set test "seen all stops" + for {set thread 1} { $thread <= $n_threads } { incr thread } { + if {[gdb_test_multiple "" $test { + -re "Thread ${tid_re} ${any} stopped" { + incr stops + } + }] != 0} { + break + } + } + + # This we haven't seen all stops, then the + # gdb_test_multiple in the loop above will have + # already issued a FAIL. + if {$stops != $n_threads} { + kill_wait_spawned_process $test_spawn_id + return + } + pass $test + } + + # Set threads stepping over a breakpoint continuously. + gdb_test "break $srcfile:$bp_lineno if 0" "Breakpoint.*" \ + "break LOC if 0" + + if {$attempt < $attempts} { + # Kick the time out timer for another round. + gdb_test "print again = 1" " = 1" "reset timer in the inferior" + # Show the time we had left in the logs, in case + # something goes wrong. + gdb_test "print seconds_left" " = .*" + } + + if {$non_stop} { + set cont_cmd "continue -a &" + } else { + set cont_cmd "continue &" + } + + set cont_cmd_re [string_to_regexp $cont_cmd] + gdb_test_multiple $cont_cmd "" { + -re "^$cont_cmd_re\r\nContinuing\.\r\n$gdb_prompt " { + pass $gdb_test_name + } + } + + # Wait a bit, to give time for the threads to hit the + # breakpoint. + sleep 1 + + set running_count 0 + set interrupted 0 + gdb_test_multiple "info threads" "all threads running" { + -re "\\(running\\)" { + incr running_count + exp_continue + } + -re "Cannot execute this command while the target is running.*$gdb_prompt $" { + # Testing against a remote server that doesn't do + # non-stop mode. Explicitly interrupt. This + # doesn't test the same code paths in GDB, but + # it's still something. + set interrupted 1 + gdb_test_multiple "interrupt" "" { + -re "$gdb_prompt " { + gdb_test_multiple "" $gdb_test_name { + -re "received signal SIGINT, Interrupt" { + pass $gdb_test_name + } + } + } + } + } + -re "$gdb_prompt $" { + gdb_assert {$running_count == ($n_threads + 1) * 2} $gdb_test_name + } + } + + gdb_test "detach" "Detaching from.*" + + if {!$interrupted} { + # Now test whether inferior 2's thread were really left + # running. Currently an inline step-over stops all + # threads of all processes. If detach aborts such a step + # over, then threads of other inferiors should be + # re-resumed. Test for that by sending a signal to + # inferior 2. + remote_exec target "kill -SIGUSR1 ${pid_inf2}" + + gdb_test_multiple "" "stop with SIGUSR1" { + -re "received signal SIGUSR1" { + pass $gdb_test_name + } + } + } + + delete_breakpoints + } + } + kill_wait_spawned_process $test_spawn_id +} + +# The test program exits after a while, in case GDB crashes. Make it +# wait at least as long as we may wait before declaring a time out +# failure. +set options { "additional_flags=-DTIMEOUT=$timeout" debug pthreads } + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options] == -1} { + return -1 +} + +if ![runto_main] { + return -1 +} + +# Probe support for "set breakpoint condition-evaluation target". +# This setting influences who steps over the breakpoint, the (remote) +# target (e.g. gdbserver) or gdb, thus exposing issues on either the +# target or gdb. +set supports_condition_eval_target 1 +set cmd "set breakpoint condition-evaluation target" +gdb_test_multiple $cmd "probe condition-evaluation target support" { + -re "warning: Target does not support breakpoint condition evaluation.\r\nUsing host evaluation mode instead.\r\n$gdb_prompt $" { + # Target doesn't support breakpoint condition evaluation on + # its side. + set supports_condition_eval_target 0 + pass $gdb_test_name + } + -re "^$cmd\r\n$gdb_prompt $" { + pass $gdb_test_name + } +} + +foreach_with_prefix breakpoint-condition-evaluation {"host" "target"} { + if {!$supports_condition_eval_target && ${breakpoint-condition-evaluation} == "target"} { + continue + } + + foreach_with_prefix target-non-stop {"off" "on"} { + foreach_with_prefix non-stop {"off" "on"} { + if {${non-stop} && !${target-non-stop}} { + # "set non-stop" overrides "maint set + # target-non-stop", no use testing this combination. + continue + } + + foreach_with_prefix displaced {"off" "auto"} { + test ${breakpoint-condition-evaluation} ${target-non-stop} ${non-stop} ${displaced} + } + } + } +} -- 2.26.2