diff options
| author | Jeff Carr <[email protected]> | 2025-03-17 16:45:22 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-03-17 16:45:22 -0500 |
| commit | 5a9ae561ee0dbd9b82285594b6fb8b9fe3eea0bd (patch) | |
| tree | 42c95dc30a1fc0bf77b991a559b0407300fa5a52 /stack.go | |
gin for protobufs Day1v0.0.1
Diffstat (limited to 'stack.go')
| -rw-r--r-- | stack.go | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/stack.go b/stack.go new file mode 100644 index 0000000..d4fed60 --- /dev/null +++ b/stack.go @@ -0,0 +1,91 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package pinpb + +// this is similar to 'gin' but specifically only for +// sending and working with protocol buffers +// +// also, it is as close to possible a golang 'primitive' +// package (there is no go.sum file) + +import ( + "bytes" + "fmt" + "os" + "runtime" + "time" +) + +var ( + dunno = []byte("???") + centerDot = []byte("·") + dot = []byte(".") + slash = []byte("/") +) + +// stack returns a nicely formatted stack frame, skipping skip frames. +func Stack(skip int) []byte { + buf := new(bytes.Buffer) // the returned data + // As we loop, we open files and read them. These variables record the currently + // loaded file. + var lines [][]byte + var lastFile string + for i := skip; ; i++ { // Skip the expected number of frames + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + // Print this much at least. If we can't find the source, it won't show. + fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) + if file != lastFile { + data, err := os.ReadFile(file) + if err != nil { + continue + } + lines = bytes.Split(data, []byte{'\n'}) + lastFile = file + } + fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) + } + return buf.Bytes() +} + +// source returns a space-trimmed slice of the n'th line. +func source(lines [][]byte, n int) []byte { + n-- // in stack trace, lines are 1-indexed but our array is 0-indexed + if n < 0 || n >= len(lines) { + return dunno + } + return bytes.TrimSpace(lines[n]) +} + +// function returns, if possible, the name of the function containing the PC. +func function(pc uintptr) []byte { + fn := runtime.FuncForPC(pc) + if fn == nil { + return dunno + } + name := []byte(fn.Name()) + // The name includes the path name to the package, which is unnecessary + // since the file name is already included. Plus, it has center dots. + // That is, we see + // runtime/debug.*T·ptrmethod + // and want + // *T.ptrmethod + // Also the package path might contain dot (e.g. code.google.com/...), + // so first eliminate the path prefix + if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { + name = name[lastSlash+1:] + } + if period := bytes.Index(name, dot); period >= 0 { + name = name[period+1:] + } + name = bytes.ReplaceAll(name, centerDot, dot) + return name +} + +// timeFormat returns a customized time string for logger. +func timeFormat(t time.Time) string { + return t.Format("2006/01/02 - 15:04:05") +} |
