Coverage Report

Created: 2025-04-27 17:16

/root/doris/be/src/common/phdr_cache.cpp
Line
Count
Source (jump to first uncovered line)
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
#if defined(__clang__)
22
#pragma clang diagnostic ignored "-Wreserved-identifier"
23
#endif
24
25
/// This code was based on the code by Fedor Korotkiy https://www.linkedin.com/in/fedor-korotkiy-659a1838/
26
27
#if defined(__linux__) && !defined(THREAD_SANITIZER) && !defined(USE_MUSL)
28
#define USE_PHDR_CACHE 1
29
#endif
30
31
/// Thread Sanitizer uses dl_iterate_phdr function on initialization and fails if we provide our own.
32
#ifdef USE_PHDR_CACHE
33
34
#if defined(__clang__)
35
#pragma clang diagnostic ignored "-Wreserved-id-macro"
36
#pragma clang diagnostic ignored "-Wunused-macros"
37
#endif
38
39
#define __msan_unpoison(X, Y) // NOLINT
40
#if defined(__clang__) && defined(__has_feature)
41
#if __has_feature(memory_sanitizer)
42
#undef __msan_unpoison
43
#include <sanitizer/msan_interface.h>
44
#endif
45
#endif
46
47
#include <dlfcn.h>
48
#include <link.h>
49
50
#include <atomic>
51
#include <cstddef>
52
#include <stdexcept>
53
#include <vector>
54
55
namespace {
56
57
// This is adapted from
58
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.hh
59
// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.cc
60
61
using DLIterateFunction = int (*)(int (*callback)(dl_phdr_info* info, size_t size, void* data),
62
                                  void* data);
63
64
1
DLIterateFunction getOriginalDLIteratePHDR() {
65
1
    void* func = dlsym(RTLD_NEXT, "dl_iterate_phdr");
66
1
    if (!func) {
67
0
        throw std::runtime_error("Cannot find dl_iterate_phdr function with dlsym");
68
0
    }
69
1
    return reinterpret_cast<DLIterateFunction>(func);
70
1
}
71
72
using PHDRCache = std::vector<dl_phdr_info>;
73
std::atomic<PHDRCache*> phdr_cache {};
74
75
} // namespace
76
77
// Jemalloc heap profile follows libgcc's way of backtracing by default.
78
// rewrites dl_iterate_phdr will cause Jemalloc to fail to run after enable profile.
79
80
// TODO, two solutions:
81
// 1. Jemalloc specifies GNU libunwind as the prof backtracing way, but my test failed,
82
//    `--enable-prof-libunwind` not work: https://github.com/jemalloc/jemalloc/issues/2504
83
// 2. ClickHouse/libunwind solves Jemalloc profile backtracing, but the branch of ClickHouse/libunwind
84
//    has been out of touch with GNU libunwind and LLVM libunwind, which will leave the fate to others.
85
/*
86
extern "C"
87
#ifndef __clang__
88
        [[gnu::visibility("default")]] [[gnu::externally_visible]]
89
#endif
90
        int
91
        dl_iterate_phdr(int (*callback)(dl_phdr_info* info, size_t size, void* data), void* data) {
92
    auto* current_phdr_cache = phdr_cache.load();
93
    if (!current_phdr_cache) {
94
        // Cache is not yet populated, pass through to the original function.
95
        return getOriginalDLIteratePHDR()(callback, data);
96
    }
97
98
    int result = 0;
99
    for (auto& entry : *current_phdr_cache) {
100
        result = callback(&entry, offsetof(dl_phdr_info, dlpi_adds), data);
101
        if (result != 0) {
102
            break;
103
        }
104
    }
105
    return result;
106
}
107
*/
108
109
extern "C" {
110
#ifdef ADDRESS_SANITIZER
111
void __lsan_ignore_object(const void*);
112
#else
113
void __lsan_ignore_object(const void*) {} // NOLINT
114
#endif
115
}
116
117
1
void updatePHDRCache() {
118
    // Fill out ELF header cache for access without locking.
119
    // This assumes no dynamic object loading/unloading after this point
120
121
1
    PHDRCache* new_phdr_cache = new PHDRCache;
122
1
    getOriginalDLIteratePHDR()(
123
13
            [](dl_phdr_info* info, size_t /*size*/, void* data) {
124
                // `info` is created by dl_iterate_phdr, which is a non-instrumented
125
                // libc function, so we have to unpoison it manually.
126
13
                __msan_unpoison(info, sizeof(*info));
127
128
13
                reinterpret_cast<PHDRCache*>(data)->push_back(*info);
129
13
                return 0;
130
13
            },
131
1
            new_phdr_cache);
132
1
    phdr_cache.store(new_phdr_cache);
133
134
    /// Memory is intentionally leaked.
135
1
    __lsan_ignore_object(new_phdr_cache);
136
1
}
137
138
0
bool hasPHDRCache() {
139
0
    return phdr_cache.load() != nullptr;
140
0
}
141
142
#else
143
144
void updatePHDRCache() {}
145
146
#if defined(USE_MUSL)
147
/// With statically linked with musl, dl_iterate_phdr is immutable.
148
bool hasPHDRCache() {
149
    return true;
150
}
151
#else
152
bool hasPHDRCache() {
153
    return false;
154
}
155
#endif
156
157
#endif