Index: lib/xray/CMakeLists.txt =================================================================== --- lib/xray/CMakeLists.txt +++ lib/xray/CMakeLists.txt @@ -2,7 +2,8 @@ set(XRAY_SOURCES xray_init.cc - xray_interface.cc + xray_interface.cc + xray_inmemory_log.cc ) include_directories(..) @@ -13,9 +14,9 @@ set(XRAY_COMMON_DEFINITIONS XRAY_HAS_EXCEPTIONS=1) add_compiler_rt_object_libraries(RTXray - ARCHS ${XRAY_SUPPORTED_ARCH} - SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS} - DEFS ${XRAY_COMMON_DEFINITIONS}) + ARCHS ${XRAY_SUPPORTED_ARCH} + SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS} + DEFS ${XRAY_COMMON_DEFINITIONS}) add_custom_target(xray) set(XRAY_COMMON_RUNTIME_OBJECT_LIBS RTXray) Index: lib/xray/xray_init.cc =================================================================== --- lib/xray/xray_init.cc +++ lib/xray/xray_init.cc @@ -42,47 +42,6 @@ // This should always be updated before XRayInitialized is updated. std::atomic<__xray::XRaySledMap> XRayInstrMap{}; -void __xray_dump() { - // FIXME: This is just a proof-of-concept implementation, should make this - // more accessible for the real thing. - auto LocalEntries = XRayInstrMap.load(std::memory_order_acquire); - if (LocalEntries.Entries == 0) { - printf("Instrumentation map is empty.\n"); - return; - } - - printf("__xray_instr_map@%p..%p\n", __start_xray_instr_map, - __stop_xray_instr_map); - auto Countdown = LocalEntries.Entries; - auto Sled = LocalEntries.Sleds; - static constexpr char EntrySled[] = "E"; - static constexpr char ExitSled[] = "X"; - static constexpr char Always[] = "*"; - static constexpr char Maybe[] = "?"; - while (Countdown != 0) { - printf("%lx\t%s\t%s\t@function(%lx)\n", Sled->Address, - Sled->Kind == static_cast(XRayEntryType::ENTRY) - ? EntrySled - : ExitSled, - Sled->AlwaysInstrument == 1 ? Always : Maybe, Sled->Function); - --Countdown; - ++Sled; - } -} - -extern "C" { -void __xray_DemoLog(int32_t FuncId, XRayEntryType Type) { - uint64_t Hi; - uint32_t Lo, CPUId; - __asm__ __volatile__("rdtscp" : "=a"(Lo), "=d"(Hi), "=c"(CPUId)); - int ignored = printf( - "%lu: [%lu] %s%d\n", CPUId, (Hi << 32) | Lo, - Type == static_cast(XRayEntryType::ENTRY) ? "E" : "X", - FuncId); - (void)(ignored); -} -} - // __xray_init() will do the actual loading of the current process' memory map // and then proceed to look for the .xray_instr_map section/segment. void __xray_init() { @@ -101,9 +60,7 @@ XRayInstrMap.store(SledMap, std::memory_order_release); XRayInitialized.store(true, std::memory_order_release); - // FIXME: Only for demo, patch the functions before we run main. - __xray_dump(); - __xray_set_handler(__xray_DemoLog); + // FIXME: Only for demo, dump the table and patch functions before main. __xray_patch(); } Index: lib/xray/xray_inmemory_log.cc =================================================================== --- /dev/null +++ lib/xray/xray_inmemory_log.cc @@ -0,0 +1,153 @@ +//===-- xray_inmemory_log.cc ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a dynamic runtime instrumentation system. +// +// Implementation of a simple in-memory log of XRay events. This defines a +// logging function that's compatible with the XRay handler interface, and +// routines for exporting data to files. +// +//===----------------------------------------------------------------------===// + +#include "xray_interface_internal.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +void __xray_InMemoryRawLog(int32_t FuncId, XRayEntryType Type); +} + +// __xray_InMemoryRawLog will use a thread-local aligned buffer capped to a +// certain size (32kb by default) and use it as if it were a circular buffer for +// events. We store simple fixed-sized entries in the log for external analysis. +namespace __xray { +struct alignas(32) XRayRecord { + // Get the full 8 bytes of the TSC when we get the log record. + uint64_t TSC = 0; + + // The thread ID for the currently running thread. + pid_t TId = 0; + + // The CPU where the thread is running. We assume number of CPUs <= 256. + uint8_t CPU = 0; + + // The type of the event. Usually either ENTER = 0 or EXIT = 1. + uint8_t Type = 0; + + // The function ID for the record. + int32_t FuncId = 0; +}; + +static_assert(sizeof(XRayRecord) == 32, "XRayRecord != 32 bytes"); + +std::mutex LogMutex; + +static constexpr size_t BuffLen = 1024; + +static void RetryingWrite(int Fd, char *Begin, char *End) { + if (Begin == End) + return; + auto TotalBytes = std::distance(Begin, End); + while (auto Written = write(Fd, Begin, TotalBytes)) { + if (Written <= 0) { + if (errno == EINTR) + continue; // Try again. + fprintf(stderr, "Failed to write; errno = %d", errno); + return; + } + + // FIXME: Figure out whether/how to assert properly. + assert(static_cast(Written) <= TotalBytes); + TotalBytes -= Written; + if (TotalBytes == 0) + break; + Begin += Written; + } +} + +class ThreadExitFlusher { +public: + explicit ThreadExitFlusher(int Fd, XRayRecord *Start, size_t &Offset) + : Fd(Fd), Start(Start), Offset(Offset) {} + + ~ThreadExitFlusher() noexcept { + std::lock_guard L(LogMutex); + RetryingWrite(Fd, reinterpret_cast(Start), + reinterpret_cast(Start + Offset)); + fsync(Fd); + } + +private: + int Fd; + XRayRecord *Start; + size_t &Offset; +}; +} + +void __xray_InMemoryRawLog(int32_t FuncId, XRayEntryType Type) { + using Buffer = std::aligned_storage::type; + thread_local static Buffer InMemoryBuffer[__xray::BuffLen] = {}; + thread_local static size_t Offset = 0; + static int Fd = [] { + // Open a temporary file once for the log. + static char TmpFilename[] = "/tmp/xray-log-XXXXXX"; + int Fd = mkstemp(TmpFilename); + if (Fd == -1) { + fprintf(stderr, + "XRay: Failed opening temporary file '%s'; not logging events.", + TmpFilename); + return -1; + } + + // FIXME: Figure out how to either control this or to make it available some + // other way. + printf("XRay: Log file in '%s'\n", TmpFilename); + return Fd; + }(); + if (Fd == -1) + return; + thread_local __xray::ThreadExitFlusher Flusher( + Fd, reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer), Offset); + thread_local pid_t TId = syscall(SYS_gettid); + + // First we get the useful data, and stuff it into the already aligned buffer + // through a pointer offset. + auto &R = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer)[Offset]; + uint32_t CPU; + R.TSC = __rdtscp(&CPU); + R.CPU = CPU; + R.TId = TId; + R.Type = Type; + R.FuncId = FuncId; + ++Offset; + if (Offset == __xray::BuffLen) { + std::lock_guard L(__xray::LogMutex); + auto RecordBuffer = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer); + __xray::RetryingWrite(Fd, reinterpret_cast(RecordBuffer), + reinterpret_cast(RecordBuffer + Offset)); + Offset = 0; + } +} + +static auto Unused = [] { + __xray_set_handler(__xray_InMemoryRawLog); + return true; +}();