From ac74944d53ae08faa2f35301d5f40fa1f2b4b5a6 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Sat, 18 Aug 2018 12:12:39 +0200 Subject: [PATCH] proc,service,terminal: information about stack trace truncation Add a flag to Stackframe that indicates where the stack frame is the bottom-most frame of the stack. This allows clients to know whether the stack trace terminated normally or if it was truncated because the maximum depth was reached. Add a truncation message to the 'stack' command. --- pkg/proc/stack.go | 3 +++ pkg/terminal/command.go | 6 ++++++ pkg/terminal/command_test.go | 19 ++++++++++++++++++- service/api/types.go | 2 ++ service/debugger/debugger.go | 2 ++ service/rpc2/server.go | 4 ++-- 6 files changed, 33 insertions(+), 3 deletions(-) diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 8222aeec..5c7b3f17 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -47,6 +47,8 @@ type Stackframe struct { SystemStack bool // Inlined is true if this frame is actually an inlined call. Inlined bool + // Bottom is true if this is the bottom of the stack + Bottom bool // lastpc is a memory address guaranteed to belong to the last instruction // executed in this stack frame. @@ -339,6 +341,7 @@ func (it *stackIterator) switchStack() bool { // Frame returns the frame the iterator is pointing at. func (it *stackIterator) Frame() Stackframe { + it.frame.Bottom = it.atend return it.frame } diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 29273654..5350a32a 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -1530,6 +1530,8 @@ func digits(n int) int { return int(math.Floor(math.Log10(float64(n)))) + 1 } +const stacktraceTruncatedMessage = "(truncated)" + func printStack(stack []api.Stackframe, ind string, offsets bool) { if len(stack) == 0 { return @@ -1582,6 +1584,10 @@ func printStack(stack []api.Stackframe, ind string, offsets bool) { fmt.Println() } } + + if len(stack) > 0 && !stack[len(stack)-1].Bottom { + fmt.Printf("%s"+stacktraceTruncatedMessage+"\n", ind) + } } func printcontext(t *Term, state *api.DebuggerState) error { diff --git a/pkg/terminal/command_test.go b/pkg/terminal/command_test.go index bf8bb8bd..0715e206 100644 --- a/pkg/terminal/command_test.go +++ b/pkg/terminal/command_test.go @@ -224,7 +224,7 @@ func TestExecuteFile(t *testing.T) { func TestIssue354(t *testing.T) { printStack([]api.Stackframe{}, "", false) - printStack([]api.Stackframe{{api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, nil, nil, 0, 0, nil, ""}}, "", false) + printStack([]api.Stackframe{{api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil}, nil, nil, 0, 0, nil, true, ""}}, "", false) } func TestIssue411(t *testing.T) { @@ -827,3 +827,20 @@ func TestOptimizationCheck(t *testing.T) { }) } } + +func TestTruncateStacktrace(t *testing.T) { + withTestTerminal("stacktraceprog", t, func(term *FakeTerminal) { + term.MustExec("break main.stacktraceme") + term.MustExec("continue") + out1 := term.MustExec("stack") + t.Logf("untruncated output %q", out1) + if strings.Contains(out1, stacktraceTruncatedMessage) { + t.Fatalf("stacktrace was truncated") + } + out2 := term.MustExec("stack 1") + t.Logf("truncated output %q", out2) + if !strings.Contains(out2, stacktraceTruncatedMessage) { + t.Fatalf("stacktrace was not truncated") + } + }) +} diff --git a/service/api/types.go b/service/api/types.go index 61b4c0f3..df047d59 100644 --- a/service/api/types.go +++ b/service/api/types.go @@ -131,6 +131,8 @@ type Stackframe struct { Defers []Defer + Bottom bool `json:"Bottom,omitempty"` // Bottom is true if this is the bottom frame of the stack + Err string } diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 2c3a8283..96df5c5b 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -932,6 +932,8 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo FramePointerOffset: rawlocs[i].FramePointerOffset(), Defers: d.convertDefers(rawlocs[i].Defers), + + Bottom: rawlocs[i].Bottom, } if rawlocs[i].Err != nil { frame.Err = rawlocs[i].Err.Error() diff --git a/service/rpc2/server.go b/service/rpc2/server.go index 7822e16c..cde9dbe5 100644 --- a/service/rpc2/server.go +++ b/service/rpc2/server.go @@ -172,11 +172,11 @@ func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error { if cfg == nil && arg.Full { cfg = &api.LoadConfig{true, 1, 64, 64, -1} } - locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Defers, api.LoadConfigToProc(cfg)) + var err error + out.Locations, err = s.debugger.Stacktrace(arg.Id, arg.Depth, arg.Defers, api.LoadConfigToProc(cfg)) if err != nil { return err } - out.Locations = locs return nil }