From 6d1c00e56de888e1cb73b53ad2e0a26fbb5d83d0 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Tue, 5 Jan 2021 19:59:17 +0100 Subject: [PATCH] terminal,service: print WaitReason, WaitSince for goroutines (#2270) Fixes #637 --- cmd/dlv/dlv_test.go | 4 +-- pkg/terminal/command.go | 54 +++++++++++++++++++++++++++++++++++--- service/api/conversions.go | 1 + service/api/types.go | 6 +++++ 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index 52ab06b5..e5e58b7f 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -322,9 +322,7 @@ func TestRedirect(t *testing.T) { // and detach from and kill the headless instance client := rpc2.NewClient(listenAddr) - if err := client.Detach(true); err != nil { - t.Fatalf("error detaching from headless instance: %v", err) - } + _ = client.Detach(true) cmd.Wait() } diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index eac66195..c68dccbe 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -20,6 +20,7 @@ import ( "strconv" "strings" "text/tabwriter" + "time" "github.com/cosiner/argv" "github.com/go-delve/delve/pkg/locspec" @@ -922,11 +923,58 @@ func (t *Term) formatGoroutine(g *api.Goroutine, fgl formatGoroutineLoc) string locname = "Start" loc = g.StartLoc } - thread := "" + + buf := new(strings.Builder) + fmt.Fprintf(buf, "%d - %s: %s", g.ID, locname, t.formatLocation(loc)) if g.ThreadID != 0 { - thread = fmt.Sprintf(" (thread %d)", g.ThreadID) + fmt.Fprintf(buf, " (thread %d)", g.ThreadID) } - return fmt.Sprintf("%d - %s: %s%s", g.ID, locname, t.formatLocation(loc), thread) + + if (g.Status == api.GoroutineWaiting || g.Status == api.GoroutineSyscall) && g.WaitReason != 0 { + var wr string + if g.WaitReason > 0 && g.WaitReason < int64(len(waitReasonStrings)) { + wr = waitReasonStrings[g.WaitReason] + } else { + wr = fmt.Sprintf("unknown wait reason %d", g.WaitReason) + } + fmt.Fprintf(buf, " [%s", wr) + if g.WaitSince > 0 { + fmt.Fprintf(buf, " %s", time.Since(time.Unix(0, g.WaitSince)).String()) + } + fmt.Fprintf(buf, "]") + } + + return buf.String() +} + +var waitReasonStrings = [...]string{ + "", + "GC assist marking", + "IO wait", + "chan receive (nil chan)", + "chan send (nil chan)", + "dumping heap", + "garbage collection", + "garbage collection scan", + "panicwait", + "select", + "select (no cases)", + "GC assist wait", + "GC sweep wait", + "GC scavenge wait", + "chan receive", + "chan send", + "finalizer wait", + "force gc (idle)", + "semacquire", + "sleep", + "sync.Cond.Wait", + "timer goroutine (idle)", + "trace reader (blocked)", + "wait for GC cycle", + "GC worker (idle)", + "preempted", + "debug call", } func writeGoroutineLong(t *Term, w io.Writer, g *api.Goroutine, prefix string) { diff --git a/service/api/conversions.go b/service/api/conversions.go index 76151528..29a59f55 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -298,6 +298,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine { WaitSince: g.WaitSince, WaitReason: g.WaitReason, Labels: g.Labels(), + Status: g.Status, } } diff --git a/service/api/types.go b/service/api/types.go index a64a5da9..7e4bdbd4 100644 --- a/service/api/types.go +++ b/service/api/types.go @@ -314,6 +314,7 @@ type Goroutine struct { StartLoc Location `json:"startLoc"` // ID of the associated thread for running goroutines ThreadID int `json:"threadID"` + Status uint64 `json:"status"` WaitSince int64 `json:"waitSince"` WaitReason int64 `json:"waitReason"` Unreadable string `json:"unreadable"` @@ -321,6 +322,11 @@ type Goroutine struct { Labels map[string]string `json:"labels,omitempty"` } +const ( + GoroutineWaiting = proc.Gwaiting + GoroutineSyscall = proc.Gsyscall +) + // DebuggerCommand is a command which changes the debugger's execution state. type DebuggerCommand struct { // Name is the command to run.