terminal,api: move PrintStack function (#2537)

Commit 30cdedae6910f5e9af6739845bacfd5b8778e745 introduced a dependency
from service/dap to pkg/terminal to call a stack printing function,
it's weird to have code that implements the DAP protocol depend on the
code for the JSON-RPC client.
Move PrintStack to a different package that can be called by both.
This commit is contained in:
Alessandro Arzilli 2021-06-16 22:05:17 +02:00 committed by GitHub
parent 1ecdb3be05
commit 29825d41a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 66 deletions

@ -2331,71 +2331,8 @@ func digits(n int) int {
return int(math.Floor(math.Log10(float64(n)))) + 1
}
const stacktraceTruncatedMessage = "(truncated)"
func printStack(t *Term, out io.Writer, stack []api.Stackframe, ind string, offsets bool) {
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, include func(api.Stackframe) bool) {
if len(stack) == 0 {
return
}
extranl := offsets
for i := range stack {
if extranl {
break
}
extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
}
d := digits(len(stack) - 1)
fmtstr := "%s%" + strconv.Itoa(d) + "d 0x%016x in %s\n"
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
}
fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s, formatPath(stack[i].File), stack[i].Line)
if offsets {
fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
}
for j, d := range stack[i].Defers {
deferHeader := fmt.Sprintf("%s defer %d: ", s, j+1)
s2 := strings.Repeat(" ", len(deferHeader))
if d.Unreadable != "" {
fmt.Fprintf(out, "%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
continue
}
fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s2, formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), formatPath(d.DeferLoc.File), d.DeferLoc.Line)
}
for j := range stack[i].Arguments {
fmt.Fprintf(out, "%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
}
for j := range stack[i].Locals {
fmt.Fprintf(out, "%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
}
if extranl {
fmt.Fprintln(out)
}
}
if len(stack) > 0 && !stack[len(stack)-1].Bottom {
fmt.Fprintf(out, "%s"+stacktraceTruncatedMessage+"\n", ind)
}
api.PrintStack(t.formatPath, out, stack, ind, offsets, func(api.Stackframe) bool { return true })
}
func printcontext(t *Term, state *api.DebuggerState) {

@ -943,6 +943,7 @@ func TestOptimizationCheck(t *testing.T) {
}
func TestTruncateStacktrace(t *testing.T) {
const stacktraceTruncatedMessage = "(truncated)"
withTestTerminal("stacktraceprog", t, func(term *FakeTerminal) {
term.MustExec("break main.stacktraceme")
term.MustExec("continue")

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"math"
"reflect"
"strconv"
"strings"
@ -493,3 +494,73 @@ func byteArrayToUInt64(buf []byte, isLittleEndian bool) uint64 {
}
return n
}
const stacktraceTruncatedMessage = "(truncated)"
func digits(n int) int {
if n <= 0 {
return 1
}
return int(math.Floor(math.Log10(float64(n)))) + 1
}
func PrintStack(formatPath func(string) string, out io.Writer, stack []Stackframe, ind string, offsets bool, include func(Stackframe) bool) {
if len(stack) == 0 {
return
}
extranl := offsets
for i := range stack {
if extranl {
break
}
extranl = extranl || (len(stack[i].Defers) > 0) || (len(stack[i].Arguments) > 0) || (len(stack[i].Locals) > 0)
}
d := digits(len(stack) - 1)
fmtstr := "%s%" + strconv.Itoa(d) + "d 0x%016x in %s\n"
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
}
fmt.Fprintf(out, fmtstr, ind, i, stack[i].PC, stack[i].Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s, formatPath(stack[i].File), stack[i].Line)
if offsets {
fmt.Fprintf(out, "%sframe: %+#x frame pointer %+#x\n", s, stack[i].FrameOffset, stack[i].FramePointerOffset)
}
for j, d := range stack[i].Defers {
deferHeader := fmt.Sprintf("%s defer %d: ", s, j+1)
s2 := strings.Repeat(" ", len(deferHeader))
if d.Unreadable != "" {
fmt.Fprintf(out, "%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
continue
}
fmt.Fprintf(out, "%s%#016x in %s\n", deferHeader, d.DeferredLoc.PC, d.DeferredLoc.Function.Name())
fmt.Fprintf(out, "%sat %s:%d\n", s2, formatPath(d.DeferredLoc.File), d.DeferredLoc.Line)
fmt.Fprintf(out, "%sdeferred by %s at %s:%d\n", s2, d.DeferLoc.Function.Name(), formatPath(d.DeferLoc.File), d.DeferLoc.Line)
}
for j := range stack[i].Arguments {
fmt.Fprintf(out, "%s %s = %s\n", s, stack[i].Arguments[j].Name, stack[i].Arguments[j].SinglelineString())
}
for j := range stack[i].Locals {
fmt.Fprintf(out, "%s %s = %s\n", s, stack[i].Locals[j].Name, stack[i].Locals[j].SinglelineString())
}
if extranl {
fmt.Fprintln(out)
}
}
if len(stack) > 0 && !stack[len(stack)-1].Bottom {
fmt.Fprintf(out, "%s"+stacktraceTruncatedMessage+"\n", ind)
}
}

@ -32,7 +32,6 @@ import (
"github.com/go-delve/delve/pkg/locspec"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/terminal"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/debugger"
@ -2475,7 +2474,7 @@ func (s *Server) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) {
fmt.Fprintln(&buf, "Stack:")
userLoc := g.UserCurrent()
userFuncPkg := fnPackageName(&userLoc)
terminal.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false, func(s api.Stackframe) bool {
api.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" {