Index: compiler-rt/lib/xray/xray_flags.inc =================================================================== --- compiler-rt/lib/xray/xray_flags.inc +++ compiler-rt/lib/xray/xray_flags.inc @@ -19,7 +19,8 @@ XRAY_FLAG(const char *, xray_logfile_base, "xray-log.", "Filename base for the xray logfile.") XRAY_FLAG(const char *, xray_mode, "", "Mode to install by default.") - +XRAY_FLAG(uptr, xray_page_size_override, 0, + "Override the default page size for the system, in bytes.") // Basic (Naive) Mode logging options. XRAY_FLAG(bool, xray_naive_log, false, Index: compiler-rt/lib/xray/xray_interface.cc =================================================================== --- compiler-rt/lib/xray/xray_interface.cc +++ compiler-rt/lib/xray/xray_interface.cc @@ -23,12 +23,11 @@ #include "sanitizer_common/sanitizer_common.h" #include "xray_defs.h" +#include "xray_flags.h" namespace __xray { #if defined(__x86_64__) -// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect() -// ? static const int16_t cSledLength = 12; #elif defined(__aarch64__) static const int16_t cSledLength = 32; @@ -119,7 +118,6 @@ return 0; } - int __xray_remove_handler() XRAY_NEVER_INSTRUMENT { return __xray_set_handler(nullptr); } @@ -154,19 +152,6 @@ inline bool patchSled(const XRaySledEntry &Sled, bool Enable, int32_t FuncId) XRAY_NEVER_INSTRUMENT { - // While we're here, we should patch the nop sled. To do that we mprotect - // the page containing the function to be writeable. - const uint64_t PageSize = GetPageSizeCached(); - void *PageAlignedAddr = - reinterpret_cast(Sled.Address & ~(PageSize - 1)); - std::size_t MProtectLen = (Sled.Address + cSledLength) - - reinterpret_cast(PageAlignedAddr); - MProtectHelper Protector(PageAlignedAddr, MProtectLen); - if (Protector.MakeWriteable() == -1) { - printf("Failed mprotect: %d\n", errno); - return XRayPatchingStatus::FAILED; - } - bool Success = false; switch (Sled.Kind) { case XRayEntryType::ENTRY: @@ -211,8 +196,6 @@ __sanitizer::memory_order_release); }); - // Step 1: Compute the function id, as a unique identifier per function in the - // instrumentation map. XRaySledMap InstrMap; { __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex); @@ -221,16 +204,47 @@ if (InstrMap.Entries == 0) return XRayPatchingStatus::NOT_INITIALIZED; - const uint64_t PageSize = GetPageSizeCached(); + uint32_t FuncId = 1; + uint64_t CurFun = 0; + + // First we want to find the bounds for which we have instrumentation points, + // and try to get as few calls to mprotect(...) as possible. We're assuming + // that all the sleds for the instrumentation map are contiguous as a single + // set of pages. When we do support dynamic shared object instrumentation, + // we'll need to do this for each set of page load offsets per DSO loaded. For + // now we're assuming we can mprotect the whole section of text between the + // minimum sled address and the maximum sled address (+ the largest sled + // size). + auto MinSled = InstrMap.Sleds[0]; + auto MaxSled = InstrMap.Sleds[InstrMap.Entries - 1]; + for (std::size_t I = 0; I < InstrMap.Entries; I++) { + auto &Sled = InstrMap.Sleds[I]; + if (Sled.Address < MinSled.Address) + MinSled = Sled; + if (Sled.Address > MaxSled.Address) + MaxSled = Sled; + } + + const size_t PageSize = flags()->xray_page_size_override > 0 + ? flags()->xray_page_size_override + : GetPageSizeCached(); if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { Report("System page size is not a power of two: %lld\n", PageSize); return XRayPatchingStatus::FAILED; } - uint32_t FuncId = 1; - uint64_t CurFun = 0; - for (std::size_t I = 0; I < InstrMap.Entries; I++) { - auto Sled = InstrMap.Sleds[I]; + void *PageAlignedAddr = + reinterpret_cast(MinSled.Address & ~(PageSize - 1)); + size_t MProtectLen = + (MaxSled.Address - reinterpret_cast(PageAlignedAddr)) + cSledLength; + MProtectHelper Protector(PageAlignedAddr, MProtectLen); + if (Protector.MakeWriteable() == -1) { + Report("Failed mprotect: %d\n", errno); + return XRayPatchingStatus::FAILED; + } + + for (std::size_t I = 0; I < InstrMap.Entries; ++I) { + auto &Sled = InstrMap.Sleds[I]; auto F = Sled.Function; if (CurFun == 0) CurFun = F; @@ -239,9 +253,10 @@ CurFun = F; } patchSled(Sled, Enable, FuncId); + __sanitizer::atomic_store(&XRayPatching, false, + __sanitizer::memory_order_release); } - __sanitizer::atomic_store(&XRayPatching, false, - __sanitizer::memory_order_release); + PatchingSuccess = true; return XRayPatchingStatus::SUCCESS; } @@ -303,13 +318,62 @@ return XRayPatchingStatus::SUCCESS; } +static XRayPatchingStatus __xray_patch_helper(int32_t FuncId, bool Enable) { + XRaySledMap InstrMap; + { + __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex); + InstrMap = XRayInstrMap; + } + + // FuncId must be a positive number, less than the number of functions + // instrumented. + if (FuncId <= 0 || static_cast(FuncId) > InstrMap.Functions) { + Report("Invalid function id provided: %d\n", FuncId); + return XRayPatchingStatus::FAILED; + } + + const size_t PageSize = flags()->xray_page_size_override > 0 + ? flags()->xray_page_size_override + : GetPageSizeCached(); + if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { + Report("Provided page size is not a power of two: %lld\n", PageSize); + return XRayPatchingStatus::FAILED; + } + + // Here we compute the minumum sled and maximum sled associated with a + // particular function ID. + auto SledRange = InstrMap.SledsIndex[FuncId - 1]; + auto *f = SledRange.Begin; + auto *e = SledRange.End; + auto MinSled = *f; + auto MaxSled = *(SledRange.End - 1); + while (f != e) { + if (f->Address < MinSled.Address) + MinSled = *f; + if (f->Address > MaxSled.Address) + MaxSled = *f; + ++f; + } + + void *PageAlignedAddr = + reinterpret_cast(MinSled.Address & ~(PageSize - 1)); + size_t MProtectLen = + (MaxSled.Address - reinterpret_cast(PageAlignedAddr)) + cSledLength; + MProtectHelper Protector(PageAlignedAddr, MProtectLen); + if (Protector.MakeWriteable() == -1) { + Report("Failed mprotect: %d\n", errno); + return XRayPatchingStatus::FAILED; + } + return patchFunction(FuncId, Enable); +} + XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { - return patchFunction(FuncId, true); + return __xray_patch_helper(FuncId, true); } XRayPatchingStatus __xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { - return patchFunction(FuncId, false); + return __xray_patch_helper(FuncId, false); } int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) { Index: compiler-rt/test/xray/TestCases/Linux/coverage-sample.cc =================================================================== --- compiler-rt/test/xray/TestCases/Linux/coverage-sample.cc +++ compiler-rt/test/xray/TestCases/Linux/coverage-sample.cc @@ -9,6 +9,7 @@ #include #include +#include std::set function_ids; @@ -36,9 +37,9 @@ [[clang::xray_always_instrument]] int main(int argc, char *argv[]) { __xray_set_handler(coverage_handler); - __xray_patch(); + assert(__xray_patch() == XRayPatchingStatus::SUCCESS); foo(); - __xray_unpatch(); + assert(__xray_unpatch() == XRayPatchingStatus::SUCCESS); // print out the function_ids. printf("first pass.\n"); @@ -58,11 +59,11 @@ // patch the functions we've called before. for (const auto id : called_fns) - __xray_patch_function(id); + assert(__xray_patch_function(id) == XRayPatchingStatus::SUCCESS); // then call them again. foo(); - __xray_unpatch(); + assert(__xray_unpatch() == XRayPatchingStatus::SUCCESS); // confirm that we've seen the same functions again. printf("second pass.\n"); @@ -76,10 +77,10 @@ // Now we want to make sure that if we unpatch one, that we're only going to // see two calls of the coverage_handler. function_ids.clear(); - __xray_patch(); - __xray_unpatch_function(1); + assert(__xray_patch() == XRayPatchingStatus::SUCCESS); + assert(__xray_unpatch_function(1) == XRayPatchingStatus::SUCCESS); foo(); - __xray_unpatch(); + assert(__xray_unpatch() == XRayPatchingStatus::SUCCESS); // confirm that we don't see function id one called anymore. printf("missing 1.\n");