1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <iomanip>
20 #include <string>
21 #include <vector>
22
23 namespace facebook {
24 namespace lyra {
25
26 constexpr size_t kDefaultLimit = 64;
27
28 using InstructionPointer = const void*;
29
30 class StackTraceElement {
31 public:
StackTraceElement(InstructionPointer absoluteProgramCounter,InstructionPointer libraryBase,InstructionPointer functionAddress,std::string libraryName,std::string functionName)32 StackTraceElement(
33 InstructionPointer absoluteProgramCounter,
34 InstructionPointer libraryBase,
35 InstructionPointer functionAddress,
36 std::string libraryName,
37 std::string functionName)
38 : absoluteProgramCounter_{absoluteProgramCounter},
39 libraryBase_{libraryBase},
40 functionAddress_{functionAddress},
41 libraryName_{std::move(libraryName)},
42 functionName_{std::move(functionName)},
43 hasBuildId_{false},
44 buildId_{} {}
45
libraryBase()46 InstructionPointer libraryBase() const noexcept {
47 return libraryBase_;
48 }
49
functionAddress()50 InstructionPointer functionAddress() const noexcept {
51 return functionAddress_;
52 }
53
absoluteProgramCounter()54 InstructionPointer absoluteProgramCounter() const noexcept {
55 return absoluteProgramCounter_;
56 }
57
libraryName()58 const std::string& libraryName() const noexcept {
59 return libraryName_;
60 }
61
functionName()62 const std::string& functionName() const noexcept {
63 return functionName_;
64 }
65
66 /**
67 * The offset of the program counter to the base of the library (i.e. the
68 * address that addr2line takes as input>
69 */
libraryOffset()70 std::ptrdiff_t libraryOffset() const noexcept {
71 auto absoluteLibrary = static_cast<const char*>(libraryBase_);
72 auto absoluteabsoluteProgramCounter =
73 static_cast<const char*>(absoluteProgramCounter_);
74 return absoluteabsoluteProgramCounter - absoluteLibrary;
75 }
76
77 /**
78 * The offset within the current function
79 */
functionOffset()80 int functionOffset() const noexcept {
81 auto absoluteSymbol = static_cast<const char*>(functionAddress_);
82 auto absoluteabsoluteProgramCounter =
83 static_cast<const char*>(absoluteProgramCounter_);
84 return absoluteabsoluteProgramCounter - absoluteSymbol;
85 }
86
87 std::string buildId() const;
88
89 private:
90 const InstructionPointer absoluteProgramCounter_;
91 const InstructionPointer libraryBase_;
92 const InstructionPointer functionAddress_;
93 const std::string libraryName_;
94 const std::string functionName_;
95
96 mutable bool hasBuildId_;
97 mutable std::string buildId_;
98 };
99
100 /**
101 * If a library identifier function is set, it is passed a libraryName
102 * for the frame, and returns a library build id string, which will be
103 * included in the logged stack trace. The most common use for this
104 * will be correlating stack traces with breakpad identifiers.
105 */
106 typedef std::string (*LibraryIdentifierFunctionType)(const std::string&);
107
108 void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func);
109
110 /**
111 * Populate the vector with the current stack trace
112 *
113 * Note that this trace needs to be symbolicated to get the library offset even
114 * if it is to be symbolicated off-line.
115 *
116 * Beware of a bug on some platforms, which makes the trace loop until the
117 * buffer is full when it reaches a noexpr function. It seems to be fixed in
118 * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
119 *
120 * @param stackTrace The vector that will receive the stack trace. Before
121 * filling the vector it will be cleared. The vector will never grow so the
122 * number of frames captured is limited by the capacity of it.
123 *
124 * @param skip The number of frames to skip before capturing the trace
125 */
126 void getStackTrace(
127 std::vector<InstructionPointer>& stackTrace,
128 size_t skip = 0);
129
130 /**
131 * Creates a vector and populates it with the current stack trace
132 *
133 * Note that this trace needs to be symbolicated to get the library offset even
134 * if it is to be symbolicated off-line.
135 *
136 * Beware of a bug on some platforms, which makes the trace loop until the
137 * buffer is full when it reaches a noexpr function. It seems to be fixed in
138 * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
139 *
140 * @param skip The number of frames to skip before capturing the trace
141 *
142 * @limit The maximum number of frames captured
143 */
144 inline std::vector<InstructionPointer> getStackTrace(
145 size_t skip = 0,
146 size_t limit = kDefaultLimit) {
147 auto stackTrace = std::vector<InstructionPointer>{};
148 stackTrace.reserve(limit);
149 getStackTrace(stackTrace, skip + 1);
150 return stackTrace;
151 }
152
153 /**
154 * Symbolicates a stack trace into a given vector
155 *
156 * @param symbols The vector to receive the output. The vector is cleared and
157 * enough room to keep the frames are reserved.
158 *
159 * @param stackTrace The input stack trace
160 */
161 void getStackTraceSymbols(
162 std::vector<StackTraceElement>& symbols,
163 const std::vector<InstructionPointer>& trace);
164
165 /**
166 * Symbolicates a stack trace into a new vector
167 *
168 * @param stackTrace The input stack trace
169 */
getStackTraceSymbols(const std::vector<InstructionPointer> & trace)170 inline std::vector<StackTraceElement> getStackTraceSymbols(
171 const std::vector<InstructionPointer>& trace) {
172 auto symbols = std::vector<StackTraceElement>{};
173 getStackTraceSymbols(symbols, trace);
174 return symbols;
175 }
176
177 /**
178 * Captures and symbolicates a stack trace
179 *
180 * Beware of a bug on some platforms, which makes the trace loop until the
181 * buffer is full when it reaches a noexpr function. It seems to be fixed in
182 * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
183 *
184 * @param skip The number of frames before capturing the trace
185 *
186 * @param limit The maximum number of frames captured
187 */
188 inline std::vector<StackTraceElement> getStackTraceSymbols(
189 size_t skip = 0,
190 size_t limit = kDefaultLimit) {
191 return getStackTraceSymbols(getStackTrace(skip + 1, limit));
192 }
193
194 /**
195 * Formatting a stack trace element
196 */
197 std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);
198
199 /**
200 * Formatting a stack trace
201 */
202 std::ostream& operator<<(
203 std::ostream& out,
204 const std::vector<StackTraceElement>& trace);
205
206 /**
207 * Log stack trace
208 *
209 * Makes it possible to log a trace without using a temporary stream when the
210 * underlying log API is not stream based.
211 */
212 void logStackTrace(const std::vector<StackTraceElement>& trace);
213
214 } // namespace lyra
215 } // namespace facebook
216