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:
parent
30b3cc2c6f
commit
aa377789b0
@ -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,
|
||||
}})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user