timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
backtrace.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25#pragma once
26
32
33#if defined(TIMEMORY_USE_LIBUNWIND)
34# include <libunwind.h>
35#endif
36
37#if defined(TIMEMORY_UNIX)
38# include <execinfo.h>
39#endif
40
41// C library
42#include <array>
43#include <cctype>
44#include <cerrno>
45#include <cstdint>
46#include <cstdio>
47#include <cstdlib>
48#include <cstring>
49#include <iostream>
50#include <stdexcept>
51#include <string>
52
53namespace tim
54{
55#if defined(TIMEMORY_UNIX)
56//
58 demangle_backtrace(const char* cstr);
59//
61 demangle_backtrace(const std::string& str);
62//
64 demangle_unw_backtrace(const char* cstr);
65//
67 demangle_unw_backtrace(const std::string& str);
68//
69template <size_t Depth, size_t Offset = 1>
70TIMEMORY_NOINLINE auto
71get_backtrace()
72{
73 static_assert(Depth > 0, "Error !(Depth > 0)");
74 static_assert(Offset >= 0, "Error !(Offset >= 0)");
75
76 // destination
77 std::array<char[512], Depth> btrace{};
78 for(auto& itr : btrace)
79 itr[0] = '\0';
80
81 // plus one for this stack-frame
82 std::array<void*, Depth + Offset> buffer{};
83 buffer.fill(nullptr);
84
85 // size of returned buffer
86 auto sz = ::backtrace(buffer.data(), Depth + Offset);
87 // size of relevant data
88 auto n = sz - Offset;
89
90 // skip ahead (Offset + 1) stack frames
91 char** bsym = ::backtrace_symbols(buffer.data() + Offset, n);
92
93 // report errors
94 if(bsym == nullptr)
95 {
96 ::perror("backtrace_symbols");
97 }
98 else
99 {
100 for(decltype(n) i = 0; i < n; ++i)
101 {
102 ::snprintf(btrace[i], sizeof(btrace[i]), "%s", bsym[i]);
103 }
104 }
105 return btrace;
106}
107//
108template <size_t Depth, size_t Offset = 1>
109TIMEMORY_NOINLINE auto
110get_unw_backtrace()
111{
112# if defined(TIMEMORY_USE_LIBUNWIND)
113 static_assert(Depth > 0, "Error !(Depth > 0)");
114 static_assert(Offset >= 0, "Error !(Offset >= 0)");
115
116 unw_cursor_t cursor{};
117 unw_context_t context{};
118
119 // destination
120 std::array<char[512], Depth> btrace{};
121 for(auto& itr : btrace)
122 itr[0] = '\0';
123
124 // Initialize cursor to current frame for local unwinding.
125 unw_getcontext(&context);
126 if(unw_init_local(&cursor, &context) < 0)
127 {
128 return btrace;
129 }
130
131 size_t tot_idx = 0;
132 while(unw_step(&cursor) > 0)
133 {
134 unw_word_t ip{}; // stack pointer
135 unw_word_t off{}; // offset
136 auto _idx = ++tot_idx;
137 if(_idx >= Depth + Offset)
138 break;
139 unw_get_reg(&cursor, UNW_REG_IP, &ip);
140 if(ip == 0)
141 break;
142 char name[496];
143 name[0] = '\0';
144 if(unw_get_proc_name(&cursor, name, sizeof(name), &off) == 0)
145 {
146 if(_idx >= Offset)
147 {
148 auto _lidx = _idx - Offset;
149 if(off)
150 snprintf(btrace[_lidx], sizeof(btrace[_lidx]), "%s +0x%lx", name,
151 (long) off);
152 else
153 snprintf(btrace[_lidx], sizeof(btrace[_lidx]), "%s", name);
154 }
155 }
156 }
157# else
158 std::array<char[512], Depth> btrace{};
159 throw std::runtime_error("[timemory]> libunwind not available");
160# endif
161 return btrace;
162}
163//
164template <size_t Depth, size_t Offset = 1, typename Func>
165TIMEMORY_NOINLINE auto
166get_backtrace(Func&& func)
167{
168 static_assert(Depth > 0, "Error !(Depth > 0)");
169 static_assert(Offset >= 0, "Error !(Offset >= 0)");
170
171 using type = std::result_of_t<Func(const char[512])>;
172 // destination
173 std::array<type, Depth> btrace{};
174
175 auto&& _data = ::tim::get_backtrace<Depth, Offset + 1>();
176 auto _n = _data.size();
177 for(decltype(_n) i = 0; i < _n; ++i)
178 btrace[i] = func(_data[i]);
179 return btrace;
180}
181//
182template <size_t Depth, size_t Offset = 1, typename Func>
183TIMEMORY_NOINLINE auto
184get_unw_backtrace(Func&& func)
185{
186 static_assert(Depth > 0, "Error !(Depth > 0)");
187 static_assert(Offset >= 0, "Error !(Offset >= 0)");
188
189 using type = std::result_of_t<Func(const char[512])>;
190 // destination
191 std::array<type, Depth> btrace{};
192
193 auto&& _data = ::tim::get_unw_backtrace<Depth, Offset + 1>();
194 auto _n = _data.size();
195 for(decltype(_n) i = 0; i < _n; ++i)
196 btrace[i] = func(_data[i]);
197 return btrace;
198}
199//
200//--------------------------------------------------------------------------------------//
201//
202template <size_t Depth, size_t Offset = 1>
203TIMEMORY_NOINLINE auto
204get_demangled_backtrace()
205{
206 auto demangle_bt = [](const char cstr[512]) { return demangle_backtrace(cstr); };
207 return get_backtrace<Depth, Offset + 1>(demangle_bt);
208}
209//
210//--------------------------------------------------------------------------------------//
211//
212template <size_t Depth, size_t Offset = 1>
213TIMEMORY_NOINLINE auto
214get_demangled_unw_backtrace()
215{
216 auto demangle_bt = [](const char cstr[512]) { return demangle_unw_backtrace(cstr); };
217 return get_unw_backtrace<Depth, Offset + 1>(demangle_bt);
218}
219//
220//--------------------------------------------------------------------------------------//
221//
222template <size_t Depth, size_t Offset = 2>
223TIMEMORY_NOINLINE std::ostream&
224 print_backtrace(std::ostream& os = std::cerr, std::string _prefix = "",
225 const std::string& _info = "", const std::string& _indent = " ")
226{
227 os << _indent.substr(0, _indent.length() / 2) << "Backtrace";
228 if(!_info.empty())
229 os << " " << _info;
230 os << ":\n" << std::flush;
231 auto bt = tim::get_backtrace<Depth, Offset>();
232 if(!_prefix.empty() && _prefix.find_last_of(" \t") != _prefix.length() - 1)
233 _prefix += " ";
234 for(const auto& itr : bt)
235 {
236 if(strlen(itr) > 0)
237 os << _indent << _prefix << itr << "\n";
238 }
239 os << std::flush;
240 return os;
241}
242//
243//--------------------------------------------------------------------------------------//
244//
245template <size_t Depth, size_t Offset = 2>
246TIMEMORY_NOINLINE std::ostream&
247 print_demangled_backtrace(std::ostream& os = std::cerr, std::string _prefix = "",
248 const std::string& _info = "",
249 const std::string& _indent = " ")
250{
251 os << _indent.substr(0, _indent.length() / 2) << "Backtrace";
252 if(!_info.empty())
253 os << " " << _info;
254 os << ":\n" << std::flush;
255 auto bt = tim::get_demangled_backtrace<Depth, Offset>();
256 if(!_prefix.empty() && _prefix.find_last_of(" \t") != _prefix.length() - 1)
257 _prefix += " ";
258 for(const auto& itr : bt)
259 {
260 if(itr.length() > 0)
261 os << _indent << _prefix << itr << "\n";
262 }
263 os << std::flush;
264 return os;
265}
266//
267//--------------------------------------------------------------------------------------//
268//
269template <size_t Depth, size_t Offset = 2>
270TIMEMORY_NOINLINE std::ostream&
271 print_unw_backtrace(std::ostream& os = std::cerr, std::string _prefix = "",
272 const std::string& _info = "", const std::string& _indent = " ")
273{
274 os << _indent.substr(0, _indent.length() / 2) << "Backtrace";
275 if(!_info.empty())
276 os << " " << _info;
277 os << ":\n" << std::flush;
278 auto bt = tim::get_unw_backtrace<Depth, Offset>();
279 if(!_prefix.empty() && _prefix.find_last_of(" \t") != _prefix.length() - 1)
280 _prefix += " ";
281 for(const auto& itr : bt)
282 {
283 if(strlen(itr) > 0)
284 os << _indent << _prefix << itr << "\n";
285 }
286 os << std::flush;
287 return os;
288}
289//
290//--------------------------------------------------------------------------------------//
291//
292template <size_t Depth, size_t Offset = 3>
293TIMEMORY_NOINLINE std::ostream&
294 print_demangled_unw_backtrace(std::ostream& os = std::cerr, std::string _prefix = "",
295 const std::string& _info = "",
296 const std::string& _indent = " ")
297{
298 os << _indent.substr(0, _indent.length() / 2) << "Backtrace";
299 if(!_info.empty())
300 os << " " << _info;
301 os << ":\n" << std::flush;
302 auto bt = tim::get_demangled_unw_backtrace<Depth, Offset>();
303 if(!_prefix.empty() && _prefix.find_last_of(" \t") != _prefix.length() - 1)
304 _prefix += " ";
305 for(const auto& itr : bt)
306 {
307 if(itr.length() > 0)
308 os << _indent << _prefix << itr << "\n";
309 }
310 os << std::flush;
311 return os;
312}
313//
314#else
315//
316// define these dummy functions since they are used in operation::decode
317//
318static inline auto
319demangle_backtrace(const char* cstr)
320{
321 return std::string{ cstr };
322}
323//
324static inline auto
325demangle_backtrace(const std::string& str)
326{
327 return str;
328}
329//
330template <size_t Depth, size_t Offset = 2>
331static inline std::ostream&
332print_backtrace(std::ostream& os = std::cerr, std::string = {}, std::string = {},
333 std::string = {})
334{
335 os << "[timemory]> Backtrace not supported on this platform\n";
336 return os;
337}
338//
339template <size_t Depth, size_t Offset = 3>
340static inline std::ostream&
341print_demangled_backtrace(std::ostream& os = std::cerr, std::string = {},
342 std::string = {}, std::string = {})
343{
344 os << "[timemory]> Backtrace not supported on this platform\n";
345 return os;
346}
347//
348template <size_t Depth, size_t Offset = 2>
349static inline std::ostream&
350print_unw_backtrace(std::ostream& os = std::cerr, std::string = {}, std::string = {},
351 std::string = {})
352{
353 os << "[timemory]> libunwind backtrace not supported on this platform\n";
354 return os;
355}
356//
357template <size_t Depth, size_t Offset = 3>
358static inline std::ostream&
359print_demangled_unw_backtrace(std::ostream& os = std::cerr, std::string = {},
360 std::string = {}, std::string = {})
361{
362 os << "[timemory]> libunwind backtrace not supported on this platform\n";
363 return os;
364}
365//
366#endif
367//
368} // namespace tim
Definition: kokkosp.cpp:39
char const std::string & _prefix
Definition: config.cpp:55
tim::mpl::apply< std::string > string
Definition: macros.hpp:53
const std::string std::ostream * os
#define TIMEMORY_UTILITY_INLINE
Definition: macros.hpp:308