stack_trace.hpp
1 /*
2  * Copyright (c) 2020-2021, NVIDIA CORPORATION.
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 <rmm/detail/error.hpp>
20 
21 // execinfo is a linux-only library, so stack traces will only be available on
22 // linux systems.
23 #if (defined(__GNUC__) && !defined(__MINGW32__) && !defined(__MINGW64__))
24 #define RMM_ENABLE_STACK_TRACES
25 #endif
26 
27 #include <sstream>
28 
29 #if defined(RMM_ENABLE_STACK_TRACES)
30 #include <cxxabi.h>
31 #include <dlfcn.h>
32 #include <execinfo.h>
33 
34 #include <cstddef>
35 #include <memory>
36 #include <vector>
37 #endif
38 
39 namespace rmm::detail {
40 
50 class stack_trace {
51  public:
52  stack_trace()
53  {
54 #if defined(RMM_ENABLE_STACK_TRACES)
55  const int MaxStackDepth = 64;
56  std::array<void*, MaxStackDepth> stack{};
57  auto const depth = backtrace(stack.begin(), MaxStackDepth);
58  stack_ptrs.insert(stack_ptrs.end(), stack.begin(), stack.begin() + depth);
59 #endif // RMM_ENABLE_STACK_TRACES
60  }
61 
62  friend std::ostream& operator<<(std::ostream& os, const stack_trace& trace)
63  {
64 #if defined(RMM_ENABLE_STACK_TRACES)
65  std::unique_ptr<char*, decltype(&::free)> strings(
66  backtrace_symbols(trace.stack_ptrs.data(), static_cast<int>(trace.stack_ptrs.size())),
67  &::free);
68 
69  RMM_EXPECTS(strings != nullptr, "Unexpected null stack trace symbols");
70  // Iterate over the stack pointers converting to a string
71  for (std::size_t i = 0; i < trace.stack_ptrs.size(); ++i) {
72  // Leading index
73  os << "#" << i << " in ";
74 
75  auto const str = [&] {
76  Dl_info info;
77  if (dladdr(trace.stack_ptrs[i], &info) != 0) {
78  int status = -1; // Demangle the name. This can occasionally fail
79 
80  std::unique_ptr<char, decltype(&::free)> demangled(
81  abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status), &::free);
82  // If it fails, fallback to the dli_name.
83  if (status == 0 or (info.dli_sname != nullptr)) {
84  auto const* name = status == 0 ? demangled.get() : info.dli_sname;
85  return name + std::string(" from ") + info.dli_fname;
86  }
87  }
88  return std::string(strings.get()[i]);
89  }();
90 
91  os << str << std::endl;
92  }
93 #else
94  os << "stack traces disabled" << std::endl;
95 #endif // RMM_ENABLE_STACK_TRACES
96  return os;
97  };
98 
99 #if defined(RMM_ENABLE_STACK_TRACES)
100  private:
101  std::vector<void*> stack_ptrs;
102 #endif // RMM_ENABLE_STACK_TRACES
103 };
104 
105 } // namespace rmm::detail
rmm::detail::stack_trace
stack_trace is a class that will capture a stack on instatiation for output later....
Definition: stack_trace.hpp:50