Coverage Report

Created: 2026-06-26 03:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/common/phdr_cache.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  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
// This file is copied from
18
// https://github.com/ClickHouse/ClickHouse/blob/master/src/base/phdr_cache.cpp
19
// and modified by Doris
20
21
#include "common/phdr_cache.h"
22
23
#if defined(__clang__)
24
#pragma clang diagnostic ignored "-Wreserved-identifier"
25
#endif
26
27
/// This code was based on the code by Fedor Korotkiy https://www.linkedin.com/in/fedor-korotkiy-659a1838/
28
29
#if defined(__linux__) && !defined(THREAD_SANITIZER) && !defined(USE_MUSL)
30
#define USE_PHDR_CACHE 1
31
#endif
32
33
/// Thread Sanitizer uses dl_iterate_phdr function on initialization and fails if we provide our own.
34
#ifdef USE_PHDR_CACHE
35
36
#if defined(__clang__)
37
#pragma clang diagnostic ignored "-Wreserved-id-macro"
38
#pragma clang diagnostic ignored "-Wunused-macros"
39
#endif
40
41
#define __msan_unpoison(X, Y) // NOLINT
42
#if defined(__clang__) && defined(__has_feature)
43
#if __has_feature(memory_sanitizer)
44
#undef __msan_unpoison
45
#include <sanitizer/msan_interface.h>
46
#endif
47
#endif
48
49
#include <dlfcn.h>
50
#include <link.h>
51
52
#include <atomic>
53
#include <cstddef>
54
#include <stdexcept>
55
#include <vector>
56
57
namespace {
58
59
// This is adapted from
60
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.hh
61
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.cc
62
63
using DLIterateFunction = int (*)(int (*callback)(dl_phdr_info* info, size_t size, void* data),
64
                                  void* data);
65
66
2.12M
DLIterateFunction getOriginalDLIteratePHDR() {
67
2.12M
    void* func = dlsym(RTLD_NEXT, "dl_iterate_phdr");
68
2.12M
    if (!func) {
69
0
        throw std::runtime_error("Cannot find dl_iterate_phdr function with dlsym");
70
0
    }
71
2.12M
    return reinterpret_cast<DLIterateFunction>(func);
72
2.12M
}
73
74
using PHDRCache = std::vector<dl_phdr_info>;
75
std::atomic<PHDRCache*> phdr_cache {};
76
// This flag is flipped inside the stack-trace signal handler. Force a static TLS access model so
77
// reading it from our dl_iterate_phdr interposer does not call into the dynamic loader's TLS path.
78
__thread bool use_phdr_cache __attribute__((tls_model("initial-exec"))) = false;
79
80
} // namespace
81
82
extern "C"
83
#ifndef __clang__
84
        [[gnu::visibility("default")]] [[gnu::externally_visible]]
85
#endif
86
        int
87
2.12M
        dl_iterate_phdr(int (*callback)(dl_phdr_info* info, size_t size, void* data), void* data) {
88
2.12M
    if (!use_phdr_cache) {
89
2.12M
        return getOriginalDLIteratePHDR()(callback, data);
90
2.12M
    }
91
92
0
    auto* current_phdr_cache = phdr_cache.load(std::memory_order_acquire);
93
0
    if (!current_phdr_cache) {
94
0
        return getOriginalDLIteratePHDR()(callback, data);
95
0
    }
96
97
0
    int result = 0;
98
52
    for (auto& entry : *current_phdr_cache) {
99
52
        result = callback(&entry, offsetof(dl_phdr_info, dlpi_adds), data);
100
52
        if (result != 0) {
101
20
            break;
102
20
        }
103
52
    }
104
0
    return result;
105
0
}
106
107
#include "util/debug/leak_annotations.h"
108
109
25
void updatePHDRCache() {
110
    // Fill out ELF header cache for access without locking.
111
    // Old snapshots are intentionally kept alive because another thread may already be unwinding
112
    // through the previous cache when a Doris-controlled dlopen/dlclose refreshes this one.
113
114
25
    PHDRCache* new_phdr_cache = new PHDRCache;
115
25
    getOriginalDLIteratePHDR()(
116
473
            [](dl_phdr_info* info, size_t /*size*/, void* data) {
117
                // `info` is created by dl_iterate_phdr, which is a non-instrumented
118
                // libc function, so we have to unpoison it manually.
119
473
                __msan_unpoison(info, sizeof(*info));
120
121
473
                reinterpret_cast<PHDRCache*>(data)->push_back(*info);
122
473
                return 0;
123
473
            },
124
25
            new_phdr_cache);
125
25
    phdr_cache.store(new_phdr_cache, std::memory_order_release);
126
127
    /// Memory is intentionally leaked.
128
25
    ANNOTATE_LEAKING_OBJECT_PTR(new_phdr_cache);
129
25
}
130
131
28
bool hasPHDRCache() {
132
28
    return phdr_cache.load(std::memory_order_acquire) != nullptr;
133
28
}
134
135
7
ScopedPHDRCacheRead::ScopedPHDRCacheRead() : _previous(use_phdr_cache) {
136
7
    use_phdr_cache = true;
137
7
}
138
139
7
ScopedPHDRCacheRead::~ScopedPHDRCacheRead() {
140
7
    use_phdr_cache = _previous;
141
7
}
142
143
#else
144
145
void updatePHDRCache() {}
146
147
#if defined(USE_MUSL)
148
/// With statically linked with musl, dl_iterate_phdr is immutable.
149
bool hasPHDRCache() {
150
    return true;
151
}
152
#else
153
bool hasPHDRCache() {
154
    return false;
155
}
156
#endif
157
158
ScopedPHDRCacheRead::ScopedPHDRCacheRead() = default;
159
160
ScopedPHDRCacheRead::~ScopedPHDRCacheRead() = default;
161
162
#endif