From d27d16bfdc3f275b379793214db9bd9467b6f0f8 Mon Sep 17 00:00:00 2001 From: Richard Bunt Date: Thu, 19 Apr 2018 23:02:35 -0400 Subject: [PATCH] Add test case for a known hang in infrun The hang occurs when GDB tries to call inferior functions on two different threads with scheduler-locking turned on. The first call works fine, with the call to infrun_async(1) causing the signal_handler to be marked and the event to be handled, but then the event loop resets the "ready" member to zero, while leaving infrun_is_async set to 1. As a result, GDB hangs if the user switches to another thread and calls a second function because calling infrun_async(1) a second time has no effect, meaning the inferior call events are never handled. The added test case provokes the above issue. gdb/testsuite/ChangeLog: * gdb.threads/multiple-successive-infcall.c: New test. * gdb.threads/multiple-successive-infcall.exp: New file. --- gdb/testsuite/ChangeLog | 5 + .../gdb.threads/multiple-successive-infcall.c | 111 ++++++++++++++++++ .../multiple-successive-infcall.exp | 72 ++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 gdb/testsuite/gdb.threads/multiple-successive-infcall.c create mode 100644 gdb/testsuite/gdb.threads/multiple-successive-infcall.exp diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 68ae3d7b5a2..d5e94290413 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2018-04-19 Richard Bunt + + * gdb.threads/multiple-successive-infcall.c: New test. + * gdb.threads/multiple-successive-infcall.exp: New file. + 2018-04-17 Tom Tromey * gdb.rust/simple.rs (Union): New type. diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.c b/gdb/testsuite/gdb.threads/multiple-successive-infcall.c new file mode 100644 index 00000000000..9bdb9a27277 --- /dev/null +++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.c @@ -0,0 +1,111 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2018 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 + +/* This defines the number of threads to spawn. */ +#define THREADCOUNT 4 + +/* Global barrier type to control synchronization between threads. */ +static pthread_barrier_t print_barrier; + +/* Define global thread identifiers. */ +static pthread_t threads[THREADCOUNT]; + +/* Hold values for each thread at the index supplied to the thread + on creation. */ +static int thread_ids[THREADCOUNT]; + +/* Find the value associated with the calling thread. */ +static int +get_value () +{ + for (int tid = 0; tid < THREADCOUNT; ++tid) + { + if (pthread_equal (threads[tid], pthread_self ())) + return thread_ids[tid]; + } + /* Value for the main thread. */ + return 1; +} + +/* Return the nth Fibonacci number. */ +static unsigned long +fast_fib (unsigned int n) +{ + int a = 0; + int b = 1; + int t; + for (unsigned int i = 0; i < n; ++i) + { + t = b; + b = a + b; + a = t; + } + return a; +} + +/* Encapsulate the synchronization of the threads. Perform a barrier before + and after the computation. */ +static void * +thread_function (void *args) +{ + int tid = *((int *) args); + int status = pthread_barrier_wait (&print_barrier); + if (status == PTHREAD_BARRIER_SERIAL_THREAD) + printf ("All threads entering compute region\n"); + + unsigned long result = fast_fib (100); /* testmarker01 */ + status = pthread_barrier_wait (&print_barrier); + if (status == PTHREAD_BARRIER_SERIAL_THREAD) + printf ("All threads outputting results\n"); + + pthread_barrier_wait (&print_barrier); + printf ("Thread %d Result: %lu\n", tid, result); +} + +int +main (void) +{ + int err = pthread_barrier_init (&print_barrier, NULL, THREADCOUNT); + if (err != 0) + { + fprintf (stderr, "Barrier creation failed\n"); + return EXIT_FAILURE; + } + /* Create the worker threads (main). */ + printf ("Spawning worker threads\n"); + for (int tid = 0; tid < THREADCOUNT; ++tid) + { + /* Add 2 so the value maps to the debugger's thread identifiers. */ + thread_ids[tid] = tid + 2; /* prethreadcreationmarker */ + err = pthread_create (&threads[tid], NULL, thread_function, + (void *) &thread_ids[tid]); + if (err != 0) + { + fprintf (stderr, "Thread creation failed\n"); + return EXIT_FAILURE; + } + } + /* Wait for the threads to complete then exit. */ + for (int tid = 0; tid < THREADCOUNT; ++tid) + pthread_join (threads[tid], NULL); + + return EXIT_SUCCESS; +} diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp new file mode 100644 index 00000000000..a71f991d6f2 --- /dev/null +++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp @@ -0,0 +1,72 @@ +# Copyright (C) 2018 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 . */ + +# multiple-successive-infcall.exp -- Test if GDB can invoke functions on +# multiple inferiors, one after the other. + +standard_testfile + +if [get_compiler_info] { + return -1 +} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable {debug}] != "" } { + return -1 +} + +clean_restart "${binfile}" + +if ![runto_main] then { + fail "can't run to main" + return 0 +} + +# Ensure that each new thread is detected by GDB in the order that the +# test case creates them, so the thread identifiers match between +# test and test case. +gdb_breakpoint [gdb_get_line_number "prethreadcreationmarker"] +gdb_continue_to_breakpoint "prethreadcreationmarker" +set after_new_thread_message "created new thread" +foreach_with_prefix thread {5 4 3} { + gdb_test_multiple "continue" "${after_new_thread_message}" { + -re "\\\[New Thread ${hex} \\\(LWP \[0-9\]+\\\)\\\].*${gdb_prompt}" { + pass "${after_new_thread_message}" + } + } +} + +gdb_breakpoint [gdb_get_line_number "testmarker01"] +gdb_continue_to_breakpoint "testmarker01" +gdb_test_no_output "set scheduler-locking on" +gdb_test "show scheduler-locking" \ + "Mode for locking scheduler during execution is \"on\"." + +foreach_with_prefix thread {5 4 3 2 1} { + gdb_test "thread ${thread}" "Switching to .*" + set command "call get_value()" + set hang_message "testing if ${command} hangs" + gdb_test_multiple "${command}" "${hang_message}" { + -re "= ${thread}\[\r\n]+${gdb_prompt} $" { + pass "${hang_message}" + } + timeout { + kfail "gdb/22882" "${hang_message}" + # Exit. The debugger has hung, so there is no point in wasting + # time timing out on further calls to get_value(). + return 0 + } + } +} -- 2.30.2