Seastar
High performance C++ framework for concurrent servers
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
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 <seastar/core/format.hh>
25#include <seastar/core/sstring.hh>
27#include <seastar/core/shared_ptr.hh>
28#include <seastar/util/modules.hh>
29
30#ifndef SEASTAR_MODULE
31#if __has_include(<execinfo.h>)
32#include <execinfo.h>
33#define HAVE_EXECINFO
34#endif
35#include <iosfwd>
36#include <memory>
37#include <variant>
38#include <boost/container/static_vector.hpp>
39#include <fmt/ostream.h>
40#endif
41
42namespace seastar {
43
45 sstring name;
46 uintptr_t begin;
47 uintptr_t end; // C++-style, last addr + 1
48};
49
50struct frame {
51 const shared_object* so;
52 uintptr_t addr;
53};
54
55bool operator==(const frame& a, const frame& b) noexcept;
56
57
58// If addr doesn't seem to belong to any of the provided shared objects, it
59// will be considered as part of the executable.
60frame decorate(uintptr_t addr) noexcept;
61
62// Invokes func for each frame passing it as argument.
63SEASTAR_MODULE_EXPORT
64template<typename Func>
65void backtrace(Func&& func) noexcept(noexcept(func(frame()))) {
66#ifdef HAVE_EXECINFO
67 constexpr size_t max_backtrace = 100;
68 void* buffer[max_backtrace];
69 int n = ::backtrace(buffer, max_backtrace);
70 for (int i = 0; i < n; ++i) {
71 auto ip = reinterpret_cast<uintptr_t>(buffer[i]);
72 func(decorate(ip - 1));
73 }
74#else
75// Not implemented yet
76#define SEASTAR_BACKTRACE_UNIMPLEMENTED
77#endif
78}
79
80// Represents a call stack of a single thread.
81SEASTAR_MODULE_EXPORT
83public:
84 using vector_type = boost::container::static_vector<frame, 64>;
85private:
86 vector_type _frames;
87 size_t _hash;
88 char _delimeter;
89private:
90 size_t calculate_hash() const noexcept;
91public:
92 simple_backtrace(vector_type f, char delimeter = ' ') noexcept : _frames(std::move(f)), _hash(calculate_hash()), _delimeter(delimeter) {}
93 simple_backtrace(char delimeter = ' ') noexcept : simple_backtrace({}, delimeter) {}
94
95 size_t hash() const noexcept { return _hash; }
96 char delimeter() const noexcept { return _delimeter; }
97
98 friend std::ostream& operator<<(std::ostream& out, const simple_backtrace&);
99
100 bool operator==(const simple_backtrace& o) const noexcept {
101 return _hash == o._hash && _frames == o._frames;
102 }
103
104 bool operator!=(const simple_backtrace& o) const noexcept {
105 return !(*this == o);
106 }
107};
108
110
111// Represents a task object inside a tasktrace.
113 const std::type_info* _task_type;
114public:
115 task_entry(const std::type_info& ti) noexcept
116 : _task_type(&ti)
117 { }
118
119 friend std::ostream& operator<<(std::ostream& out, const task_entry&);
120
121 bool operator==(const task_entry& o) const noexcept {
122 return *_task_type == *o._task_type;
123 }
124
125 bool operator!=(const task_entry& o) const noexcept {
126 return !(*this == o);
127 }
128
129 size_t hash() const noexcept { return _task_type->hash_code(); }
130};
131
132// Extended backtrace which consists of a backtrace of the currently running task
133// and information about the chain of tasks waiting for the current operation to complete.
134SEASTAR_MODULE_EXPORT
136public:
137 using entry = std::variant<shared_backtrace, task_entry>;
138 using vector_type = boost::container::static_vector<entry, 16>;
139private:
140 simple_backtrace _main;
141 vector_type _prev;
143 size_t _hash;
144public:
145 tasktrace() = default;
146 tasktrace(simple_backtrace main, vector_type prev, size_t prev_hash, scheduling_group sg) noexcept;
147 tasktrace(const tasktrace&) = default;
148 tasktrace& operator=(const tasktrace&) = default;
149 ~tasktrace();
150
151 size_t hash() const noexcept { return _hash; }
152 char delimeter() const noexcept { return _main.delimeter(); }
153
154 friend std::ostream& operator<<(std::ostream& out, const tasktrace&);
155
156 bool operator==(const tasktrace& o) const noexcept;
157
158 bool operator!=(const tasktrace& o) const noexcept {
159 return !(*this == o);
160 }
161};
162
163}
164
165namespace std {
166
167SEASTAR_MODULE_EXPORT
168template<>
169struct hash<seastar::simple_backtrace> {
170 size_t operator()(const seastar::simple_backtrace& b) const {
171 return b.hash();
172 }
173};
174
175SEASTAR_MODULE_EXPORT
176template<>
177struct hash<seastar::tasktrace> {
178 size_t operator()(const seastar::tasktrace& b) const {
179 return b.hash();
180 }
181};
182
183}
184
185#if FMT_VERSION >= 90000
186template <> struct fmt::formatter<seastar::tasktrace> : fmt::ostream_formatter {};
187template <> struct fmt::formatter<seastar::simple_backtrace> : fmt::ostream_formatter {};
188#endif
189
190namespace seastar {
191
192using saved_backtrace = tasktrace;
193
194saved_backtrace current_backtrace() noexcept;
195
196tasktrace current_tasktrace() noexcept;
197
198// Collects backtrace only within the currently executing task.
199simple_backtrace current_backtrace_tasklocal() noexcept;
200
201std::ostream& operator<<(std::ostream& out, const tasktrace& b);
202
203namespace internal {
204
205template<class Exc>
206class backtraced : public Exc {
207 std::shared_ptr<sstring> _backtrace;
208public:
209 template<typename... Args>
210 backtraced(Args&&... args)
211 : Exc(std::forward<Args>(args)...)
212 , _backtrace(std::make_shared<sstring>(format("{} Backtrace: {}", Exc::what(), current_backtrace()))) {}
213
219 virtual const char* what() const noexcept override {
220 assert(_backtrace);
221 return _backtrace->c_str();
222 }
223};
224
225}
226
227
235SEASTAR_MODULE_EXPORT
236template <class Exc, typename... Args>
237std::exception_ptr make_backtraced_exception_ptr(Args&&... args) {
238 using exc_type = std::decay_t<Exc>;
239 static_assert(std::is_base_of_v<std::exception, exc_type>,
240 "throw_with_backtrace only works with exception types");
241 return std::make_exception_ptr<internal::backtraced<exc_type>>(Exc(std::forward<Args>(args)...));
242}
243
253SEASTAR_MODULE_EXPORT
254template <class Exc, typename... Args>
255[[noreturn]]
256void
257throw_with_backtrace(Args&&... args) {
258 std::rethrow_exception(make_backtraced_exception_ptr<Exc>(std::forward<Args>(args)...));
259};
260
261}
Definition: shared_ptr.hh:268
Identifies function calls that are accounted as a group.
Definition: scheduling.hh:285
Definition: backtrace.hh:82
Definition: backtrace.hh:112
Definition: backtrace.hh:135
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
sstring format(fmt::format_string< A... > fmt, A &&... a)
Definition: format.hh:42
void throw_with_backtrace(Args &&... args)
Definition: backtrace.hh:257
std::exception_ptr make_backtraced_exception_ptr(Args &&... args)
Definition: backtrace.hh:237
Definition: backtrace.hh:50
Definition: backtrace.hh:44
STL namespace.