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 zap 22 23import ( 24 "runtime" 25 "strings" 26 "sync" 27 28 "go.uber.org/zap/internal/bufferpool" 29) 30 31const _zapPackage = "go.uber.org/zap" 32 33var ( 34 _stacktracePool = sync.Pool{ 35 New: func() interface{} { 36 return newProgramCounters(64) 37 }, 38 } 39 40 // We add "." and "/" suffixes to the package name to ensure we only match 41 // the exact package and not any package with the same prefix. 42 _zapStacktracePrefixes = addPrefix(_zapPackage, ".", "/") 43 _zapStacktraceVendorContains = addPrefix("/vendor/", _zapStacktracePrefixes...) 44) 45 46func takeStacktrace() string { 47 buffer := bufferpool.Get() 48 defer buffer.Free() 49 programCounters := _stacktracePool.Get().(*programCounters) 50 defer _stacktracePool.Put(programCounters) 51 52 var numFrames int 53 for { 54 // Skip the call to runtime.Counters and takeStacktrace so that the 55 // program counters start at the caller of takeStacktrace. 56 numFrames = runtime.Callers(2, programCounters.pcs) 57 if numFrames < len(programCounters.pcs) { 58 break 59 } 60 // Don't put the too-short counter slice back into the pool; this lets 61 // the pool adjust if we consistently take deep stacktraces. 62 programCounters = newProgramCounters(len(programCounters.pcs) * 2) 63 } 64 65 i := 0 66 skipZapFrames := true // skip all consecutive zap frames at the beginning. 67 frames := runtime.CallersFrames(programCounters.pcs[:numFrames]) 68 69 // Note: On the last iteration, frames.Next() returns false, with a valid 70 // frame, but we ignore this frame. The last frame is a a runtime frame which 71 // adds noise, since it's only either runtime.main or runtime.goexit. 72 for frame, more := frames.Next(); more; frame, more = frames.Next() { 73 if skipZapFrames && isZapFrame(frame.Function) { 74 continue 75 } else { 76 skipZapFrames = false 77 } 78 79 if i != 0 { 80 buffer.AppendByte('\n') 81 } 82 i++ 83 buffer.AppendString(frame.Function) 84 buffer.AppendByte('\n') 85 buffer.AppendByte('\t') 86 buffer.AppendString(frame.File) 87 buffer.AppendByte(':') 88 buffer.AppendInt(int64(frame.Line)) 89 } 90 91 return buffer.String() 92} 93 94func isZapFrame(function string) bool { 95 for _, prefix := range _zapStacktracePrefixes { 96 if strings.HasPrefix(function, prefix) { 97 return true 98 } 99 } 100 101 // We can't use a prefix match here since the location of the vendor 102 // directory affects the prefix. Instead we do a contains match. 103 for _, contains := range _zapStacktraceVendorContains { 104 if strings.Contains(function, contains) { 105 return true 106 } 107 } 108 109 return false 110} 111 112type programCounters struct { 113 pcs []uintptr 114} 115 116func newProgramCounters(size int) *programCounters { 117 return &programCounters{make([]uintptr, size)} 118} 119 120func addPrefix(prefix string, ss ...string) []string { 121 withPrefix := make([]string, len(ss)) 122 for i, s := range ss { 123 withPrefix[i] = prefix + s 124 } 125 return withPrefix 126} 127