1 //===-- xray_interface.cpp --------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
12 // Implementation of the API functions.
14 //===----------------------------------------------------------------------===//
16 #include "xray_interface_internal.h"
24 #include "sanitizer_common/sanitizer_common.h"
25 #include "xray_defs.h"
26 #include "xray_flags.h"
28 extern __sanitizer::SpinMutex XRayInstrMapMutex;
29 extern __sanitizer::atomic_uint8_t XRayInitialized;
30 extern __xray::XRaySledMap XRayInstrMap;
34 #if defined(__x86_64__)
35 static const int16_t cSledLength = 12;
36 #elif defined(__aarch64__)
37 static const int16_t cSledLength = 32;
38 #elif defined(__arm__)
39 static const int16_t cSledLength = 28;
40 #elif SANITIZER_MIPS32
41 static const int16_t cSledLength = 48;
42 #elif SANITIZER_MIPS64
43 static const int16_t cSledLength = 64;
44 #elif defined(__powerpc64__)
45 static const int16_t cSledLength = 8;
47 #error "Unsupported CPU Architecture"
48 #endif /* CPU architecture */
50 // This is the function to call when we encounter the entry or exit sleds.
51 __sanitizer::atomic_uintptr_t XRayPatchedFunction{0};
53 // This is the function to call from the arg1-enabled sleds/trampolines.
54 __sanitizer::atomic_uintptr_t XRayArgLogger{0};
56 // This is the function to call when we encounter a custom event log call.
57 __sanitizer::atomic_uintptr_t XRayPatchedCustomEvent{0};
59 // This is the global status to determine whether we are currently
60 // patching/unpatching.
61 __sanitizer::atomic_uint8_t XRayPatching{0};
63 // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
64 // any successful mprotect(...) changes. This is used to make a page writeable
65 // and executable, and upon destruction if it was successful in doing so returns
66 // the page into a read-only and executable page.
68 // This is only used specifically for runtime-patching of the XRay
69 // instrumentation points. This assumes that the executable pages are originally
70 // read-and-execute only.
71 class MProtectHelper {
72 void *PageAlignedAddr;
73 std::size_t MProtectLen;
77 explicit MProtectHelper(void *PageAlignedAddr,
78 std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
79 : PageAlignedAddr(PageAlignedAddr),
80 MProtectLen(MProtectLen),
83 int MakeWriteable() XRAY_NEVER_INSTRUMENT {
84 auto R = mprotect(PageAlignedAddr, MProtectLen,
85 PROT_READ | PROT_WRITE | PROT_EXEC);
91 ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
93 mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
100 bool patchSled(const XRaySledEntry &Sled, bool Enable,
101 int32_t FuncId) XRAY_NEVER_INSTRUMENT {
102 bool Success = false;
104 case XRayEntryType::ENTRY:
105 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);
107 case XRayEntryType::EXIT:
108 Success = patchFunctionExit(Enable, FuncId, Sled);
110 case XRayEntryType::TAIL:
111 Success = patchFunctionTailExit(Enable, FuncId, Sled);
113 case XRayEntryType::LOG_ARGS_ENTRY:
114 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);
116 case XRayEntryType::CUSTOM_EVENT:
117 Success = patchCustomEvent(Enable, FuncId, Sled);
120 Report("Unsupported sled kind '%d' @%04x\n", Sled.Address, int(Sled.Kind));
126 XRayPatchingStatus patchFunction(int32_t FuncId,
127 bool Enable) XRAY_NEVER_INSTRUMENT {
128 if (!__sanitizer::atomic_load(&XRayInitialized,
129 __sanitizer::memory_order_acquire))
130 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
132 uint8_t NotPatching = false;
133 if (!__sanitizer::atomic_compare_exchange_strong(
134 &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
135 return XRayPatchingStatus::ONGOING; // Already patching.
137 // Next, we look for the function index.
138 XRaySledMap InstrMap;
140 __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
141 InstrMap = XRayInstrMap;
144 // If we don't have an index, we can't patch individual functions.
145 if (InstrMap.Functions == 0)
146 return XRayPatchingStatus::NOT_INITIALIZED;
148 // FuncId must be a positive number, less than the number of functions
150 if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
151 Report("Invalid function id provided: %d\n", FuncId);
152 return XRayPatchingStatus::FAILED;
155 // Now we patch ths sleds for this specific function.
156 auto SledRange = InstrMap.SledsIndex[FuncId - 1];
157 auto *f = SledRange.Begin;
158 auto *e = SledRange.End;
160 bool SucceedOnce = false;
162 SucceedOnce |= patchSled(*f++, Enable, FuncId);
164 __sanitizer::atomic_store(&XRayPatching, false,
165 __sanitizer::memory_order_release);
168 Report("Failed patching any sled for function '%d'.", FuncId);
169 return XRayPatchingStatus::FAILED;
172 return XRayPatchingStatus::SUCCESS;
175 // controlPatching implements the common internals of the patching/unpatching
176 // implementation. |Enable| defines whether we're enabling or disabling the
177 // runtime XRay instrumentation.
178 XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
179 if (!__sanitizer::atomic_load(&XRayInitialized,
180 __sanitizer::memory_order_acquire))
181 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
183 uint8_t NotPatching = false;
184 if (!__sanitizer::atomic_compare_exchange_strong(
185 &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
186 return XRayPatchingStatus::ONGOING; // Already patching.
188 uint8_t PatchingSuccess = false;
189 auto XRayPatchingStatusResetter =
190 __sanitizer::at_scope_exit([&PatchingSuccess] {
191 if (!PatchingSuccess)
192 __sanitizer::atomic_store(&XRayPatching, false,
193 __sanitizer::memory_order_release);
196 XRaySledMap InstrMap;
198 __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
199 InstrMap = XRayInstrMap;
201 if (InstrMap.Entries == 0)
202 return XRayPatchingStatus::NOT_INITIALIZED;
207 // First we want to find the bounds for which we have instrumentation points,
208 // and try to get as few calls to mprotect(...) as possible. We're assuming
209 // that all the sleds for the instrumentation map are contiguous as a single
210 // set of pages. When we do support dynamic shared object instrumentation,
211 // we'll need to do this for each set of page load offsets per DSO loaded. For
212 // now we're assuming we can mprotect the whole section of text between the
213 // minimum sled address and the maximum sled address (+ the largest sled
215 auto MinSled = InstrMap.Sleds[0];
216 auto MaxSled = InstrMap.Sleds[InstrMap.Entries - 1];
217 for (std::size_t I = 0; I < InstrMap.Entries; I++) {
218 const auto &Sled = InstrMap.Sleds[I];
219 if (Sled.Address < MinSled.Address)
221 if (Sled.Address > MaxSled.Address)
225 const size_t PageSize = flags()->xray_page_size_override > 0
226 ? flags()->xray_page_size_override
227 : GetPageSizeCached();
228 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
229 Report("System page size is not a power of two: %lld\n", PageSize);
230 return XRayPatchingStatus::FAILED;
233 void *PageAlignedAddr =
234 reinterpret_cast<void *>(MinSled.Address & ~(PageSize - 1));
236 (MaxSled.Address - reinterpret_cast<uptr>(PageAlignedAddr)) + cSledLength;
237 MProtectHelper Protector(PageAlignedAddr, MProtectLen);
238 if (Protector.MakeWriteable() == -1) {
239 Report("Failed mprotect: %d\n", errno);
240 return XRayPatchingStatus::FAILED;
243 for (std::size_t I = 0; I < InstrMap.Entries; ++I) {
244 auto &Sled = InstrMap.Sleds[I];
245 auto F = Sled.Function;
252 patchSled(Sled, Enable, FuncId);
254 __sanitizer::atomic_store(&XRayPatching, false,
255 __sanitizer::memory_order_release);
256 PatchingSuccess = true;
257 return XRayPatchingStatus::SUCCESS;
260 XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId,
261 bool Enable) XRAY_NEVER_INSTRUMENT {
262 XRaySledMap InstrMap;
264 __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
265 InstrMap = XRayInstrMap;
268 // FuncId must be a positive number, less than the number of functions
270 if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
271 Report("Invalid function id provided: %d\n", FuncId);
272 return XRayPatchingStatus::FAILED;
275 const size_t PageSize = flags()->xray_page_size_override > 0
276 ? flags()->xray_page_size_override
277 : GetPageSizeCached();
278 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
279 Report("Provided page size is not a power of two: %lld\n", PageSize);
280 return XRayPatchingStatus::FAILED;
283 // Here we compute the minumum sled and maximum sled associated with a
284 // particular function ID.
285 auto SledRange = InstrMap.SledsIndex[FuncId - 1];
286 auto *f = SledRange.Begin;
287 auto *e = SledRange.End;
289 auto MaxSled = *(SledRange.End - 1);
291 if (f->Address < MinSled.Address)
293 if (f->Address > MaxSled.Address)
298 void *PageAlignedAddr =
299 reinterpret_cast<void *>(MinSled.Address & ~(PageSize - 1));
301 (MaxSled.Address - reinterpret_cast<uptr>(PageAlignedAddr)) + cSledLength;
302 MProtectHelper Protector(PageAlignedAddr, MProtectLen);
303 if (Protector.MakeWriteable() == -1) {
304 Report("Failed mprotect: %d\n", errno);
305 return XRayPatchingStatus::FAILED;
307 return patchFunction(FuncId, Enable);
312 } // namespace __xray
314 using namespace __xray;
316 // The following functions are declared `extern "C" {...}` in the header, hence
317 // they're defined in the global namespace.
319 int __xray_set_handler(void (*entry)(int32_t,
320 XRayEntryType)) XRAY_NEVER_INSTRUMENT {
321 if (__sanitizer::atomic_load(&XRayInitialized,
322 __sanitizer::memory_order_acquire)) {
324 __sanitizer::atomic_store(&__xray::XRayPatchedFunction,
325 reinterpret_cast<uintptr_t>(entry),
326 __sanitizer::memory_order_release);
332 int __xray_set_customevent_handler(void (*entry)(void *, size_t))
333 XRAY_NEVER_INSTRUMENT {
334 if (__sanitizer::atomic_load(&XRayInitialized,
335 __sanitizer::memory_order_acquire)) {
336 __sanitizer::atomic_store(&__xray::XRayPatchedCustomEvent,
337 reinterpret_cast<uintptr_t>(entry),
338 __sanitizer::memory_order_release);
344 int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
345 return __xray_set_handler(nullptr);
348 int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT {
349 return __xray_set_customevent_handler(nullptr);
352 XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
353 return controlPatching(true);
356 XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
357 return controlPatching(false);
360 XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
361 return mprotectAndPatchFunction(FuncId, true);
365 __xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
366 return mprotectAndPatchFunction(FuncId, false);
369 int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) {
370 if (!__sanitizer::atomic_load(&XRayInitialized,
371 __sanitizer::memory_order_acquire))
374 // A relaxed write might not be visible even if the current thread gets
375 // scheduled on a different CPU/NUMA node. We need to wait for everyone to
376 // have this handler installed for consistency of collected data across CPUs.
377 __sanitizer::atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry),
378 __sanitizer::memory_order_release);
382 int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }
384 uintptr_t __xray_function_address(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
385 __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
386 if (FuncId <= 0 || static_cast<size_t>(FuncId) > XRayInstrMap.Functions)
388 return XRayInstrMap.SledsIndex[FuncId - 1].Begin->Function
389 // On PPC, function entries are always aligned to 16 bytes. The beginning of a
390 // sled might be a local entry, which is always +8 based on the global entry.
391 // Always return the global entry.
398 size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT {
399 __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
400 return XRayInstrMap.Functions;