From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28547 invoked by alias); 2 Oct 2009 22:13:09 -0000 Received: (qmail 28533 invoked by uid 22791); 2 Oct 2009 22:13:06 -0000 X-SWARE-Spam-Status: No, hits=-1.7 required=5.0 tests=AWL,BAYES_00,SPF_HELO_PASS,SPF_PASS,ZMIde_GENERICSPAM1 X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 02 Oct 2009 22:13:00 +0000 Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n92MCwr6001945 for ; Fri, 2 Oct 2009 18:12:58 -0400 Received: from host0.dyn.jankratochvil.net (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id n92MCtEI011708 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Fri, 2 Oct 2009 18:12:57 -0400 Received: from host0.dyn.jankratochvil.net (localhost [127.0.0.1]) by host0.dyn.jankratochvil.net (8.14.3/8.14.3) with ESMTP id n92MCt7d008047 for ; Sat, 3 Oct 2009 00:12:55 +0200 Received: (from jkratoch@localhost) by host0.dyn.jankratochvil.net (8.14.3/8.14.3/Submit) id n92MCspZ008046 for gdb-patches@sourceware.org; Sat, 3 Oct 2009 00:12:54 +0200 Date: Fri, 02 Oct 2009 22:13:00 -0000 From: Jan Kratochvil To: gdb-patches@sourceware.org Subject: [patch 2/4] Fix hw watchpoints: reordered / simultaneously hit [fixup #1] Message-ID: <20091002221254.GA7767@host0.dyn.jankratochvil.net> References: <20090817194612.GC10694@host0.dyn.jankratochvil.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable In-Reply-To: <20090817194612.GC10694@host0.dyn.jankratochvil.net> User-Agent: Mutt/1.5.20 (2009-08-17) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-10/txt/msg00075.txt.bz2 Hi, the patch got extended by this incremental change (fixing a regression): =20=20=20=20 gdb/ Fix assertion failure with `set debug infrun 1'. * infrun.c (handle_inferior_event ): New variable old_chain. Temporarily switch INFERIOR_PTID. * target.h (target_stopped_by_watchpoint): Extend the comment. (target_stopped_data_address): New comment. Rename X as ADDR_P. gdb/testsuite/ * gdb.threads/watchthreads-reorder.exp: Call `set debug infrun 1'. Thanks, Jan ---------------------------------------------------------------------------= --- If you hit two watchpoints at the same time in the default all-stop mode GDB does not cope well with the pending watchpoint hit in non-current LWP. Moreover when you change the watchpoints setup during such stop. The is a bit problem the dependency on debug register number is specific to x86* (on other arches hardware may directly report the address of triggered watchpoint). But it is also dependent on Linux as it depends on LWPs (so i386-nat.c is too general). And it would need to be duplicate in both {i386,amd64}-linux-nat.c. Therefore the code is put in linux-nat.c where = it does not hurt non-x86* arches (it may not even get installed on those). The testcase reproducer is difficult to make reliable (as is now): GDB must find two threads both with a watchpoint hit at the single stop. The used SIGSTOP of GDB is a bit fragile as the inferior being traced must not trigg= er a ptrace event otherwise it deadlocks with its own debugger SIGSTOPped. gdb/ 2009-10-03 Jan Kratochvil Fix reordered watchpoints triggered in other threads during all-stop. * i386-nat.c (i386_stopped_by_watchpoint): Call i386_stopped_data_address through the target vector. * linux-nat.c (save_sigtrap, linux_nat_stopped_data_address): New. (stop_wait_callback, linux_nat_filter_event): Call save_sigtrap. (linux_nat_add_target): Install linux_nat_stopped_data_address. * linux-nat.h (struct lwp_info): New fields watchpoint_hit_set and watchpoint_hit. * infrun.c (handle_inferior_event ): New variable old_chain. Temporarily switch INFERIOR_PTID. * target.h (target_stopped_by_watchpoint): Extend the comment. (target_stopped_data_address): New comment. Rename X as ADDR_P. gdb/testsuite/ 2009-10-03 Jan Kratochvil * watchthreads-reorder.exp, watchthreads-reorder.c: New. --- a/gdb/i386-nat.c +++ b/gdb/i386-nat.c @@ -590,7 +590,7 @@ static int i386_stopped_by_watchpoint (void) { CORE_ADDR addr =3D 0; - return i386_stopped_data_address (¤t_target, &addr); + return target_stopped_data_address (¤t_target, &addr); } =20 /* Return non-zero if the inferior has some break/watchpoint that --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -2839,6 +2839,9 @@ targets should add new threads to the thread list the= mselves in non-stop mode.") { struct regcache *regcache =3D get_thread_regcache (ecs->ptid); struct gdbarch *gdbarch =3D get_regcache_arch (regcache); + struct cleanup *old_chain =3D save_inferior_ptid (); + + inferior_ptid =3D ecs->ptid; =20 fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc =3D %s\n", paddress (gdbarch, stop_pc)); @@ -2855,6 +2858,8 @@ targets should add new threads to the thread list the= mselves in non-stop mode.") fprintf_unfiltered (gdb_stdlog, "infrun: (no data address available)\n"); } + + do_cleanups (old_chain); } =20 if (stepping_past_singlestep_breakpoint) --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2406,6 +2406,45 @@ maybe_clear_ignore_sigint (struct lwp_info *lp) } } =20 +/* Fetch the possible triggered data watchpoint info and store it to LP. + The hardware data watchpoint trigger gets cleared during this fetch. */ + +static void +save_sigtrap (struct lwp_info *lp) +{ + struct cleanup *old_chain; + + /* linux_nat_stopped_data_address is not even installed in this case. */ + if (linux_ops->to_stopped_data_address =3D=3D NULL) + return; + + old_chain =3D save_inferior_ptid (); + inferior_ptid =3D lp->ptid; + + lp->watchpoint_hit_set =3D + linux_ops->to_stopped_data_address (¤t_target, &lp->watchpoint_h= it); + + do_cleanups (old_chain); +} + +/* Wrap target_stopped_data_address where the GNU/Linux native target may = be + directed by the watchpoint/debug register number. Base the reported va= lue + on the triggered data address instead. During inferior stop the assign= ment + of watchpoint/debug registers may change making the register number spe= cific + trigger info stale. */ + +static int +linux_nat_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) +{ + struct lwp_info *lp =3D find_lwp_pid (inferior_ptid); + + gdb_assert (lp !=3D NULL); + + *addr_p =3D lp->watchpoint_hit; + + return lp->watchpoint_hit_set; +} + /* Wait until LP is stopped. */ =20 static int @@ -2457,6 +2496,8 @@ stop_wait_callback (struct lwp_info *lp, void *data) /* Save the trap's siginfo in case we need it later. */ save_siginfo (lp); =20 + save_sigtrap (lp); + /* Now resume this LWP and get the SIGSTOP event. */ errno =3D 0; ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); @@ -2856,9 +2897,13 @@ linux_nat_filter_event (int lwpid, int status, int o= ptions) return NULL; } =20 - /* Save the trap's siginfo in case we need it later. */ if (WIFSTOPPED (status) && WSTOPSIG (status) =3D=3D SIGTRAP) - save_siginfo (lp); + { + /* Save the trap's siginfo in case we need it later. */ + save_siginfo (lp); + + save_sigtrap (lp); + } =20 /* Check if the thread has exited. */ if ((WIFEXITED (status) || WIFSIGNALED (status)) @@ -5152,6 +5197,8 @@ linux_nat_add_target (struct target_ops *t) t->to_thread_alive =3D linux_nat_thread_alive; t->to_pid_to_str =3D linux_nat_pid_to_str; t->to_has_thread_control =3D tc_schedlock; + if (linux_ops->to_stopped_data_address) + t->to_stopped_data_address =3D linux_nat_stopped_data_address; =20 t->to_can_async_p =3D linux_nat_can_async_p; t->to_is_async_p =3D linux_nat_is_async_p; --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -62,6 +62,12 @@ struct lwp_info be the address of a hardware watchpoint. */ struct siginfo siginfo; =20 + /* WATCHPOINT_HIT_SET is non-zero if this LWP stopped with a trap and a = data + watchpoint has been found as triggered. In such case WATCHPOINT_HIT + contains data address of the triggered data watchpoint. */ + unsigned watchpoint_hit_set : 1; + CORE_ADDR watchpoint_hit; + /* Non-zero if we expect a duplicated SIGINT. */ int ignore_sigint; =20 --- a/gdb/target.h +++ b/gdb/target.h @@ -1124,7 +1124,7 @@ extern char *normal_pid_to_str (ptid_t ptid); /* Hardware watchpoint interfaces. */ =20 /* Returns non-zero if we were stopped by a hardware watchpoint (memory re= ad or - write). */ + write). Only the INFERIOR_PTID task is being queried. */ =20 #define target_stopped_by_watchpoint \ (*current_target.to_stopped_by_watchpoint) @@ -1172,8 +1172,11 @@ extern char *normal_pid_to_str (ptid_t ptid); #define target_remove_hw_breakpoint(gdbarch, bp_tgt) \ (*current_target.to_remove_hw_breakpoint) (gdbarch, bp_tgt) =20 -#define target_stopped_data_address(target, x) \ - (*target.to_stopped_data_address) (target, x) +/* Return non-zero if target knows the data address which triggered this + target_stopped_by_watchpoint, in such case place it to *ADDR_P. Only t= he + INFERIOR_PTID task is being queried. */ +#define target_stopped_data_address(target, addr_p) \ + (*target.to_stopped_data_address) (target, addr_p) =20 #define target_watchpoint_addr_within_range(target, addr, start, length) \ (*target.to_watchpoint_addr_within_range) (target, addr, start, length) --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.c @@ -0,0 +1,366 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2009 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 +#include +#include +#include +#include + +#define gettid() syscall (__NR_gettid) + +/* Terminate always in the main task, it can lock up with SIGSTOPped GDB + otherwise. */ +#define TIMEOUT (gettid () =3D=3D getpid() ? 10 : 15) + +static pthread_mutex_t gdbstop_mutex =3D PTHREAD_ERRORCHECK_MUTEX_INITIALI= ZER_NP; + +static pid_t thread1_tid; +static pthread_cond_t thread1_tid_cond =3D PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread1_tid_mutex =3D PTHREAD_ERRORCHECK_MUTEX_INIT= IALIZER_NP; + +static pid_t thread2_tid; +static pthread_cond_t thread2_tid_cond =3D PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread2_tid_mutex =3D PTHREAD_ERRORCHECK_MUTEX_INIT= IALIZER_NP; + +static pthread_mutex_t terminate_mutex =3D PTHREAD_ERRORCHECK_MUTEX_INITIA= LIZER_NP; + +static volatile int thread1_rwatch; +static volatile int thread2_rwatch; + +static int unused1_rwatch; +static int unused2_rwatch; + +/* Do not use alarm as it would create a ptrace event which would hang up = us if + * we are being traced by GDB which we stopped ourselves. */ + +static void timed_mutex_lock (pthread_mutex_t *mutex) +{ + int i; + struct timespec start, now; + + i =3D clock_gettime (CLOCK_MONOTONIC, &start); + assert (i =3D=3D 0); + + do + { + i =3D pthread_mutex_trylock (mutex); + if (i =3D=3D 0) + return; + assert (i =3D=3D EBUSY); + + i =3D clock_gettime (CLOCK_MONOTONIC, &now); + assert (i =3D=3D 0); + assert (now.tv_sec >=3D start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for internal lock!\n"); + exit (EXIT_FAILURE); +} + +static void * +thread1_func (void *unused) +{ + int i; + volatile int rwatch_store; + + thread1_tid =3D gettid (); + i =3D pthread_cond_signal (&thread1_tid_cond); + assert (i =3D=3D 0); + + /* Be sure GDB is already stopped before continuing. */ + timed_mutex_lock (&gdbstop_mutex); + i =3D pthread_mutex_unlock (&gdbstop_mutex); + assert (i =3D=3D 0); + + rwatch_store =3D thread1_rwatch; + + /* Be sure the "T (tracing stop)" test can proceed for both threads. */ + timed_mutex_lock (&terminate_mutex); + i =3D pthread_mutex_unlock (&terminate_mutex); + assert (i =3D=3D 0); + + return NULL; +} + +static void * +thread2_func (void *unused) +{ + int i; + volatile int rwatch_store; + + thread2_tid =3D gettid (); + i =3D pthread_cond_signal (&thread2_tid_cond); + assert (i =3D=3D 0); + + /* Be sure GDB is already stopped before continuing. */ + timed_mutex_lock (&gdbstop_mutex); + i =3D pthread_mutex_unlock (&gdbstop_mutex); + assert (i =3D=3D 0); + + rwatch_store =3D thread2_rwatch; + + /* Be sure the "T (tracing stop)" test can proceed for both threads. */ + timed_mutex_lock (&terminate_mutex); + i =3D pthread_mutex_unlock (&terminate_mutex); + assert (i =3D=3D 0); + + return NULL; +} + +static const char * +proc_string (const char *filename, const char *line) +{ + FILE *f; + static char buf[LINE_MAX]; + size_t line_len =3D strlen (line); + + f =3D fopen (filename, "r"); + if (f =3D=3D NULL) + { + fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + while (errno =3D 0, fgets (buf, sizeof (buf), f)) + { + char *s; + + s =3D strchr (buf, '\n'); + assert (s !=3D NULL); + *s =3D 0; + + if (strncmp (buf, line, line_len) !=3D 0) + continue; + + if (fclose (f)) + { + fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + + return &buf[line_len]; + } + if (errno !=3D 0) + { + fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno)); + exit (EXIT_FAILURE); + } + fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line); + exit (EXIT_FAILURE); +} + +static unsigned long +proc_ulong (const char *filename, const char *line) +{ + const char *s =3D proc_string (filename, line); + long retval; + char *end; + + errno =3D 0; + retval =3D strtol (s, &end, 10); + if (retval < 0 || retval >=3D LONG_MAX || (end && *end)) + { + fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval, + strerror (errno)); + exit (EXIT_FAILURE); + } + return retval; +} + +static void +state_wait (pid_t process, const char *wanted) +{ + char *filename; + int i; + struct timespec start, now; + const char *state; + + i =3D asprintf (&filename, "/proc/%lu/status", (unsigned long) process); + assert (i > 0); + + i =3D clock_gettime (CLOCK_MONOTONIC, &start); + assert (i =3D=3D 0); + + do + { + state =3D proc_string (filename, "State:\t"); + if (strcmp (state, wanted) =3D=3D 0) + { + free (filename); + return; + } + + if (sched_yield ()) + { + perror ("sched_yield()"); + exit (EXIT_FAILURE); + } + + i =3D clock_gettime (CLOCK_MONOTONIC, &now); + assert (i =3D=3D 0); + assert (now.tv_sec >=3D start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\"= )!\n", + (unsigned long) process, wanted, state); + exit (EXIT_FAILURE); +} + +static volatile pid_t tracer =3D 0; +static pthread_t thread1, thread2; + +static void +cleanup (void) +{ + printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + int i; + int tracer_save =3D tracer; + + tracer =3D 0; + + i =3D kill (tracer_save, SIGCONT); + assert (i =3D=3D 0); + } +} + +int +main (int argc, char **argv) +{ + int i; + int standalone =3D 0; + + if (argc =3D=3D 2 && strcmp (argv[1], "-s") =3D=3D 0) + standalone =3D 1; + else + assert (argc =3D=3D 1); + + setbuf (stdout, NULL); + + timed_mutex_lock (&gdbstop_mutex); + + timed_mutex_lock (&terminate_mutex); + + i =3D pthread_create (&thread1, NULL, thread1_func, NULL); + assert (i =3D=3D 0); + + i =3D pthread_create (&thread2, NULL, thread2_func, NULL); + assert (i =3D=3D 0); + + if (!standalone) + { + tracer =3D proc_ulong ("/proc/self/status", "TracerPid:\t"); + if (tracer =3D=3D 0) + { + fprintf (stderr, "The testcase must be run by GDB!\n"); + exit (EXIT_FAILURE); + } + if (tracer !=3D getppid ()) + { + fprintf (stderr, "The testcase parent must be our GDB tracer!\n"); + exit (EXIT_FAILURE); + } + } + + /* SIGCONT our debugger in the case of our crash as we would deadlock + otherwise. */ + + atexit (cleanup); + + printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + i =3D kill (tracer, SIGSTOP); + assert (i =3D=3D 0); + state_wait (tracer, "T (stopped)"); + } + + timed_mutex_lock (&thread1_tid_mutex); + timed_mutex_lock (&thread2_tid_mutex); + + /* Let the threads start. */ + i =3D pthread_mutex_unlock (&gdbstop_mutex); + assert (i =3D=3D 0); + + printf ("Waiting till the threads initialize their TIDs.\n"); + + if (thread1_tid =3D=3D 0) + { + i =3D pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex); + assert (i =3D=3D 0); + + assert (thread1_tid > 0); + } + + if (thread2_tid =3D=3D 0) + { + i =3D pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex); + assert (i =3D=3D 0); + + assert (thread2_tid > 0); + } + + printf ("Thread 1 TID =3D %lu, thread 2 TID =3D %lu, PID =3D %lu.\n", + (unsigned long) thread1_tid, (unsigned long) thread2_tid, + (unsigned long) getpid ()); + + printf ("Waiting till the threads get trapped by the watchpoints.\n"); + + if (tracer) + { + /* s390x-unknown-linux-gnu will fail with "R (running)". */ + + state_wait (thread1_tid, "T (tracing stop)"); + + state_wait (thread2_tid, "T (tracing stop)"); + } + + cleanup (); + + printf ("Joining the threads.\n"); + + i =3D pthread_mutex_unlock (&terminate_mutex); + assert (i =3D=3D 0); + + i =3D pthread_join (thread1, NULL); + assert (i =3D=3D 0); + + i =3D pthread_join (thread2, NULL); + assert (i =3D=3D 0); + + printf ("Exiting.\n"); /* break-at-exit */ + + /* Just prevent compiler `warning: =1B$B!F=1B(BunusedX_rwatch=1B$B!G=1B(= B defined but not used'. */ + unused1_rwatch =3D 1; + unused2_rwatch =3D 2; + + return EXIT_SUCCESS; +} --- /dev/null +++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp @@ -0,0 +1,105 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2009 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 can cope with two watchpoints being hit by different threads at= the +# same time, GDB reports one of them and after "continue" to report the ot= her +# one GDB should not be confused by differently set watchpoints that time. +# This is the goal of "reorder1". "reorder0" tests the basic functionalit= y of +# two watchpoint being hit at the same time, without reordering them durin= g the +# stop. The formerly broken functionality is due to the all-stop mode def= ault +# "show breakpoint always-inserted" being "off". Formerly the remembered = hit +# could be assigned during continuation of a thread with pending SIGTRAP t= o the +# different/new watchpoint, just based on the watchpoint/debug register nu= mber. + +if {[target_info exists gdb,no_hardware_watchpoints] + || ![istarget *-*-linux*]} { + return 0; +} + +set testfile "watchthreads-reorder" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" ${binfile} exec= utable [list debug additional_flags=3D-lrt]] !=3D "" } { + return -1 +} + +foreach reorder {0 1} { + + global pf_prefix + set prefix_test $pf_prefix + lappend pf_prefix "reorder$reorder:" + + clean_restart $testfile + + gdb_test "set can-use-hw-watchpoints 1" + + if ![runto_main] { + gdb_suppress_tests + } + + # Use "rwatch" as "watch" would report the watchpoint changed just bas= ed on its + # read memory value during a stop by unrelated event. We are interest= ed to not + # to lose the hardware watchpoint trigger. + + gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: t= hread1_rwatch" + gdb_test {set $rwatch1=3D$bpnum} + set test "rwatch thread2_rwatch" + gdb_test_multiple $test $test { + -re "Target does not support this type of hardware watchpoint\\.\r\n$gdb_= prompt $" { + # ppc64 supports at most 1 hw watchpoints. + unsupported $test + return + } + -re "Hardware read watchpoint \[0-9\]+: thread2_rwatch\r\n$gdb_prompt $" { + pass $test + } + } + gdb_test {set $rwatch2=3D$bpnum} + gdb_breakpoint [gdb_get_line_number "break-at-exit"] + + # The watchpoints can happen in arbitrary order depending on random: + # SEL: Found 2 SIGTRAP events, selecting #[01] + # As GDB contains no srand() on the specific host/OS it will behave al= ways the + # same. Such order cannot be guaranteed for GDB in general. + + gdb_test "continue" \ + "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue= =3D 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \ + "continue a" + + if $reorder { + gdb_test {delete $rwatch1} + gdb_test {delete $rwatch2} + + gdb_test "rwatch unused1_rwatch" "Hardware read watchpoint \[0-9\]+: unus= ed1_rwatch" + gdb_test "rwatch unused2_rwatch" "Hardware read watchpoint \[0-9\]+: unus= ed2_rwatch" + + gdb_test "rwatch thread1_rwatch" "Hardware read watchpoint \[0-9\]+: thre= ad1_rwatch" + gdb_test "rwatch thread2_rwatch" "Hardware read watchpoint \[0-9\]+: thre= ad2_rwatch" + } + + gdb_test "continue" \ + "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue= =3D 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \ + "continue b" + + # While the debug output itself is not checked in this testcase one bu= g was + # found in the DEBUG_INFRUN code path. + gdb_test "set debug infrun 1" + + gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*" + + set pf_prefix $prefix_test +}