| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| //===-- 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" | ||
|
|
||
| // 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 { | ||
|
|
||
| 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 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, | ||
| }; | ||
| RecordKinds RecordKind : 7; // Use 7 bits to identify this record type. | ||
| 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 Type : 1; | ||
| enum class RecordKinds { | ||
| FunctionEnter = 0x00, | ||
| FunctionExit = 0x01, | ||
| FunctionTailExit = 0x02, | ||
| }; | ||
| RecordKinds 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."); | ||
|
|
||
| // Options used by the FDR implementation. | ||
| struct FDRLoggingOptions { | ||
| bool ReportErrors = false; | ||
| int Fd = -1; | ||
| }; | ||
|
|
||
| // Flight Data Recorder mode implementation interfaces. | ||
| XRayLogInitStatus FDRLogging_init(std::size_t BufferSize, std::size_t BufferMax, | ||
| void *Options, size_t OptionsSize); | ||
| XRayLogInitStatus FDRLogging_finalize(); | ||
| void FDRLogging_handleArg0(int32_t FuncId, XRayEntryType Entry); | ||
| XRayLogFlushStatus FDRLogging_flush(); | ||
| XRayLogInitStatus FDRLogging_reset(); | ||
|
|
||
| } // namespace __xray | ||
|
|
||
| #endif // XRAY_XRAY_FDR_LOGGING_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| //===-- xray_log_interface.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/xray_log_interface.h" | ||
|
|
||
| #include "xray/xray_interface.h" | ||
| #include "xray_defs.h" | ||
|
|
||
| #include <memory> | ||
| #include <mutex> | ||
|
|
||
| std::mutex XRayImplMutex; | ||
| std::unique_ptr<XRayLogImpl> GlobalXRayImpl; | ||
|
|
||
| void __xray_set_log_impl(XRayLogImpl Impl) XRAY_NEVER_INSTRUMENT { | ||
| if (Impl.log_init == nullptr || Impl.log_finalize == nullptr || | ||
| Impl.handle_arg0 == nullptr || Impl.flush_log == nullptr) { | ||
| std::lock_guard<std::mutex> Guard(XRayImplMutex); | ||
| GlobalXRayImpl.reset(); | ||
| return; | ||
| } | ||
|
|
||
| std::lock_guard<std::mutex> Guard(XRayImplMutex); | ||
| GlobalXRayImpl.reset(new XRayLogImpl); | ||
| *GlobalXRayImpl = Impl; | ||
| } | ||
|
|
||
| XRayLogInitStatus __xray_init(size_t BufferSize, size_t MaxBuffers, void *Args, | ||
| size_t ArgsSize) XRAY_NEVER_INSTRUMENT { | ||
| std::lock_guard<std::mutex> Guard(XRayImplMutex); | ||
| if (!GlobalXRayImpl) | ||
| return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; | ||
| return GlobalXRayImpl->log_init(BufferSize, MaxBuffers, Args, ArgsSize); | ||
| } | ||
|
|
||
| XRayLogInitStatus __xray_log_finalize() XRAY_NEVER_INSTRUMENT { | ||
| std::lock_guard<std::mutex> Guard(XRayImplMutex); | ||
| if (!GlobalXRayImpl) | ||
| return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; | ||
| return GlobalXRayImpl->log_finalize(); | ||
| } | ||
|
|
||
| XRayLogFlushStatus __xray_log_flushLog() XRAY_NEVER_INSTRUMENT { | ||
| std::lock_guard<std::mutex> Guard(XRayImplMutex); | ||
| if (!GlobalXRayImpl) | ||
| return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; | ||
| return GlobalXRayImpl->flush_log(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| //===-- xray_utils.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. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| #include "xray_utils.h" | ||
|
|
||
| #include "sanitizer_common/sanitizer_common.h" | ||
| #include "xray_defs.h" | ||
| #include "xray_flags.h" | ||
| #include <cstdio> | ||
| #include <fcntl.h> | ||
| #include <iterator> | ||
| #include <sys/types.h> | ||
| #include <tuple> | ||
| #include <unistd.h> | ||
| #include <utility> | ||
|
|
||
| #if defined(__x86_64__) | ||
| #include "xray_x86_64.h" | ||
| #elif defined(__arm__) || defined(__aarch64__) | ||
| #include "xray_emulate_tsc.h" | ||
| #else | ||
| #error "Unsupported CPU Architecture" | ||
| #endif /* CPU architecture */ | ||
|
|
||
| namespace __xray { | ||
|
|
||
| void PrintToStdErr(const char *Buffer) XRAY_NEVER_INSTRUMENT { | ||
| fprintf(stderr, "%s", Buffer); | ||
| } | ||
|
|
||
| void retryingWriteAll(int Fd, char *Begin, char *End) XRAY_NEVER_INSTRUMENT { | ||
| 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. | ||
| Report("Failed to write; errno = %d\n", errno); | ||
| return; | ||
| } | ||
| TotalBytes -= Written; | ||
| if (TotalBytes == 0) | ||
| break; | ||
| Begin += Written; | ||
| } | ||
| } | ||
|
|
||
| std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin, | ||
| char *End) XRAY_NEVER_INSTRUMENT { | ||
| auto BytesToRead = std::distance(Begin, End); | ||
| ssize_t BytesRead; | ||
| ssize_t TotalBytesRead = 0; | ||
| while (BytesToRead && (BytesRead = read(Fd, Begin, BytesToRead))) { | ||
| if (BytesRead == -1) { | ||
| if (errno == EINTR) | ||
| continue; | ||
| Report("Read error; errno = %d\n", errno); | ||
| return std::make_pair(TotalBytesRead, false); | ||
| } | ||
|
|
||
| TotalBytesRead += BytesRead; | ||
| BytesToRead -= BytesRead; | ||
| Begin += BytesRead; | ||
| } | ||
| return std::make_pair(TotalBytesRead, true); | ||
| } | ||
|
|
||
| bool readValueFromFile(const char *Filename, | ||
| long long *Value) XRAY_NEVER_INSTRUMENT { | ||
| int Fd = open(Filename, O_RDONLY | O_CLOEXEC); | ||
| if (Fd == -1) | ||
| return false; | ||
| static constexpr size_t BufSize = 256; | ||
| char Line[BufSize] = {}; | ||
| ssize_t BytesRead; | ||
| bool Success; | ||
| std::tie(BytesRead, Success) = retryingReadSome(Fd, Line, Line + BufSize); | ||
| if (!Success) | ||
| return false; | ||
| close(Fd); | ||
| char *End = nullptr; | ||
| long long Tmp = internal_simple_strtoll(Line, &End, 10); | ||
| bool Result = false; | ||
| if (Line[0] != '\0' && (*End == '\n' || *End == '\0')) { | ||
| *Value = Tmp; | ||
| Result = true; | ||
| } | ||
| return Result; | ||
| } | ||
|
|
||
| long long getCPUFrequency() XRAY_NEVER_INSTRUMENT { | ||
| // Get the cycle frequency from SysFS on Linux. | ||
| long long CPUFrequency = -1; | ||
| #if defined(__x86_64__) | ||
| if (readValueFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", | ||
| &CPUFrequency)) { | ||
| CPUFrequency *= 1000; | ||
| } else if (readValueFromFile( | ||
| "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", | ||
| &CPUFrequency)) { | ||
| CPUFrequency *= 1000; | ||
| } else { | ||
| Report("Unable to determine CPU frequency for TSC accounting.\n"); | ||
| } | ||
| #elif defined(__arm__) || defined(__aarch64__) | ||
| // There is no instruction like RDTSCP in user mode on ARM. ARM's CP15 does | ||
| // not have a constant frequency like TSC on x86(_64), it may go faster | ||
| // or slower depending on CPU turbo or power saving mode. Furthermore, | ||
| // to read from CP15 on ARM a kernel modification or a driver is needed. | ||
| // We can not require this from users of compiler-rt. | ||
| // So on ARM we use clock_gettime() which gives the result in nanoseconds. | ||
| // To get the measurements per second, we scale this by the number of | ||
| // nanoseconds per second, pretending that the TSC frequency is 1GHz and | ||
| // one TSC tick is 1 nanosecond. | ||
| CPUFrequency = NanosecondsPerSecond; | ||
| #else | ||
| #error "Unsupported CPU Architecture" | ||
| #endif /* CPU architecture */ | ||
| return CPUFrequency; | ||
| } | ||
|
|
||
| int getLogFD() XRAY_NEVER_INSTRUMENT { | ||
| // FIXME: Figure out how to make this less stderr-dependent. | ||
| SetPrintfAndReportCallback(PrintToStdErr); | ||
| // Open a temporary file once for the log. | ||
| static char TmpFilename[256] = {}; | ||
| static char TmpWildcardPattern[] = "XXXXXX"; | ||
| auto Argv = GetArgv(); | ||
| const char *Progname = Argv[0] == nullptr ? "(unknown)" : Argv[0]; | ||
| const char *LastSlash = internal_strrchr(Progname, '/'); | ||
|
|
||
| if (LastSlash != nullptr) | ||
| Progname = LastSlash + 1; | ||
|
|
||
| const int HalfLength = sizeof(TmpFilename) / 2 - sizeof(TmpWildcardPattern); | ||
| int NeededLength = internal_snprintf( | ||
| TmpFilename, sizeof(TmpFilename), "%.*s%.*s.%s", HalfLength, | ||
| flags()->xray_logfile_base, HalfLength, Progname, TmpWildcardPattern); | ||
| if (NeededLength > int(sizeof(TmpFilename))) { | ||
| Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename); | ||
| return -1; | ||
| } | ||
| int Fd = mkstemp(TmpFilename); | ||
| if (Fd == -1) { | ||
| Report("XRay: Failed opening temporary file '%s'; not logging events.\n", | ||
| TmpFilename); | ||
| return -1; | ||
| } | ||
| if (Verbosity()) | ||
| fprintf(stderr, "XRay: Log file in '%s'\n", TmpFilename); | ||
|
|
||
| return Fd; | ||
| } | ||
|
|
||
| } // namespace __xray |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| //===-- xray_utils.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. | ||
| // | ||
| // Some shared utilities for the XRay runtime implementation. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| #ifndef XRAY_UTILS_H | ||
| #define XRAY_UTILS_H | ||
|
|
||
| #include <sys/types.h> | ||
| #include <utility> | ||
|
|
||
| namespace __xray { | ||
|
|
||
| // Default implementation of the reporting interface for sanitizer errors. | ||
| void PrintToStdErr(const char *Buffer); | ||
|
|
||
| // EINTR-safe write routine, provided a file descriptor and a character range. | ||
| void retryingWriteAll(int Fd, char *Begin, char *End); | ||
|
|
||
| // Reads a long long value from a provided file. | ||
| bool readValueFromFile(const char *Filename, long long *Value); | ||
|
|
||
| // EINTR-safe read routine, providing a file descriptor and a character range. | ||
| std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin, char *End); | ||
|
|
||
| // EINTR-safe open routine, uses flag-provided values for initialising a log | ||
| // file. | ||
| int getLogFD(); | ||
|
|
||
| // EINTR-safe read of CPU frquency for the current CPU. | ||
| long long getCPUFrequency(); | ||
|
|
||
| } // namespace __xray | ||
|
|
||
| #endif // XRAY_UTILS_H |