summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-errors/errors/stackframe.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-errors/errors/stackframe.go')
-rw-r--r--vendor/github.com/go-errors/errors/stackframe.go102
1 files changed, 102 insertions, 0 deletions
diff --git a/vendor/github.com/go-errors/errors/stackframe.go b/vendor/github.com/go-errors/errors/stackframe.go
new file mode 100644
index 000000000..750ab9a52
--- /dev/null
+++ b/vendor/github.com/go-errors/errors/stackframe.go
@@ -0,0 +1,102 @@
+package errors
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "runtime"
+ "strings"
+)
+
+// A StackFrame contains all necessary information about to generate a line
+// in a callstack.
+type StackFrame struct {
+ // The path to the file containing this ProgramCounter
+ File string
+ // The LineNumber in that file
+ LineNumber int
+ // The Name of the function that contains this ProgramCounter
+ Name string
+ // The Package that contains this function
+ Package string
+ // The underlying ProgramCounter
+ ProgramCounter uintptr
+}
+
+// NewStackFrame popoulates a stack frame object from the program counter.
+func NewStackFrame(pc uintptr) (frame StackFrame) {
+
+ frame = StackFrame{ProgramCounter: pc}
+ if frame.Func() == nil {
+ return
+ }
+ frame.Package, frame.Name = packageAndName(frame.Func())
+
+ // pc -1 because the program counters we use are usually return addresses,
+ // and we want to show the line that corresponds to the function call
+ frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1)
+ return
+
+}
+
+// Func returns the function that contained this frame.
+func (frame *StackFrame) Func() *runtime.Func {
+ if frame.ProgramCounter == 0 {
+ return nil
+ }
+ return runtime.FuncForPC(frame.ProgramCounter)
+}
+
+// String returns the stackframe formatted in the same way as go does
+// in runtime/debug.Stack()
+func (frame *StackFrame) String() string {
+ str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter)
+
+ source, err := frame.SourceLine()
+ if err != nil {
+ return str
+ }
+
+ return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source)
+}
+
+// SourceLine gets the line of code (from File and Line) of the original source if possible.
+func (frame *StackFrame) SourceLine() (string, error) {
+ data, err := ioutil.ReadFile(frame.File)
+
+ if err != nil {
+ return "", New(err)
+ }
+
+ lines := bytes.Split(data, []byte{'\n'})
+ if frame.LineNumber <= 0 || frame.LineNumber >= len(lines) {
+ return "???", nil
+ }
+ // -1 because line-numbers are 1 based, but our array is 0 based
+ return string(bytes.Trim(lines[frame.LineNumber-1], " \t")), nil
+}
+
+func packageAndName(fn *runtime.Func) (string, string) {
+ name := fn.Name()
+ pkg := ""
+
+ // 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
+ // Since the package path might contains dots (e.g. code.google.com/...),
+ // we first remove the path prefix if there is one.
+ if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
+ pkg += name[:lastslash] + "/"
+ name = name[lastslash+1:]
+ }
+ if period := strings.Index(name, "."); period >= 0 {
+ pkg += name[:period]
+ name = name[period+1:]
+ }
+
+ name = strings.Replace(name, "·", ".", -1)
+ return pkg, name
+}