From patchwork Thu Oct 17 22:50:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 35117 Received: (qmail 108681 invoked by alias); 17 Oct 2019 22:50:55 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 107998 invoked by uid 89); 17 Oct 2019 22:50:50 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=upfront X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 17 Oct 2019 22:50:46 +0000 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 2637C2A09D2 for ; Thu, 17 Oct 2019 22:50:45 +0000 (UTC) Received: from localhost.localdomain (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id A975F1001B33 for ; Thu, 17 Oct 2019 22:50:44 +0000 (UTC) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 19/24] Add multi-target tests Date: Thu, 17 Oct 2019 23:50:21 +0100 Message-Id: <20191017225026.30496-20-palves@redhat.com> In-Reply-To: <20191017225026.30496-1-palves@redhat.com> References: <20191017225026.30496-1-palves@redhat.com> This adds a testcase exercising multi-target features. It spawns 6 inferiors, like this: inferior 1 -> native inferior 2 -> extended-remote 1 inferior 3 -> core inferior 4 -> native inferior 5 -> extended-remote 2 inferior 6 -> core and then tests various details, including: - running to breakpoints - interrupting with Ctrl-C and "interrupt -a" - "next" bouncing between two breakpoints in two threads running in different targets. - since we have cores and live inferiors mixed in the same session, this makes sure that gdb doesn't try to remove a core dump's threads. - all-stop and non-stop modes. This testcase caught a _lot_ of bugs in development. gdb/testsuite/ChangeLog: yyyy-mm-dd Pedro Alves * gdb.multi/multi-target.c: New file. * gdb.multi/multi-target.exp: New file. * lib/gdbserver-support.exp (gdb_target_cmd): Handle "Non-stop mode requested, but remote does not support non-stop". --- gdb/testsuite/gdb.multi/multi-target.c | 100 +++++++++ gdb/testsuite/gdb.multi/multi-target.exp | 361 +++++++++++++++++++++++++++++++ gdb/testsuite/lib/gdbserver-support.exp | 4 + 3 files changed, 465 insertions(+) create mode 100644 gdb/testsuite/gdb.multi/multi-target.c create mode 100644 gdb/testsuite/gdb.multi/multi-target.exp diff --git a/gdb/testsuite/gdb.multi/multi-target.c b/gdb/testsuite/gdb.multi/multi-target.c new file mode 100644 index 0000000000..856226e6b9 --- /dev/null +++ b/gdb/testsuite/gdb.multi/multi-target.c @@ -0,0 +1,100 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2017-2019 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 . */ + +#include +#include +#include +#include +#include +#include + +#define NUM_THREADS 1 + +static pthread_barrier_t barrier; + +static void * +thread_start (void *arg) +{ + pthread_barrier_wait (&barrier); + + while (1) + sleep (1); + return NULL; +} + +static void +all_started (void) +{ +} + +int wait_for_gdb; + +static void +function1 (void) +{ + while (wait_for_gdb) + sleep (1); +} + +static void +function2 (void) +{ + while (wait_for_gdb) + sleep (1); +} + +static void +function3 (void) +{ +} + +static void +function4 (void) +{ +} + +static void +function5 (void) +{ +} + +int +main (int argc, char ** argv) +{ + pthread_t thread; + int len; + + alarm (360); + + pthread_barrier_init (&barrier, NULL, NUM_THREADS + 1); + pthread_create (&thread, NULL, thread_start, NULL); + + pthread_barrier_wait (&barrier); + all_started (); + + while (1) + { + function1 (); /* set break 1 here */ + function2 (); /* set break 2 here */ + function3 (); + function4 (); + function5 (); + sleep (1); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.multi/multi-target.exp b/gdb/testsuite/gdb.multi/multi-target.exp new file mode 100644 index 0000000000..3b71e7446b --- /dev/null +++ b/gdb/testsuite/gdb.multi/multi-target.exp @@ -0,0 +1,361 @@ +# Copyright 2017-2019 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 multi-target features. + +load_lib gdbserver-support.exp + +standard_testfile + +# The plain remote target can't do multiple inferiors. +if {[target_info gdb_protocol] != ""} { + return +} + +if { [prepare_for_testing "failed to prepare" ${binfile} "${srcfile}" \ + {debug pthreads}] } { + return +} + +proc connect_target_extended_remote {binfile} { + set res [gdbserver_start "--multi" ""] + set gdbserver_gdbport [lindex $res 1] + return [gdb_target_cmd "extended-remote" $gdbserver_gdbport] +} + +# Add and start inferior number NUM. Returns true on success, false +# otherwise. +proc add_inferior {num target binfile {gcorefile ""}} { + # Start another inferior. + gdb_test "add-inferior -no-connection" "Added inferior $num" \ + "add empty inferior $num" + gdb_test "inferior $num" "Switching to inferior $num.*" \ + "switch to inferior $num" + gdb_test "file ${binfile}" ".*" "load file in inferior $num" + gdb_test_no_output "set remote exec-file ${binfile}" \ + "set remote-exec file in inferior $num" + + if {$target == "core"} { + gdb_test "core $gcorefile" "Core was generated by.*" \ + "core [file tail $gcorefile]" + return 1 + } + + if {$target == "extended-remote"} { + if {[connect_target_extended_remote $binfile]} { + return 0 + } + } + if ![runto "all_started"] then { + return 0 + } + delete_breakpoints + + return 1 +} + +proc prepare_core {} { + global gcorefile gcore_created + global binfile + + clean_restart ${binfile} + + if ![runto all_started] then { + return -1 + } + + global testfile + set gcorefile [standard_output_file $testfile.gcore] + set gcore_created [gdb_gcore_cmd $gcorefile "save a core file"] +} + +proc next_live_inferior {inf} { + incr inf + if {$inf == 3} { + # 3 is a core. + return 4 + } + if {$inf > 5} { + # 6 is a core. + return 1 + } + + return $inf +} + +# Return true on success, false otherwise. + +proc setup {non-stop} { + global gcorefile gcore_created + global binfile + + clean_restart ${binfile} + + # multi-target depends on target running in non-stop mode. Force + # it on for remote targets, until this is the default. + gdb_test_no_output "maint set target-non-stop on" + + gdb_test_no_output "set non-stop ${non-stop}" + + if ![runto all_started] then { + return 0 + } + + delete_breakpoints + + # inferior 1 -> native + # inferior 2 -> extended-remote + # inferior 3 -> core + # inferior 4 -> native + # inferior 5 -> extended-remote + # inferior 6 -> core + if {![add_inferior 2 "extended-remote" $binfile]} { + return 0 + } + if {![add_inferior 3 "core" $binfile $gcorefile]} { + return 0 + } + if {![add_inferior 4 "native" $binfile]} { + return 0 + } + if {![add_inferior 5 "extended-remote" $binfile]} { + return 0 + } + if {![add_inferior 6 "core" $binfile $gcorefile]} { + return 0 + } + + # For debugging. + gdb_test "info inferiors" ".*" + gdb_test "info threads" ".*" + + # Make "continue" resume all inferiors. + if {${non-stop} == "off"} { + gdb_test_no_output "set schedule-multiple on" + } + + return 1 +} + +# Test "continue" to breakpoints in different targets. In non-stop +# mode, also tests "interrupt -a". +proc test_continue {non-stop} { + if {![setup ${non-stop}]} { + untested "setup failed" + return + } + + proc set_break {inf} { + gdb_test "break function${inf} thread ${inf}.1" \ + "Breakpoint .* function${inf}\\..*" + } + + # Select inferior INF, and then run to a breakpoint on inferior + # INF+1. + proc test_continue_inf {inf} { + upvar 1 non-stop non-stop + + global gdb_prompt + delete_breakpoints + + set next_inf [next_live_inferior $inf] + + gdb_test "inferior $inf" "Switching to inferior $inf.*" + set_break $next_inf + + if {${non-stop} == "off"} { + gdb_test "continue" "hit Breakpoint .* function${next_inf}.*" + } else { + set msg "continue" + gdb_test_multiple "continue -a&" $msg { + -re "Continuing.*$gdb_prompt " { + pass $msg + } + } + + set msg "hit bp" + gdb_test_multiple "" $msg { + -re "hit Breakpoint .* function${next_inf}" { + pass $msg + } + } + + set msg "stop all threads" + gdb_test_multiple "interrupt -a" $msg { + -re "$gdb_prompt " { + for {set i 0} {$i < 7} {incr i} { + set ok 0 + gdb_test_multiple "" $msg { + -re "Thread\[^\r\n\]*stopped\\." { + set ok 1 + } + } + if {!$ok} { + break + } + } + gdb_assert $ok $msg + } + } + } + } + + for {set i 1} {$i <= 5} {incr i} { + if {$i == 3} { + # This is a core inferior. + continue + } + + with_test_prefix "inf$i" { + test_continue_inf $i + } + } +} + +# Test interrupting multiple targets with Ctrl-C. + +proc test_ctrlc {} { + if {![setup "off"]} { + untested "setup failed" + return + } + + delete_breakpoints + + # Select inferior INF, continue all inferiors, and then Ctrl-C. + proc test_ctrlc_inf {inf} { + global gdb_prompt + + gdb_test "inferior $inf" "Switching to inferior $inf.*" + + set msg "continue" + gdb_test_multiple "continue" $msg { + -re "Continuing" { + pass $msg + } + } + + after 200 { send_gdb "\003" } + + set msg "send_gdb control C" + gdb_test_multiple "" $msg { + -re "received signal SIGINT.*$gdb_prompt $" { + pass $msg + } + } + + set msg "all threads stopped" + gdb_test_multiple "info threads" "$msg" { + -re "\\\(running\\\).*$gdb_prompt $" { + fail $msg + } + -re "$gdb_prompt $" { + pass $msg + } + } + } + + for {set i 1} {$i <= 5} {incr i} { + if {$i == 3} { + # This is a core inferior. + continue + } + + with_test_prefix "inf$i" { + test_ctrlc_inf $i + } + } +} + +# Test "next" bouncing between two breakpoints in two threads running +# in different targets. +proc test_ping_pong_next {} { + global srcfile + + if {![setup "off"]} { + untested "setup failed" + return + } + + # block/unblock inferiors 1 and 2 according to INF1 and INF2. + proc block {inf1 inf2} { + gdb_test "thread apply 1.1 p wait_for_gdb = $inf1" " = $inf1" + gdb_test "thread apply 2.1 p wait_for_gdb = $inf2" " = $inf2" + } + + # We're use inferiors 1 and 2. Make sure they're really connected + # to different targets. + gdb_test "thread apply 1.1 maint print target-stack" \ + "- native.*" + gdb_test "thread apply 2.1 maint print target-stack" \ + "- extended-remote.*" + + # Set two breakpoints, one for each of inferior 1 and 2. Inferior + # 1 is running on the native target, and inferior 2 is running on + # extended-gdbserver. Run to breakpoint 1 to gets things started. + set line1 [gdb_get_line_number "set break 1 here"] + set line2 [gdb_get_line_number "set break 2 here"] + + gdb_test "thread 1.1" "Switching to thread 1.1 .*" + + gdb_test "break $srcfile:$line1 thread 1.1" \ + "Breakpoint .*$srcfile:$line1\\..*" + + gdb_test "continue" "hit Breakpoint .*" + + gdb_test "break $srcfile:$line2 thread 2.1" \ + "Breakpoint .*$srcfile:$line2\\..*" + + # Now block inferior 1 and issue "next". We should stop at the + # breakpoint for inferior 2, given schedlock off. + with_test_prefix "next inf 1" { + block 1 0 + gdb_test "next" "Thread 2.1 .*hit Breakpoint .*$srcfile:$line2.*" + } + + # Now unblock inferior 2 and block inferior 1. "next" should run + # into the breakpoint in inferior 1. + with_test_prefix "next inf 2" { + block 0 1 + gdb_test "next" "Thread 1.1 .*hit Breakpoint .*$srcfile:$line1.*" + } + + # Try nexting inferior 1 again. + with_test_prefix "next inf 1 again" { + block 1 0 + gdb_test "next" "Thread 2.1 .*hit Breakpoint .*$srcfile:$line2.*" + } +} + +# Make a core file with two threads upfront. Several tests load the +# same core file. +prepare_core + +# Some basic "continue" + breakpoints tests. +with_test_prefix "continue" { + foreach_with_prefix non-stop {"off" "on"} { + test_continue ${non-stop} + } +} + +# Some basic all-stop Ctrl-C tests. +with_test_prefix "interrupt" { + test_ctrlc +} + +# Test ping-ponging between two targets with "next". +with_test_prefix "ping-pong" { + test_ping_pong_next +} diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp index 00f46c8264..a5d88ec314 100644 --- a/gdb/testsuite/lib/gdbserver-support.exp +++ b/gdb/testsuite/lib/gdbserver-support.exp @@ -64,6 +64,10 @@ proc gdb_target_cmd_ext { targetname serialport {additional_text ""} } { -re "Couldn't establish connection to remote.*$gdb_prompt $" { verbose "Connection failed" } + -re "Non-stop mode requested, but remote does not support non-stop.*$gdb_prompt $" { + verbose "remote does not support non-stop" + return 1 + } -re "Remote MIPS debugging.*$additional_text.*$gdb_prompt" { verbose "Set target to $targetname" return 0