]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/xray/xray_fdr_logging.cc
Merge compiler-rt trunk r300890, and update build glue.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / xray / xray_fdr_logging.cc
1 //===-- xray_fdr_logging.cc ------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 //
12 // Here we implement the Flight Data Recorder mode for XRay, where we use
13 // compact structures to store records in memory as well as when writing out the
14 // data to files.
15 //
16 //===----------------------------------------------------------------------===//
17 #include "xray_fdr_logging.h"
18 #include <algorithm>
19 #include <bitset>
20 #include <cerrno>
21 #include <cstring>
22 #include <sys/syscall.h>
23 #include <sys/time.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <unordered_map>
27
28 #include "sanitizer_common/sanitizer_atomic.h"
29 #include "sanitizer_common/sanitizer_common.h"
30 #include "xray/xray_interface.h"
31 #include "xray/xray_records.h"
32 #include "xray_buffer_queue.h"
33 #include "xray_defs.h"
34 #include "xray_fdr_logging_impl.h"
35 #include "xray_flags.h"
36 #include "xray_tsc.h"
37 #include "xray_utils.h"
38
39 namespace __xray {
40
41 // Global BufferQueue.
42 std::shared_ptr<BufferQueue> BQ;
43
44 __sanitizer::atomic_sint32_t LoggingStatus = {
45     XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
46
47 __sanitizer::atomic_sint32_t LogFlushStatus = {
48     XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
49
50 std::unique_ptr<FDRLoggingOptions> FDROptions;
51
52 XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax,
53                                  void *Options,
54                                  size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
55   if (OptionsSize != sizeof(FDRLoggingOptions))
56     return static_cast<XRayLogInitStatus>(__sanitizer::atomic_load(
57         &LoggingStatus, __sanitizer::memory_order_acquire));
58   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
59   if (!__sanitizer::atomic_compare_exchange_strong(
60           &LoggingStatus, &CurrentStatus,
61           XRayLogInitStatus::XRAY_LOG_INITIALIZING,
62           __sanitizer::memory_order_release))
63     return static_cast<XRayLogInitStatus>(CurrentStatus);
64
65   FDROptions.reset(new FDRLoggingOptions());
66   memcpy(FDROptions.get(), Options, OptionsSize);
67   bool Success = false;
68   BQ = std::make_shared<BufferQueue>(BufferSize, BufferMax, Success);
69   if (!Success) {
70     Report("BufferQueue init failed.\n");
71     return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
72   }
73
74   // Install the actual handleArg0 handler after initialising the buffers.
75   __xray_set_handler(fdrLoggingHandleArg0);
76
77   __sanitizer::atomic_store(&LoggingStatus,
78                             XRayLogInitStatus::XRAY_LOG_INITIALIZED,
79                             __sanitizer::memory_order_release);
80   Report("XRay FDR init successful.\n");
81   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
82 }
83
84 // Must finalize before flushing.
85 XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
86   if (__sanitizer::atomic_load(&LoggingStatus,
87                                __sanitizer::memory_order_acquire) !=
88       XRayLogInitStatus::XRAY_LOG_FINALIZED)
89     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
90
91   s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
92   if (!__sanitizer::atomic_compare_exchange_strong(
93           &LogFlushStatus, &Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING,
94           __sanitizer::memory_order_release))
95     return static_cast<XRayLogFlushStatus>(Result);
96
97   // Make a copy of the BufferQueue pointer to prevent other threads that may be
98   // resetting it from blowing away the queue prematurely while we're dealing
99   // with it.
100   auto LocalBQ = BQ;
101
102   // We write out the file in the following format:
103   //
104   //   1) We write down the XRay file header with version 1, type FDR_LOG.
105   //   2) Then we use the 'apply' member of the BufferQueue that's live, to
106   //      ensure that at this point in time we write down the buffers that have
107   //      been released (and marked "used") -- we dump the full buffer for now
108   //      (fixed-sized) and let the tools reading the buffers deal with the data
109   //      afterwards.
110   //
111   int Fd = FDROptions->Fd;
112   if (Fd == -1)
113     Fd = getLogFD();
114   if (Fd == -1) {
115     auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
116     __sanitizer::atomic_store(&LogFlushStatus, Result,
117                               __sanitizer::memory_order_release);
118     return Result;
119   }
120
121   // Test for required CPU features and cache the cycle frequency
122   static bool TSCSupported = probeRequiredCPUFeatures();
123   static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency()
124                                    : __xray::NanosecondsPerSecond;
125
126   XRayFileHeader Header;
127   Header.Version = 1;
128   Header.Type = FileTypes::FDR_LOG;
129   Header.CycleFrequency = CycleFrequency;
130   // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
131   // before setting the values in the header.
132   Header.ConstantTSC = 1;
133   Header.NonstopTSC = 1;
134   Header.FdrData = FdrAdditionalHeaderData{LocalBQ->ConfiguredBufferSize()};
135   retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
136                    reinterpret_cast<char *>(&Header) + sizeof(Header));
137
138   LocalBQ->apply([&](const BufferQueue::Buffer &B) {
139     uint64_t BufferSize = B.Size;
140     if (BufferSize > 0) {
141       retryingWriteAll(Fd, reinterpret_cast<char *>(B.Buffer),
142                        reinterpret_cast<char *>(B.Buffer) + B.Size);
143     }
144   });
145   __sanitizer::atomic_store(&LogFlushStatus,
146                             XRayLogFlushStatus::XRAY_LOG_FLUSHED,
147                             __sanitizer::memory_order_release);
148   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
149 }
150
151 XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
152   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
153   if (!__sanitizer::atomic_compare_exchange_strong(
154           &LoggingStatus, &CurrentStatus,
155           XRayLogInitStatus::XRAY_LOG_FINALIZING,
156           __sanitizer::memory_order_release))
157     return static_cast<XRayLogInitStatus>(CurrentStatus);
158
159   // Do special things to make the log finalize itself, and not allow any more
160   // operations to be performed until re-initialized.
161   BQ->finalize();
162
163   __sanitizer::atomic_store(&LoggingStatus,
164                             XRayLogInitStatus::XRAY_LOG_FINALIZED,
165                             __sanitizer::memory_order_release);
166   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
167 }
168
169 XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT {
170   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED;
171   if (__sanitizer::atomic_compare_exchange_strong(
172           &LoggingStatus, &CurrentStatus,
173           XRayLogInitStatus::XRAY_LOG_INITIALIZED,
174           __sanitizer::memory_order_release))
175     return static_cast<XRayLogInitStatus>(CurrentStatus);
176
177   // Release the in-memory buffer queue.
178   BQ.reset();
179
180   // Spin until the flushing status is flushed.
181   s32 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
182   while (__sanitizer::atomic_compare_exchange_weak(
183       &LogFlushStatus, &CurrentFlushingStatus,
184       XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING,
185       __sanitizer::memory_order_release)) {
186     if (CurrentFlushingStatus == XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING)
187       break;
188     CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
189   }
190
191   // At this point, we know that the status is flushed, and that we can assume
192   return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
193 }
194
195 void fdrLoggingHandleArg0(int32_t FuncId,
196                           XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
197   // We want to get the TSC as early as possible, so that we can check whether
198   // we've seen this CPU before. We also do it before we load anything else, to
199   // allow for forward progress with the scheduling.
200   unsigned char CPU;
201   uint64_t TSC;
202
203   // Test once for required CPU features
204   static bool TSCSupported = probeRequiredCPUFeatures();
205
206   if(TSCSupported) {
207     TSC = __xray::readTSC(CPU);
208   } else {
209     // FIXME: This code needs refactoring as it appears in multiple locations
210     timespec TS;
211     int result = clock_gettime(CLOCK_REALTIME, &TS);
212     if (result != 0) {
213       Report("clock_gettime(2) return %d, errno=%d", result, int(errno));
214       TS = {0, 0};
215     }
216     CPU = 0;
217     TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;
218   }
219
220   __xray_fdr_internal::processFunctionHook(FuncId, Entry, TSC, CPU,
221                                            clock_gettime, LoggingStatus, BQ);
222 }
223
224 } // namespace __xray
225
226 static auto UNUSED Unused = [] {
227   using namespace __xray;
228   if (flags()->xray_fdr_log) {
229     XRayLogImpl Impl{
230         fdrLoggingInit, fdrLoggingFinalize, fdrLoggingHandleArg0,
231         fdrLoggingFlush,
232     };
233     __xray_set_log_impl(Impl);
234   }
235   return true;
236 }();