From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 3932 invoked by alias); 4 Apr 2014 11:54:40 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 3878 invoked by uid 89); 4 Apr 2014 11:54:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.1 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 04 Apr 2014 11:54:34 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s34BsRYn020404 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 4 Apr 2014 07:54:30 -0400 Received: from [127.0.0.1] (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id s34BsOii024120; Fri, 4 Apr 2014 07:54:26 -0400 Message-ID: <533E9D6F.8040200@redhat.com> Date: Fri, 04 Apr 2014 11:54:00 -0000 From: Pedro Alves User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130625 Thunderbird/17.0.7 MIME-Version: 1.0 To: Alan Lawrence CC: gdb-patches@sourceware.org Subject: Re: [PATCH 5/6] Handle multiple step-overs References: <53330643.4040402@arm.com> In-Reply-To: <53330643.4040402@arm.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit X-SW-Source: 2014-04/txt/msg00061.txt.bz2 Sorry for the delay -- just a note to say "ack". I haven't managed to find time to look at this yet. I had found another regression this series caused (hbreak2.exp against gdbserver), and been trying to fix that one first. -- Pedro Alves On 03/26/2014 04:54 PM, Alan Lawrence wrote: > Following this patch, we're seeing an assertion failure of infrun.c:5192, > > gdb_assert (!tp->control.trap_expected); > > on the AArch64 platform. The testcase is that added in git commit > beb460e8d2ddf5327a6ab146055a6e6e9f552a4b, condbreak-call-false.{c,exp} - I've > tried this testcase both before and after your multiple step-over patch, and it > succeeds without the patch. I'm not very familiar with gdb internals and > stepwise comparing AArch64 against ARM (on which the test passes) sounds at best > laborious; hoping there may be some experts here who can help? > > I have reproduced the failure on (a) qemu (-2.0.0-rc0) running on x86_64 host, > (b) native AArch64 hardware, (c) running cross gdb hosted on x86_64-linux > against remote target running natively on aarch64-linux; in the latter case, > it's the host gdb which is critical (i.e.: fails/succeeds with/out the multiple > stepover patch, regardless of whether the remote gdbserver includes this patch > or not); remote gdbserver succeeds in all 4=(remote/local)(with/without) cases. > > firstly: > $ aarch64-none-linux-gnu-gcc -g -o condbreak-call-false > binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c > $ aarch64-linux-user/qemu-aarch64 -g 23456 -L $MY_QEMU_LIB_PATH > condbreak-call-false & > > then: > $ aarch64-none-linux-gnu-gdb > GNU gdb (unknown) 7.7.50.20140326-cvs > Copyright (C) 2014 Free Software Foundation, Inc. > License GPLv3+: GNU GPL version 3 or later > This is free software: you are free to change and redistribute it. > There is NO WARRANTY, to the extent permitted by law. Type "show copying" > and "show warranty" for details. > This GDB was configured as "--host=x86_64-unknown-linux-gnu > --target=aarch64-none-linux-gnu". > Type "show configuration" for configuration details. > For bug reporting instructions, please see: > . > Find the GDB manual and other documentation resources online at: > . > For help, type "help". > Type "apropos word" to search for commands related to "word". > (gdb) file condbreak-call-false > Reading symbols from condbreak-call-false...done. > (gdb) target remote localhost:23456 > Remote debugging using dsgci-1.euhpc.arm.com:23456 > warning: Unable to find dynamic linker breakpoint function. > GDB will be unable to debug shared library initializers > and track explicitly loaded dynamic code. > 0x0000004000a01d00 in ?? () > (gdb) break main > Breakpoint 1 at 0x400504: file > srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c, line 37. > (gdb) continue > Continuing. > warning: `/lib64/libc.so.6': Shared library architecture unknown is not > compatible with target architecture aarch64. > warning: Could not load shared library symbols for /lib/ld-linux-aarch64.so.1. > Do you need "set solib-search-path" or "set sysroot"? > > Breakpoint 1, main () > at srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c:37 > 37 foo (); > (gdb) break foo if zero() > Breakpoint 2 at 0x4004f0: file > srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c, line 25. > (gdb) continue > Continuing. > /work/alalaw01/oban/srca64t/binutils-gdb/gdb/infrun.c:5192: internal-error: > switch_back_to_stepped_thread: Assertion `!tp->control.trap_expected' failed. > A problem internal to GDB has been detected, > further debugging may prove unreliable. > Quit this debugging session? (y or n) > > or: > $ aarch64-none-elf-gdb > GNU gdb (unknown) 7.7.50.20140320-cvs > Copyright (C) 2014 Free Software Foundation, Inc. > License GPLv3+: GNU GPL version 3 or later > This is free software: you are free to change and redistribute it. > There is NO WARRANTY, to the extent permitted by law. Type "show copying" > and "show warranty" for details. > This GDB was configured as "--host=x86_64-unknown-linux-gnu > --target=aarch64-none-elf". > Type "show configuration" for configuration details. > For bug reporting instructions, please see: > . > Find the GDB manual and other documentation resources online at: > . > For help, type "help". > Type "apropos word" to search for commands related to "word". > (gdb) file condbreak-call-false > Reading symbols from condbreak-call-false...done. > (gdb) target remote localhost:23456 > Remote debugging using localhost:23456 > _start () at /work/alalaw01/oban/srcfsf/binutils-gdb/libgloss/aarch64/crt0.S:90 > 90 adr x1, .LC0 > (gdb) break main > Breakpoint 1 at 0x4002d4: file > srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c, line 37. > (gdb) continue # can do this in either order with next > Continuing. > > Breakpoint 1, main () > at srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c:37 > 37 foo (); > (gdb) break foo if zero() # can do this in either order with previous > Breakpoint 2 at 0x4002c0: file > srcfsf/binutils-gdb/gdb/testsuite/gdb.base/condbreak-call-false.c, line 25. > (gdb) continue > Continuing. > /work/alalaw01/oban/srca64t/binutils-gdb/gdb/infrun.c:5192: internal-error: > switch_back_to_stepped_thread: Assertion `!tp->control.trap_expected' failed. > A problem internal to GDB has been detected, > further debugging may prove unreliable. > Quit this debugging session? (y or n) > > > >> >> From: Pedro Alves >> To: gdb-patches at sourceware dot org >> Date: Tue, 25 Feb 2014 20:32:42 +0000 >> Subject: [PATCH 5/6] Handle multiple step-overs. >> Authentication-results: sourceware.org; auth=none >> References: <1393360363-5603-1-git-send-email-palves at redhat dot com> >> >> This test fails with current mainline. >> >> If the program stopped for a breakpoint in thread 1, and then the user >> switches to thread 2, and resumes the program, GDB first switches back >> to thread 1 to step it over the breakpoint, in order to make progress. >> >> However, that logic only considers the last reported event, assuming >> only one thread needs that stepping over dance. >> >> That's actually not true when we play with scheduler-locking. The >> patch adds an example to the testsuite of multiple threads needing a >> step-over before the stepping thread can be resumed. With current >> mainline, the program re-traps the same breakpoint it had already >> trapped before. >> >> E.g.: >> >> Breakpoint 2, main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 >> 99 wait_threads (); /* set wait-threads breakpoint here */ >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint >> info threads >> Id Target Id Frame >> 3 Thread 0x7ffff77c9700 (LWP 4310) "multiple-step-o" 0x00000000004007ca in child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:43 >> 2 Thread 0x7ffff7fca700 (LWP 4309) "multiple-step-o" 0x0000000000400827 in child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:60 >> * 1 Thread 0x7ffff7fcb740 (LWP 4305) "multiple-step-o" main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: info threads shows all threads >> set scheduler-locking on >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: set scheduler-locking on >> break 44 >> Breakpoint 3 at 0x4007d3: file ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c, line 44. >> (gdb) break 61 >> Breakpoint 4 at 0x40082d: file ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c, line 61. >> (gdb) thread 3 >> [Switching to thread 3 (Thread 0x7ffff77c9700 (LWP 4310))] >> #0 0x00000000004007ca in child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:43 >> 43 (*myp) ++; >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 3 >> continue >> Continuing. >> >> Breakpoint 3, child_function_3 (arg=0x1) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:44 >> 44 callme (); /* set breakpoint thread 3 here */ >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint in thread 3 >> p *myp = 0 >> $1 = 0 >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: unbreak loop in thread 3 >> thread 2 >> [Switching to thread 2 (Thread 0x7ffff7fca700 (LWP 4309))] >> #0 0x0000000000400827 in child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:60 >> 60 (*myp) ++; >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 2 >> continue >> Continuing. >> >> Breakpoint 4, child_function_2 (arg=0x0) at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:61 >> 61 callme (); /* set breakpoint thread 2 here */ >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: continue to breakpoint: run to breakpoint in thread 2 >> p *myp = 0 >> $2 = 0 >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: unbreak loop in thread 2 >> thread 1 >> [Switching to thread 1 (Thread 0x7ffff7fcb740 (LWP 4305))] >> #0 main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 >> 99 wait_threads (); /* set wait-threads breakpoint here */ >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: thread 1 >> set scheduler-locking off >> (gdb) PASS: gdb.threads/multiple-step-overs.exp: step: set scheduler-locking off >> >> At this point all thread are stopped for a breakpoint that needs stepping over. >> >> (gdb) step >> >> Breakpoint 2, main () at ../../../src/gdb/testsuite/gdb.threads/multiple-step-overs.c:99 >> 99 wait_threads (); /* set wait-threads breakpoint here */ >> (gdb) FAIL: gdb.threads/multiple-step-overs.exp: step >> >> But that "step" retriggers the same breakpoint instead of making >> progress. >> >> The patch teaches GDB to step over all breakpoints of all threads >> before resuming the stepping thread. >> >> Tested on x86_64 Fedora 17, against pristine mainline, and also my >> branch that implements software single-stepping on x86. >> >> gdb/ >> 2014-02-25 Pedro Alves >> >> * infrun.c (prepare_to_proceed): Delete. >> (thread_still_needs_step_over): New function. >> (find_thread_needs_step_over): New function. >> (proceed): If the current thread needs a step-over, set its >> steping_over_breakpoint flag. Adjust to use >> find_thread_needs_step_over instead of prepare_to_proceed. >> (process_event_stop_test): For BPSTAT_WHAT_STOP_NOISY and >> BPSTAT_WHAT_STOP_SILENT, assume the thread stopped for a >> breakpoint. >> (switch_back_to_stepped_thread): Step over breakpoints of all >> threads not the stepping thread, before switching back to the >> stepping thread. >> >> gdb/testsuite/ >> 2014-02-25 Pedro Alves >> >> * gdb.threads/multiple-step-overs.c: New file. >> * gdb.threads/multiple-step-overs.exp: New file. >> * gdb.threads/signal-while-stepping-over-bp-other-thread.exp: >> Adjust expected infrun debug output. >> --- >> gdb/infrun.c | 238 ++++++++++++--------- >> gdb/testsuite/gdb.threads/multiple-step-overs.c | 105 +++++++++ >> gdb/testsuite/gdb.threads/multiple-step-overs.exp | 80 +++++++ >> .../signal-while-stepping-over-bp-other-thread.exp | 2 +- >> 4 files changed, 326 insertions(+), 99 deletions(-) >> create mode 100644 gdb/testsuite/gdb.threads/multiple-step-overs.c >> create mode 100644 gdb/testsuite/gdb.threads/multiple-step-overs.exp >> >> diff --git a/gdb/infrun.c b/gdb/infrun.c >> index 1ea8d04..bde40c5 100644 >> --- a/gdb/infrun.c >> +++ b/gdb/infrun.c >> @@ -90,8 +90,6 @@ static int currently_stepping_or_nexting_callback (struct thread_info *tp, >> >> static void xdb_handle_command (char *args, int from_tty); >> >> -static int prepare_to_proceed (int); >> - >> static void print_exited_reason (int exitstatus); >> >> static void print_signal_exited_reason (enum gdb_signal siggnal); >> @@ -2053,75 +2051,74 @@ clear_proceed_status (void) >> } >> } >> >> -/* 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. */ >> +/* Returns true if TP is still stopped at a breakpoint that needs >> + stepping-over in order to make progress. If the breakpoint is gone >> + meanwhile, we can skip the whole step-over dance. */ >> >> static int >> -prepare_to_proceed (int step) >> +thread_still_needs_step_over (struct thread_info *tp) >> +{ >> + if (tp->stepping_over_breakpoint) >> + { >> + struct regcache *regcache = get_thread_regcache (tp->ptid); >> + >> + if (breakpoint_here_p (get_regcache_aspace (regcache), >> + regcache_read_pc (regcache))) >> + return 1; >> + >> + tp->stepping_over_breakpoint = 0; >> + } >> + >> + return 0; >> +} >> + >> +/* Look a thread other than EXCEPT that has previously reported a >> + breakpoint event, and thus needs a step-over in order to make >> + progress. Returns NULL is none is found. STEP indicates whether >> + we're about to step the current thread, in order to decide whether >> + "set scheduler-locking step" applies. */ >> + >> +static struct thread_info * >> +find_thread_needs_step_over (int step, struct thread_info *except) >> { >> - ptid_t wait_ptid; >> - struct target_waitstatus wait_status; >> int schedlock_enabled; >> + struct thread_info *tp, *current; >> >> /* 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); >> - >> - /* Make sure we were stopped at a breakpoint. */ >> - if (wait_status.kind != TARGET_WAITKIND_STOPPED >> - || (wait_status.value.sig != GDB_SIGNAL_TRAP >> - && wait_status.value.sig != GDB_SIGNAL_ILL >> - && wait_status.value.sig != GDB_SIGNAL_SEGV >> - && wait_status.value.sig != GDB_SIGNAL_EMT)) >> - { >> - return 0; >> - } >> - >> schedlock_enabled = (scheduler_mode == schedlock_on >> || (scheduler_mode == schedlock_step >> && step)); >> >> - /* Don't switch over to WAIT_PTID if scheduler locking is on. */ >> - if (schedlock_enabled) >> - return 0; >> - >> - /* Don't switch over if we're about to resume some other process >> - other than WAIT_PTID's, and schedule-multiple is off. */ >> - if (!sched_multi >> - && ptid_get_pid (wait_ptid) != ptid_get_pid (inferior_ptid)) >> - return 0; >> + current = inferior_thread (); >> >> - /* Switched over from WAIT_PID. */ >> - if (!ptid_equal (wait_ptid, minus_one_ptid) >> - && !ptid_equal (inferior_ptid, wait_ptid)) >> + /* If scheduler locking applies, we can avoid iterating over all >> + threads. */ >> + if (schedlock_enabled) >> { >> - struct regcache *regcache = get_thread_regcache (wait_ptid); >> + if (except != current >> + && thread_still_needs_step_over (current)) >> + return current; >> >> - if (breakpoint_here_p (get_regcache_aspace (regcache), >> - regcache_read_pc (regcache))) >> - { >> - /* Switch back to WAIT_PID thread. */ >> - switch_to_thread (wait_ptid); >> + return NULL; >> + } >> >> - if (debug_infrun) >> - fprintf_unfiltered (gdb_stdlog, >> - "infrun: prepare_to_proceed (step=%d), " >> - "switched to [%s]\n", >> - step, target_pid_to_str (inferior_ptid)); >> + ALL_THREADS (tp) >> + { >> + /* Ignore the EXCEPT thread. */ >> + if (tp == except) >> + continue; >> + /* Ignore threads of processes we're not resuming. */ >> + if (!sched_multi >> + && ptid_get_pid (tp->ptid) != ptid_get_pid (inferior_ptid)) >> + continue; >> >> - /* We return 1 to indicate that there is a breakpoint here, >> - so we need to step over it before continuing to avoid >> - hitting it straight away. */ >> - return 1; >> - } >> + if (thread_still_needs_step_over (tp)) >> + return tp; >> } >> >> - return 0; >> + return NULL; >> } >> >> /* Basic routine for continuing the program in various fashions. >> @@ -2144,8 +2141,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> struct thread_info *tp; >> CORE_ADDR pc; >> struct address_space *aspace; >> - /* GDB may force the inferior to step due to various reasons. */ >> - int force_step = 0; >> >> /* If we're stopped at a fork/vfork, follow the branch set by the >> "set follow-fork-mode" command; otherwise, we'll just proceed >> @@ -2173,6 +2168,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> if (step < 0) >> stop_after_trap = 1; >> >> + /* Fill in with reasonable starting values. */ >> + init_thread_stepping_state (tp); >> + >> if (addr == (CORE_ADDR) -1) >> { >> if (pc == stop_pc && breakpoint_here_p (aspace, pc) >> @@ -2185,14 +2183,13 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> Note, we don't do this in reverse, because we won't >> actually be executing the breakpoint insn anyway. >> We'll be (un-)executing the previous instruction. */ >> - >> - force_step = 1; >> + tp->stepping_over_breakpoint = 1; >> else if (gdbarch_single_step_through_delay_p (gdbarch) >> && gdbarch_single_step_through_delay (gdbarch, >> get_current_frame ())) >> /* We stepped onto an instruction that needs to be stepped >> again before re-inserting the breakpoint, do so. */ >> - force_step = 1; >> + tp->stepping_over_breakpoint = 1; >> } >> else >> { >> @@ -2211,6 +2208,8 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> ; >> else >> { >> + struct thread_info *step_over; >> + >> /* In a multi-threaded task we may select another thread and >> then continue or step. >> >> @@ -2219,31 +2218,29 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> execution (i.e. it will report a breakpoint hit incorrectly). >> So we must step over it first. >> >> - prepare_to_proceed checks the current thread against the >> - thread that reported the most recent event. If a step-over >> - is required it returns TRUE and sets the current thread to >> - the old thread. */ >> - >> - /* Store the prev_pc for the stepping thread too, needed by >> - switch_back_to_stepping thread. */ >> - tp->prev_pc = regcache_read_pc (get_current_regcache ()); >> - >> - if (prepare_to_proceed (step)) >> + Look for a thread other than the current (TP) that reported a >> + breakpoint hit and hasn't been resumed yet since. */ >> + step_over = find_thread_needs_step_over (step, tp); >> + if (step_over != NULL) >> { >> - force_step = 1; >> - /* The current thread changed. */ >> - tp = inferior_thread (); >> + if (debug_infrun) >> + fprintf_unfiltered (gdb_stdlog, >> + "infrun: need to step-over [%s] first\n", >> + target_pid_to_str (step_over->ptid)); >> + >> + /* Store the prev_pc for the stepping thread too, needed by >> + switch_back_to_stepping thread. */ >> + tp->prev_pc = regcache_read_pc (get_current_regcache ()); >> + switch_to_thread (step_over->ptid); >> + tp = step_over; >> } >> } >> >> - if (force_step) >> - tp->control.trap_expected = 1; >> - >> /* If we need to step over a breakpoint, and we're not using >> displaced stepping to do so, insert all breakpoints (watchpoints, >> etc.) but the one we're stepping over, step one instruction, and >> then re-insert the breakpoint when that step is finished. */ >> - if (tp->control.trap_expected && !use_displaced_stepping (gdbarch)) >> + if (tp->stepping_over_breakpoint && !use_displaced_stepping (gdbarch)) >> { >> struct regcache *regcache = get_current_regcache (); >> >> @@ -2258,6 +2255,8 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> >> insert_breakpoints (); >> >> + tp->control.trap_expected = tp->stepping_over_breakpoint; >> + >> if (!non_stop) >> { >> /* Pass the last stop signal to the thread we're resuming, >> @@ -2321,14 +2320,11 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) >> correctly when the inferior is stopped. */ >> tp->prev_pc = regcache_read_pc (get_current_regcache ()); >> >> - /* Fill in with reasonable starting values. */ >> - init_thread_stepping_state (tp); >> - >> /* Reset to normal state. */ >> init_infwait_state (); >> >> /* Resume inferior. */ >> - resume (force_step || step || bpstat_should_step (), >> + resume (tp->control.trap_expected || step || bpstat_should_step (), >> tp->suspend.stop_signal); >> >> /* Wait for it to stop (if not standalone) >> @@ -4468,8 +4464,10 @@ process_event_stop_test (struct execution_control_state *ecs) >> fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n"); >> stop_print_frame = 1; >> >> - /* We are about to nuke the step_resume_breakpointt via the >> - cleanup chain, so no need to worry about it here. */ >> + /* Assume the thread stopped for a breapoint. We'll still check >> + whether a/the breakpoint is there when the thread is next >> + resumed. */ >> + ecs->event_thread->stepping_over_breakpoint = 1; >> >> stop_stepping (ecs); >> return; >> @@ -4479,9 +4477,10 @@ process_event_stop_test (struct execution_control_state *ecs) >> fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_SILENT\n"); >> stop_print_frame = 0; >> >> - /* We are about to nuke the step_resume_breakpoin via the >> - cleanup chain, so no need to worry about it here. */ >> - >> + /* Assume the thread stopped for a breapoint. We'll still check >> + whether a/the breakpoint is there when the thread is next >> + resumed. */ >> + ecs->event_thread->stepping_over_breakpoint = 1; >> stop_stepping (ecs); >> return; >> >> @@ -5106,25 +5105,68 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) >> if (!non_stop) >> { >> struct thread_info *tp; >> - >> - tp = iterate_over_threads (currently_stepping_or_nexting_callback, >> - ecs->event_thread); >> - if (tp) >> + struct thread_info *stepping_thread; >> + >> + /* If any thread is blocked on some internal breakpoint, and we >> + simply need to step over that breakpoint to get it going >> + again, do that first. */ >> + >> + /* However, if we see an event for the stepping thread, then we >> + know all other threads have been moved past their breakpoints >> + already. Let the caller check whether the step is finished, >> + etc., before deciding to move it past a breakpoint. */ >> + if (ecs->event_thread->control.step_range_end) >> + return 0; >> + >> + /* Check if the current thread is blocking on an incomplete >> + step-over, interrupted by a random signal. */ >> + if (ecs->event_thread->control.trap_expected >> + && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_TRAP) >> { >> - struct frame_info *frame; >> - struct gdbarch *gdbarch; >> + if (debug_infrun) >> + { >> + fprintf_unfiltered (gdb_stdlog, >> + "infrun: need to finish step-over of [%s]\n", >> + target_pid_to_str (ecs->event_thread->ptid)); >> + } >> + keep_going (ecs); >> + return 1; >> + } >> >> - /* However, if the current thread is blocked on some internal >> - breakpoint, and we simply need to step over that breakpoint >> - to get it going again, do that first. */ >> - if ((ecs->event_thread->control.trap_expected >> - && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_TRAP) >> - || ecs->event_thread->stepping_over_breakpoint) >> + stepping_thread >> + = iterate_over_threads (currently_stepping_or_nexting_callback, >> + ecs->event_thread); >> + >> + /* Check if any other thread except the stepping thread that >> + needs to start a step-over. Do that before actually >> + proceeding with step/next/etc. */ >> + tp = find_thread_needs_step_over (stepping_thread != NULL, >> + stepping_thread); >> + if (tp != NULL) >> + { >> + if (debug_infrun) >> { >> - keep_going (ecs); >> - return 1; >> + fprintf_unfiltered (gdb_stdlog, >> + "infrun: need to step-over [%s]\n", >> + target_pid_to_str (tp->ptid)); >> } >> >> + gdb_assert (!tp->control.trap_expected); >> + gdb_assert (!tp->control.step_range_end); >> + >> + ecs->ptid = tp->ptid; >> + ecs->event_thread = tp; >> + switch_to_thread (ecs->ptid); >> + keep_going (ecs); >> + return 1; >> + } >> + >> + tp = stepping_thread; >> + if (tp != NULL) >> + { >> + struct frame_info *frame; >> + struct gdbarch *gdbarch; >> + >> /* If the stepping thread exited, then don't try to switch >> back and resume it, which could fail in several different >> ways depending on the target. Instead, just keep going. >> @@ -5687,7 +5729,7 @@ keep_going (struct execution_control_state *ecs) >> (watchpoints, etc.) but the one we're stepping over, step one >> instruction, and then re-insert the breakpoint when that step >> is finished. */ >> - if (ecs->event_thread->stepping_over_breakpoint >> + if (thread_still_needs_step_over (ecs->event_thread) >> && !use_displaced_stepping (get_regcache_arch (regcache))) >> { >> /* Can't step over more than one breakpoint simultaneously >> diff --git a/gdb/testsuite/gdb.threads/multiple-step-overs.c b/gdb/testsuite/gdb.threads/multiple-step-overs.c >> new file mode 100644 >> index 0000000..523a88a >> --- /dev/null >> +++ b/gdb/testsuite/gdb.threads/multiple-step-overs.c >> @@ -0,0 +1,105 @@ >> +/* This testcase is part of GDB, the GNU debugger. >> + >> + Copyright 2009-2014 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 >> +#include >> + >> +unsigned int args[2]; >> + >> +pthread_barrier_t barrier; >> +pthread_t child_thread_2, child_thread_3; >> + >> +void >> +callme (void) >> +{ >> +} >> + >> +void * >> +child_function_3 (void *arg) >> +{ >> + int my_number = (long) arg; >> + volatile int *myp = (int *) &args[my_number]; >> + >> + pthread_barrier_wait (&barrier); >> + >> + while (*myp > 0) >> + { >> + (*myp) ++; >> + callme (); /* set breakpoint thread 3 here */ >> + } >> + >> + pthread_exit (NULL); >> +} >> + >> +void * >> +child_function_2 (void *arg) >> +{ >> + int my_number = (long) arg; >> + volatile int *myp = (int *) &args[my_number]; >> + >> + pthread_barrier_wait (&barrier); >> + >> + while (*myp > 0) >> + { >> + (*myp) ++; >> + callme (); /* set breakpoint thread 2 here */ >> + } >> + >> + pthread_exit (NULL); >> +} >> + >> +static int >> +wait_threads (void) >> +{ >> + return 1; /* in wait_threads */ >> +} >> + >> +int >> +main () >> +{ >> + int res; >> + long i; >> + >> + /* Call these early so that PLTs for these are resolved soon, >> + instead of in the threads. RTLD_NOW should work as well. */ >> + usleep (0); >> + pthread_barrier_init (&barrier, NULL, 1); >> + pthread_barrier_wait (&barrier); >> + >> + pthread_barrier_init (&barrier, NULL, 2); >> + >> + i = 0; >> + args[i] = 1; >> + res = pthread_create (&child_thread_2, >> + NULL, child_function_2, (void *) i); >> + pthread_barrier_wait (&barrier); >> + callme (); >> + >> + i = 1; >> + args[i] = 1; >> + res = pthread_create (&child_thread_3, >> + NULL, child_function_3, (void *) i); >> + pthread_barrier_wait (&barrier); >> + wait_threads (); /* set wait-threads breakpoint here */ >> + >> + pthread_join (child_thread_2, NULL); >> + pthread_join (child_thread_3, NULL); >> + >> + exit(EXIT_SUCCESS); >> +} >> diff --git a/gdb/testsuite/gdb.threads/multiple-step-overs.exp b/gdb/testsuite/gdb.threads/multiple-step-overs.exp >> new file mode 100644 >> index 0000000..4426586 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.threads/multiple-step-overs.exp >> @@ -0,0 +1,80 @@ >> +# Copyright (C) 2011-2014 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 that GDB steps over all breakpoints of threads not the stepping >> +# thread, before actually proceeding with the stepped thread. >> + >> +standard_testfile >> +set executable ${testfile} >> + >> +if [target_info exists gdb,nosignals] { >> + verbose "Skipping ${testfile}.exp because of nosignals." >> + return -1 >> +} >> + >> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ >> + executable [list debug "incdir=${objdir}"]] != "" } { >> + return -1 >> +} >> + >> +# Prepare environment for test. PREFIX is used as prefix in test >> +# messages. >> + >> +proc setup { prefix } { >> + global executable >> + >> + with_test_prefix $prefix { >> + clean_restart $executable >> + >> + if ![runto_main] { >> + return -1 >> + } >> + >> + gdb_breakpoint [gdb_get_line_number "set wait-threads breakpoint here"] >> + gdb_continue_to_breakpoint "run to breakpoint" >> + gdb_test "info threads" "3 .* 2 .*\\\* 1.*" "info threads shows all threads" >> + >> + gdb_test_no_output "set scheduler-locking on" >> + >> + gdb_breakpoint [gdb_get_line_number "set breakpoint thread 3 here"] >> + gdb_breakpoint [gdb_get_line_number "set breakpoint thread 2 here"] >> + >> + gdb_test "thread 3" "Switching.*" >> + gdb_continue_to_breakpoint "run to breakpoint in thread 3" >> + gdb_test "p *myp = 0" " = 0" "unbreak loop in thread 3" >> + >> + gdb_test "thread 2" "Switching.*" >> + gdb_continue_to_breakpoint "run to breakpoint in thread 2" >> + gdb_test "p *myp = 0" " = 0" "unbreak loop in thread 2" >> + >> + # Switch back to thread 1 and disable scheduler locking. >> + gdb_test "thread 1" "Switching.*" >> + gdb_test "set scheduler-locking off" >> + >> + # Now all 3 threads are stopped for a breakpoint that needs to >> + # be stepped over before thread 1 is resumed. >> + } >> +} >> + >> +setup "step" >> +gdb_test "step" "in wait_threads .*" >> + >> +setup "next" >> +gdb_test "set debug infrun 1" ".*" >> +gdb_test "next" "pthread_join.*" >> + >> +setup "continue" >> +gdb_breakpoint [gdb_get_line_number "EXIT_SUCCESS"] >> +gdb_test "continue" "EXIT_SUCCESS.*" >> diff --git a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp >> index bf5ea60..c46dad2 100644 >> --- a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp >> +++ b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp >> @@ -107,7 +107,7 @@ gdb_test "set debug infrun 1" >> >> gdb_test \ >> "step" \ >> - ".*prepare_to_proceed \\(step=1\\), switched to.*signal arrived while stepping over breakpoint.*switching back to stepped thread.*stepped to a different line.*callme.*" \ >> + ".*need to step-over.*resume \\(step=1.*signal arrived while stepping over breakpoint.*switching back to stepped thread.*stepped to a different line.*callme.*" \ >> "step" >> >> set cnt_after [get_value "args\[$my_number\]" "get count after step"] >> -- >> 1.7.11.7 >> >