blob: 39cf8efb340763aa35ca4c179ef743923d6b9c73 [file] [log] [blame]
Dean Michael Berris938c5032016-07-21 07:39:55 +00001//===-- xray_interface.cpp --------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of XRay, a dynamic runtime instrumentation system.
11//
12// Implementation of the API functions.
13//
14//===----------------------------------------------------------------------===//
15
16#include "xray_interface_internal.h"
Dean Michael Berris17a586e2016-07-29 07:11:58 +000017
Dean Michael Berris938c5032016-07-21 07:39:55 +000018#include <atomic>
19#include <cstdint>
20#include <cstdio>
21#include <errno.h>
22#include <limits>
23#include <sys/mman.h>
24
Dean Michael Berris17a586e2016-07-29 07:11:58 +000025#include "sanitizer_common/sanitizer_common.h"
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000026#include "xray_defs.h"
Dean Michael Berris17a586e2016-07-29 07:11:58 +000027
Dean Michael Berris938c5032016-07-21 07:39:55 +000028namespace __xray {
29
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000030#if defined(__x86_64__)
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000031// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect()
32// ?
33static const int16_t cSledLength = 12;
Dean Michael Berrisbad8f0f2016-11-21 03:20:43 +000034#elif defined(__aarch64__)
35static const int16_t cSledLength = 32;
Serge Rogatch882fc5d2016-12-05 23:29:56 +000036#elif defined(__arm__)
37static const int16_t cSledLength = 28;
Sagar Thakurea831e42017-02-15 10:54:09 +000038#elif SANITIZER_MIPS32
39static const int16_t cSledLength = 48;
40#elif SANITIZER_MIPS64
41static const int16_t cSledLength = 64;
Tim Shenc6ce73b2017-02-15 22:40:29 +000042#elif defined(__powerpc64__)
43static const int16_t cSledLength = 8;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000044#else
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000045#error "Unsupported CPU Architecture"
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000046#endif /* CPU architecture */
47
Dean Michael Berris938c5032016-07-21 07:39:55 +000048// This is the function to call when we encounter the entry or exit sleds.
49std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr};
50
Dean Michael Berrisa814c942017-03-06 07:25:41 +000051// This is the function to call from the arg1-enabled sleds/trampolines.
52std::atomic<void (*)(int32_t, XRayEntryType, uint64_t)> XRayArgLogger{nullptr};
53
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000054// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
55// any successful mprotect(...) changes. This is used to make a page writeable
56// and executable, and upon destruction if it was successful in doing so returns
57// the page into a read-only and executable page.
58//
59// This is only used specifically for runtime-patching of the XRay
60// instrumentation points. This assumes that the executable pages are originally
61// read-and-execute only.
62class MProtectHelper {
63 void *PageAlignedAddr;
64 std::size_t MProtectLen;
65 bool MustCleanup;
66
67public:
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000068 explicit MProtectHelper(void *PageAlignedAddr,
69 std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
70 : PageAlignedAddr(PageAlignedAddr),
71 MProtectLen(MProtectLen),
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000072 MustCleanup(false) {}
73
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000074 int MakeWriteable() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000075 auto R = mprotect(PageAlignedAddr, MProtectLen,
76 PROT_READ | PROT_WRITE | PROT_EXEC);
77 if (R != -1)
78 MustCleanup = true;
79 return R;
80 }
81
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000082 ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000083 if (MustCleanup) {
84 mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
85 }
86 }
87};
88
Dean Michael Berris938c5032016-07-21 07:39:55 +000089} // namespace __xray
90
Dean Michael Berris938c5032016-07-21 07:39:55 +000091extern std::atomic<bool> XRayInitialized;
92extern std::atomic<__xray::XRaySledMap> XRayInstrMap;
93
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000094int __xray_set_handler(void (*entry)(int32_t,
95 XRayEntryType)) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris938c5032016-07-21 07:39:55 +000096 if (XRayInitialized.load(std::memory_order_acquire)) {
97 __xray::XRayPatchedFunction.store(entry, std::memory_order_release);
98 return 1;
99 }
100 return 0;
101}
102
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000103int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
104 return __xray_set_handler(nullptr);
105}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000106
Dean Michael Berris938c5032016-07-21 07:39:55 +0000107std::atomic<bool> XRayPatching{false};
108
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000109using namespace __xray;
110
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000111// FIXME: Figure out whether we can move this class to sanitizer_common instead
112// as a generic "scope guard".
113template <class Function> class CleanupInvoker {
114 Function Fn;
115
116public:
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000117 explicit CleanupInvoker(Function Fn) XRAY_NEVER_INSTRUMENT : Fn(Fn) {}
118 CleanupInvoker(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = default;
119 CleanupInvoker(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = default;
120 CleanupInvoker &
121 operator=(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = delete;
122 CleanupInvoker &operator=(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = delete;
123 ~CleanupInvoker() XRAY_NEVER_INSTRUMENT { Fn(); }
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000124};
125
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000126template <class Function>
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000127CleanupInvoker<Function> scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000128 return CleanupInvoker<Function>{Fn};
129}
130
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000131// controlPatching implements the common internals of the patching/unpatching
Dean Michael Berris68e74842016-08-08 03:10:22 +0000132// implementation. |Enable| defines whether we're enabling or disabling the
133// runtime XRay instrumentation.
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000134XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000135 if (!XRayInitialized.load(std::memory_order_acquire))
136 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
137
138 static bool NotPatching = false;
139 if (!XRayPatching.compare_exchange_strong(NotPatching, true,
140 std::memory_order_acq_rel,
141 std::memory_order_acquire)) {
142 return XRayPatchingStatus::ONGOING; // Already patching.
143 }
144
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000145 bool PatchingSuccess = false;
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000146 auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] {
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000147 if (!PatchingSuccess) {
148 XRayPatching.store(false, std::memory_order_release);
149 }
150 });
151
Dean Michael Berris938c5032016-07-21 07:39:55 +0000152 // Step 1: Compute the function id, as a unique identifier per function in the
153 // instrumentation map.
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000154 XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire);
Dean Michael Berris938c5032016-07-21 07:39:55 +0000155 if (InstrMap.Entries == 0)
156 return XRayPatchingStatus::NOT_INITIALIZED;
157
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000158 const uint64_t PageSize = GetPageSizeCached();
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000159 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
Dean Michael Berris3076d432016-11-23 04:47:41 +0000160 Report("System page size is not a power of two: %lld\n", PageSize);
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000161 return XRayPatchingStatus::FAILED;
162 }
163
164 uint32_t FuncId = 1;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000165 uint64_t CurFun = 0;
166 for (std::size_t I = 0; I < InstrMap.Entries; I++) {
167 auto Sled = InstrMap.Sleds[I];
168 auto F = Sled.Function;
169 if (CurFun == 0)
170 CurFun = F;
171 if (F != CurFun) {
172 ++FuncId;
173 CurFun = F;
174 }
175
176 // While we're here, we should patch the nop sled. To do that we mprotect
177 // the page containing the function to be writeable.
178 void *PageAlignedAddr =
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000179 reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1));
180 std::size_t MProtectLen = (Sled.Address + cSledLength) -
181 reinterpret_cast<uint64_t>(PageAlignedAddr);
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000182 MProtectHelper Protector(PageAlignedAddr, MProtectLen);
183 if (Protector.MakeWriteable() == -1) {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000184 printf("Failed mprotect: %d\n", errno);
185 return XRayPatchingStatus::FAILED;
186 }
187
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000188 bool Success = false;
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000189 switch (Sled.Kind) {
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000190 case XRayEntryType::ENTRY:
Dean Michael Berrisa814c942017-03-06 07:25:41 +0000191 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000192 break;
193 case XRayEntryType::EXIT:
194 Success = patchFunctionExit(Enable, FuncId, Sled);
195 break;
Dean Michael Berris1b09aae2016-10-13 23:56:54 +0000196 case XRayEntryType::TAIL:
197 Success = patchFunctionTailExit(Enable, FuncId, Sled);
198 break;
Dean Michael Berrisa814c942017-03-06 07:25:41 +0000199 case XRayEntryType::LOG_ARGS_ENTRY:
200 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);
201 break;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000202 default:
Dean Michael Berris3076d432016-11-23 04:47:41 +0000203 Report("Unsupported sled kind: %d\n", int(Sled.Kind));
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000204 continue;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000205 }
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000206 (void)Success;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000207 }
208 XRayPatching.store(false, std::memory_order_release);
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000209 PatchingSuccess = true;
210 return XRayPatchingStatus::SUCCESS;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000211}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000212
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000213XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000214 return controlPatching(true);
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000215}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000216
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000217XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000218 return controlPatching(false);
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000219}
Dean Michael Berrisa814c942017-03-06 07:25:41 +0000220
221int __xray_set_handler_arg1(void (*Handler)(int32_t, XRayEntryType, uint64_t))
222{
223 if (!XRayInitialized.load(std::memory_order_acquire)) {
224 return 0;
225 }
226 // A relaxed write might not be visible even if the current thread gets
227 // scheduled on a different CPU/NUMA node. We need to wait for everyone to
228 // have this handler installed for consistency of collected data across CPUs.
229 XRayArgLogger.store(Handler, std::memory_order_release);
230 return 1;
231}
232int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }