1 // +build go1.7 2 3 // Package stack implements utilities to capture, manipulate, and format call 4 // stacks. It provides a simpler API than package runtime. 5 // 6 // The implementation takes care of the minutia and special cases of 7 // interpreting the program counter (pc) values returned by runtime.Callers. 8 // 9 // Package stack's types implement fmt.Formatter, which provides a simple and 10 // flexible way to declaratively configure formatting when used with logging 11 // or error tracking packages. 12 package stack 13 14 import ( 15 "bytes" 16 "errors" 17 "fmt" 18 "io" 19 "runtime" 20 "strconv" 21 "strings" 22 ) 23 24 // Call records a single function invocation from a goroutine stack. 25 type Call struct { 26 frame runtime.Frame 27 } 28 29 // Caller returns a Call from the stack of the current goroutine. The argument 30 // skip is the number of stack frames to ascend, with 0 identifying the 31 // calling function. 32 func Caller(skip int) Call { 33 // As of Go 1.9 we need room for up to three PC entries. 34 // 35 // 0. An entry for the stack frame prior to the target to check for 36 // special handling needed if that prior entry is runtime.sigpanic. 37 // 1. A possible second entry to hold metadata about skipped inlined 38 // functions. If inline functions were not skipped the target frame 39 // PC will be here. 40 // 2. A third entry for the target frame PC when the second entry 41 // is used for skipped inline functions. 42 var pcs [3]uintptr 43 n := runtime.Callers(skip+1, pcs[:]) 44 frames := runtime.CallersFrames(pcs[:n]) 45 frame, _ := frames.Next() 46 frame, _ = frames.Next() 47 48 return Call{ 49 frame: frame, 50 } 51 } 52 53 // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c). 54 func (c Call) String() string { 55 return fmt.Sprint(c) 56 } 57 58 // MarshalText implements encoding.TextMarshaler. It formats the Call the same 59 // as fmt.Sprintf("%v", c). 60 func (c Call) MarshalText() ([]byte, error) { 61 if c.frame == (runtime.Frame{}) { 62 return nil, ErrNoFunc 63 } 64 65 buf := bytes.Buffer{} 66 fmt.Fprint(&buf, c) 67 return buf.Bytes(), nil 68 } 69 70 // ErrNoFunc means that the Call has a nil *runtime.Func. The most likely 71 // cause is a Call with the zero value. 72 var ErrNoFunc = errors.New("no call stack information") 73 74 // Format implements fmt.Formatter with support for the following verbs. 75 // 76 // %s source file 77 // %d line number 78 // %n function name 79 // %k last segment of the package path 80 // %v equivalent to %s:%d 81 // 82 // It accepts the '+' and '#' flags for most of the verbs as follows. 83 // 84 // %+s path of source file relative to the compile time GOPATH, 85 // or the module path joined to the path of source file relative 86 // to module root 87 // %#s full path of source file 88 // %+n import path qualified function name 89 // %+k full package path 90 // %+v equivalent to %+s:%d 91 // %#v equivalent to %#s:%d 92 func (c Call) Format(s fmt.State, verb rune) { 93 if c.frame == (runtime.Frame{}) { 94 fmt.Fprintf(s, "%%!%c(NOFUNC)", verb) 95 return 96 } 97 98 switch verb { 99 case 's', 'v': 100 file := c.frame.File 101 switch { 102 case s.Flag('#'): 103 // done 104 case s.Flag('+'): 105 file = pkgFilePath(&c.frame) 106 default: 107 const sep = "/" 108 if i := strings.LastIndex(file, sep); i != -1 { 109 file = file[i+len(sep):] 110 } 111 } 112 io.WriteString(s, file) 113 if verb == 'v' { 114 buf := [7]byte{':'} 115 s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10)) 116 } 117 118 case 'd': 119 buf := [6]byte{} 120 s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10)) 121 122 case 'k': 123 name := c.frame.Function 124 const pathSep = "/" 125 start, end := 0, len(name) 126 if i := strings.LastIndex(name, pathSep); i != -1 { 127 start = i + len(pathSep) 128 } 129 const pkgSep = "." 130 if i := strings.Index(name[start:], pkgSep); i != -1 { 131 end = start + i 132 } 133 if s.Flag('+') { 134 start = 0 135 } 136 io.WriteString(s, name[start:end]) 137 138 case 'n': 139 name := c.frame.Function 140 if !s.Flag('+') { 141 const pathSep = "/" 142 if i := strings.LastIndex(name, pathSep); i != -1 { 143 name = name[i+len(pathSep):] 144 } 145 const pkgSep = "." 146 if i := strings.Index(name, pkgSep); i != -1 { 147 name = name[i+len(pkgSep):] 148 } 149 } 150 io.WriteString(s, name) 151 } 152 } 153 154 // Frame returns the call frame infomation for the Call. 155 func (c Call) Frame() runtime.Frame { 156 return c.frame 157 } 158 159 // PC returns the program counter for this call frame; multiple frames may 160 // have the same PC value. 161 // 162 // Deprecated: Use Call.Frame instead. 163 func (c Call) PC() uintptr { 164 return c.frame.PC 165 } 166 167 // CallStack records a sequence of function invocations from a goroutine 168 // stack. 169 type CallStack []Call 170 171 // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs). 172 func (cs CallStack) String() string { 173 return fmt.Sprint(cs) 174 } 175 176 var ( 177 openBracketBytes = []byte("[") 178 closeBracketBytes = []byte("]") 179 spaceBytes = []byte(" ") 180 ) 181 182 // MarshalText implements encoding.TextMarshaler. It formats the CallStack the 183 // same as fmt.Sprintf("%v", cs). 184 func (cs CallStack) MarshalText() ([]byte, error) { 185 buf := bytes.Buffer{} 186 buf.Write(openBracketBytes) 187 for i, pc := range cs { 188 if i > 0 { 189 buf.Write(spaceBytes) 190 } 191 fmt.Fprint(&buf, pc) 192 } 193 buf.Write(closeBracketBytes) 194 return buf.Bytes(), nil 195 } 196 197 // Format implements fmt.Formatter by printing the CallStack as square brackets 198 // ([, ]) surrounding a space separated list of Calls each formatted with the 199 // supplied verb and options. 200 func (cs CallStack) Format(s fmt.State, verb rune) { 201 s.Write(openBracketBytes) 202 for i, pc := range cs { 203 if i > 0 { 204 s.Write(spaceBytes) 205 } 206 pc.Format(s, verb) 207 } 208 s.Write(closeBracketBytes) 209 } 210 211 // Trace returns a CallStack for the current goroutine with element 0 212 // identifying the calling function. 213 func Trace() CallStack { 214 var pcs [512]uintptr 215 n := runtime.Callers(1, pcs[:]) 216 217 frames := runtime.CallersFrames(pcs[:n]) 218 cs := make(CallStack, 0, n) 219 220 // Skip extra frame retrieved just to make sure the runtime.sigpanic 221 // special case is handled. 222 frame, more := frames.Next() 223 224 for more { 225 frame, more = frames.Next() 226 cs = append(cs, Call{frame: frame}) 227 } 228 229 return cs 230 } 231 232 // TrimBelow returns a slice of the CallStack with all entries below c 233 // removed. 234 func (cs CallStack) TrimBelow(c Call) CallStack { 235 for len(cs) > 0 && cs[0] != c { 236 cs = cs[1:] 237 } 238 return cs 239 } 240 241 // TrimAbove returns a slice of the CallStack with all entries above c 242 // removed. 243 func (cs CallStack) TrimAbove(c Call) CallStack { 244 for len(cs) > 0 && cs[len(cs)-1] != c { 245 cs = cs[:len(cs)-1] 246 } 247 return cs 248 } 249 250 // pkgIndex returns the index that results in file[index:] being the path of 251 // file relative to the compile time GOPATH, and file[:index] being the 252 // $GOPATH/src/ portion of file. funcName must be the name of a function in 253 // file as returned by runtime.Func.Name. 254 func pkgIndex(file, funcName string) int { 255 // As of Go 1.6.2 there is no direct way to know the compile time GOPATH 256 // at runtime, but we can infer the number of path segments in the GOPATH. 257 // We note that runtime.Func.Name() returns the function name qualified by 258 // the import path, which does not include the GOPATH. Thus we can trim 259 // segments from the beginning of the file path until the number of path 260 // separators remaining is one more than the number of path separators in 261 // the function name. For example, given: 262 // 263 // GOPATH /home/user 264 // file /home/user/src/pkg/sub/file.go 265 // fn.Name() pkg/sub.Type.Method 266 // 267 // We want to produce: 268 // 269 // file[:idx] == /home/user/src/ 270 // file[idx:] == pkg/sub/file.go 271 // 272 // From this we can easily see that fn.Name() has one less path separator 273 // than our desired result for file[idx:]. We count separators from the 274 // end of the file path until it finds two more than in the function name 275 // and then move one character forward to preserve the initial path 276 // segment without a leading separator. 277 const sep = "/" 278 i := len(file) 279 for n := strings.Count(funcName, sep) + 2; n > 0; n-- { 280 i = strings.LastIndex(file[:i], sep) 281 if i == -1 { 282 i = -len(sep) 283 break 284 } 285 } 286 // get back to 0 or trim the leading separator 287 return i + len(sep) 288 } 289 290 // pkgFilePath returns the frame's filepath relative to the compile-time GOPATH, 291 // or its module path joined to its path relative to the module root. 292 // 293 // As of Go 1.11 there is no direct way to know the compile time GOPATH or 294 // module paths at runtime, but we can piece together the desired information 295 // from available information. We note that runtime.Frame.Function contains the 296 // function name qualified by the package path, which includes the module path 297 // but not the GOPATH. We can extract the package path from that and append the 298 // last segments of the file path to arrive at the desired package qualified 299 // file path. For example, given: 300 // 301 // GOPATH /home/user 302 // import path pkg/sub 303 // frame.File /home/user/src/pkg/sub/file.go 304 // frame.Function pkg/sub.Type.Method 305 // Desired return pkg/sub/file.go 306 // 307 // It appears that we simply need to trim ".Type.Method" from frame.Function and 308 // append "/" + path.Base(file). 309 // 310 // But there are other wrinkles. Although it is idiomatic to do so, the internal 311 // name of a package is not required to match the last segment of its import 312 // path. In addition, the introduction of modules in Go 1.11 allows working 313 // without a GOPATH. So we also must make these work right: 314 // 315 // GOPATH /home/user 316 // import path pkg/go-sub 317 // package name sub 318 // frame.File /home/user/src/pkg/go-sub/file.go 319 // frame.Function pkg/sub.Type.Method 320 // Desired return pkg/go-sub/file.go 321 // 322 // Module path pkg/v2 323 // import path pkg/v2/go-sub 324 // package name sub 325 // frame.File /home/user/cloned-pkg/go-sub/file.go 326 // frame.Function pkg/v2/sub.Type.Method 327 // Desired return pkg/v2/go-sub/file.go 328 // 329 // We can handle all of these situations by using the package path extracted 330 // from frame.Function up to, but not including, the last segment as the prefix 331 // and the last two segments of frame.File as the suffix of the returned path. 332 // This preserves the existing behavior when working in a GOPATH without modules 333 // and a semantically equivalent behavior when used in module aware project. 334 func pkgFilePath(frame *runtime.Frame) string { 335 pre := pkgPrefix(frame.Function) 336 post := pathSuffix(frame.File) 337 if pre == "" { 338 return post 339 } 340 return pre + "/" + post 341 } 342 343 // pkgPrefix returns the import path of the function's package with the final 344 // segment removed. 345 func pkgPrefix(funcName string) string { 346 const pathSep = "/" 347 end := strings.LastIndex(funcName, pathSep) 348 if end == -1 { 349 return "" 350 } 351 return funcName[:end] 352 } 353 354 // pathSuffix returns the last two segments of path. 355 func pathSuffix(path string) string { 356 const pathSep = "/" 357 lastSep := strings.LastIndex(path, pathSep) 358 if lastSep == -1 { 359 return path 360 } 361 return path[strings.LastIndex(path[:lastSep], pathSep)+1:] 362 } 363 364 var runtimePath string 365 366 func init() { 367 var pcs [3]uintptr 368 runtime.Callers(0, pcs[:]) 369 frames := runtime.CallersFrames(pcs[:]) 370 frame, _ := frames.Next() 371 file := frame.File 372 373 idx := pkgIndex(frame.File, frame.Function) 374 375 runtimePath = file[:idx] 376 if runtime.GOOS == "windows" { 377 runtimePath = strings.ToLower(runtimePath) 378 } 379 } 380 381 func inGoroot(c Call) bool { 382 file := c.frame.File 383 if len(file) == 0 || file[0] == '?' { 384 return true 385 } 386 if runtime.GOOS == "windows" { 387 file = strings.ToLower(file) 388 } 389 return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go") 390 } 391 392 // TrimRuntime returns a slice of the CallStack with the topmost entries from 393 // the go runtime removed. It considers any calls originating from unknown 394 // files, files under GOROOT, or _testmain.go as part of the runtime. 395 func (cs CallStack) TrimRuntime() CallStack { 396 for len(cs) > 0 && inGoroot(cs[len(cs)-1]) { 397 cs = cs[:len(cs)-1] 398 } 399 return cs 400 } 401