Skip to content

Commit

Permalink
Add Xray instrumentation support to FreeBSD
Browse files Browse the repository at this point in the history
Summary:
- Enabling the build.
- Using assembly for the cpuid parts.
- Using thr_self FreeBSD call to get the thread id 

Patch by: David CARLIER

Reviewers: dberris, rnk, krytarowski

Reviewed By: dberris, krytarowski

Subscribers: emaste, stevecheckoway, nglevin, srhines, kubamracek, dberris, mgorny, krytarowski, llvm-commits, #sanitizers

Differential Revision: https://reviews.llvm.org/D43278

llvm-svn: 325240
  • Loading branch information
krytarowski committed Feb 15, 2018
1 parent 892ea68 commit 4d4ed0e
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 22 deletions.
2 changes: 1 addition & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ else()
endif()

if (COMPILER_RT_HAS_SANITIZER_COMMON AND XRAY_SUPPORTED_ARCH AND
OS_NAME MATCHES "Darwin|Linux")
OS_NAME MATCHES "Darwin|Linux|FreeBSD")
set(COMPILER_RT_HAS_XRAY TRUE)
else()
set(COMPILER_RT_HAS_XRAY FALSE)
Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@
// We can use .preinit_array section on Linux to call sanitizer initialization
// functions very early in the process startup (unless PIC macro is defined).
// FIXME: do we have anything like this on Mac?
#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
#if ((SANITIZER_LINUX && !SANITIZER_ANDROID) || \
SANITIZER_FREEBSD) && !defined(PIC)
# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
// Before Solaris 11.4, .preinit_array is fully supported only with GNU ld.
// FIXME: Check for those conditions.
Expand Down
7 changes: 5 additions & 2 deletions compiler-rt/lib/xray/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH})
macro(add_xray_unittest testname)
cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
if(UNIX AND NOT APPLE)
set(CMAKE_DL_LIBS_INIT "")
foreach(lib ${CMAKE_DL_LIBS})
list(APPEND CMAKE_DL_LIBS_INIT -l${lib})
endforeach()
foreach(arch ${XRAY_TEST_ARCH})
set(TEST_OBJECTS)
generate_compiler_rt_tests(TEST_OBJECTS
Expand All @@ -25,8 +29,7 @@ macro(add_xray_unittest testname)
LINK_FLAGS -fxray-instrument
${TARGET_LINK_FLAGS}
-lstdc++ -lm ${CMAKE_THREAD_LIBS_INIT}
-lpthread
-ldl -lrt)
${CMAKE_DL_LIBS_INIT} -lrt)
set_target_properties(XRayUnitTests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endforeach()
endif()
Expand Down
11 changes: 6 additions & 5 deletions compiler-rt/lib/xray/tests/unit/fdr_logging_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// This file is a part of XRay, a function call tracing system.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "xray_fdr_logging.h"
#include "gtest/gtest.h"

Expand Down Expand Up @@ -154,12 +155,12 @@ TEST(FDRLoggingTest, MultiThreadedCycling) {
// 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;
std::array<tid_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);
Threads[I] = __sanitizer::GetTid();
}};
t.join();
}
Expand Down Expand Up @@ -192,9 +193,9 @@ TEST(FDRLoggingTest, MultiThreadedCycling) {
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]);
int32_t Latest = 0;
memcpy(&Latest, MDR1.Data, sizeof(int32_t));
ASSERT_EQ(Latest, static_cast<int32_t>(Threads[1]));
}

} // namespace
Expand Down
11 changes: 6 additions & 5 deletions compiler-rt/lib/xray/xray_fdr_logging_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace __xray_fdr_internal {

/// Writes the new buffer record and wallclock time that begin a buffer for the
/// current thread.
static void writeNewBufferPreamble(pid_t Tid, timespec TS);
static void writeNewBufferPreamble(tid_t Tid, timespec TS);

/// Writes a Function Record to the buffer associated with the current thread.
static void writeFunctionRecord(int FuncId, uint32_t TSCDelta,
Expand Down Expand Up @@ -185,7 +185,7 @@ class RecursionGuard {

} // namespace

static void writeNewBufferPreamble(pid_t Tid,
static void writeNewBufferPreamble(tid_t Tid,
timespec TS) XRAY_NEVER_INSTRUMENT {
static constexpr int InitRecordsCount = 2;
auto &TLD = getThreadLocalData();
Expand All @@ -195,11 +195,12 @@ static void writeNewBufferPreamble(pid_t Tid,
// buffer, associated with a particular thread, with a new CPU. For the
// data, we have 15 bytes to squeeze as much information as we can. At this
// point we only write down the following bytes:
// - Thread ID (pid_t, 4 bytes)
// - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes)
auto &NewBuffer = Metadata[0];
NewBuffer.Type = uint8_t(RecordType::Metadata);
NewBuffer.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewBuffer);
std::memcpy(&NewBuffer.Data, &Tid, sizeof(pid_t));
int32_t tid = static_cast<int32_t>(Tid);
std::memcpy(&NewBuffer.Data, &tid, sizeof(tid));
}

// Also write the WalltimeMarker record.
Expand Down Expand Up @@ -236,7 +237,7 @@ inline void setupNewBuffer(int (*wall_clock_reader)(
auto &TLD = getThreadLocalData();
auto &B = TLD.Buffer;
TLD.RecordPtr = static_cast<char *>(B.Data);
pid_t Tid = syscall(SYS_gettid);
tid_t Tid = __sanitizer::GetTid();
timespec TS{0, 0};
// This is typically clock_gettime, but callers have injection ability.
wall_clock_reader(CLOCK_MONOTONIC, &TS);
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/xray/xray_flags.inc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ XRAY_FLAG(int, xray_naive_log_func_duration_threshold_us, 5,
"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.")
"any more and the recordings will be dropped.")
XRAY_FLAG(int, xray_naive_log_thread_buffer_size, 1024,
"The number of entries to keep on a per-thread buffer.")

Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/xray/xray_inmemory_log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct alignas(64) ThreadLocalData {
size_t StackSize = 0;
size_t StackEntries = 0;
int Fd = -1;
pid_t TID = 0;
tid_t TID = 0;
};

static pthread_key_t PThreadKey;
Expand Down
26 changes: 25 additions & 1 deletion compiler-rt/lib/xray/xray_x86_64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
#include "xray_defs.h"
#include "xray_interface_internal.h"

#if SANITIZER_FREEBSD
#include <sys/sysctl.h>
#endif

#include <atomic>
#include <cstdint>
#include <errno.h>
Expand All @@ -14,6 +18,7 @@

namespace __xray {

#if SANITIZER_LINUX
static std::pair<ssize_t, bool>
retryingReadSome(int Fd, char *Begin, char *End) XRAY_NEVER_INSTRUMENT {
auto BytesToRead = std::distance(Begin, End);
Expand Down Expand Up @@ -71,6 +76,24 @@ uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
}
return TSCFrequency == -1 ? 0 : static_cast<uint64_t>(TSCFrequency);
}
#elif SANITIZER_FREEBSD
uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
long long TSCFrequency = -1;
size_t tscfreqsz = sizeof(TSCFrequency);

if (sysctlbyname("machdep.tsc_freq", &TSCFrequency, &tscfreqsz,
NULL, 0) != -1) {
return static_cast<uint64_t>(TSCFrequency);
} else {
Report("Unable to determine CPU frequency for TSC accounting.\n");
}

return 0;

}
#else
#error "Platform not supported"
#endif

static constexpr uint8_t CallOpCode = 0xe8;
static constexpr uint16_t MovR10Seq = 0xba41;
Expand Down Expand Up @@ -259,7 +282,8 @@ bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT {
// We check whether rdtscp support is enabled. According to the x86_64 manual,
// level should be set at 0x80000001, and we should have a look at bit 27 in
// EDX. That's 0x8000000 (or 1u << 27).
__get_cpuid(0x80000001, &EAX, &EBX, &ECX, &EDX);
__asm__ __volatile__("cpuid" : "=a"(EAX), "=b"(EBX), "=c"(ECX), "=d"(EDX)
: "0"(0x80000001));
if (!(EDX & (1u << 27))) {
Report("Missing rdtscp support.\n");
return false;
Expand Down
5 changes: 3 additions & 2 deletions compiler-rt/lib/xray/xray_x86_64.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ namespace __xray {

ALWAYS_INLINE uint64_t readTSC(uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
unsigned LongCPU;
uint64_t TSC = __rdtscp(&LongCPU);
unsigned long Rax, Rdx;
__asm__ __volatile__("rdtscp\n" : "=a"(Rax), "=d"(Rdx), "=c"(LongCPU) ::);
CPU = LongCPU;
return TSC;
return (Rdx << 32) + Rax;
}

uint64_t getTSCFrequency();
Expand Down
12 changes: 9 additions & 3 deletions compiler-rt/test/xray/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ def build_invocation(compile_flags):
llvm_xray = os.path.join(config.llvm_tools_dir, 'llvm-xray')

# Setup substitutions.
xraylib_flags = '-lm -pthread -lrt'
if config.host_os == 'Linux':
xraylib_flags += ' -ldl'

xraylib_flags += ' -L%s -Wl,-whole-archive -lclang_rt.xray-%s '
'-Wl,-no-whole-archive'

config.substitutions.append(
('%clang ', build_invocation([config.target_cflags])))
config.substitutions.append(
Expand All @@ -33,14 +40,13 @@ config.substitutions.append(
('%llvm_xray', llvm_xray))
config.substitutions.append(
('%xraylib',
('-lm -lpthread -ldl -lrt -L%s '
'-Wl,-whole-archive -lclang_rt.xray-%s -Wl,-no-whole-archive')
(xraylib_flags)
% (config.compiler_rt_libdir, config.host_arch)))

# Default test suffixes.
config.suffixes = ['.c', '.cc', '.cpp']

if config.host_os not in ['Linux']:
if config.host_os not in ['Linux', 'FreeBSD']:
config.unsupported = True
elif '64' not in config.host_arch:
if 'arm' in config.host_arch:
Expand Down

0 comments on commit 4d4ed0e

Please sign in to comment.