1 //===-- xray_inmemory_log.cc ------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
12 // Implementation of a simple in-memory log of XRay events. This defines a
13 // logging function that's compatible with the XRay handler interface, and
14 // routines for exporting data to files.
16 //===----------------------------------------------------------------------===//
24 #include <sys/syscall.h>
25 #include <sys/types.h>
29 #if defined(__x86_64__)
30 #include "xray_x86_64.h"
31 #elif defined(__arm__) || defined(__aarch64__)
32 #include "xray_emulate_tsc.h"
34 #error "Unsupported CPU Architecture"
35 #endif /* Architecture-specific inline intrinsics */
37 #include "sanitizer_common/sanitizer_libc.h"
38 #include "xray/xray_records.h"
39 #include "xray_defs.h"
40 #include "xray_flags.h"
41 #include "xray_interface_internal.h"
43 // __xray_InMemoryRawLog will use a thread-local aligned buffer capped to a
44 // certain size (32kb by default) and use it as if it were a circular buffer for
45 // events. We store simple fixed-sized entries in the log for external analysis.
48 void __xray_InMemoryRawLog(int32_t FuncId,
49 XRayEntryType Type) XRAY_NEVER_INSTRUMENT;
56 static void retryingWriteAll(int Fd, char *Begin,
57 char *End) XRAY_NEVER_INSTRUMENT {
60 auto TotalBytes = std::distance(Begin, End);
61 while (auto Written = write(Fd, Begin, TotalBytes)) {
64 continue; // Try again.
65 Report("Failed to write; errno = %d\n", errno);
68 TotalBytes -= Written;
75 class ThreadExitFlusher {
81 explicit ThreadExitFlusher(int Fd, XRayRecord *Start,
82 size_t &Offset) XRAY_NEVER_INSTRUMENT
87 ~ThreadExitFlusher() XRAY_NEVER_INSTRUMENT {
88 std::lock_guard<std::mutex> L(LogMutex);
89 if (Fd > 0 && Start != nullptr) {
90 retryingWriteAll(Fd, reinterpret_cast<char *>(Start),
91 reinterpret_cast<char *>(Start + Offset));
92 // Because this thread's exit could be the last one trying to write to the
93 // file and that we're not able to close out the file properly, we sync
94 // instead and hope that the pending writes are flushed as the thread
101 } // namespace __xray
103 using namespace __xray;
105 void PrintToStdErr(const char *Buffer) XRAY_NEVER_INSTRUMENT {
106 fprintf(stderr, "%s", Buffer);
109 static int __xray_OpenLogFile() XRAY_NEVER_INSTRUMENT {
110 // FIXME: Figure out how to make this less stderr-dependent.
111 SetPrintfAndReportCallback(PrintToStdErr);
112 // Open a temporary file once for the log.
113 static char TmpFilename[256] = {};
114 static char TmpWildcardPattern[] = "XXXXXX";
115 auto Argv = GetArgv();
116 const char *Progname = Argv[0] == nullptr ? "(unknown)" : Argv[0];
117 const char *LastSlash = internal_strrchr(Progname, '/');
119 if (LastSlash != nullptr)
120 Progname = LastSlash + 1;
122 const int HalfLength = sizeof(TmpFilename) / 2 - sizeof(TmpWildcardPattern);
123 int NeededLength = internal_snprintf(TmpFilename, sizeof(TmpFilename),
125 HalfLength, flags()->xray_logfile_base,
126 HalfLength, Progname,
128 if (NeededLength > int(sizeof(TmpFilename))) {
129 Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename);
132 int Fd = mkstemp(TmpFilename);
134 Report("XRay: Failed opening temporary file '%s'; not logging events.\n",
139 fprintf(stderr, "XRay: Log file in '%s'\n", TmpFilename);
141 // Since we're here, we get to write the header. We set it up so that the
142 // header will only be written once, at the start, and let the threads
143 // logging do writes which just append.
144 XRayFileHeader Header;
146 Header.Type = FileTypes::NAIVE_LOG;
147 Header.CycleFrequency = __xray::cycleFrequency();
149 // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
150 // before setting the values in the header.
151 Header.ConstantTSC = 1;
152 Header.NonstopTSC = 1;
153 retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
154 reinterpret_cast<char *>(&Header) + sizeof(Header));
158 void __xray_InMemoryRawLog(int32_t FuncId,
159 XRayEntryType Type) XRAY_NEVER_INSTRUMENT {
161 std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type;
162 static constexpr size_t BuffLen = 1024;
163 thread_local static Buffer InMemoryBuffer[BuffLen] = {};
164 thread_local static size_t Offset = 0;
165 static int Fd = __xray_OpenLogFile();
168 thread_local __xray::ThreadExitFlusher Flusher(
169 Fd, reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer), Offset);
170 thread_local pid_t TId = syscall(SYS_gettid);
172 // First we get the useful data, and stuff it into the already aligned buffer
173 // through a pointer offset.
174 auto &R = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer)[Offset];
175 R.RecordType = RecordTypes::NORMAL;
176 R.TSC = __xray::readTSC(R.CPU);
181 if (Offset == BuffLen) {
182 std::lock_guard<std::mutex> L(LogMutex);
183 auto RecordBuffer = reinterpret_cast<__xray::XRayRecord *>(InMemoryBuffer);
184 retryingWriteAll(Fd, reinterpret_cast<char *>(RecordBuffer),
185 reinterpret_cast<char *>(RecordBuffer + Offset));
190 static auto Unused = [] {
191 if (flags()->xray_naive_log)
192 __xray_set_handler(__xray_InMemoryRawLog);