Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,115 @@
# Build for the XRay runtime support library.
# XRay runtime library implementation files.
set(XRAY_SOURCES
xray_inmemory_log.cc
xray_init.cc
xray_flags.cc
xray_interface.cc
xray_buffer_queue.cc
xray_log_interface.cc
xray_fdr_logging.cc
xray_utils.cc)
set(x86_64_SOURCES
xray_x86_64.cc
xray_trampoline_x86_64.S)
set(arm_SOURCES
xray_arm.cc
xray_trampoline_arm.S)
set(armhf_SOURCES
${arm_SOURCES})
set(aarch64_SOURCES
xray_AArch64.cc
xray_trampoline_AArch64.S)
set(mips_SOURCES
xray_mips.cc
xray_trampoline_mips.S)
set(mipsel_SOURCES
xray_mips.cc
xray_trampoline_mips.S)
set(mips64_SOURCES
xray_mips64.cc
xray_trampoline_mips64.S)
set(mips64el_SOURCES
xray_mips64.cc
xray_trampoline_mips64.S)
set(powerpc64le_SOURCES
xray_powerpc64.cc
xray_trampoline_powerpc64.cc
xray_trampoline_powerpc64_asm.S)
include_directories(..)
include_directories(../../include)
set(XRAY_CFLAGS ${SANITIZER_COMMON_CFLAGS})
set(XRAY_COMMON_DEFINITIONS XRAY_HAS_EXCEPTIONS=1)
append_list_if(
COMPILER_RT_HAS_XRAY_COMPILER_FLAG XRAY_SUPPORTED=1 XRAY_COMMON_DEFINITIONS)
append_list_if(
COMPILER_RT_BUILD_XRAY_NO_PREINIT XRAY_NO_PREINIT XRAY_COMMON_DEFINITIONS)
add_compiler_rt_component(xray)
set(XRAY_COMMON_RUNTIME_OBJECT_LIBS
RTXray
RTSanitizerCommon
RTSanitizerCommonLibc)
if (APPLE)
set(XRAY_LINK_LIBS ${SANITIZER_COMMON_LINK_LIBS})
add_asm_sources(XRAY_ASM_SOURCES xray_trampoline_x86_64.S)
add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
add_weak_symbols("xray" WEAK_SYMBOL_LINK_FLAGS)
add_compiler_rt_object_libraries(RTXray
OS ${XRAY_SUPPORTED_OS}
ARCHS ${XRAY_SUPPORTED_ARCH}
SOURCES ${x86_64_SOURCES}
CFLAGS ${XRAY_CFLAGS}
DEFS ${XRAY_COMMON_DEFINITIONS})
# We only support running on osx for now.
add_compiler_rt_runtime(clang_rt.xray
STATIC
OS ${XRAY_SUPPORTED_OS}
ARCHS ${XRAY_SUPPORTED_ARCH}
OBJECT_LIBS RTXray
RTSanitizerCommon
RTSanitizerCommonLibc
CFLAGS ${XRAY_CFLAGS}
DEFS ${XRAY_COMMON_DEFINITIONS}
LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
LINK_LIBS ${XRAY_LINK_LIBS}
PARENT_TARGET xray)
else()
foreach(arch ${XRAY_SUPPORTED_ARCH})
if(CAN_TARGET_${arch})
add_compiler_rt_object_libraries(RTXray
ARCHS ${arch}
SOURCES ${XRAY_SOURCES} CFLAGS ${XRAY_CFLAGS}
DEFS ${XRAY_COMMON_DEFINITIONS})
add_compiler_rt_runtime(clang_rt.xray
STATIC
ARCHS ${arch}
SOURCES ${${arch}_SOURCES}
CFLAGS ${XRAY_CFLAGS}
DEFS ${XRAY_COMMON_DEFINITIONS}
OBJECT_LIBS ${XRAY_COMMON_RUNTIME_OBJECT_LIBS}
PARENT_TARGET xray)
endif()
endforeach()
endif()
if(COMPILER_RT_INCLUDE_TESTS)
add_subdirectory(tests)
endif()

View File

@@ -0,0 +1,37 @@
include_directories(..)
add_custom_target(XRayUnitTests)
set_target_properties(XRayUnitTests PROPERTIES FOLDER "XRay unittests")
set(XRAY_UNITTEST_CFLAGS
${XRAY_CFLAGS}
${COMPILER_RT_UNITTEST_CFLAGS}
${COMPILER_RT_GTEST_CFLAGS}
-I${COMPILER_RT_SOURCE_DIR}/include
-I${COMPILER_RT_SOURCE_DIR}/lib/xray
-I${COMPILER_RT_SOURCE_DIR}/lib)
set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH})
macro(add_xray_unittest testname)
cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
if(UNIX AND NOT APPLE)
foreach(arch ${XRAY_TEST_ARCH})
set(TEST_OBJECTS)
generate_compiler_rt_tests(TEST_OBJECTS
XRayUnitTests "${testname}-${arch}-Test" "${arch}"
SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}
DEPS gtest xray llvm-xray
CFLAGS ${XRAY_UNITTEST_CFLAGS}
LINK_FLAGS -fxray-instrument
${TARGET_LINK_FLAGS}
-lstdc++ -lm ${CMAKE_THREAD_LIBS_INIT}
-lpthread
-ldl -lrt)
set_target_properties(XRayUnitTests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endforeach()
endif()
endmacro()
if(COMPILER_RT_CAN_EXECUTE_TESTS)
add_subdirectory(unit)
endif()

View File

@@ -0,0 +1,4 @@
add_xray_unittest(XRayBufferQueueTest SOURCES
buffer_queue_test.cc xray_unit_test_main.cc)
add_xray_unittest(XRayFDRLoggingTest SOURCES
fdr_logging_test.cc xray_unit_test_main.cc)

View File

@@ -0,0 +1,114 @@
//===-- buffer_queue_test.cc ----------------------------------------------===//
//
// 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 function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
#include "gtest/gtest.h"
#include <future>
#include <unistd.h>
namespace __xray {
static constexpr size_t kSize = 4096;
TEST(BufferQueueTest, API) {
bool Success = false;
BufferQueue Buffers(kSize, 1, Success);
ASSERT_TRUE(Success);
}
TEST(BufferQueueTest, GetAndRelease) {
bool Success = false;
BufferQueue Buffers(kSize, 1, Success);
ASSERT_TRUE(Success);
BufferQueue::Buffer Buf;
ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok);
ASSERT_NE(nullptr, Buf.Buffer);
ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok);
ASSERT_EQ(nullptr, Buf.Buffer);
}
TEST(BufferQueueTest, GetUntilFailed) {
bool Success = false;
BufferQueue Buffers(kSize, 1, Success);
ASSERT_TRUE(Success);
BufferQueue::Buffer Buf0;
EXPECT_EQ(Buffers.getBuffer(Buf0), BufferQueue::ErrorCode::Ok);
BufferQueue::Buffer Buf1;
EXPECT_EQ(BufferQueue::ErrorCode::NotEnoughMemory, Buffers.getBuffer(Buf1));
EXPECT_EQ(Buffers.releaseBuffer(Buf0), BufferQueue::ErrorCode::Ok);
}
TEST(BufferQueueTest, ReleaseUnknown) {
bool Success = false;
BufferQueue Buffers(kSize, 1, Success);
ASSERT_TRUE(Success);
BufferQueue::Buffer Buf;
Buf.Buffer = reinterpret_cast<void *>(0xdeadbeef);
Buf.Size = kSize;
EXPECT_EQ(BufferQueue::ErrorCode::UnrecognizedBuffer,
Buffers.releaseBuffer(Buf));
}
TEST(BufferQueueTest, ErrorsWhenFinalising) {
bool Success = false;
BufferQueue Buffers(kSize, 2, Success);
ASSERT_TRUE(Success);
BufferQueue::Buffer Buf;
ASSERT_EQ(Buffers.getBuffer(Buf), BufferQueue::ErrorCode::Ok);
ASSERT_NE(nullptr, Buf.Buffer);
ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
BufferQueue::Buffer OtherBuf;
ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing,
Buffers.getBuffer(OtherBuf));
ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing,
Buffers.finalize());
ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok);
}
TEST(BufferQueueTest, MultiThreaded) {
bool Success = false;
BufferQueue Buffers(kSize, 100, Success);
ASSERT_TRUE(Success);
auto F = [&] {
BufferQueue::Buffer B;
while (true) {
auto EC = Buffers.getBuffer(B);
if (EC != BufferQueue::ErrorCode::Ok)
return;
Buffers.releaseBuffer(B);
}
};
auto T0 = std::async(std::launch::async, F);
auto T1 = std::async(std::launch::async, F);
auto T2 = std::async(std::launch::async, [&] {
while (Buffers.finalize() != BufferQueue::ErrorCode::Ok)
;
});
F();
}
TEST(BufferQueueTest, Apply) {
bool Success = false;
BufferQueue Buffers(kSize, 10, Success);
ASSERT_TRUE(Success);
auto Count = 0;
BufferQueue::Buffer B;
for (int I = 0; I < 10; ++I) {
ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
}
Buffers.apply([&](const BufferQueue::Buffer &B) { ++Count; });
ASSERT_EQ(Count, 10);
}
} // namespace __xray

View File

@@ -0,0 +1,201 @@
//===-- fdr_logging_test.cc -----------------------------------------------===//
//
// 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 function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "xray_fdr_logging.h"
#include "gtest/gtest.h"
#include <array>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <system_error>
#include <thread>
#include <unistd.h>
#include "xray/xray_records.h"
namespace __xray {
namespace {
constexpr auto kBufferSize = 16384;
constexpr auto kBufferMax = 10;
struct ScopedFileCloserAndDeleter {
explicit ScopedFileCloserAndDeleter(int Fd, const char *Filename)
: Fd(Fd), Filename(Filename) {}
~ScopedFileCloserAndDeleter() {
if (Map)
munmap(Map, Size);
if (Fd) {
close(Fd);
unlink(Filename);
}
}
void registerMap(void *M, size_t S) {
Map = M;
Size = S;
}
int Fd;
const char *Filename;
void *Map = nullptr;
size_t Size = 0;
};
TEST(FDRLoggingTest, Simple) {
FDRLoggingOptions Options;
Options.ReportErrors = true;
char TmpFilename[] = "fdr-logging-test.XXXXXX";
Options.Fd = mkstemp(TmpFilename);
ASSERT_NE(Options.Fd, -1);
ASSERT_EQ(fdrLoggingInit(kBufferSize, kBufferMax, &Options,
sizeof(FDRLoggingOptions)),
XRayLogInitStatus::XRAY_LOG_INITIALIZED);
fdrLoggingHandleArg0(1, XRayEntryType::ENTRY);
fdrLoggingHandleArg0(1, XRayEntryType::EXIT);
ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
// To do this properly, we have to close the file descriptor then re-open the
// file for reading this time.
ASSERT_EQ(close(Options.Fd), 0);
int Fd = open(TmpFilename, O_RDONLY);
ASSERT_NE(-1, Fd);
ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
auto Size = lseek(Fd, 0, SEEK_END);
ASSERT_NE(Size, 0);
// Map the file contents.
void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
const char *Contents = static_cast<const char *>(Map);
Guard.registerMap(Map, Size);
ASSERT_NE(Contents, nullptr);
XRayFileHeader H;
memcpy(&H, Contents, sizeof(XRayFileHeader));
ASSERT_EQ(H.Version, 2);
ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
// We require one buffer at least to have the "extents" metadata record,
// followed by the NewBuffer record.
MetadataRecord MDR0, MDR1;
memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
sizeof(MetadataRecord));
ASSERT_EQ(MDR0.RecordKind,
uint8_t(MetadataRecord::RecordKinds::BufferExtents));
ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
}
TEST(FDRLoggingTest, Multiple) {
FDRLoggingOptions Options;
char TmpFilename[] = "fdr-logging-test.XXXXXX";
Options.Fd = mkstemp(TmpFilename);
ASSERT_NE(Options.Fd, -1);
ASSERT_EQ(fdrLoggingInit(kBufferSize, kBufferMax, &Options,
sizeof(FDRLoggingOptions)),
XRayLogInitStatus::XRAY_LOG_INITIALIZED);
for (uint64_t I = 0; I < 100; ++I) {
fdrLoggingHandleArg0(1, XRayEntryType::ENTRY);
fdrLoggingHandleArg0(1, XRayEntryType::EXIT);
}
ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
// To do this properly, we have to close the file descriptor then re-open the
// file for reading this time.
ASSERT_EQ(close(Options.Fd), 0);
int Fd = open(TmpFilename, O_RDONLY);
ASSERT_NE(-1, Fd);
ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
auto Size = lseek(Fd, 0, SEEK_END);
ASSERT_NE(Size, 0);
// Map the file contents.
void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
const char *Contents = static_cast<const char *>(Map);
Guard.registerMap(Map, Size);
ASSERT_NE(Contents, nullptr);
XRayFileHeader H;
memcpy(&H, Contents, sizeof(XRayFileHeader));
ASSERT_EQ(H.Version, 2);
ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
MetadataRecord MDR0, MDR1;
memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
sizeof(MetadataRecord));
ASSERT_EQ(MDR0.RecordKind,
uint8_t(MetadataRecord::RecordKinds::BufferExtents));
ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
}
TEST(FDRLoggingTest, MultiThreadedCycling) {
FDRLoggingOptions Options;
char TmpFilename[] = "fdr-logging-test.XXXXXX";
Options.Fd = mkstemp(TmpFilename);
ASSERT_NE(Options.Fd, -1);
ASSERT_EQ(fdrLoggingInit(kBufferSize, 1, &Options, sizeof(FDRLoggingOptions)),
XRayLogInitStatus::XRAY_LOG_INITIALIZED);
// Now we want to create one thread, do some logging, then create another one,
// in succession and making sure that we're able to get thread records from
// the latest thread (effectively being able to recycle buffers).
std::array<pid_t, 2> Threads;
for (uint64_t I = 0; I < 2; ++I) {
std::thread t{[I, &Threads] {
fdrLoggingHandleArg0(I + 1, XRayEntryType::ENTRY);
fdrLoggingHandleArg0(I + 1, XRayEntryType::EXIT);
Threads[I] = syscall(SYS_gettid);
}};
t.join();
}
ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
// To do this properly, we have to close the file descriptor then re-open the
// file for reading this time.
ASSERT_EQ(close(Options.Fd), 0);
int Fd = open(TmpFilename, O_RDONLY);
ASSERT_NE(-1, Fd);
ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
auto Size = lseek(Fd, 0, SEEK_END);
ASSERT_NE(Size, 0);
// Map the file contents.
void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
const char *Contents = static_cast<const char *>(Map);
Guard.registerMap(Map, Size);
ASSERT_NE(Contents, nullptr);
XRayFileHeader H;
memcpy(&H, Contents, sizeof(XRayFileHeader));
ASSERT_EQ(H.Version, 2);
ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
MetadataRecord MDR0, MDR1;
memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
sizeof(MetadataRecord));
ASSERT_EQ(MDR0.RecordKind,
uint8_t(MetadataRecord::RecordKinds::BufferExtents));
ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
pid_t Latest = 0;
memcpy(&Latest, MDR1.Data, sizeof(pid_t));
ASSERT_EQ(Latest, Threads[1]);
}
} // namespace
} // namespace __xray

View File

@@ -0,0 +1,18 @@
//===-- xray_unit_test_main.cc --------------------------------------------===//
//
// 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 function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,4 @@
___start_xray_fn_idx
___start_xray_instr_map
___stop_xray_fn_idx
___stop_xray_instr_map

View File

@@ -0,0 +1,122 @@
//===-- xray_AArch64.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 AArch64-specific routines (64-bit).
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"
#include "xray_interface_internal.h"
#include <atomic>
#include <cassert>
extern "C" void __clear_cache(void *start, void *end);
namespace __xray {
// The machine codes for some instructions used in runtime patching.
enum class PatchOpcodes : uint32_t {
PO_StpX0X30SP_m16e = 0xA9BF7BE0, // STP X0, X30, [SP, #-16]!
PO_LdrW0_12 = 0x18000060, // LDR W0, #12
PO_LdrX16_12 = 0x58000070, // LDR X16, #12
PO_BlrX16 = 0xD63F0200, // BLR X16
PO_LdpX0X30SP_16 = 0xA8C17BE0, // LDP X0, X30, [SP], #16
PO_B32 = 0x14000008 // B #32
};
inline static bool patchSled(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
// When |Enable| == true,
// We replace the following compile-time stub (sled):
//
// xray_sled_n:
// B #32
// 7 NOPs (24 bytes)
//
// With the following runtime patch:
//
// xray_sled_n:
// STP X0, X30, [SP, #-16]! ; PUSH {r0, lr}
// LDR W0, #12 ; W0 := function ID
// LDR X16,#12 ; X16 := address of the trampoline
// BLR X16
// ;DATA: 32 bits of function ID
// ;DATA: lower 32 bits of the address of the trampoline
// ;DATA: higher 32 bits of the address of the trampoline
// LDP X0, X30, [SP], #16 ; POP {r0, lr}
//
// Replacement of the first 4-byte instruction should be the last and atomic
// operation, so that the user code which reaches the sled concurrently
// either jumps over the whole sled, or executes the whole sled when the
// latter is ready.
//
// When |Enable|==false, we set back the first instruction in the sled to be
// B #32
uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.Address);
uint32_t *CurAddress = FirstAddress + 1;
if (Enable) {
*CurAddress = uint32_t(PatchOpcodes::PO_LdrW0_12);
CurAddress++;
*CurAddress = uint32_t(PatchOpcodes::PO_LdrX16_12);
CurAddress++;
*CurAddress = uint32_t(PatchOpcodes::PO_BlrX16);
CurAddress++;
*CurAddress = FuncId;
CurAddress++;
*reinterpret_cast<void (**)()>(CurAddress) = TracingHook;
CurAddress += 2;
*CurAddress = uint32_t(PatchOpcodes::PO_LdpX0X30SP_16);
CurAddress++;
std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
uint32_t(PatchOpcodes::PO_StpX0X30SP_m16e), std::memory_order_release);
} else {
std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
uint32_t(PatchOpcodes::PO_B32), std::memory_order_release);
}
__clear_cache(reinterpret_cast<char *>(FirstAddress),
reinterpret_cast<char *>(CurAddress));
return true;
}
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, Trampoline);
}
bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, __xray_FunctionExit);
}
bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, __xray_FunctionTailExit);
}
bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled)
XRAY_NEVER_INSTRUMENT { // FIXME: Implement in aarch64?
return false;
}
// FIXME: Maybe implement this better?
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }
} // namespace __xray
extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
// FIXME: this will have to be implemented in the trampoline assembly file
}

View File

@@ -0,0 +1,6 @@
# List of function matchers common to C/C++ applications that make sense to
# always instrument. You can use this as an argument to
# -fxray-always-instrument=<path> along with your project-specific lists.
# Always instrument the main function.
fun:main

View File

@@ -0,0 +1,159 @@
//===-- xray_arm.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 ARM-specific routines (32-bit).
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"
#include "xray_interface_internal.h"
#include <atomic>
#include <cassert>
extern "C" void __clear_cache(void *start, void *end);
namespace __xray {
// The machine codes for some instructions used in runtime patching.
enum class PatchOpcodes : uint32_t {
PO_PushR0Lr = 0xE92D4001, // PUSH {r0, lr}
PO_BlxIp = 0xE12FFF3C, // BLX ip
PO_PopR0Lr = 0xE8BD4001, // POP {r0, lr}
PO_B20 = 0xEA000005 // B #20
};
// 0xUUUUWXYZ -> 0x000W0XYZ
inline static uint32_t getMovwMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT {
return (Value & 0xfff) | ((Value & 0xf000) << 4);
}
// 0xWXYZUUUU -> 0x000W0XYZ
inline static uint32_t getMovtMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT {
return getMovwMask(Value >> 16);
}
// Writes the following instructions:
// MOVW R<regNo>, #<lower 16 bits of the |Value|>
// MOVT R<regNo>, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadReg(uint8_t regNo, uint32_t *Address,
const uint32_t Value) XRAY_NEVER_INSTRUMENT {
// This is a fatal error: we cannot just report it and continue execution.
assert(regNo <= 15 && "Register number must be 0 to 15.");
// MOVW R, #0xWXYZ in machine code is 0xE30WRXYZ
*Address = (0xE3000000 | (uint32_t(regNo) << 12) | getMovwMask(Value));
Address++;
// MOVT R, #0xWXYZ in machine code is 0xE34WRXYZ
*Address = (0xE3400000 | (uint32_t(regNo) << 12) | getMovtMask(Value));
return Address + 1;
}
// Writes the following instructions:
// MOVW r0, #<lower 16 bits of the |Value|>
// MOVT r0, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadR0(uint32_t *Address,
const uint32_t Value) XRAY_NEVER_INSTRUMENT {
return write32bitLoadReg(0, Address, Value);
}
// Writes the following instructions:
// MOVW ip, #<lower 16 bits of the |Value|>
// MOVT ip, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadIP(uint32_t *Address,
const uint32_t Value) XRAY_NEVER_INSTRUMENT {
return write32bitLoadReg(12, Address, Value);
}
inline static bool patchSled(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
// When |Enable| == true,
// We replace the following compile-time stub (sled):
//
// xray_sled_n:
// B #20
// 6 NOPs (24 bytes)
//
// With the following runtime patch:
//
// xray_sled_n:
// PUSH {r0, lr}
// MOVW r0, #<lower 16 bits of function ID>
// MOVT r0, #<higher 16 bits of function ID>
// MOVW ip, #<lower 16 bits of address of TracingHook>
// MOVT ip, #<higher 16 bits of address of TracingHook>
// BLX ip
// POP {r0, lr}
//
// Replacement of the first 4-byte instruction should be the last and atomic
// operation, so that the user code which reaches the sled concurrently
// either jumps over the whole sled, or executes the whole sled when the
// latter is ready.
//
// When |Enable|==false, we set back the first instruction in the sled to be
// B #20
uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.Address);
uint32_t *CurAddress = FirstAddress + 1;
if (Enable) {
CurAddress =
write32bitLoadR0(CurAddress, reinterpret_cast<uint32_t>(FuncId));
CurAddress =
write32bitLoadIP(CurAddress, reinterpret_cast<uint32_t>(TracingHook));
*CurAddress = uint32_t(PatchOpcodes::PO_BlxIp);
CurAddress++;
*CurAddress = uint32_t(PatchOpcodes::PO_PopR0Lr);
CurAddress++;
std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
uint32_t(PatchOpcodes::PO_PushR0Lr), std::memory_order_release);
} else {
std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
uint32_t(PatchOpcodes::PO_B20), std::memory_order_release);
}
__clear_cache(reinterpret_cast<char *>(FirstAddress),
reinterpret_cast<char *>(CurAddress));
return true;
}
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, Trampoline);
}
bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, __xray_FunctionExit);
}
bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, __xray_FunctionTailExit);
}
bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled)
XRAY_NEVER_INSTRUMENT { // FIXME: Implement in arm?
return false;
}
// FIXME: Maybe implement this better?
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }
} // namespace __xray
extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
// FIXME: this will have to be implemented in the trampoline assembly file
}

View File

@@ -0,0 +1,114 @@
//===-- xray_buffer_queue.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 instruementation system.
//
// Defines the interface for a buffer queue implementation.
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
using namespace __xray;
using namespace __sanitizer;
BufferQueue::BufferQueue(size_t B, size_t N, bool &Success)
: BufferSize(B), Buffers(new BufferRep[N]()), BufferCount(N), Finalizing{0},
OwnedBuffers(new void *[N]()), Next(Buffers), First(Buffers),
LiveBuffers(0) {
for (size_t i = 0; i < N; ++i) {
auto &T = Buffers[i];
void *Tmp = InternalAlloc(BufferSize, nullptr, 64);
if (Tmp == nullptr) {
Success = false;
return;
}
void *Extents = InternalAlloc(sizeof(BufferExtents), nullptr, 64);
if (Extents == nullptr) {
Success = false;
return;
}
auto &Buf = T.Buff;
Buf.Buffer = Tmp;
Buf.Size = B;
Buf.Extents = reinterpret_cast<BufferExtents *>(Extents);
OwnedBuffers[i] = Tmp;
}
Success = true;
}
BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) {
if (__sanitizer::atomic_load(&Finalizing, __sanitizer::memory_order_acquire))
return ErrorCode::QueueFinalizing;
__sanitizer::SpinMutexLock Guard(&Mutex);
if (LiveBuffers == BufferCount)
return ErrorCode::NotEnoughMemory;
auto &T = *Next;
auto &B = T.Buff;
Buf = B;
T.Used = true;
++LiveBuffers;
if (++Next == (Buffers + BufferCount))
Next = Buffers;
return ErrorCode::Ok;
}
BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
// Blitz through the buffers array to find the buffer.
bool Found = false;
for (auto I = OwnedBuffers, E = OwnedBuffers + BufferCount; I != E; ++I) {
if (*I == Buf.Buffer) {
Found = true;
break;
}
}
if (!Found)
return ErrorCode::UnrecognizedBuffer;
__sanitizer::SpinMutexLock Guard(&Mutex);
// This points to a semantic bug, we really ought to not be releasing more
// buffers than we actually get.
if (LiveBuffers == 0)
return ErrorCode::NotEnoughMemory;
// Now that the buffer has been released, we mark it as "used".
First->Buff = Buf;
First->Used = true;
Buf.Buffer = nullptr;
Buf.Size = 0;
--LiveBuffers;
if (++First == (Buffers + BufferCount))
First = Buffers;
return ErrorCode::Ok;
}
BufferQueue::ErrorCode BufferQueue::finalize() {
if (__sanitizer::atomic_exchange(&Finalizing, 1,
__sanitizer::memory_order_acq_rel))
return ErrorCode::QueueFinalizing;
return ErrorCode::Ok;
}
BufferQueue::~BufferQueue() {
for (auto I = Buffers, E = Buffers + BufferCount; I != E; ++I) {
auto &T = *I;
auto &Buf = T.Buff;
InternalFree(Buf.Buffer);
InternalFree(Buf.Extents);
}
delete[] Buffers;
delete[] OwnedBuffers;
}

View File

@@ -0,0 +1,159 @@
//===-- xray_buffer_queue.h ------------------------------------*- 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.
//
// Defines the interface for a buffer queue implementation.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_BUFFER_QUEUE_H
#define XRAY_BUFFER_QUEUE_H
#include <cstddef>
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_mutex.h"
namespace __xray {
/// BufferQueue implements a circular queue of fixed sized buffers (much like a
/// freelist) but is concerned mostly with making it really quick to initialise,
/// finalise, and get/return buffers to the queue. This is one key component of
/// the "flight data recorder" (FDR) mode to support ongoing XRay function call
/// trace collection.
class BufferQueue {
public:
struct alignas(64) BufferExtents {
__sanitizer::atomic_uint64_t Size;
};
struct Buffer {
void *Buffer = nullptr;
size_t Size = 0;
BufferExtents* Extents;
};
private:
struct BufferRep {
// The managed buffer.
Buffer Buff;
// This is true if the buffer has been returned to the available queue, and
// is considered "used" by another thread.
bool Used = false;
};
// Size of each individual Buffer.
size_t BufferSize;
BufferRep *Buffers;
size_t BufferCount;
__sanitizer::SpinMutex Mutex;
__sanitizer::atomic_uint8_t Finalizing;
// Pointers to buffers managed/owned by the BufferQueue.
void **OwnedBuffers;
// Pointer to the next buffer to be handed out.
BufferRep *Next;
// Pointer to the entry in the array where the next released buffer will be
// placed.
BufferRep *First;
// Count of buffers that have been handed out through 'getBuffer'.
size_t LiveBuffers;
public:
enum class ErrorCode : unsigned {
Ok,
NotEnoughMemory,
QueueFinalizing,
UnrecognizedBuffer,
AlreadyFinalized,
};
static const char *getErrorString(ErrorCode E) {
switch (E) {
case ErrorCode::Ok:
return "(none)";
case ErrorCode::NotEnoughMemory:
return "no available buffers in the queue";
case ErrorCode::QueueFinalizing:
return "queue already finalizing";
case ErrorCode::UnrecognizedBuffer:
return "buffer being returned not owned by buffer queue";
case ErrorCode::AlreadyFinalized:
return "queue already finalized";
}
return "unknown error";
}
/// Initialise a queue of size |N| with buffers of size |B|. We report success
/// through |Success|.
BufferQueue(size_t B, size_t N, bool &Success);
/// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an
/// error in case there are no available buffers to return when we will run
/// over the upper bound for the total buffers.
///
/// Requirements:
/// - BufferQueue is not finalising.
///
/// Returns:
/// - ErrorCode::NotEnoughMemory on exceeding MaxSize.
/// - ErrorCode::Ok when we find a Buffer.
/// - ErrorCode::QueueFinalizing or ErrorCode::AlreadyFinalized on
/// a finalizing/finalized BufferQueue.
ErrorCode getBuffer(Buffer &Buf);
/// Updates |Buf| to point to nullptr, with size 0.
///
/// Returns:
/// - ErrorCode::Ok when we successfully release the buffer.
/// - ErrorCode::UnrecognizedBuffer for when this BufferQueue does not own
/// the buffer being released.
ErrorCode releaseBuffer(Buffer &Buf);
bool finalizing() const {
return __sanitizer::atomic_load(&Finalizing,
__sanitizer::memory_order_acquire);
}
/// Returns the configured size of the buffers in the buffer queue.
size_t ConfiguredBufferSize() const { return BufferSize; }
/// Sets the state of the BufferQueue to finalizing, which ensures that:
///
/// - All subsequent attempts to retrieve a Buffer will fail.
/// - All releaseBuffer operations will not fail.
///
/// After a call to finalize succeeds, all subsequent calls to finalize will
/// fail with ErrorCode::QueueFinalizing.
ErrorCode finalize();
/// Applies the provided function F to each Buffer in the queue, only if the
/// Buffer is marked 'used' (i.e. has been the result of getBuffer(...) and a
/// releaseBuffer(...) operation).
template <class F>
void apply(F Fn) {
__sanitizer::SpinMutexLock G(&Mutex);
for (auto I = Buffers, E = Buffers + BufferCount; I != E; ++I) {
const auto &T = *I;
if (T.Used) Fn(T.Buff);
}
}
// Cleans up allocated buffers.
~BufferQueue();
};
} // namespace __xray
#endif // XRAY_BUFFER_QUEUE_H

View File

@@ -0,0 +1,22 @@
//===-- xray_defs.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Common definitions useful for XRay sources.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_DEFS_H
#define XRAY_XRAY_DEFS_H
#if XRAY_SUPPORTED
#define XRAY_NEVER_INSTRUMENT __attribute__((xray_never_instrument))
#else
#define XRAY_NEVER_INSTRUMENT
#endif
#endif // XRAY_XRAY_DEFS_H

View File

@@ -0,0 +1,69 @@
//===-- xray_fdr_log_records.h -------------------------------------------===//
//
// 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 function call tracing system.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_FDR_LOG_RECORDS_H
#define XRAY_XRAY_FDR_LOG_RECORDS_H
enum class RecordType : uint8_t { Function, Metadata };
// A MetadataRecord encodes the kind of record in its first byte, and have 15
// additional bytes in the end to hold free-form data.
struct alignas(16) MetadataRecord {
// A MetadataRecord must always have a type of 1.
/* RecordType */ uint8_t Type : 1;
// Each kind of record is represented as a 7-bit value (even though we use an
// unsigned 8-bit enum class to do so).
enum class RecordKinds : uint8_t {
NewBuffer,
EndOfBuffer,
NewCPUId,
TSCWrap,
WalltimeMarker,
CustomEventMarker,
CallArgument,
BufferExtents,
};
// Use 7 bits to identify this record type.
/* RecordKinds */ uint8_t RecordKind : 7;
char Data[15];
} __attribute__((packed));
static_assert(sizeof(MetadataRecord) == 16, "Wrong size for MetadataRecord.");
struct alignas(8) FunctionRecord {
// A FunctionRecord must always have a type of 0.
/* RecordType */ uint8_t Type : 1;
enum class RecordKinds {
FunctionEnter = 0x00,
FunctionExit = 0x01,
FunctionTailExit = 0x02,
};
/* RecordKinds */ uint8_t RecordKind : 3;
// We only use 28 bits of the function ID, so that we can use as few bytes as
// possible. This means we only support 2^28 (268,435,456) unique function ids
// in a single binary.
int FuncId : 28;
// We use another 4 bytes to hold the delta between the previous entry's TSC.
// In case we've found that the distance is greater than the allowable 32 bits
// (either because we are running in a different CPU and the TSC might be
// different then), we should use a MetadataRecord before this FunctionRecord
// that will contain the full TSC for that CPU, and keep this to 0.
uint32_t TSCDelta;
} __attribute__((packed));
static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord.");
#endif // XRAY_XRAY_FDR_LOG_RECORDS_H

View File

@@ -0,0 +1,386 @@
//===-- xray_fdr_logging.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.
//
// Here we implement the Flight Data Recorder mode for XRay, where we use
// compact structures to store records in memory as well as when writing out the
// data to files.
//
//===----------------------------------------------------------------------===//
#include "xray_fdr_logging.h"
#include <errno.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "xray/xray_interface.h"
#include "xray/xray_records.h"
#include "xray_buffer_queue.h"
#include "xray_defs.h"
#include "xray_fdr_logging_impl.h"
#include "xray_flags.h"
#include "xray_tsc.h"
#include "xray_utils.h"
namespace __xray {
// Global BufferQueue.
BufferQueue *BQ = nullptr;
__sanitizer::atomic_sint32_t LogFlushStatus = {
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
FDRLoggingOptions FDROptions;
__sanitizer::SpinMutex FDROptionsMutex;
// Must finalize before flushing.
XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
if (__sanitizer::atomic_load(&LoggingStatus,
__sanitizer::memory_order_acquire) !=
XRayLogInitStatus::XRAY_LOG_FINALIZED) {
if (__sanitizer::Verbosity())
Report("Not flushing log, implementation is not finalized.\n");
return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
}
s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
if (!__sanitizer::atomic_compare_exchange_strong(
&LogFlushStatus, &Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING,
__sanitizer::memory_order_release)) {
if (__sanitizer::Verbosity())
Report("Not flushing log, implementation is still finalizing.\n");
return static_cast<XRayLogFlushStatus>(Result);
}
if (BQ == nullptr) {
if (__sanitizer::Verbosity())
Report("Cannot flush when global buffer queue is null.\n");
return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
}
// We wait a number of milliseconds to allow threads to see that we've
// finalised before attempting to flush the log.
__sanitizer::SleepForMillis(flags()->xray_fdr_log_grace_period_ms);
// We write out the file in the following format:
//
// 1) We write down the XRay file header with version 1, type FDR_LOG.
// 2) Then we use the 'apply' member of the BufferQueue that's live, to
// ensure that at this point in time we write down the buffers that have
// been released (and marked "used") -- we dump the full buffer for now
// (fixed-sized) and let the tools reading the buffers deal with the data
// afterwards.
//
int Fd = -1;
{
__sanitizer::SpinMutexLock Guard(&FDROptionsMutex);
Fd = FDROptions.Fd;
}
if (Fd == -1)
Fd = getLogFD();
if (Fd == -1) {
auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
__sanitizer::atomic_store(&LogFlushStatus, Result,
__sanitizer::memory_order_release);
return Result;
}
// Test for required CPU features and cache the cycle frequency
static bool TSCSupported = probeRequiredCPUFeatures();
static uint64_t CycleFrequency =
TSCSupported ? getTSCFrequency() : __xray::NanosecondsPerSecond;
XRayFileHeader Header;
// Version 2 of the log writes the extents of the buffer, instead of relying
// on an end-of-buffer record.
Header.Version = 2;
Header.Type = FileTypes::FDR_LOG;
Header.CycleFrequency = CycleFrequency;
// FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
// before setting the values in the header.
Header.ConstantTSC = 1;
Header.NonstopTSC = 1;
Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
BQ->apply([&](const BufferQueue::Buffer &B) {
// Starting at version 2 of the FDR logging implementation, we only write
// the records identified by the extents of the buffer. We use the Extents
// from the Buffer and write that out as the first record in the buffer.
// We still use a Metadata record, but fill in the extents instead for the
// data.
MetadataRecord ExtentsRecord;
auto BufferExtents = __sanitizer::atomic_load(
&B.Extents->Size, __sanitizer::memory_order_acquire);
assert(BufferExtents <= B.Size);
ExtentsRecord.Type = uint8_t(RecordType::Metadata);
ExtentsRecord.RecordKind =
uint8_t(MetadataRecord::RecordKinds::BufferExtents);
std::memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));
if (BufferExtents > 0) {
retryingWriteAll(Fd, reinterpret_cast<char *>(&ExtentsRecord),
reinterpret_cast<char *>(&ExtentsRecord) +
sizeof(MetadataRecord));
retryingWriteAll(Fd, reinterpret_cast<char *>(B.Buffer),
reinterpret_cast<char *>(B.Buffer) + BufferExtents);
}
});
__sanitizer::atomic_store(&LogFlushStatus,
XRayLogFlushStatus::XRAY_LOG_FLUSHED,
__sanitizer::memory_order_release);
return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
}
XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
if (!__sanitizer::atomic_compare_exchange_strong(
&LoggingStatus, &CurrentStatus,
XRayLogInitStatus::XRAY_LOG_FINALIZING,
__sanitizer::memory_order_release)) {
if (__sanitizer::Verbosity())
Report("Cannot finalize log, implementation not initialized.\n");
return static_cast<XRayLogInitStatus>(CurrentStatus);
}
// Do special things to make the log finalize itself, and not allow any more
// operations to be performed until re-initialized.
BQ->finalize();
__sanitizer::atomic_store(&LoggingStatus,
XRayLogInitStatus::XRAY_LOG_FINALIZED,
__sanitizer::memory_order_release);
return XRayLogInitStatus::XRAY_LOG_FINALIZED;
}
XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT {
s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED;
if (__sanitizer::atomic_compare_exchange_strong(
&LoggingStatus, &CurrentStatus,
XRayLogInitStatus::XRAY_LOG_INITIALIZED,
__sanitizer::memory_order_release))
return static_cast<XRayLogInitStatus>(CurrentStatus);
// Release the in-memory buffer queue.
delete BQ;
BQ = nullptr;
// Spin until the flushing status is flushed.
s32 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
while (__sanitizer::atomic_compare_exchange_weak(
&LogFlushStatus, &CurrentFlushingStatus,
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING,
__sanitizer::memory_order_release)) {
if (CurrentFlushingStatus == XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING)
break;
CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
}
// At this point, we know that the status is flushed, and that we can assume
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
}
struct TSCAndCPU {
uint64_t TSC = 0;
unsigned char CPU = 0;
};
static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {
// We want to get the TSC as early as possible, so that we can check whether
// we've seen this CPU before. We also do it before we load anything else, to
// allow for forward progress with the scheduling.
TSCAndCPU Result;
// Test once for required CPU features
static bool TSCSupported = probeRequiredCPUFeatures();
if (TSCSupported) {
Result.TSC = __xray::readTSC(Result.CPU);
} else {
// FIXME: This code needs refactoring as it appears in multiple locations
timespec TS;
int result = clock_gettime(CLOCK_REALTIME, &TS);
if (result != 0) {
Report("clock_gettime(2) return %d, errno=%d", result, int(errno));
TS = {0, 0};
}
Result.CPU = 0;
Result.TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;
}
return Result;
}
void fdrLoggingHandleArg0(int32_t FuncId,
XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
auto TC = getTimestamp();
__xray_fdr_internal::processFunctionHook(FuncId, Entry, TC.TSC, TC.CPU, 0,
clock_gettime, BQ);
}
void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry,
uint64_t Arg) XRAY_NEVER_INSTRUMENT {
auto TC = getTimestamp();
__xray_fdr_internal::processFunctionHook(FuncId, Entry, TC.TSC, TC.CPU, Arg,
clock_gettime, BQ);
}
void fdrLoggingHandleCustomEvent(void *Event,
std::size_t EventSize) XRAY_NEVER_INSTRUMENT {
using namespace __xray_fdr_internal;
auto TC = getTimestamp();
auto &TSC = TC.TSC;
auto &CPU = TC.CPU;
RecursionGuard Guard{Running};
if (!Guard)
return;
if (EventSize > std::numeric_limits<int32_t>::max()) {
using Empty = struct {};
static Empty Once = [&] {
Report("Event size too large = %zu ; > max = %d\n", EventSize,
std::numeric_limits<int32_t>::max());
return Empty();
}();
(void)Once;
}
int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
auto &TLD = getThreadLocalData();
if (!isLogInitializedAndReady(TLD.BQ, TSC, CPU, clock_gettime))
return;
// Here we need to prepare the log to handle:
// - The metadata record we're going to write. (16 bytes)
// - The additional data we're going to write. Currently, that's the size of
// the event we're going to dump into the log as free-form bytes.
if (!prepareBuffer(TSC, CPU, clock_gettime, MetadataRecSize + EventSize)) {
TLD.BQ = nullptr;
return;
}
// Write the custom event metadata record, which consists of the following
// information:
// - 8 bytes (64-bits) for the full TSC when the event started.
// - 4 bytes (32-bits) for the length of the data.
MetadataRecord CustomEvent;
CustomEvent.Type = uint8_t(RecordType::Metadata);
CustomEvent.RecordKind =
uint8_t(MetadataRecord::RecordKinds::CustomEventMarker);
constexpr auto TSCSize = sizeof(TC.TSC);
std::memcpy(&CustomEvent.Data, &ReducedEventSize, sizeof(int32_t));
std::memcpy(&CustomEvent.Data[sizeof(int32_t)], &TSC, TSCSize);
std::memcpy(TLD.RecordPtr, &CustomEvent, sizeof(CustomEvent));
TLD.RecordPtr += sizeof(CustomEvent);
std::memcpy(TLD.RecordPtr, Event, ReducedEventSize);
incrementExtents(MetadataRecSize + EventSize);
endBufferIfFull();
}
XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax,
void *Options,
size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
if (OptionsSize != sizeof(FDRLoggingOptions)) {
if (__sanitizer::Verbosity())
Report("Cannot initialize FDR logging; wrong size for options: %d\n",
OptionsSize);
return static_cast<XRayLogInitStatus>(__sanitizer::atomic_load(
&LoggingStatus, __sanitizer::memory_order_acquire));
}
s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
if (!__sanitizer::atomic_compare_exchange_strong(
&LoggingStatus, &CurrentStatus,
XRayLogInitStatus::XRAY_LOG_INITIALIZING,
__sanitizer::memory_order_release)) {
if (__sanitizer::Verbosity())
Report("Cannot initialize already initialized implementation.\n");
return static_cast<XRayLogInitStatus>(CurrentStatus);
}
{
__sanitizer::SpinMutexLock Guard(&FDROptionsMutex);
memcpy(&FDROptions, Options, OptionsSize);
}
bool Success = false;
if (BQ != nullptr) {
delete BQ;
BQ = nullptr;
}
if (BQ == nullptr)
BQ = new BufferQueue(BufferSize, BufferMax, Success);
if (!Success) {
Report("BufferQueue init failed.\n");
if (BQ != nullptr) {
delete BQ;
BQ = nullptr;
}
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
}
static bool UNUSED Once = [] {
pthread_key_create(&__xray_fdr_internal::Key, +[](void *) {
auto &TLD = __xray_fdr_internal::getThreadLocalData();
if (TLD.BQ == nullptr)
return;
auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
if (EC != BufferQueue::ErrorCode::Ok)
Report("At thread exit, failed to release buffer at %p; error=%s\n",
TLD.Buffer.Buffer, BufferQueue::getErrorString(EC));
});
return false;
}();
// Arg1 handler should go in first to avoid concurrent code accidentally
// falling back to arg0 when it should have ran arg1.
__xray_set_handler_arg1(fdrLoggingHandleArg1);
// Install the actual handleArg0 handler after initialising the buffers.
__xray_set_handler(fdrLoggingHandleArg0);
__xray_set_customevent_handler(fdrLoggingHandleCustomEvent);
__sanitizer::atomic_store(&LoggingStatus,
XRayLogInitStatus::XRAY_LOG_INITIALIZED,
__sanitizer::memory_order_release);
if (__sanitizer::Verbosity())
Report("XRay FDR init successful.\n");
return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
}
bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
using namespace __xray;
XRayLogImpl Impl{
fdrLoggingInit,
fdrLoggingFinalize,
fdrLoggingHandleArg0,
fdrLoggingFlush,
};
auto RegistrationResult = __xray_log_register_mode("xray-fdr", Impl);
if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
__sanitizer::Verbosity())
Report("Cannot register XRay FDR mode to 'xray-fdr'; error = %d\n",
RegistrationResult);
if (flags()->xray_fdr_log ||
!__sanitizer::internal_strcmp(flags()->xray_mode, "xray-fdr"))
__xray_set_log_impl(Impl);
return true;
}
} // namespace __xray
static auto UNUSED Unused = __xray::fdrLogDynamicInitializer();

View File

@@ -0,0 +1,39 @@
//===-- xray_fdr_logging.h ------------------------------------------------===//
//
// 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 function call tracing system.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_FDR_LOGGING_H
#define XRAY_XRAY_FDR_LOGGING_H
#include "xray/xray_log_interface.h"
#include "xray_fdr_log_records.h"
// FDR (Flight Data Recorder) Mode
// ===============================
//
// The XRay whitepaper describes a mode of operation for function call trace
// logging that involves writing small records into an in-memory circular
// buffer, that then gets logged to disk on demand. To do this efficiently and
// capture as much data as we can, we use smaller records compared to the
// default mode of always writing fixed-size records.
namespace __xray {
XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
void *Options, size_t OptionsSize);
XRayLogInitStatus fdrLoggingFinalize();
void fdrLoggingHandleArg0(int32_t FuncId, XRayEntryType Entry);
void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry, uint64_t Arg1);
XRayLogFlushStatus fdrLoggingFlush();
XRayLogInitStatus fdrLoggingReset();
} // namespace __xray
#endif // XRAY_XRAY_FDR_LOGGING_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,86 @@
//===-- xray_flags.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.
//
// XRay flag parsing logic.
//===----------------------------------------------------------------------===//
#include "xray_flags.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "xray_defs.h"
using namespace __sanitizer;
namespace __xray {
Flags xray_flags_dont_use_directly; // use via flags().
void Flags::setDefaults() XRAY_NEVER_INSTRUMENT {
#define XRAY_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
#include "xray_flags.inc"
#undef XRAY_FLAG
}
static void registerXRayFlags(FlagParser *P, Flags *F) XRAY_NEVER_INSTRUMENT {
#define XRAY_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(P, #Name, Description, &F->Name);
#include "xray_flags.inc"
#undef XRAY_FLAG
}
// This function, as defined with the help of a macro meant to be introduced at
// build time of the XRay runtime, passes in a statically defined list of
// options that control XRay. This means users/deployments can tweak the
// defaults that override the hard-coded defaults in the xray_flags.inc at
// compile-time using the XRAY_DEFAULT_OPTIONS macro.
static const char *useCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
#ifdef XRAY_DEFAULT_OPTIONS
// Do the double-layered string conversion to prevent badly crafted strings
// provided through the XRAY_DEFAULT_OPTIONS from causing compilation issues (or
// changing the semantics of the implementation through the macro). This ensures
// that we convert whatever XRAY_DEFAULT_OPTIONS is defined as a string literal.
#define XRAY_STRINGIZE(x) #x
#define XRAY_STRINGIZE_OPTIONS(options) XRAY_STRINGIZE(options)
return XRAY_STRINGIZE_OPTIONS(XRAY_DEFAULT_OPTIONS);
#else
return "";
#endif
}
void initializeFlags() XRAY_NEVER_INSTRUMENT {
SetCommonFlagsDefaults();
auto *F = flags();
F->setDefaults();
FlagParser XRayParser;
registerXRayFlags(&XRayParser, F);
RegisterCommonFlags(&XRayParser);
// Use options defaulted at compile-time for the runtime.
const char *XRayCompileFlags = useCompilerDefinedFlags();
XRayParser.ParseString(XRayCompileFlags);
// Override from environment variables.
XRayParser.ParseString(GetEnv("XRAY_OPTIONS"));
// Override from command line.
InitializeCommonFlags();
if (Verbosity())
ReportUnrecognizedFlags();
if (common_flags()->help) {
XRayParser.PrintFlagDescriptions();
}
}
} // namespace __xray

View File

@@ -0,0 +1,38 @@
//===-- xray_flags.h -------------------------------------------*- 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 instruementation system.
//
// XRay runtime flags.
//===----------------------------------------------------------------------===//
#ifndef XRAY_FLAGS_H
#define XRAY_FLAGS_H
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
namespace __xray {
struct Flags {
#define XRAY_FLAG(Type, Name, DefaultValue, Description) Type Name;
#include "xray_flags.inc"
#undef XRAY_FLAG
void setDefaults();
};
extern Flags xray_flags_dont_use_directly;
inline Flags *flags() { return &xray_flags_dont_use_directly; }
void initializeFlags();
} // namespace __xray
#endif // XRAY_FLAGS_H

View File

@@ -0,0 +1,49 @@
//===-- xray_flags.inc ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// XRay runtime flags.
//
//===----------------------------------------------------------------------===//
#ifndef XRAY_FLAG
#error "Define XRAY_FLAG prior to including this file!"
#endif
XRAY_FLAG(bool, patch_premain, false,
"Whether to patch instrumentation points before main.")
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. The size "
"should be a power-of-two.")
// Basic (Naive) Mode logging options.
XRAY_FLAG(bool, xray_naive_log, false,
"DEPRECATED: Use xray_mode=xray-basic instead.")
XRAY_FLAG(int, xray_naive_log_func_duration_threshold_us, 5,
"Naive logging will try to skip functions that execute for fewer "
"microseconds than this threshold.")
XRAY_FLAG(int, xray_naive_log_max_stack_depth, 64,
"Naive logging will keep track of at most this deep a call stack, "
"any more and the recordings will be droppped.")
XRAY_FLAG(int, xray_naive_log_thread_buffer_size, 1024,
"The number of entries to keep on a per-thread buffer.")
// FDR (Flight Data Recorder) Mode logging options.
XRAY_FLAG(bool, xray_fdr_log, false,
"DEPRECATED: Use xray_mode=xray-fdr instead.")
XRAY_FLAG(int, xray_fdr_log_func_duration_threshold_us, 5,
"FDR logging will try to skip functions that execute for fewer "
"microseconds than this threshold.")
XRAY_FLAG(int, xray_fdr_log_grace_period_us, 0,
"DEPRECATED: use xray_fdr_log_grace_period_ms instead.")
XRAY_FLAG(int, xray_fdr_log_grace_period_ms, 100,
"FDR logging will wait this much time in microseconds before "
"actually flushing the log; this gives a chance for threads to "
"notice that the log has been finalized and clean up.")

Some files were not shown because too many files have changed in this diff Show More