mirror of
https://github.com/darlinghq/darling-gdb.git
synced 2025-01-09 05:31:41 +00:00
Test attaching to a program that constantly spawns short-lived threads
Before the previous fixes, on Linux, this would trigger several different problems, like: [New LWP 27106] [New LWP 27047] warning: unable to open /proc file '/proc/-1/status' [New LWP 27813] [New LWP 27869] warning: Can't attach LWP 11962: No child processes Warning: couldn't activate thread debugging using libthread_db: Cannot find new threads: debugger service failed warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available. gdb/testsuite/ 2015-01-09 Pedro Alves <palves@redhat.com> * gdb.threads/attach-many-short-lived-threads.c: New file. * gdb.threads/attach-many-short-lived-threads.exp: New file.
This commit is contained in:
parent
c1a747c109
commit
c945a99f01
@ -1,3 +1,8 @@
|
||||
2015-01-09 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.threads/attach-many-short-lived-threads.c: New file.
|
||||
* gdb.threads/attach-many-short-lived-threads.exp: New file.
|
||||
|
||||
2014-01-09 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* gdb.threads/fork-thread-pending.exp: Switch to the main thread
|
||||
|
151
gdb/testsuite/gdb.threads/attach-many-short-lived-threads.c
Normal file
151
gdb/testsuite/gdb.threads/attach-many-short-lived-threads.c
Normal file
@ -0,0 +1,151 @@
|
||||
/* This testcase is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2014-2015 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/>. */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
pthread_t main_thread;
|
||||
pthread_attr_t detached_attr;
|
||||
pthread_attr_t joinable_attr;
|
||||
|
||||
/* Number of threads we'll create of each variant
|
||||
(joinable/detached). */
|
||||
int n_threads = 50;
|
||||
|
||||
/* Mutex used to hold creating detached threads. */
|
||||
pthread_mutex_t dthrds_create_mutex;
|
||||
|
||||
/* Wrapper for pthread_create. */
|
||||
|
||||
void
|
||||
create_thread (pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg)
|
||||
{
|
||||
pthread_t child;
|
||||
int rc;
|
||||
|
||||
while ((rc = pthread_create (&child, attr, start_routine, arg)) != 0)
|
||||
{
|
||||
fprintf (stderr, "unexpected error from pthread_create: %s (%d)\n",
|
||||
strerror (rc), rc);
|
||||
sleep (1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
break_fn (void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Data passed to joinable threads on creation. This is allocated on
|
||||
the heap and ownership transferred from parent to child. (We do
|
||||
this because it's not portable to cast pthread_t to pointer.) */
|
||||
|
||||
struct thread_arg
|
||||
{
|
||||
pthread_t parent;
|
||||
};
|
||||
|
||||
/* Entry point for joinable threads. These threads first join their
|
||||
parent before spawning a new child (and exiting). The parent's tid
|
||||
is passed as pthread_create argument, encapsulated in a struct
|
||||
thread_arg object. */
|
||||
|
||||
void *
|
||||
joinable_fn (void *arg)
|
||||
{
|
||||
struct thread_arg *p = arg;
|
||||
|
||||
pthread_setname_np (pthread_self (), "joinable");
|
||||
|
||||
if (p->parent != main_thread)
|
||||
assert (pthread_join (p->parent, NULL) == 0);
|
||||
|
||||
p->parent = pthread_self ();
|
||||
|
||||
create_thread (&joinable_attr, joinable_fn, p);
|
||||
|
||||
break_fn ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Entry point for detached threads. */
|
||||
|
||||
void *
|
||||
detached_fn (void *arg)
|
||||
{
|
||||
pthread_setname_np (pthread_self (), "detached");
|
||||
|
||||
/* This should throttle threads a bit in case we manage to spawn
|
||||
threads faster than they exit. */
|
||||
pthread_mutex_lock (&dthrds_create_mutex);
|
||||
|
||||
create_thread (&detached_attr, detached_fn, NULL);
|
||||
|
||||
/* Note this is called before the mutex is unlocked otherwise in
|
||||
non-stop mode, when the breakpoint is hit we'd keep spawning more
|
||||
threads forever while the old threads stay alive (stopped in the
|
||||
breakpoint). */
|
||||
break_fn ();
|
||||
|
||||
pthread_mutex_unlock (&dthrds_create_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc > 1)
|
||||
n_threads = atoi (argv[1]);
|
||||
|
||||
pthread_mutex_init (&dthrds_create_mutex, NULL);
|
||||
|
||||
pthread_attr_init (&detached_attr);
|
||||
pthread_attr_setdetachstate (&detached_attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_init (&joinable_attr);
|
||||
pthread_attr_setdetachstate (&joinable_attr, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
main_thread = pthread_self ();
|
||||
|
||||
/* Spawn the initial set of test threads. Some threads are
|
||||
joinable, others are detached. This exercises different code
|
||||
paths in the runtime. */
|
||||
for (i = 0; i < n_threads; ++i)
|
||||
{
|
||||
struct thread_arg *p;
|
||||
|
||||
p = malloc (sizeof *p);
|
||||
p->parent = main_thread;
|
||||
create_thread (&joinable_attr, joinable_fn, p);
|
||||
|
||||
create_thread (&detached_attr, detached_fn, NULL);
|
||||
}
|
||||
|
||||
/* Long enough for all the attach/detach sequences done by the .exp
|
||||
file. */
|
||||
sleep (180);
|
||||
return 0;
|
||||
}
|
132
gdb/testsuite/gdb.threads/attach-many-short-lived-threads.exp
Normal file
132
gdb/testsuite/gdb.threads/attach-many-short-lived-threads.exp
Normal file
@ -0,0 +1,132 @@
|
||||
# Copyright 2008-2015 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/>.
|
||||
|
||||
# Test attaching to a program that is constantly spawning short-lived
|
||||
# threads. The stresses the edge cases of attaching to threads that
|
||||
# have just been created or are in process of dying. In addition, the
|
||||
# test attaches, debugs, detaches, reattaches in a loop a few times,
|
||||
# to stress the behavior of the debug API around detach (some systems
|
||||
# end up leaving stale state behind that confuse the following
|
||||
# attach).
|
||||
|
||||
if {![can_spawn_for_attach]} {
|
||||
return 0
|
||||
}
|
||||
|
||||
standard_testfile
|
||||
|
||||
# The test proper. See description above.
|
||||
|
||||
proc test {} {
|
||||
global binfile
|
||||
global gdb_prompt
|
||||
global decimal
|
||||
|
||||
clean_restart ${binfile}
|
||||
|
||||
set testpid [spawn_wait_for_attach $binfile]
|
||||
|
||||
set attempts 10
|
||||
for {set attempt 1} { $attempt <= $attempts } { incr attempt } {
|
||||
with_test_prefix "iter $attempt" {
|
||||
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 "warning: Cannot attach to lwp $decimal: Operation not permitted" {
|
||||
# On Linux, PTRACE_ATTACH sometimes fails with
|
||||
# EPERM, even though /proc/PID/status indicates
|
||||
# the thread is running.
|
||||
set eperm 1
|
||||
exp_continue
|
||||
}
|
||||
-re "debugger service failed.*$gdb_prompt $" {
|
||||
fail $test
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
if {$eperm} {
|
||||
xfail "$test (EPERM)"
|
||||
} else {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
-re "Attaching to program.*process $testpid.*$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
# Sleep a bit and try updating the thread list. We should
|
||||
# know about all threads already at this point. If we see
|
||||
# "New Thread" or similar being output, then "attach" is
|
||||
# failing to actually attach to all threads in the process,
|
||||
# which would be a bug.
|
||||
sleep 1
|
||||
|
||||
set test "no new threads"
|
||||
gdb_test_multiple "info threads" $test {
|
||||
-re "New .*$gdb_prompt $" {
|
||||
fail $test
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
pass $test
|
||||
}
|
||||
}
|
||||
|
||||
# Force breakpoints always inserted, so that threads we might
|
||||
# have failed to attach to hit them even when threads we do
|
||||
# know about are stopped.
|
||||
gdb_test_no_output "set breakpoint always-inserted on"
|
||||
|
||||
# Run to a breakpoint a few times. A few threads should spawn
|
||||
# and die meanwhile. This checks that thread creation/death
|
||||
# events carry on correctly after attaching. Also, be
|
||||
# detaching from the program and reattaching, we check that
|
||||
# the program doesn't die due to gdb leaving a pending
|
||||
# breakpoint hit on a new thread unprocessed.
|
||||
gdb_test "break break_fn" "Breakpoint.*" "break break_fn"
|
||||
|
||||
# Wait a bit, to give time for most threads to hit the
|
||||
# breakpoint, including threads we might have failed to
|
||||
# attach.
|
||||
sleep 2
|
||||
|
||||
set bps 3
|
||||
for {set bp 1} { $bp <= $bps } { incr bp } {
|
||||
gdb_test "continue" "Breakpoint.*" "break at break_fn: $bp"
|
||||
}
|
||||
|
||||
if {$attempt < $attempts} {
|
||||
gdb_test "detach" "Detaching from.*"
|
||||
} else {
|
||||
gdb_test "kill" "" "kill process" "Kill the program being debugged.*y or n. $" "y"
|
||||
}
|
||||
|
||||
gdb_test_no_output "set breakpoint always-inserted off"
|
||||
delete_breakpoints
|
||||
}
|
||||
}
|
||||
|
||||
remote_exec target "kill -9 ${testpid}"
|
||||
}
|
||||
|
||||
if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug pthreads}] == -1} {
|
||||
return -1
|
||||
}
|
||||
|
||||
test
|
Loading…
Reference in New Issue
Block a user