diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index fa0e4f11..8b3d6cda 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -2334,10 +2334,10 @@ func digits(n int) int { const stacktraceTruncatedMessage = "(truncated)" func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) { - PrintStack(t.formatPath, out, stack, ind, offsets) + PrintStack(t.formatPath, out, stack, ind, offsets, func(api.Stackframe) bool { return true }) } -func PrintStack(formatPath func(string) string, out io.Writer, stack []api.Stackframe, ind string, offsets bool) { +func PrintStack(formatPath func(string) string, out io.Writer, stack []api.Stackframe, ind string, offsets bool, include func(api.Stackframe) bool) { if len(stack) == 0 { return } @@ -2355,6 +2355,9 @@ func PrintStack(formatPath func(string) string, out io.Writer, stack []api.Stack s := ind + strings.Repeat(" ", d+2+len(ind)) for i := range stack { + if !include(stack[i]) { + continue + } if stack[i].Err != "" { fmt.Fprintf(out, "%serror: %s\n", s, stack[i].Err) continue diff --git a/service/dap/server.go b/service/dap/server.go index 5b8cbc71..7da8b033 100644 --- a/service/dap/server.go +++ b/service/dap/server.go @@ -1320,6 +1320,14 @@ func fnName(loc *proc.Location) string { return loc.Fn.Name } +func fnPackageName(loc *proc.Location) string { + if loc.Fn == nil { + // attribute unknown functions to the runtime + return "runtime" + } + return loc.Fn.PackageName() +} + // onThreadsRequest handles 'threads' request. // This is a mandatory request to support. // It is sent in response to configurationDone response and stopped events. @@ -1528,6 +1536,15 @@ func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) { return } + // Determine if the goroutine is a system goroutine. + // TODO(suzmue): Use the System() method defined in: https://github.com/go-delve/delve/pull/2504 + g, err := s.debugger.FindGoroutine(goroutineID) + var isSystemGoroutine bool + if err == nil { + userLoc := g.UserCurrent() + isSystemGoroutine = fnPackageName(&userLoc) == "runtime" + } + stackFrames := make([]dap.StackFrame, len(frames)) for i, frame := range frames { loc := &frame.Call @@ -1538,6 +1555,11 @@ func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) { stackFrames[i].Source = dap.Source{Name: filepath.Base(clientPath), Path: clientPath} } stackFrames[i].Column = 0 + + packageName := fnPackageName(loc) + if !isSystemGoroutine && packageName == "runtime" { + stackFrames[i].Source.PresentationHint = "deemphasize" + } } // Since the backend doesn't support paging, we load all frames up to // pre-configured depth every time and then slice them here per @@ -2446,7 +2468,16 @@ func (s *Server) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) { if err == nil { var buf bytes.Buffer fmt.Fprintln(&buf, "Stack:") - terminal.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false) + userLoc := g.UserCurrent() + userFuncPkg := fnPackageName(&userLoc) + terminal.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false, func(s api.Stackframe) bool { + // Include all stack frames if the stack trace is for a system goroutine, + // otherwise, skip runtime stack frames. + if userFuncPkg == "runtime" { + return true + } + return s.Location.Function != nil && !strings.HasPrefix(s.Location.Function.Name(), "runtime.") + }) body.Details.StackTrace = buf.String() } } diff --git a/service/dap/server_test.go b/service/dap/server_test.go index 6a82d11e..a027ba35 100644 --- a/service/dap/server_test.go +++ b/service/dap/server_test.go @@ -516,7 +516,7 @@ func TestPreSetBreakpoint(t *testing.T) { wantMain := dap.Thread{Id: 1, Name: "* [Go 1] main.Increment (Thread ...)"} wantRuntime := dap.Thread{Id: 2, Name: "[Go 2] runtime.gopark"} for _, got := range tResp.Body.Threads { - if got.Id != 1 && !reMain.MatchString(got.Name) && !strings.Contains(got.Name, "runtime") { + if got.Id != 1 && !reMain.MatchString(got.Name) && !strings.Contains(got.Name, "runtime.") { t.Errorf("\ngot %#v\nwant []dap.Thread{%#v, %#v, ...}", tResp.Body.Threads, wantMain, wantRuntime) } } @@ -3373,6 +3373,19 @@ func TestPanicBreakpointOnContinue(t *testing.T) { if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != "\"BOOM!\"" { t.Errorf("\ngot %#v\nwant ExceptionId=\"panic\" Description=\"\"BOOM!\"\"", eInfo) } + + client.StackTraceRequest(se.Body.ThreadId, 0, 20) + st := client.ExpectStackTraceResponse(t) + for i, frame := range st.Body.StackFrames { + if strings.HasPrefix(frame.Name, "runtime.") { + if frame.Source.PresentationHint != "deemphasize" { + t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"deemphasize\"", i, frame) + } + } else if frame.Source.PresentationHint != "" { + t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame) + } + + } }, disconnect: true, }})