From eb0b4e93921f07638033403a7cc2e8be09b029f1 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Fri, 21 Aug 2015 22:13:54 -0500 Subject: [PATCH] proc.Next: Further improve handling of highly parallel programs This patch forces Delve to be more mindful of how it handles many threads and the goroutine context switching that occurs in such cases. --- proc/proc.go | 75 ++++++++++++++++++++-------------------------- proc/proc_darwin.c | 5 ++-- 2 files changed, 34 insertions(+), 46 deletions(-) diff --git a/proc/proc.go b/proc/proc.go index 64eba5de..0d7b4a27 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -272,64 +272,53 @@ func (dbp *Process) next() (err error) { return err } - var goroutineExiting bool - threadNext := func(thread *Thread) error { - if err = thread.setNextBreakpoints(); err != nil { - switch t := err.(type) { - case ThreadBlockedError, NoReturnAddr: // Noop - case GoroutineExitingError: - goroutineExiting = t.goid == g.Id - default: - return err - } - } - return thread.Continue() - } - // Make sure that we halt the process at the end of this // function. We could get into a situation where we have // started some, but not all threads. defer func() { err = dbp.Halt() }() - // Set next breakpoints and then continue each thread. + var goroutineExiting bool + if err = dbp.CurrentThread.setNextBreakpoints(); err != nil { + switch t := err.(type) { + case ThreadBlockedError, NoReturnAddr: // Noop + case GoroutineExitingError: + goroutineExiting = t.goid == g.Id + default: + return err + } + } + for _, th := range dbp.Threads { - if err := threadNext(th); err != nil { + if err := th.Continue(); err != nil { return err } } for { - if _, err := dbp.trapWait(-1); err != nil { + th, err := dbp.trapWait(-1) + if err != nil { return err } - // We need to wait for our goroutine to execute, which may not happen - // immediately. - // - // Loop through all threads, and for each stopped thread - // see if it is the thread that we care about (thread.g == original.g). - // If so, we're done. Otherwise set next temp breakpoints for - // each thread and continue them. The reason we do this is because - // if our goroutine is paused, we must execute other threads in order - // for them to get to a scheduling point, so they can pick up the - // goroutine we care about and begin executing it. - for _, thr := range dbp.Threads { - if !thr.Stopped() { - continue - } - tg, err := thr.GetG() - if err != nil { - return err - } - // Make sure we're on the same goroutine, unless it has exited. - if tg.Id == g.Id || goroutineExiting { - if dbp.CurrentThread != thr { - dbp.SwitchThread(thr.Id) + tg, err := th.GetG() + if err != nil { + return err + } + // Make sure we're on the same goroutine, unless it has exited. + if tg.Id == g.Id || goroutineExiting { + // Check to see if the goroutine has switched to another + // thread, if so make it the current thread. + if dbp.CurrentThread.Id != th.Id { + if err := dbp.SwitchThread(th.Id); err != nil { + return err } - return nil - } - if err := threadNext(thr); err != nil { - return err } + return nil + } + // This thread was not running our goroutine. + // We continue it since our goroutine could + // potentially be on this threads queue. + if err := th.Continue(); err != nil { + return err } } } diff --git a/proc/proc_darwin.c b/proc/proc_darwin.c index 238fdd82..1bb02c22 100644 --- a/proc/proc_darwin.c +++ b/proc/proc_darwin.c @@ -145,13 +145,12 @@ mach_port_wait(mach_port_t port_set) { return mach_port_wait(port_set); } } - break; + return thread; case 72: // Death return msg.hdr.msgh_local_port; } - - return thread; + return 0; } kern_return_t