1// Copyright (c) 2016 Uber Technologies, Inc. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a copy 4// of this software and associated documentation files (the "Software"), to deal 5// in the Software without restriction, including without limitation the rights 6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7// copies of the Software, and to permit persons to whom the Software is 8// furnished to do so, subject to the following conditions: 9// 10// The above copyright notice and this permission notice shall be included in 11// all copies or substantial portions of the Software. 12// 13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19// THE SOFTWARE. 20 21package hclog 22 23import ( 24 "bytes" 25 "runtime" 26 "strconv" 27 "strings" 28 "sync" 29) 30 31var ( 32 _stacktraceIgnorePrefixes = []string{ 33 "runtime.goexit", 34 "runtime.main", 35 } 36 _stacktracePool = sync.Pool{ 37 New: func() interface{} { 38 return newProgramCounters(64) 39 }, 40 } 41) 42 43// CapturedStacktrace represents a stacktrace captured by a previous call 44// to log.Stacktrace. If passed to a logging function, the stacktrace 45// will be appended. 46type CapturedStacktrace string 47 48// Stacktrace captures a stacktrace of the current goroutine and returns 49// it to be passed to a logging function. 50func Stacktrace() CapturedStacktrace { 51 return CapturedStacktrace(takeStacktrace()) 52} 53 54func takeStacktrace() string { 55 programCounters := _stacktracePool.Get().(*programCounters) 56 defer _stacktracePool.Put(programCounters) 57 58 var buffer bytes.Buffer 59 60 for { 61 // Skip the call to runtime.Counters and takeStacktrace so that the 62 // program counters start at the caller of takeStacktrace. 63 n := runtime.Callers(2, programCounters.pcs) 64 if n < cap(programCounters.pcs) { 65 programCounters.pcs = programCounters.pcs[:n] 66 break 67 } 68 // Don't put the too-short counter slice back into the pool; this lets 69 // the pool adjust if we consistently take deep stacktraces. 70 programCounters = newProgramCounters(len(programCounters.pcs) * 2) 71 } 72 73 i := 0 74 frames := runtime.CallersFrames(programCounters.pcs) 75 for frame, more := frames.Next(); more; frame, more = frames.Next() { 76 if shouldIgnoreStacktraceFunction(frame.Function) { 77 continue 78 } 79 if i != 0 { 80 buffer.WriteByte('\n') 81 } 82 i++ 83 buffer.WriteString(frame.Function) 84 buffer.WriteByte('\n') 85 buffer.WriteByte('\t') 86 buffer.WriteString(frame.File) 87 buffer.WriteByte(':') 88 buffer.WriteString(strconv.Itoa(int(frame.Line))) 89 } 90 91 return buffer.String() 92} 93 94func shouldIgnoreStacktraceFunction(function string) bool { 95 for _, prefix := range _stacktraceIgnorePrefixes { 96 if strings.HasPrefix(function, prefix) { 97 return true 98 } 99 } 100 return false 101} 102 103type programCounters struct { 104 pcs []uintptr 105} 106 107func newProgramCounters(size int) *programCounters { 108 return &programCounters{make([]uintptr, size)} 109} 110