3 import ( |
3 import ( |
4 "fmt" |
4 "fmt" |
5 "io" |
5 "io" |
6 "path" |
6 "path" |
7 "runtime" |
7 "runtime" |
|
8 "strconv" |
8 "strings" |
9 "strings" |
9 ) |
10 ) |
10 |
11 |
11 // Frame represents a program counter inside a stack frame. |
12 // Frame represents a program counter inside a stack frame. |
|
13 // For historical reasons if Frame is interpreted as a uintptr |
|
14 // its value represents the program counter + 1. |
12 type Frame uintptr |
15 type Frame uintptr |
13 |
16 |
14 // pc returns the program counter for this frame; |
17 // pc returns the program counter for this frame; |
15 // multiple frames may have the same PC value. |
18 // multiple frames may have the same PC value. |
16 func (f Frame) pc() uintptr { return uintptr(f) - 1 } |
19 func (f Frame) pc() uintptr { return uintptr(f) - 1 } |
52 func (f Frame) Format(s fmt.State, verb rune) { |
64 func (f Frame) Format(s fmt.State, verb rune) { |
53 switch verb { |
65 switch verb { |
54 case 's': |
66 case 's': |
55 switch { |
67 switch { |
56 case s.Flag('+'): |
68 case s.Flag('+'): |
57 pc := f.pc() |
69 io.WriteString(s, f.name()) |
58 fn := runtime.FuncForPC(pc) |
70 io.WriteString(s, "\n\t") |
59 if fn == nil { |
71 io.WriteString(s, f.file()) |
60 io.WriteString(s, "unknown") |
|
61 } else { |
|
62 file, _ := fn.FileLine(pc) |
|
63 fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) |
|
64 } |
|
65 default: |
72 default: |
66 io.WriteString(s, path.Base(f.file())) |
73 io.WriteString(s, path.Base(f.file())) |
67 } |
74 } |
68 case 'd': |
75 case 'd': |
69 fmt.Fprintf(s, "%d", f.line()) |
76 io.WriteString(s, strconv.Itoa(f.line())) |
70 case 'n': |
77 case 'n': |
71 name := runtime.FuncForPC(f.pc()).Name() |
78 io.WriteString(s, funcname(f.name())) |
72 io.WriteString(s, funcname(name)) |
|
73 case 'v': |
79 case 'v': |
74 f.Format(s, 's') |
80 f.Format(s, 's') |
75 io.WriteString(s, ":") |
81 io.WriteString(s, ":") |
76 f.Format(s, 'd') |
82 f.Format(s, 'd') |
77 } |
83 } |
|
84 } |
|
85 |
|
86 // MarshalText formats a stacktrace Frame as a text string. The output is the |
|
87 // same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. |
|
88 func (f Frame) MarshalText() ([]byte, error) { |
|
89 name := f.name() |
|
90 if name == "unknown" { |
|
91 return []byte(name), nil |
|
92 } |
|
93 return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil |
78 } |
94 } |
79 |
95 |
80 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). |
96 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). |
81 type StackTrace []Frame |
97 type StackTrace []Frame |
82 |
98 |
92 switch verb { |
108 switch verb { |
93 case 'v': |
109 case 'v': |
94 switch { |
110 switch { |
95 case s.Flag('+'): |
111 case s.Flag('+'): |
96 for _, f := range st { |
112 for _, f := range st { |
97 fmt.Fprintf(s, "\n%+v", f) |
113 io.WriteString(s, "\n") |
|
114 f.Format(s, verb) |
98 } |
115 } |
99 case s.Flag('#'): |
116 case s.Flag('#'): |
100 fmt.Fprintf(s, "%#v", []Frame(st)) |
117 fmt.Fprintf(s, "%#v", []Frame(st)) |
101 default: |
118 default: |
102 fmt.Fprintf(s, "%v", []Frame(st)) |
119 st.formatSlice(s, verb) |
103 } |
120 } |
104 case 's': |
121 case 's': |
105 fmt.Fprintf(s, "%s", []Frame(st)) |
122 st.formatSlice(s, verb) |
106 } |
123 } |
|
124 } |
|
125 |
|
126 // formatSlice will format this StackTrace into the given buffer as a slice of |
|
127 // Frame, only valid when called with '%s' or '%v'. |
|
128 func (st StackTrace) formatSlice(s fmt.State, verb rune) { |
|
129 io.WriteString(s, "[") |
|
130 for i, f := range st { |
|
131 if i > 0 { |
|
132 io.WriteString(s, " ") |
|
133 } |
|
134 f.Format(s, verb) |
|
135 } |
|
136 io.WriteString(s, "]") |
107 } |
137 } |
108 |
138 |
109 // stack represents a stack of program counters. |
139 // stack represents a stack of program counters. |
110 type stack []uintptr |
140 type stack []uintptr |
111 |
141 |