From 5a43797524a9df0545e73709583a43bb404b3d9a Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Sat, 30 May 2009 00:19:13 +0000 Subject: [PATCH] * infrun.c (prepare_to_proceed): Document. Assert !non_stop. If scheduler-locking is enabled, we're not going to be singlestepping any other previously stopped thread. * gdb.threads/hand-call-in-threads.exp: New file. * gdb.threads/hand-call-in-threads.c: New file. --- gdb/ChangeLog | 6 + gdb/infrun.c | 18 +- gdb/testsuite/ChangeLog | 5 + .../gdb.threads/hand-call-in-threads.c | 127 ++++++++++++++ .../gdb.threads/hand-call-in-threads.exp | 160 ++++++++++++++++++ 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 gdb/testsuite/gdb.threads/hand-call-in-threads.c create mode 100644 gdb/testsuite/gdb.threads/hand-call-in-threads.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b620d98e184..bac438916ce 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,9 @@ +2009-05-29 Doug Evans + + * infrun.c (prepare_to_proceed): Document. Assert !non_stop. + If scheduler-locking is enabled, we're not going to be singlestepping + any other previously stopped thread. + 2009-05-29 Pedro Alves * mi/mi-interp.c (mi_on_resume): Initialize `count' to 0. diff --git a/gdb/infrun.c b/gdb/infrun.c index 88a1c4e0f05..9b302bf0b56 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1364,13 +1364,21 @@ clear_proceed_status (void) } } -/* This should be suitable for any targets that support threads. */ +/* Check the current thread against the thread that reported the most recent + event. If a step-over is required return TRUE and set the current thread + to the old thread. Otherwise return FALSE. + + This should be suitable for any targets that support threads. */ static int prepare_to_proceed (int step) { ptid_t wait_ptid; struct target_waitstatus wait_status; + int schedlock_enabled; + + /* With non-stop mode on, threads are always handled individually. */ + gdb_assert (! non_stop); /* Get the last target status returned by target_wait(). */ get_last_target_status (&wait_ptid, &wait_status); @@ -1382,9 +1390,15 @@ prepare_to_proceed (int step) return 0; } + schedlock_enabled = (scheduler_mode == schedlock_on + || (scheduler_mode == schedlock_step + && step)); + /* Switched over from WAIT_PID. */ if (!ptid_equal (wait_ptid, minus_one_ptid) - && !ptid_equal (inferior_ptid, wait_ptid)) + && !ptid_equal (inferior_ptid, wait_ptid) + /* Don't single step WAIT_PID if scheduler locking is on. */ + && !schedlock_enabled) { struct regcache *regcache = get_thread_regcache (wait_ptid); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index c444806ea23..01ee306b5bf 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2009-05-29 Doug Evans + + * gdb.threads/hand-call-in-threads.exp: New. + * gdb.threads/hand-call-in-threads.c: New. + 2009-05-28 Pedro Alves * gdb.threads/threxit-hop-specific.c: New. diff --git a/gdb/testsuite/gdb.threads/hand-call-in-threads.c b/gdb/testsuite/gdb.threads/hand-call-in-threads.c new file mode 100644 index 00000000000..98e08b8d757 --- /dev/null +++ b/gdb/testsuite/gdb.threads/hand-call-in-threads.c @@ -0,0 +1,127 @@ +/* Test case for hand function calls in multi-threaded program. + + Copyright 2008 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 + +#ifndef NR_THREADS +#define NR_THREADS 4 +#endif + +int thread_count; + +pthread_mutex_t thread_count_mutex; + +pthread_cond_t thread_count_condvar; + +void +incr_thread_count (void) +{ + pthread_mutex_lock (&thread_count_mutex); + ++thread_count; + if (thread_count == NR_THREADS) + pthread_cond_signal (&thread_count_condvar); + pthread_mutex_unlock (&thread_count_mutex); +} + +void +cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut) +{ + pthread_mutex_lock (mut); + pthread_cond_wait (cond, mut); + pthread_mutex_unlock (mut); +} + +void +noreturn (void) +{ + pthread_mutex_t mut; + pthread_cond_t cond; + + pthread_mutex_init (&mut, NULL); + pthread_cond_init (&cond, NULL); + + /* Wait for a condition that will never be signaled, so we effectively + block the thread here. */ + cond_wait (&cond, &mut); +} + +void * +forever_pthread (void *unused) +{ + incr_thread_count (); + noreturn (); +} + +void +hand_call (void) +{ +} + +/* Wait until all threads are running. */ + +void +wait_all_threads_running (void) +{ + pthread_mutex_lock (&thread_count_mutex); + if (thread_count == NR_THREADS) + { + pthread_mutex_unlock (&thread_count_mutex); + return; + } + pthread_cond_wait (&thread_count_condvar, &thread_count_mutex); + if (thread_count == NR_THREADS) + { + pthread_mutex_unlock (&thread_count_mutex); + return; + } + pthread_mutex_unlock (&thread_count_mutex); + printf ("failed waiting for all threads to start\n"); + abort (); +} + +/* Called when all threads are running. + Easy place for a breakpoint. */ + +void +all_threads_running (void) +{ +} + +int +main (void) +{ + pthread_t forever[NR_THREADS]; + int i; + + pthread_mutex_init (&thread_count_mutex, NULL); + pthread_cond_init (&thread_count_condvar, NULL); + + for (i = 0; i < NR_THREADS; ++i) + pthread_create (&forever[i], NULL, forever_pthread, NULL); + + wait_all_threads_running (); + all_threads_running (); + + return 0; +} + diff --git a/gdb/testsuite/gdb.threads/hand-call-in-threads.exp b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp new file mode 100644 index 00000000000..2b65e8fc2bb --- /dev/null +++ b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp @@ -0,0 +1,160 @@ +# Copyright (C) 2004, 2007, 2008 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 making hand function calls in multiple threads. + +set NR_THREADS 4 + +if $tracelevel then { + strace $tracelevel +} + +set testfile "hand-call-in-threads" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}" "additional_flags=-DNR_THREADS=$NR_THREADS"]] != "" } { + return -1 +} + +# Some targets can't do function calls, so don't even bother with this +# test. +if [target_info exists gdb,cannot_call_functions] { + setup_xfail "*-*-*" 2416 + fail "This target can not call functions" + continue +} + +proc get_dummy_frame_number { } { + global gdb_prompt + + send_gdb "bt\n" + gdb_expect { + -re "#(\[0-9\]*) *.*$gdb_prompt $" + { + return $expect_out(1,string) + } + -re "$gdb_prompt $" + { + return "" + } + timeout + { + return "" + } + } + return "" +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if { ![runto_main] } { + fail "Can't run to main" + return 0 +} + +gdb_test "break all_threads_running" \ + "Breakpoint 2 at .*: file .*${srcfile}, line .*" \ + "breakpoint on all_threads_running" + +gdb_test "break hand_call" \ + "Breakpoint 3 at .*: file .*${srcfile}, line .*" \ + "breakpoint on hand_call" + +# Run the program and make sure GDB reports that we stopped after +# hitting breakpoint 2 in all_threads_running(). + +gdb_test "continue" \ + ".*Breakpoint 2, all_threads_running ().*" \ + "run to all_threads_running" + +# Before we start making hand function calls, turn on scheduler locking. + +gdb_test "set scheduler-locking on" "" "enable scheduler locking" +gdb_test "show scheduler-locking" ".* locking scheduler .* is \"on\"." "show scheduler locking on" + +# Now hand-call a function in each thread, having the function +# stop without returning. + +# Add one for the main thread. +set total_nr_threads [expr $NR_THREADS + 1] + +# Thread numbering in gdb is origin-1, so begin numbering at 1. +for { set i 1 } { $i <= $total_nr_threads } { incr i } { + set thread_nr $i + gdb_test "thread $thread_nr" "" "prepare to make hand call, thread $thread_nr" + gdb_test "call hand_call()" "Breakpoint 3, .*" "hand call, thread $thread_nr" +} + +# Now have each hand-called function return. + +# Turn confirmation off for the "return" command. +gdb_test "set confirm off" "" + +clear_xfail "*-*-*" + +for { set i 1 } { $i <= $total_nr_threads } { incr i } { + set thread_nr $i + gdb_test "thread $thread_nr" "" "prepare to discard hand call, thread $thread_nr" + set frame_number [get_dummy_frame_number] + if { "$frame_number" == "" } { + fail "dummy stack frame number, thread $thread_nr" + # Need something. + set frame_number 0 + } else { + pass "dummy stack frame number, thread $thread_nr" + } + # Pop the dummy frame. + gdb_test "frame $frame_number" "" "setting frame, thread $thread_nr" + gdb_test "return" "" "discard hand call, thread $thread_nr" + # In case getting the dummy frame number failed, re-enable for next iter. + clear_xfail "*-*-*" +} + +# Make sure all dummy frames got popped. + +gdb_test_multiple "maint print dummy-frames" "all dummies popped" { + -re ".*stack=.*$gdb_prompt $" { + fail "all dummies popped" + } + -re ".*$gdb_prompt $" { + pass "all dummies popped" + } +} + +# Before we resume the full program, turn of scheduler locking. +gdb_test "set scheduler-locking off" "" "disable scheduler locking" +gdb_test "show scheduler-locking" ".* locking scheduler .* is \"off\"." "show scheduler locking off" + +# Continue one last time, the program should exit normally. +# +# ??? This currently doesn't work because gdb doesn't know how to singlestep +# over reported breakpoints that weren't in the last thread to run. +# Commented out until then. +# +# For reference sake ... +# An alternative is to manually work around the issue by manually setting +# the thread back to the first thread: the program is still at the +# all_threads_running breakpoint, which wasn't the last thread to run, +# and gdb doesn't know how to singlestep over reported breakpoints that +# weren't in the last thread to run. +#gdb_test "thread 1" "" "set thread to 1, prepare to resume" +# +#gdb_continue_to_end "hand-call-in-threads" + +return 0 -- 2.30.2