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.
This commit is contained in:
parent
ebf7373d2f
commit
d27d16bfdc
|
@ -1,3 +1,8 @@
|
|||
2018-04-19 Richard Bunt <richard.bunt@arm.com>
|
||||
|
||||
* gdb.threads/multiple-successive-infcall.c: New test.
|
||||
* gdb.threads/multiple-successive-infcall.exp: New file.
|
||||
|
||||
2018-04-17 Tom Tromey <tom@tromey.com>
|
||||
|
||||
* gdb.rust/simple.rs (Union): New type.
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* 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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
# 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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue