]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/src/util/backtrace.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / src / util / backtrace.cc
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 2017 ScyllaDB
20 */
21 #include <seastar/util/backtrace.hh>
22
23 #include <link.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <errno.h>
28 #include <string.h>
29
30 #include <seastar/core/print.hh>
31 #include <seastar/core/thread.hh>
32 #include <seastar/core/reactor.hh>
33
34
35 namespace seastar {
36
37 static int dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data)
38 {
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];
42
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;
46 }
47 }
48
49 reinterpret_cast<std::vector<shared_object>*>(data)->push_back({info->dlpi_name, info->dlpi_addr, info->dlpi_addr + total_size});
50
51 return 0;
52 }
53
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);
57
58 return shared_objects;
59 }
60
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()};
63
64 bool operator==(const frame& a, const frame& b) noexcept {
65 return a.so == b.so && a.addr == b.addr;
66 }
67
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};
73 }
74
75 auto it = std::find_if(shared_objects.begin(), shared_objects.end(), [&] (const shared_object& so) {
76 return addr >= so.begin && addr < so.end;
77 });
78
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};
82 }
83
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));
89 }
90 });
91 return simple_backtrace(std::move(v));
92 }
93
94 size_t simple_backtrace::calculate_hash() const noexcept {
95 size_t h = 0;
96 for (auto f : _frames) {
97 h = ((h << 5) - h) ^ (f.so->begin + f.addr);
98 }
99 return h;
100 }
101
102 std::ostream& operator<<(std::ostream& out, const frame& f) {
103 if (!f.so->name.empty()) {
104 out << f.so->name << "+";
105 }
106 out << format("0x{:x}", f.addr);
107 return out;
108 }
109
110 std::ostream& operator<<(std::ostream& out, const simple_backtrace& b) {
111 char delim[2] = {'\0', '\0'};
112 for (auto f : b._frames) {
113 out << delim << f;
114 delim[0] = b.delimeter();
115 }
116 return out;
117 }
118
119 std::ostream& operator<<(std::ostream& out, const tasktrace& b) {
120 out << b._main;
121 for (auto&& e : b._prev) {
122 out << "\n --------";
123 std::visit(make_visitor([&] (const shared_backtrace& sb) {
124 out << '\n' << sb;
125 }, [&] (const task_entry& f) {
126 out << "\n " << f;
127 }), e);
128 }
129 return out;
130 }
131
132 std::ostream& operator<<(std::ostream& out, const task_entry& e) {
133 return out << seastar::pretty_type_name(*e._task_type);
134 }
135
136 tasktrace current_tasktrace() noexcept {
137 auto main = current_backtrace_tasklocal();
138
139 tasktrace::vector_type prev;
140 size_t hash = 0;
141 if (local_engine && g_current_context) {
142 task* tsk = nullptr;
143
144 thread_context* thread = thread_impl::get();
145 if (thread) {
146 tsk = thread->waiting_task();
147 } else {
148 tsk = local_engine->current_task();
149 }
150
151 while (tsk && prev.size() < prev.max_size()) {
152 shared_backtrace bt = tsk->get_backtrace();
153 hash *= 31;
154 if (bt) {
155 hash ^= bt->hash();
156 prev.push_back(bt);
157 } else {
158 const std::type_info& ti = typeid(*tsk);
159 prev.push_back(task_entry(ti));
160 hash ^= ti.hash_code();
161 }
162 tsk = tsk->waiting_task();
163 }
164 }
165
166 return tasktrace(std::move(main), std::move(prev), hash, current_scheduling_group());
167 }
168
169 saved_backtrace current_backtrace() noexcept {
170 return current_tasktrace();
171 }
172
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))
176 , _sg(sg)
177 , _hash(_main.hash() * 31 ^ prev_hash)
178 { }
179
180 bool tasktrace::operator==(const tasktrace& o) const noexcept {
181 return _hash == o._hash && _main == o._main && _prev == o._prev;
182 }
183
184 tasktrace::~tasktrace() {}
185
186 } // namespace seastar