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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
19 * Copyright 2017 ScyllaDB
21 #include <seastar/util/backtrace.hh>
24 #include <sys/types.h>
30 #include <seastar/core/print.hh>
31 #include <seastar/core/thread.hh>
32 #include <seastar/core/reactor.hh>
37 static int dl_iterate_phdr_callback(struct dl_phdr_info
*info
, size_t size
, void *data
)
39 std::size_t total_size
{0};
40 for (int i
= 0; i
< info
->dlpi_phnum
; i
++) {
41 const auto hdr
= info
->dlpi_phdr
[i
];
43 // Only account loadable, executable (text) segments
44 if (hdr
.p_type
== PT_LOAD
&& (hdr
.p_flags
& PF_X
) == PF_X
) {
45 total_size
+= hdr
.p_memsz
;
49 reinterpret_cast<std::vector
<shared_object
>*>(data
)->push_back({info
->dlpi_name
, info
->dlpi_addr
, info
->dlpi_addr
+ total_size
});
54 static std::vector
<shared_object
> enumerate_shared_objects() {
55 std::vector
<shared_object
> shared_objects
;
56 dl_iterate_phdr(dl_iterate_phdr_callback
, &shared_objects
);
58 return shared_objects
;
61 static const std::vector
<shared_object
> shared_objects
{enumerate_shared_objects()};
62 static const shared_object uknown_shared_object
{"", 0, std::numeric_limits
<uintptr_t>::max()};
64 bool operator==(const frame
& a
, const frame
& b
) noexcept
{
65 return a
.so
== b
.so
&& a
.addr
== b
.addr
;
68 frame
decorate(uintptr_t addr
) noexcept
{
69 // If the shared-objects are not enumerated yet, or the enumeration
70 // failed return the addr as-is with a dummy shared-object.
71 if (shared_objects
.empty()) {
72 return {&uknown_shared_object
, addr
};
75 auto it
= std::find_if(shared_objects
.begin(), shared_objects
.end(), [&] (const shared_object
& so
) {
76 return addr
>= so
.begin
&& addr
< so
.end
;
79 // Unidentified addresses are assumed to originate from the executable.
80 auto& so
= it
== shared_objects
.end() ? shared_objects
.front() : *it
;
81 return {&so
, addr
- so
.begin
};
84 simple_backtrace
current_backtrace_tasklocal() noexcept
{
85 simple_backtrace::vector_type v
;
86 backtrace([&] (frame f
) {
87 if (v
.size() < v
.capacity()) {
88 v
.emplace_back(std::move(f
));
91 return simple_backtrace(std::move(v
));
94 size_t simple_backtrace::calculate_hash() const noexcept
{
96 for (auto f
: _frames
) {
97 h
= ((h
<< 5) - h
) ^ (f
.so
->begin
+ f
.addr
);
102 std::ostream
& operator<<(std::ostream
& out
, const frame
& f
) {
103 if (!f
.so
->name
.empty()) {
104 out
<< f
.so
->name
<< "+";
106 out
<< format("0x{:x}", f
.addr
);
110 std::ostream
& operator<<(std::ostream
& out
, const simple_backtrace
& b
) {
111 char delim
[2] = {'\0', '\0'};
112 for (auto f
: b
._frames
) {
114 delim
[0] = b
.delimeter();
119 std::ostream
& operator<<(std::ostream
& out
, const tasktrace
& b
) {
121 for (auto&& e
: b
._prev
) {
122 out
<< "\n --------";
123 std::visit(make_visitor([&] (const shared_backtrace
& sb
) {
125 }, [&] (const task_entry
& f
) {
132 std::ostream
& operator<<(std::ostream
& out
, const task_entry
& e
) {
133 return out
<< seastar::pretty_type_name(*e
._task_type
);
136 tasktrace
current_tasktrace() noexcept
{
137 auto main
= current_backtrace_tasklocal();
139 tasktrace::vector_type prev
;
141 if (local_engine
&& g_current_context
) {
144 thread_context
* thread
= thread_impl::get();
146 tsk
= thread
->waiting_task();
148 tsk
= local_engine
->current_task();
151 while (tsk
&& prev
.size() < prev
.max_size()) {
152 shared_backtrace bt
= tsk
->get_backtrace();
158 const std::type_info
& ti
= typeid(*tsk
);
159 prev
.push_back(task_entry(ti
));
160 hash
^= ti
.hash_code();
162 tsk
= tsk
->waiting_task();
166 return tasktrace(std::move(main
), std::move(prev
), hash
, current_scheduling_group());
169 saved_backtrace
current_backtrace() noexcept
{
170 return current_tasktrace();
173 tasktrace::tasktrace(simple_backtrace main
, tasktrace::vector_type prev
, size_t prev_hash
, scheduling_group sg
) noexcept
174 : _main(std::move(main
))
175 , _prev(std::move(prev
))
177 , _hash(_main
.hash() * 31 ^ prev_hash
)
180 bool tasktrace::operator==(const tasktrace
& o
) const noexcept
{
181 return _hash
== o
._hash
&& _main
== o
._main
&& _prev
== o
._prev
;
184 tasktrace::~tasktrace() {}
186 } // namespace seastar