1 | //===-- xray_log_interface.cpp --------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of XRay, a function call tracing system. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #include "xray/xray_log_interface.h" |
13 | |
14 | #include "sanitizer_common/sanitizer_allocator_internal.h" |
15 | #include "sanitizer_common/sanitizer_atomic.h" |
16 | #include "sanitizer_common/sanitizer_mutex.h" |
17 | #include "xray/xray_interface.h" |
18 | #include "xray_defs.h" |
19 | |
20 | namespace __xray { |
21 | static SpinMutex XRayImplMutex; |
22 | static XRayLogImpl CurrentXRayImpl{.log_init: nullptr, .log_finalize: nullptr, .handle_arg0: nullptr, .flush_log: nullptr}; |
23 | static XRayLogImpl *GlobalXRayImpl = nullptr; |
24 | |
25 | // This is the default implementation of a buffer iterator, which always yields |
26 | // a null buffer. |
27 | XRayBuffer NullBufferIterator(XRayBuffer) XRAY_NEVER_INSTRUMENT { |
28 | return {.Data: nullptr, .Size: 0}; |
29 | } |
30 | |
31 | // This is the global function responsible for iterating through given buffers. |
32 | atomic_uintptr_t XRayBufferIterator{ |
33 | .val_dont_use: reinterpret_cast<uintptr_t>(&NullBufferIterator)}; |
34 | |
35 | // We use a linked list of Mode to XRayLogImpl mappings. This is a linked list |
36 | // when it should be a map because we're avoiding having to depend on C++ |
37 | // standard library data structures at this level of the implementation. |
38 | struct ModeImpl { |
39 | ModeImpl *Next; |
40 | const char *Mode; |
41 | XRayLogImpl Impl; |
42 | }; |
43 | |
44 | static ModeImpl SentinelModeImpl{ |
45 | .Next: nullptr, .Mode: nullptr, .Impl: {.log_init: nullptr, .log_finalize: nullptr, .handle_arg0: nullptr, .flush_log: nullptr}}; |
46 | static ModeImpl *ModeImpls = &SentinelModeImpl; |
47 | static const ModeImpl *CurrentMode = nullptr; |
48 | |
49 | } // namespace __xray |
50 | |
51 | using namespace __xray; |
52 | |
53 | void __xray_log_set_buffer_iterator(XRayBuffer (*Iterator)(XRayBuffer)) |
54 | XRAY_NEVER_INSTRUMENT { |
55 | atomic_store(a: &__xray::XRayBufferIterator, |
56 | v: reinterpret_cast<uintptr_t>(Iterator), mo: memory_order_release); |
57 | } |
58 | |
59 | void __xray_log_remove_buffer_iterator() XRAY_NEVER_INSTRUMENT { |
60 | __xray_log_set_buffer_iterator(Iterator: &NullBufferIterator); |
61 | } |
62 | |
63 | XRayLogRegisterStatus |
64 | __xray_log_register_mode(const char *Mode, |
65 | XRayLogImpl Impl) XRAY_NEVER_INSTRUMENT { |
66 | if (Impl.flush_log == nullptr || Impl.handle_arg0 == nullptr || |
67 | Impl.log_finalize == nullptr || Impl.log_init == nullptr) |
68 | return XRayLogRegisterStatus::XRAY_INCOMPLETE_IMPL; |
69 | |
70 | SpinMutexLock Guard(&XRayImplMutex); |
71 | // First, look for whether the mode already has a registered implementation. |
72 | for (ModeImpl *it = ModeImpls; it != &SentinelModeImpl; it = it->Next) { |
73 | if (!internal_strcmp(s1: Mode, s2: it->Mode)) |
74 | return XRayLogRegisterStatus::XRAY_DUPLICATE_MODE; |
75 | } |
76 | auto *NewModeImpl = static_cast<ModeImpl *>(InternalAlloc(size: sizeof(ModeImpl))); |
77 | NewModeImpl->Next = ModeImpls; |
78 | NewModeImpl->Mode = internal_strdup(s: Mode); |
79 | NewModeImpl->Impl = Impl; |
80 | ModeImpls = NewModeImpl; |
81 | return XRayLogRegisterStatus::XRAY_REGISTRATION_OK; |
82 | } |
83 | |
84 | XRayLogRegisterStatus |
85 | __xray_log_select_mode(const char *Mode) XRAY_NEVER_INSTRUMENT { |
86 | SpinMutexLock Guard(&XRayImplMutex); |
87 | for (ModeImpl *it = ModeImpls; it != &SentinelModeImpl; it = it->Next) { |
88 | if (!internal_strcmp(s1: Mode, s2: it->Mode)) { |
89 | CurrentMode = it; |
90 | CurrentXRayImpl = it->Impl; |
91 | GlobalXRayImpl = &CurrentXRayImpl; |
92 | __xray_set_handler(entry: it->Impl.handle_arg0); |
93 | return XRayLogRegisterStatus::XRAY_REGISTRATION_OK; |
94 | } |
95 | } |
96 | return XRayLogRegisterStatus::XRAY_MODE_NOT_FOUND; |
97 | } |
98 | |
99 | const char *__xray_log_get_current_mode() XRAY_NEVER_INSTRUMENT { |
100 | SpinMutexLock Guard(&XRayImplMutex); |
101 | if (CurrentMode != nullptr) |
102 | return CurrentMode->Mode; |
103 | return nullptr; |
104 | } |
105 | |
106 | void __xray_set_log_impl(XRayLogImpl Impl) XRAY_NEVER_INSTRUMENT { |
107 | if (Impl.log_init == nullptr || Impl.log_finalize == nullptr || |
108 | Impl.handle_arg0 == nullptr || Impl.flush_log == nullptr) { |
109 | SpinMutexLock Guard(&XRayImplMutex); |
110 | GlobalXRayImpl = nullptr; |
111 | CurrentMode = nullptr; |
112 | __xray_remove_handler(); |
113 | __xray_remove_handler_arg1(); |
114 | return; |
115 | } |
116 | |
117 | SpinMutexLock Guard(&XRayImplMutex); |
118 | CurrentXRayImpl = Impl; |
119 | GlobalXRayImpl = &CurrentXRayImpl; |
120 | __xray_set_handler(entry: Impl.handle_arg0); |
121 | } |
122 | |
123 | void __xray_remove_log_impl() XRAY_NEVER_INSTRUMENT { |
124 | SpinMutexLock Guard(&XRayImplMutex); |
125 | GlobalXRayImpl = nullptr; |
126 | __xray_remove_handler(); |
127 | __xray_remove_handler_arg1(); |
128 | } |
129 | |
130 | XRayLogInitStatus __xray_log_init(size_t BufferSize, size_t MaxBuffers, |
131 | void *Args, |
132 | size_t ArgsSize) XRAY_NEVER_INSTRUMENT { |
133 | SpinMutexLock Guard(&XRayImplMutex); |
134 | if (!GlobalXRayImpl) |
135 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
136 | return GlobalXRayImpl->log_init(BufferSize, MaxBuffers, Args, ArgsSize); |
137 | } |
138 | |
139 | XRayLogInitStatus __xray_log_init_mode(const char *Mode, const char *Config) |
140 | XRAY_NEVER_INSTRUMENT { |
141 | SpinMutexLock Guard(&XRayImplMutex); |
142 | if (!GlobalXRayImpl) |
143 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
144 | |
145 | if (Config == nullptr) |
146 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
147 | |
148 | // Check first whether the current mode is the same as what we expect. |
149 | if (CurrentMode == nullptr || internal_strcmp(s1: CurrentMode->Mode, s2: Mode) != 0) |
150 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
151 | |
152 | // Here we do some work to coerce the pointer we're provided, so that |
153 | // the implementations that still take void* pointers can handle the |
154 | // data provided in the Config argument. |
155 | return GlobalXRayImpl->log_init( |
156 | 0, 0, const_cast<void *>(static_cast<const void *>(Config)), 0); |
157 | } |
158 | |
159 | XRayLogInitStatus |
160 | __xray_log_init_mode_bin(const char *Mode, const char *Config, |
161 | size_t ConfigSize) XRAY_NEVER_INSTRUMENT { |
162 | SpinMutexLock Guard(&XRayImplMutex); |
163 | if (!GlobalXRayImpl) |
164 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
165 | |
166 | if (Config == nullptr) |
167 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
168 | |
169 | // Check first whether the current mode is the same as what we expect. |
170 | if (CurrentMode == nullptr || internal_strcmp(s1: CurrentMode->Mode, s2: Mode) != 0) |
171 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
172 | |
173 | // Here we do some work to coerce the pointer we're provided, so that |
174 | // the implementations that still take void* pointers can handle the |
175 | // data provided in the Config argument. |
176 | return GlobalXRayImpl->log_init( |
177 | 0, 0, const_cast<void *>(static_cast<const void *>(Config)), ConfigSize); |
178 | } |
179 | |
180 | XRayLogInitStatus __xray_log_finalize() XRAY_NEVER_INSTRUMENT { |
181 | SpinMutexLock Guard(&XRayImplMutex); |
182 | if (!GlobalXRayImpl) |
183 | return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; |
184 | return GlobalXRayImpl->log_finalize(); |
185 | } |
186 | |
187 | XRayLogFlushStatus __xray_log_flushLog() XRAY_NEVER_INSTRUMENT { |
188 | SpinMutexLock Guard(&XRayImplMutex); |
189 | if (!GlobalXRayImpl) |
190 | return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; |
191 | return GlobalXRayImpl->flush_log(); |
192 | } |
193 | |
194 | XRayLogFlushStatus __xray_log_process_buffers( |
195 | void (*Processor)(const char *, XRayBuffer)) XRAY_NEVER_INSTRUMENT { |
196 | // We want to make sure that there will be no changes to the global state for |
197 | // the log by synchronising on the XRayBufferIteratorMutex. |
198 | if (!GlobalXRayImpl) |
199 | return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; |
200 | auto Iterator = reinterpret_cast<XRayBuffer (*)(XRayBuffer)>( |
201 | atomic_load(a: &XRayBufferIterator, mo: memory_order_acquire)); |
202 | auto Buffer = (*Iterator)(XRayBuffer{.Data: nullptr, .Size: 0}); |
203 | auto Mode = CurrentMode ? CurrentMode->Mode : nullptr; |
204 | while (Buffer.Data != nullptr) { |
205 | (*Processor)(Mode, Buffer); |
206 | Buffer = (*Iterator)(Buffer); |
207 | } |
208 | return XRayLogFlushStatus::XRAY_LOG_FLUSHED; |
209 | } |
210 | |