blob: 0cf8c1ddffa17503b67c08b62f58ac41d7c3f03b [file] [log] [blame]
Elia Battistonc8d0d462022-02-22 16:30:51 +01001// 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 "sync"
26
27 "go.uber.org/zap/internal/bufferpool"
28)
29
30var (
31 _stacktracePool = sync.Pool{
32 New: func() interface{} {
33 return newProgramCounters(64)
34 },
35 }
36)
37
38func takeStacktrace(skip int) string {
39 buffer := bufferpool.Get()
40 defer buffer.Free()
41 programCounters := _stacktracePool.Get().(*programCounters)
42 defer _stacktracePool.Put(programCounters)
43
44 var numFrames int
45 for {
46 // Skip the call to runtime.Callers and takeStacktrace so that the
47 // program counters start at the caller of takeStacktrace.
48 numFrames = runtime.Callers(skip+2, programCounters.pcs)
49 if numFrames < len(programCounters.pcs) {
50 break
51 }
52 // Don't put the too-short counter slice back into the pool; this lets
53 // the pool adjust if we consistently take deep stacktraces.
54 programCounters = newProgramCounters(len(programCounters.pcs) * 2)
55 }
56
57 i := 0
58 frames := runtime.CallersFrames(programCounters.pcs[:numFrames])
59
60 // Note: On the last iteration, frames.Next() returns false, with a valid
61 // frame, but we ignore this frame. The last frame is a a runtime frame which
62 // adds noise, since it's only either runtime.main or runtime.goexit.
63 for frame, more := frames.Next(); more; frame, more = frames.Next() {
64 if i != 0 {
65 buffer.AppendByte('\n')
66 }
67 i++
68 buffer.AppendString(frame.Function)
69 buffer.AppendByte('\n')
70 buffer.AppendByte('\t')
71 buffer.AppendString(frame.File)
72 buffer.AppendByte(':')
73 buffer.AppendInt(int64(frame.Line))
74 }
75
76 return buffer.String()
77}
78
79type programCounters struct {
80 pcs []uintptr
81}
82
83func newProgramCounters(size int) *programCounters {
84 return &programCounters{make([]uintptr, size)}
85}