From: Pedro Alves Date: Fri, 3 May 2024 17:24:22 +0000 (+0100) Subject: Fix gdb.threads/multiple-successive-infcall.exp on Windows (depends on schedlock) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07f0471f447bc48b83e7e8f323e57d7c68a54949;p=thirdparty%2Fbinutils-gdb.git Fix gdb.threads/multiple-successive-infcall.exp on Windows (depends on schedlock) Running gdb.threads/multiple-successive-infcall.exp on Cygwin results in a series of cascading timeouts. There are two problems: #1 - Cygwin starts a few threads for its own use. When you reach "main" with "(gdb) start", info threads will already show more than one thread. The testcase spawns four threads with pthread_create, and it assumes that they get GDB thread ids 2 through 5. That is incorrect for Cygwin. E.g.: (gdb) info threads Id Target Id Frame 1 Thread 4104.0x1e60 "multiple-successive-infcall" 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 2 Thread 4104.0x2490 0x00007fff3c450ad4 in ntdll!ZwWaitForWorkViaWorkerFactory () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 3 Thread 4104.0x2bdc 0x00007fff3c450ad4 in ntdll!ZwWaitForWorkViaWorkerFactory () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 4 Thread 4104.0x1714 "sig" 0x00007fff3c44d124 in ntdll!ZwReadFile () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 5 Thread 4104.0x211c "multiple-successive-infcall" 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 6 Thread 4104.0x2070 "multiple-successive-infcall" 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll 7 Thread 4104.0x1d3c "multiple-successive-infcall" 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll * 8 Thread 4104.0xa54 "multiple-successive-infcall" thread_function (args=0x10040704c ) at /cygdrive/c/Users/alves/rocm/gdb/src/gdb/testsuite/gdb.threads/multiple-successive-infcall.c:75 #2 - The test enables scheduler locking and switches to each thread in turn, and then does an infcall. On Windows, system calls aren't interruptible like on Windows. There's no concept of EINTR and syscall restart. So if a thread is in a syscall, actually running kernel code, then an infcall will only complete once the syscall returns. If the syscall is blocked waiting for another thread to unblock it, then the infcall never completes. You can pause it with Ctrl-C, but you'll see that the thread hasn't executed any userspace instruction. For example: (gdb) t 8 [Switching to thread 8 (Thread 4104.0xa54)] #0 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll (gdb) p get_value () * hang * * press ctrl-c * Thread 8 "multiple-successive-infcall" stopped. get_value () at /cygdrive/c/Users/alves/rocm/gdb/src/gdb/testsuite/gdb.threads/multiple-successive-infcall.c:38 38 get_value () The program being debugged was signaled while in a function called from GDB. GDB remains in the frame where the signal was received. To change this behavior use "set unwindonsignal on". Evaluation of the expression containing the function (get_value) will be abandoned. When the function is done executing, GDB will silently stop. (gdb) bt #0 get_value () at /cygdrive/c/Users/alves/rocm/gdb/src/gdb/testsuite/gdb.threads/multiple-successive-infcall.c:38 #1 #2 0x00007fff3c44dbb4 in ntdll!ZwWaitForMultipleObjects () from /cygdrive/c/Windows/SYSTEM32/ntdll.dll #3 0x00007fff39cd1b40 in WaitForMultipleObjectsEx () from /cygdrive/c/Windows/System32/KERNELBASE.dll ... (gdb) si * hang * This results in failures like these: call get_value() FAIL: gdb.threads/multiple-successive-infcall.exp: thread=4: call inferior function (timeout) This commit thus tweaks the testcase to make it work on Cygwin too. First, it makes the .exp file figure out which threads are the ones created by the testcase. The current testcase also tries the infcall on the main thread. It's simpler to not worry about the main thread being blocked in a syscall. So we just no longer test the syscall through that thread. It is not important for the original motivation for the testcase. With this, the testcase now passes cleanly on Cygwin: === gdb Summary === # of expected passes 15 Change-Id: I5f69bafe6cecb83f18fb22ba7ee2368229fc4f9f --- diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.c b/gdb/testsuite/gdb.threads/multiple-successive-infcall.c index 38e193154b6..46b5ca22053 100644 --- a/gdb/testsuite/gdb.threads/multiple-successive-infcall.c +++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.c @@ -18,6 +18,7 @@ #include #include #include +#include /* This defines the number of threads to spawn. */ #define THREADCOUNT 4 @@ -41,8 +42,9 @@ get_value () if (pthread_equal (threads[tid], pthread_self ())) return thread_ids[tid]; } - /* Value for the main thread. */ - return 1; + + /* Should not get here. */ + return -1; } /* Return the nth Fibonacci number. */ @@ -93,8 +95,9 @@ main (void) 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 */ + /* Add 1 so the value maps to the testcase's thread + identifiers. */ + thread_ids[tid] = tid + 1; /* prethreadcreationmarker */ err = pthread_create (&threads[tid], NULL, thread_function, (void *) &thread_ids[tid]); if (err != 0) diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp index 1aa925304d2..2ead754b8f3 100644 --- a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp +++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp @@ -13,8 +13,7 @@ # 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. +# Test invoking functions on multiple threads, one after the other. standard_testfile @@ -29,31 +28,97 @@ if {![runto_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. +# Ensure that each new pthread is spawned one at a time so we can +# extract its target ID, and not the ID of some auxiliary thread +# spawned by the runtime. 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}" - } - -re -wrap "\\\[New Thread $decimal\\.$decimal\\\]\r\n.*" { - pass $gdb_test_name +set after_new_thread_message "detected new pthread" + +# List of the target ids detected. +set pthread_list {} + +# The first 3 pthreads are detected here. +foreach_with_prefix pthread {1 2 3} { + gdb_test_multiple "continue" "${after_new_thread_message}" { + -re -wrap "\\\[New (Thread \[^\r\n\]+)\\\]\r\n.*" { + set thr $expect_out(1,string) + verbose -log "detected pthread: $thr" + lappend pthread_list $thr + pass $gdb_test_name + } } - } } +verbose -log "detected pthread list: $pthread_list" + gdb_breakpoint [gdb_get_line_number "testmarker01"] -gdb_continue_to_breakpoint "testmarker01" + +# Continue to breakpoint. This detects that last pthread. It also +# ensures that no pthread ends up blocked in a syscall, which is +# important for Windows -- the infcall wouldn't be able to complete +# until the syscall returns, meaning, the infcall would deadlock. +with_test_prefix "pthread 4" { + gdb_test_multiple "continue" "${after_new_thread_message}" { + -re "\\\[New (Thread \[^\r\n\]+)\\\]\r\n" { + set thr $expect_out(1,string) + verbose -log "detected pthread: $thr" + lappend pthread_list $thr + pass $gdb_test_name + } + } +} + +# Consume the breakpoint hit and the prompt. +gdb_test_multiple "" "stop at testmarker01" { + -re -wrap "hit Breakpoint .* testmarker01 .*" { + pass $gdb_test_name + } +} + 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 .*" - gdb_test "call get_value()" "= ${thread}" \ - "call inferior function" +set thread_count [get_valueof "" "\$_inferior_thread_count" 0] + +# pthread id to be used in test messages in the loop below. +set testing_pthread 0 + +for {set thread 1} {$thread <= $thread_count} {incr thread} { + + # 1 if found, 0 if not found, -1 if the test failed. + set found -1 + gdb_test_multiple "thread ${thread}" "extract thread target id" { + -re -wrap "Switching to thread $decimal \\((Thread \[^\r\n\]+)\\)\\\]\r\n.*" { + set thr $expect_out(1,string) + verbose -log "extracted: $thr" + if {[lsearch -exact $pthread_list $thr] != -1} { + set found 1 + } else { + set found 0 + } + } + } + if {$found == -1} { + # gdb_test_multiple already issued a FAIL above. No point in + # continuing. + return + } elseif {$found == 0} { + # Not recognized as a testcase pthread, so it must be a thread + # spawned by the runtime or the OS. Don't use it for testing + # the infcall, as the thread may be blocked in a syscall -- on + # Windows, the infcall wouldn't be able to complete until the + # syscall returns, meaning, the infcall would deadlock. + verbose -log "not a known pthread" + continue + } else { + incr testing_pthread + with_test_prefix "pthread $testing_pthread" { + gdb_test "call get_value()" "= $testing_pthread" \ + "call inferior function" + } + } } + +gdb_assert {$testing_pthread == 4} "tested all pthreads"