]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/src/core/exception_hacks.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / seastar / src / core / exception_hacks.cc
CommitLineData
11fdf7f2
TL
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 (C) 2017 ScyllaDB
20 */
21
22// The purpose of the hacks here is to workaround C++ exception scalability problem
23// with gcc and glibc. For the best result gcc-7 is required.
24//
25// To summarize all the locks we have now and their purpose:
26// 1. There is a lock in _Unwind_Find_FDE (libgcc) that protects
27// list of "Frame description entries" registered with __register_frame*
28// functions. The catch is that dynamically linked binary do not do that,
29// so all that it protects is checking that a certain list is empty.
30// This lock no longer relevant in gcc-7 since there is a patch there
31// that checks that the list is empty outside of the lock and it will be
32// always true for us.
33// 2. The lock in dl_iterate_phdr (glibc) that protects loaded object
34// list against runtime object loading/unloading.
35//
36// To get rid of the first lock using gcc-7 is required.
37//
38// To get rid of the second one we can use the fact that we do not
39// load/unload objects dynamically (at least for now). To do that we
40// can mirror all elf header information in seastar and provide our
41// own dl_iterate_phdr symbol which uses this mirror without locking.
42//
43// Unfortunately there is another gotcha in this approach: dl_iterate_phdr
44// supplied by glibc never calls more then one callback simultaneously as an
45// unintended consequences of the lock there, but unfortunately libgcc relies
46// on that to maintain small cache of translations. The access to the cache is
47// not protected by any lock since up until now only one callback could have
48// run at a time. But luckily libgcc cannot use the cache if older version
49// of dl_phdr_info is provided to the callback because the older version
50// did not have an indication that loaded object list may have changed,
51// so libgcc does not know when cache should be invalidated and disables it
52// entirely. By calling the callback with old version of dl_phdr_info from
53// our dl_iterate_phdr we can effectively make libgcc callback thread safe.
54
55#include <link.h>
56#include <dlfcn.h>
57#include <assert.h>
58#include <vector>
59#include <cstddef>
60#include <seastar/core/exception_hacks.hh>
61#include <seastar/core/reactor.hh>
62#include <seastar/util/backtrace.hh>
63
64namespace seastar {
11fdf7f2
TL
65using dl_iterate_fn = int (*) (int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data);
66
9f95a23c 67[[gnu::no_sanitize_address]]
11fdf7f2
TL
68static dl_iterate_fn dl_iterate_phdr_org() {
69 static dl_iterate_fn org = [] {
70 auto org = (dl_iterate_fn)dlsym (RTLD_NEXT, "dl_iterate_phdr");
71 assert(org);
72 return org;
73 }();
74 return org;
75}
76
9f95a23c
TL
77// phdrs_cache has to remain valid until very late in the process
78// life, and that time is not a mirror image of when it is first used.
79// Given that, we avoid a static constructor/destructor pair and just
80// never destroy it.
81static std::vector<dl_phdr_info> *phdrs_cache = nullptr;
11fdf7f2
TL
82
83void init_phdr_cache() {
84 // Fill out elf header cache for access without locking.
85 // This assumes no dynamic object loading/unloading after this point
9f95a23c 86 phdrs_cache = new std::vector<dl_phdr_info>();
11fdf7f2 87 dl_iterate_phdr_org()([] (struct dl_phdr_info *info, size_t size, void *data) {
9f95a23c 88 phdrs_cache->push_back(*info);
11fdf7f2
TL
89 return 0;
90 }, nullptr);
91}
11fdf7f2
TL
92
93#ifndef NO_EXCEPTION_INTERCEPT
94seastar::logger exception_logger("exception");
95
96void log_exception_trace() noexcept {
97 static thread_local bool nested = false;
98 if (!nested && exception_logger.is_enabled(log_level::trace)) {
99 nested = true;
100 exception_logger.trace("Throw exception at:\n{}", current_backtrace());
101 nested = false;
102 }
103}
104#else
105void log_exception_trace() noexcept {}
106#endif
107
108}
109
11fdf7f2
TL
110extern "C"
111[[gnu::visibility("default")]]
9f95a23c
TL
112[[gnu::used]]
113[[gnu::no_sanitize_address]]
11fdf7f2 114int dl_iterate_phdr(int (*callback) (struct dl_phdr_info *info, size_t size, void *data), void *data) {
9f95a23c 115 if (!seastar::local_engine || !seastar::phdrs_cache) {
11fdf7f2
TL
116 // Cache is not yet populated, pass through to original function
117 return seastar::dl_iterate_phdr_org()(callback, data);
118 }
119 int r = 0;
9f95a23c 120 for (auto h : *seastar::phdrs_cache) {
11fdf7f2
TL
121 // Pass dl_phdr_info size that does not include dlpi_adds and dlpi_subs.
122 // This forces libgcc to disable caching which is not thread safe and
123 // requires dl_iterate_phdr to serialize calls to callback. Since we do
124 // not serialize here we have to disable caching.
125 r = callback(&h, offsetof(dl_phdr_info, dlpi_adds), data);
126 if (r) {
127 break;
128 }
129 }
130 return r;
131}
11fdf7f2
TL
132
133#ifndef NO_EXCEPTION_INTERCEPT
134extern "C"
135[[gnu::visibility("default")]]
9f95a23c 136[[gnu::used]]
11fdf7f2
TL
137int _Unwind_RaiseException(struct _Unwind_Exception *h) {
138 using throw_fn = int (*)(void *);
139 static throw_fn org = nullptr;
140
141 if (!org) {
142 org = (throw_fn)dlsym (RTLD_NEXT, "_Unwind_RaiseException");
143 }
144 if (seastar::local_engine) {
145 seastar::log_exception_trace();
146 seastar::engine()._cxx_exceptions++;
147 }
148 return org(h);
149}
150#endif