service/dap: deemphasize internal runtime stack frames (#2522)

Apply a presentation hint to the internal runtime stack frames, so that these can be deemphasized in the UI. This should allow
users to more easily inspect their own code, and will keep the option to view those frames if they choose to.
This commit is contained in:
Suzy Mueller 2021-06-10 13:59:24 -04:00 committed by GitHub
parent 30b3cc2c6f
commit aa377789b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 4 deletions

@ -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

@ -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()
}
}

@ -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,
}})