Seastar
High performance C++ framework for concurrent servers
backtrace.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 /*
19  * Copyright 2016 ScyllaDB
20  */
21 
22 #pragma once
23 
24 #include <execinfo.h>
25 #include <iosfwd>
26 #include <variant>
27 #include <boost/container/static_vector.hpp>
28 
29 #include <seastar/core/sstring.hh>
30 #include <seastar/core/print.hh>
32 #include <seastar/core/shared_ptr.hh>
33 
34 namespace seastar {
35 
36 struct shared_object {
37  sstring name;
38  uintptr_t begin;
39  uintptr_t end; // C++-style, last addr + 1
40 };
41 
42 struct frame {
43  const shared_object* so;
44  uintptr_t addr;
45 };
46 
47 bool operator==(const frame& a, const frame& b) noexcept;
48 
49 
50 // If addr doesn't seem to belong to any of the provided shared objects, it
51 // will be considered as part of the executable.
52 frame decorate(uintptr_t addr) noexcept;
53 
54 // Invokes func for each frame passing it as argument.
55 template<typename Func>
56 void backtrace(Func&& func) noexcept(noexcept(func(frame()))) {
57  constexpr size_t max_backtrace = 100;
58  void* buffer[max_backtrace];
59  int n = ::backtrace(buffer, max_backtrace);
60  for (int i = 0; i < n; ++i) {
61  auto ip = reinterpret_cast<uintptr_t>(buffer[i]);
62  func(decorate(ip - 1));
63  }
64 }
65 
66 // Represents a call stack of a single thread.
68 public:
69  using vector_type = boost::container::static_vector<frame, 64>;
70 private:
71  vector_type _frames;
72  size_t _hash;
73  char _delimeter;
74 private:
75  size_t calculate_hash() const noexcept;
76 public:
77  simple_backtrace(char delimeter = ' ') noexcept : _delimeter(delimeter) {}
78  simple_backtrace(vector_type f, char delimeter = ' ') noexcept : _frames(std::move(f)), _delimeter(delimeter) {}
79 
80  size_t hash() const noexcept { return _hash; }
81  char delimeter() const noexcept { return _delimeter; }
82 
83  friend std::ostream& operator<<(std::ostream& out, const simple_backtrace&);
84 
85  bool operator==(const simple_backtrace& o) const noexcept {
86  return _hash == o._hash && _frames == o._frames;
87  }
88 
89  bool operator!=(const simple_backtrace& o) const noexcept {
90  return !(*this == o);
91  }
92 };
93 
95 
96 // Represents a task object inside a tasktrace.
97 class task_entry {
98  const std::type_info* _task_type;
99 public:
100  task_entry(const std::type_info& ti) noexcept
101  : _task_type(&ti)
102  { }
103 
104  friend std::ostream& operator<<(std::ostream& out, const task_entry&);
105 
106  bool operator==(const task_entry& o) const noexcept {
107  return *_task_type == *o._task_type;
108  }
109 
110  bool operator!=(const task_entry& o) const noexcept {
111  return !(*this == o);
112  }
113 
114  size_t hash() const noexcept { return _task_type->hash_code(); }
115 };
116 
117 // Extended backtrace which consists of a backtrace of the currently running task
118 // and information about the chain of tasks waiting for the current operation to complete.
119 class tasktrace {
120 public:
121  using entry = std::variant<shared_backtrace, task_entry>;
122  using vector_type = boost::container::static_vector<entry, 16>;
123 private:
124  simple_backtrace _main;
125  vector_type _prev;
126  scheduling_group _sg;
127  size_t _hash;
128 public:
129  tasktrace() = default;
130  tasktrace(simple_backtrace main, vector_type prev, size_t prev_hash, scheduling_group sg) noexcept;
131  ~tasktrace();
132 
133  size_t hash() const noexcept { return _hash; }
134  char delimeter() const noexcept { return _main.delimeter(); }
135 
136  friend std::ostream& operator<<(std::ostream& out, const tasktrace&);
137 
138  bool operator==(const tasktrace& o) const noexcept;
139 
140  bool operator!=(const tasktrace& o) const noexcept {
141  return !(*this == o);
142  }
143 };
144 
145 }
146 
147 namespace std {
148 
149 template<>
150 struct hash<seastar::simple_backtrace> {
151  size_t operator()(const seastar::simple_backtrace& b) const {
152  return b.hash();
153  }
154 };
155 
156 template<>
157 struct hash<seastar::tasktrace> {
158  size_t operator()(const seastar::tasktrace& b) const {
159  return b.hash();
160  }
161 };
162 
163 }
164 
165 namespace seastar {
166 
167 using saved_backtrace = tasktrace;
168 
169 saved_backtrace current_backtrace() noexcept;
170 
171 tasktrace current_tasktrace() noexcept;
172 
173 // Collects backtrace only within the currently executing task.
174 simple_backtrace current_backtrace_tasklocal() noexcept;
175 
176 std::ostream& operator<<(std::ostream& out, const tasktrace& b);
177 
178 namespace internal {
179 
180 template<class Exc>
181 class backtraced : public Exc {
182  std::shared_ptr<sstring> _backtrace;
183 public:
184  template<typename... Args>
185  backtraced(Args&&... args)
186  : Exc(std::forward<Args>(args)...)
187  , _backtrace(std::make_shared<sstring>(format("{} Backtrace: {}", Exc::what(), current_backtrace()))) {}
188 
194  virtual const char* what() const noexcept override {
195  assert(_backtrace);
196  return _backtrace->c_str();
197  }
198 };
199 
200 }
201 
202 
210 template <class Exc, typename... Args>
211 std::exception_ptr make_backtraced_exception_ptr(Args&&... args) {
212  using exc_type = std::decay_t<Exc>;
213  static_assert(std::is_base_of<std::exception, exc_type>::value,
214  "throw_with_backtrace only works with exception types");
215  return std::make_exception_ptr<internal::backtraced<exc_type>>(Exc(std::forward<Args>(args)...));
216 }
217 
227 template <class Exc, typename... Args>
228 [[noreturn]]
229 void
230 throw_with_backtrace(Args&&... args) {
231  std::rethrow_exception(make_backtraced_exception_ptr<Exc>(std::forward<Args>(args)...));
232 };
233 
234 }
Definition: shared_ptr.hh:255
Identifies function calls that are accounted as a group.
Definition: scheduling.hh:254
Definition: backtrace.hh:67
Definition: backtrace.hh:97
Definition: backtrace.hh:119
Seastar API namespace.
Definition: abort_on_ebadf.hh:24
void throw_with_backtrace(Args &&... args)
Definition: backtrace.hh:230
sstring format(const char *fmt, A &&... a)
Definition: print.hh:134
std::exception_ptr make_backtraced_exception_ptr(Args &&... args)
Definition: backtrace.hh:211
Definition: backtrace.hh:42
Definition: backtrace.hh:36