]>
git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/src/core/exception_hacks.cc
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 (C) 2017 ScyllaDB
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.
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.
36 // To get rid of the first lock using gcc-7 is required.
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.
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.
60 #include <seastar/core/exception_hacks.hh>
61 #include <seastar/core/reactor.hh>
62 #include <seastar/util/backtrace.hh>
65 using dl_iterate_fn
= int (*) (int (*callback
) (struct dl_phdr_info
*info
, size_t size
, void *data
), void *data
);
67 [[gnu::no_sanitize_address
]]
68 static 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");
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
81 static std::vector
<dl_phdr_info
> *phdrs_cache
= nullptr;
83 void init_phdr_cache() {
84 // Fill out elf header cache for access without locking.
85 // This assumes no dynamic object loading/unloading after this point
86 phdrs_cache
= new std::vector
<dl_phdr_info
>();
87 dl_iterate_phdr_org()([] (struct dl_phdr_info
*info
, size_t size
, void *data
) {
88 phdrs_cache
->push_back(*info
);
93 #ifndef NO_EXCEPTION_INTERCEPT
94 seastar::logger
exception_logger("exception");
96 void log_exception_trace() noexcept
{
97 static thread_local
bool nested
= false;
98 if (!nested
&& exception_logger
.is_enabled(log_level::trace
)) {
100 exception_logger
.trace("Throw exception at:\n{}", current_backtrace());
105 void log_exception_trace() noexcept
{}
111 [[gnu::visibility("default")]]
113 [[gnu::no_sanitize_address
]]
114 int dl_iterate_phdr(int (*callback
) (struct dl_phdr_info
*info
, size_t size
, void *data
), void *data
) {
115 if (!seastar::local_engine
|| !seastar::phdrs_cache
) {
116 // Cache is not yet populated, pass through to original function
117 return seastar::dl_iterate_phdr_org()(callback
, data
);
120 for (auto h
: *seastar::phdrs_cache
) {
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
);
133 #ifndef NO_EXCEPTION_INTERCEPT
135 [[gnu::visibility("default")]]
137 int _Unwind_RaiseException(struct _Unwind_Exception
*h
) {
138 using throw_fn
= int (*)(void *);
139 static throw_fn org
= nullptr;
142 org
= (throw_fn
)dlsym (RTLD_NEXT
, "_Unwind_RaiseException");
144 if (seastar::local_engine
) {
145 seastar::log_exception_trace();
146 seastar::engine()._cxx_exceptions
++;