xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_fdr_controller.h (revision 3cab2bb3f667058bece8e38b12449a63a9d73c4b)
1*3cab2bb3Spatrick //===-- xray_fdr_controller.h ---------------------------------------------===//
2*3cab2bb3Spatrick //
3*3cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*3cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*3cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*3cab2bb3Spatrick //
7*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
8*3cab2bb3Spatrick //
9*3cab2bb3Spatrick // This file is a part of XRay, a function call tracing system.
10*3cab2bb3Spatrick //
11*3cab2bb3Spatrick //===----------------------------------------------------------------------===//
12*3cab2bb3Spatrick #ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
13*3cab2bb3Spatrick #define COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
14*3cab2bb3Spatrick 
15*3cab2bb3Spatrick #include <limits>
16*3cab2bb3Spatrick #include <time.h>
17*3cab2bb3Spatrick 
18*3cab2bb3Spatrick #include "xray/xray_interface.h"
19*3cab2bb3Spatrick #include "xray/xray_records.h"
20*3cab2bb3Spatrick #include "xray_buffer_queue.h"
21*3cab2bb3Spatrick #include "xray_fdr_log_writer.h"
22*3cab2bb3Spatrick 
23*3cab2bb3Spatrick namespace __xray {
24*3cab2bb3Spatrick 
25*3cab2bb3Spatrick template <size_t Version = 5> class FDRController {
26*3cab2bb3Spatrick   BufferQueue *BQ;
27*3cab2bb3Spatrick   BufferQueue::Buffer &B;
28*3cab2bb3Spatrick   FDRLogWriter &W;
29*3cab2bb3Spatrick   int (*WallClockReader)(clockid_t, struct timespec *) = 0;
30*3cab2bb3Spatrick   uint64_t CycleThreshold = 0;
31*3cab2bb3Spatrick 
32*3cab2bb3Spatrick   uint64_t LastFunctionEntryTSC = 0;
33*3cab2bb3Spatrick   uint64_t LatestTSC = 0;
34*3cab2bb3Spatrick   uint16_t LatestCPU = 0;
35*3cab2bb3Spatrick   tid_t TId = 0;
36*3cab2bb3Spatrick   pid_t PId = 0;
37*3cab2bb3Spatrick   bool First = true;
38*3cab2bb3Spatrick 
39*3cab2bb3Spatrick   uint32_t UndoableFunctionEnters = 0;
40*3cab2bb3Spatrick   uint32_t UndoableTailExits = 0;
41*3cab2bb3Spatrick 
finalized()42*3cab2bb3Spatrick   bool finalized() const XRAY_NEVER_INSTRUMENT {
43*3cab2bb3Spatrick     return BQ == nullptr || BQ->finalizing();
44*3cab2bb3Spatrick   }
45*3cab2bb3Spatrick 
hasSpace(size_t S)46*3cab2bb3Spatrick   bool hasSpace(size_t S) XRAY_NEVER_INSTRUMENT {
47*3cab2bb3Spatrick     return B.Data != nullptr && B.Generation == BQ->generation() &&
48*3cab2bb3Spatrick            W.getNextRecord() + S <= reinterpret_cast<char *>(B.Data) + B.Size;
49*3cab2bb3Spatrick   }
50*3cab2bb3Spatrick 
mask(int32_t FuncId)51*3cab2bb3Spatrick   constexpr int32_t mask(int32_t FuncId) const XRAY_NEVER_INSTRUMENT {
52*3cab2bb3Spatrick     return FuncId & ((1 << 29) - 1);
53*3cab2bb3Spatrick   }
54*3cab2bb3Spatrick 
getNewBuffer()55*3cab2bb3Spatrick   bool getNewBuffer() XRAY_NEVER_INSTRUMENT {
56*3cab2bb3Spatrick     if (BQ->getBuffer(B) != BufferQueue::ErrorCode::Ok)
57*3cab2bb3Spatrick       return false;
58*3cab2bb3Spatrick 
59*3cab2bb3Spatrick     W.resetRecord();
60*3cab2bb3Spatrick     DCHECK_EQ(W.getNextRecord(), B.Data);
61*3cab2bb3Spatrick     LatestTSC = 0;
62*3cab2bb3Spatrick     LatestCPU = 0;
63*3cab2bb3Spatrick     First = true;
64*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
65*3cab2bb3Spatrick     UndoableTailExits = 0;
66*3cab2bb3Spatrick     atomic_store(B.Extents, 0, memory_order_release);
67*3cab2bb3Spatrick     return true;
68*3cab2bb3Spatrick   }
69*3cab2bb3Spatrick 
setupNewBuffer()70*3cab2bb3Spatrick   bool setupNewBuffer() XRAY_NEVER_INSTRUMENT {
71*3cab2bb3Spatrick     if (finalized())
72*3cab2bb3Spatrick       return false;
73*3cab2bb3Spatrick 
74*3cab2bb3Spatrick     DCHECK(hasSpace(sizeof(MetadataRecord) * 3));
75*3cab2bb3Spatrick     TId = GetTid();
76*3cab2bb3Spatrick     PId = internal_getpid();
77*3cab2bb3Spatrick     struct timespec TS {
78*3cab2bb3Spatrick       0, 0
79*3cab2bb3Spatrick     };
80*3cab2bb3Spatrick     WallClockReader(CLOCK_MONOTONIC, &TS);
81*3cab2bb3Spatrick 
82*3cab2bb3Spatrick     MetadataRecord Metadata[] = {
83*3cab2bb3Spatrick         // Write out a MetadataRecord to signify that this is the start of a new
84*3cab2bb3Spatrick         // buffer, associated with a particular thread, with a new CPU. For the
85*3cab2bb3Spatrick         // data, we have 15 bytes to squeeze as much information as we can. At
86*3cab2bb3Spatrick         // this point we only write down the following bytes:
87*3cab2bb3Spatrick         //   - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8
88*3cab2bb3Spatrick         //   bytes)
89*3cab2bb3Spatrick         createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(
90*3cab2bb3Spatrick             static_cast<int32_t>(TId)),
91*3cab2bb3Spatrick 
92*3cab2bb3Spatrick         // Also write the WalltimeMarker record. We only really need microsecond
93*3cab2bb3Spatrick         // precision here, and enforce across platforms that we need 64-bit
94*3cab2bb3Spatrick         // seconds and 32-bit microseconds encoded in the Metadata record.
95*3cab2bb3Spatrick         createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
96*3cab2bb3Spatrick             static_cast<int64_t>(TS.tv_sec),
97*3cab2bb3Spatrick             static_cast<int32_t>(TS.tv_nsec / 1000)),
98*3cab2bb3Spatrick 
99*3cab2bb3Spatrick         // Also write the Pid record.
100*3cab2bb3Spatrick         createMetadataRecord<MetadataRecord::RecordKinds::Pid>(
101*3cab2bb3Spatrick             static_cast<int32_t>(PId)),
102*3cab2bb3Spatrick     };
103*3cab2bb3Spatrick 
104*3cab2bb3Spatrick     if (finalized())
105*3cab2bb3Spatrick       return false;
106*3cab2bb3Spatrick     return W.writeMetadataRecords(Metadata);
107*3cab2bb3Spatrick   }
108*3cab2bb3Spatrick 
prepareBuffer(size_t S)109*3cab2bb3Spatrick   bool prepareBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
110*3cab2bb3Spatrick     if (finalized())
111*3cab2bb3Spatrick       return returnBuffer();
112*3cab2bb3Spatrick 
113*3cab2bb3Spatrick     if (UNLIKELY(!hasSpace(S))) {
114*3cab2bb3Spatrick       if (!returnBuffer())
115*3cab2bb3Spatrick         return false;
116*3cab2bb3Spatrick       if (!getNewBuffer())
117*3cab2bb3Spatrick         return false;
118*3cab2bb3Spatrick       if (!setupNewBuffer())
119*3cab2bb3Spatrick         return false;
120*3cab2bb3Spatrick     }
121*3cab2bb3Spatrick 
122*3cab2bb3Spatrick     if (First) {
123*3cab2bb3Spatrick       First = false;
124*3cab2bb3Spatrick       W.resetRecord();
125*3cab2bb3Spatrick       atomic_store(B.Extents, 0, memory_order_release);
126*3cab2bb3Spatrick       return setupNewBuffer();
127*3cab2bb3Spatrick     }
128*3cab2bb3Spatrick 
129*3cab2bb3Spatrick     return true;
130*3cab2bb3Spatrick   }
131*3cab2bb3Spatrick 
returnBuffer()132*3cab2bb3Spatrick   bool returnBuffer() XRAY_NEVER_INSTRUMENT {
133*3cab2bb3Spatrick     if (BQ == nullptr)
134*3cab2bb3Spatrick       return false;
135*3cab2bb3Spatrick 
136*3cab2bb3Spatrick     First = true;
137*3cab2bb3Spatrick     if (finalized()) {
138*3cab2bb3Spatrick       BQ->releaseBuffer(B); // ignore result.
139*3cab2bb3Spatrick       return false;
140*3cab2bb3Spatrick     }
141*3cab2bb3Spatrick 
142*3cab2bb3Spatrick     return BQ->releaseBuffer(B) == BufferQueue::ErrorCode::Ok;
143*3cab2bb3Spatrick   }
144*3cab2bb3Spatrick 
145*3cab2bb3Spatrick   enum class PreambleResult { NoChange, WroteMetadata, InvalidBuffer };
recordPreamble(uint64_t TSC,uint16_t CPU)146*3cab2bb3Spatrick   PreambleResult recordPreamble(uint64_t TSC,
147*3cab2bb3Spatrick                                 uint16_t CPU) XRAY_NEVER_INSTRUMENT {
148*3cab2bb3Spatrick     if (UNLIKELY(LatestCPU != CPU || LatestTSC == 0)) {
149*3cab2bb3Spatrick       // We update our internal tracking state for the Latest TSC and CPU we've
150*3cab2bb3Spatrick       // seen, then write out the appropriate metadata and function records.
151*3cab2bb3Spatrick       LatestTSC = TSC;
152*3cab2bb3Spatrick       LatestCPU = CPU;
153*3cab2bb3Spatrick 
154*3cab2bb3Spatrick       if (B.Generation != BQ->generation())
155*3cab2bb3Spatrick         return PreambleResult::InvalidBuffer;
156*3cab2bb3Spatrick 
157*3cab2bb3Spatrick       W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);
158*3cab2bb3Spatrick       return PreambleResult::WroteMetadata;
159*3cab2bb3Spatrick     }
160*3cab2bb3Spatrick 
161*3cab2bb3Spatrick     DCHECK_EQ(LatestCPU, CPU);
162*3cab2bb3Spatrick 
163*3cab2bb3Spatrick     if (UNLIKELY(LatestTSC > TSC ||
164*3cab2bb3Spatrick                  TSC - LatestTSC >
165*3cab2bb3Spatrick                      uint64_t{std::numeric_limits<int32_t>::max()})) {
166*3cab2bb3Spatrick       // Either the TSC has wrapped around from the last TSC we've seen or the
167*3cab2bb3Spatrick       // delta is too large to fit in a 32-bit signed integer, so we write a
168*3cab2bb3Spatrick       // wrap-around record.
169*3cab2bb3Spatrick       LatestTSC = TSC;
170*3cab2bb3Spatrick 
171*3cab2bb3Spatrick       if (B.Generation != BQ->generation())
172*3cab2bb3Spatrick         return PreambleResult::InvalidBuffer;
173*3cab2bb3Spatrick 
174*3cab2bb3Spatrick       W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);
175*3cab2bb3Spatrick       return PreambleResult::WroteMetadata;
176*3cab2bb3Spatrick     }
177*3cab2bb3Spatrick 
178*3cab2bb3Spatrick     return PreambleResult::NoChange;
179*3cab2bb3Spatrick   }
180*3cab2bb3Spatrick 
rewindRecords(int32_t FuncId,uint64_t TSC,uint16_t CPU)181*3cab2bb3Spatrick   bool rewindRecords(int32_t FuncId, uint64_t TSC,
182*3cab2bb3Spatrick                      uint16_t CPU) XRAY_NEVER_INSTRUMENT {
183*3cab2bb3Spatrick     // Undo one enter record, because at this point we are either at the state
184*3cab2bb3Spatrick     // of:
185*3cab2bb3Spatrick     // - We are exiting a function that we recently entered.
186*3cab2bb3Spatrick     // - We are exiting a function that was the result of a sequence of tail
187*3cab2bb3Spatrick     //   exits, and we can check whether the tail exits can be re-wound.
188*3cab2bb3Spatrick     //
189*3cab2bb3Spatrick     FunctionRecord F;
190*3cab2bb3Spatrick     W.undoWrites(sizeof(FunctionRecord));
191*3cab2bb3Spatrick     if (B.Generation != BQ->generation())
192*3cab2bb3Spatrick       return false;
193*3cab2bb3Spatrick     internal_memcpy(&F, W.getNextRecord(), sizeof(FunctionRecord));
194*3cab2bb3Spatrick 
195*3cab2bb3Spatrick     DCHECK(F.RecordKind ==
196*3cab2bb3Spatrick                uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&
197*3cab2bb3Spatrick            "Expected to find function entry recording when rewinding.");
198*3cab2bb3Spatrick     DCHECK_EQ(F.FuncId, FuncId & ~(0x0F << 28));
199*3cab2bb3Spatrick 
200*3cab2bb3Spatrick     LatestTSC -= F.TSCDelta;
201*3cab2bb3Spatrick     if (--UndoableFunctionEnters != 0) {
202*3cab2bb3Spatrick       LastFunctionEntryTSC -= F.TSCDelta;
203*3cab2bb3Spatrick       return true;
204*3cab2bb3Spatrick     }
205*3cab2bb3Spatrick 
206*3cab2bb3Spatrick     LastFunctionEntryTSC = 0;
207*3cab2bb3Spatrick     auto RewindingTSC = LatestTSC;
208*3cab2bb3Spatrick     auto RewindingRecordPtr = W.getNextRecord() - sizeof(FunctionRecord);
209*3cab2bb3Spatrick     while (UndoableTailExits) {
210*3cab2bb3Spatrick       if (B.Generation != BQ->generation())
211*3cab2bb3Spatrick         return false;
212*3cab2bb3Spatrick       internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
213*3cab2bb3Spatrick       DCHECK_EQ(F.RecordKind,
214*3cab2bb3Spatrick                 uint8_t(FunctionRecord::RecordKinds::FunctionTailExit));
215*3cab2bb3Spatrick       RewindingTSC -= F.TSCDelta;
216*3cab2bb3Spatrick       RewindingRecordPtr -= sizeof(FunctionRecord);
217*3cab2bb3Spatrick       if (B.Generation != BQ->generation())
218*3cab2bb3Spatrick         return false;
219*3cab2bb3Spatrick       internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
220*3cab2bb3Spatrick 
221*3cab2bb3Spatrick       // This tail call exceeded the threshold duration. It will not be erased.
222*3cab2bb3Spatrick       if ((TSC - RewindingTSC) >= CycleThreshold) {
223*3cab2bb3Spatrick         UndoableTailExits = 0;
224*3cab2bb3Spatrick         return true;
225*3cab2bb3Spatrick       }
226*3cab2bb3Spatrick 
227*3cab2bb3Spatrick       --UndoableTailExits;
228*3cab2bb3Spatrick       W.undoWrites(sizeof(FunctionRecord) * 2);
229*3cab2bb3Spatrick       LatestTSC = RewindingTSC;
230*3cab2bb3Spatrick     }
231*3cab2bb3Spatrick     return true;
232*3cab2bb3Spatrick   }
233*3cab2bb3Spatrick 
234*3cab2bb3Spatrick public:
235*3cab2bb3Spatrick   template <class WallClockFunc>
FDRController(BufferQueue * BQ,BufferQueue::Buffer & B,FDRLogWriter & W,WallClockFunc R,uint64_t C)236*3cab2bb3Spatrick   FDRController(BufferQueue *BQ, BufferQueue::Buffer &B, FDRLogWriter &W,
237*3cab2bb3Spatrick                 WallClockFunc R, uint64_t C) XRAY_NEVER_INSTRUMENT
238*3cab2bb3Spatrick       : BQ(BQ),
239*3cab2bb3Spatrick         B(B),
240*3cab2bb3Spatrick         W(W),
241*3cab2bb3Spatrick         WallClockReader(R),
242*3cab2bb3Spatrick         CycleThreshold(C) {}
243*3cab2bb3Spatrick 
functionEnter(int32_t FuncId,uint64_t TSC,uint16_t CPU)244*3cab2bb3Spatrick   bool functionEnter(int32_t FuncId, uint64_t TSC,
245*3cab2bb3Spatrick                      uint16_t CPU) XRAY_NEVER_INSTRUMENT {
246*3cab2bb3Spatrick     if (finalized() ||
247*3cab2bb3Spatrick         !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
248*3cab2bb3Spatrick       return returnBuffer();
249*3cab2bb3Spatrick 
250*3cab2bb3Spatrick     auto PreambleStatus = recordPreamble(TSC, CPU);
251*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::InvalidBuffer)
252*3cab2bb3Spatrick       return returnBuffer();
253*3cab2bb3Spatrick 
254*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::WroteMetadata) {
255*3cab2bb3Spatrick       UndoableFunctionEnters = 1;
256*3cab2bb3Spatrick       UndoableTailExits = 0;
257*3cab2bb3Spatrick     } else {
258*3cab2bb3Spatrick       ++UndoableFunctionEnters;
259*3cab2bb3Spatrick     }
260*3cab2bb3Spatrick 
261*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
262*3cab2bb3Spatrick     LastFunctionEntryTSC = TSC;
263*3cab2bb3Spatrick     LatestTSC = TSC;
264*3cab2bb3Spatrick     return W.writeFunction(FDRLogWriter::FunctionRecordKind::Enter,
265*3cab2bb3Spatrick                            mask(FuncId), Delta);
266*3cab2bb3Spatrick   }
267*3cab2bb3Spatrick 
functionTailExit(int32_t FuncId,uint64_t TSC,uint16_t CPU)268*3cab2bb3Spatrick   bool functionTailExit(int32_t FuncId, uint64_t TSC,
269*3cab2bb3Spatrick                         uint16_t CPU) XRAY_NEVER_INSTRUMENT {
270*3cab2bb3Spatrick     if (finalized())
271*3cab2bb3Spatrick       return returnBuffer();
272*3cab2bb3Spatrick 
273*3cab2bb3Spatrick     if (!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
274*3cab2bb3Spatrick       return returnBuffer();
275*3cab2bb3Spatrick 
276*3cab2bb3Spatrick     auto PreambleStatus = recordPreamble(TSC, CPU);
277*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::InvalidBuffer)
278*3cab2bb3Spatrick       return returnBuffer();
279*3cab2bb3Spatrick 
280*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::NoChange &&
281*3cab2bb3Spatrick         UndoableFunctionEnters != 0 &&
282*3cab2bb3Spatrick         TSC - LastFunctionEntryTSC < CycleThreshold)
283*3cab2bb3Spatrick       return rewindRecords(FuncId, TSC, CPU);
284*3cab2bb3Spatrick 
285*3cab2bb3Spatrick     UndoableTailExits = UndoableFunctionEnters ? UndoableTailExits + 1 : 0;
286*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
287*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
288*3cab2bb3Spatrick     LatestTSC = TSC;
289*3cab2bb3Spatrick     return W.writeFunction(FDRLogWriter::FunctionRecordKind::TailExit,
290*3cab2bb3Spatrick                            mask(FuncId), Delta);
291*3cab2bb3Spatrick   }
292*3cab2bb3Spatrick 
functionEnterArg(int32_t FuncId,uint64_t TSC,uint16_t CPU,uint64_t Arg)293*3cab2bb3Spatrick   bool functionEnterArg(int32_t FuncId, uint64_t TSC, uint16_t CPU,
294*3cab2bb3Spatrick                         uint64_t Arg) XRAY_NEVER_INSTRUMENT {
295*3cab2bb3Spatrick     if (finalized() ||
296*3cab2bb3Spatrick         !prepareBuffer((2 * sizeof(MetadataRecord)) + sizeof(FunctionRecord)) ||
297*3cab2bb3Spatrick         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
298*3cab2bb3Spatrick       return returnBuffer();
299*3cab2bb3Spatrick 
300*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
301*3cab2bb3Spatrick     LatestTSC = TSC;
302*3cab2bb3Spatrick     LastFunctionEntryTSC = 0;
303*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
304*3cab2bb3Spatrick     UndoableTailExits = 0;
305*3cab2bb3Spatrick 
306*3cab2bb3Spatrick     return W.writeFunctionWithArg(FDRLogWriter::FunctionRecordKind::EnterArg,
307*3cab2bb3Spatrick                                   mask(FuncId), Delta, Arg);
308*3cab2bb3Spatrick   }
309*3cab2bb3Spatrick 
functionExit(int32_t FuncId,uint64_t TSC,uint16_t CPU)310*3cab2bb3Spatrick   bool functionExit(int32_t FuncId, uint64_t TSC,
311*3cab2bb3Spatrick                     uint16_t CPU) XRAY_NEVER_INSTRUMENT {
312*3cab2bb3Spatrick     if (finalized() ||
313*3cab2bb3Spatrick         !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
314*3cab2bb3Spatrick       return returnBuffer();
315*3cab2bb3Spatrick 
316*3cab2bb3Spatrick     auto PreambleStatus = recordPreamble(TSC, CPU);
317*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::InvalidBuffer)
318*3cab2bb3Spatrick       return returnBuffer();
319*3cab2bb3Spatrick 
320*3cab2bb3Spatrick     if (PreambleStatus == PreambleResult::NoChange &&
321*3cab2bb3Spatrick         UndoableFunctionEnters != 0 &&
322*3cab2bb3Spatrick         TSC - LastFunctionEntryTSC < CycleThreshold)
323*3cab2bb3Spatrick       return rewindRecords(FuncId, TSC, CPU);
324*3cab2bb3Spatrick 
325*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
326*3cab2bb3Spatrick     LatestTSC = TSC;
327*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
328*3cab2bb3Spatrick     UndoableTailExits = 0;
329*3cab2bb3Spatrick     return W.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, mask(FuncId),
330*3cab2bb3Spatrick                            Delta);
331*3cab2bb3Spatrick   }
332*3cab2bb3Spatrick 
customEvent(uint64_t TSC,uint16_t CPU,const void * Event,int32_t EventSize)333*3cab2bb3Spatrick   bool customEvent(uint64_t TSC, uint16_t CPU, const void *Event,
334*3cab2bb3Spatrick                    int32_t EventSize) XRAY_NEVER_INSTRUMENT {
335*3cab2bb3Spatrick     if (finalized() ||
336*3cab2bb3Spatrick         !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
337*3cab2bb3Spatrick         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
338*3cab2bb3Spatrick       return returnBuffer();
339*3cab2bb3Spatrick 
340*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
341*3cab2bb3Spatrick     LatestTSC = TSC;
342*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
343*3cab2bb3Spatrick     UndoableTailExits = 0;
344*3cab2bb3Spatrick     return W.writeCustomEvent(Delta, Event, EventSize);
345*3cab2bb3Spatrick   }
346*3cab2bb3Spatrick 
typedEvent(uint64_t TSC,uint16_t CPU,uint16_t EventType,const void * Event,int32_t EventSize)347*3cab2bb3Spatrick   bool typedEvent(uint64_t TSC, uint16_t CPU, uint16_t EventType,
348*3cab2bb3Spatrick                   const void *Event, int32_t EventSize) XRAY_NEVER_INSTRUMENT {
349*3cab2bb3Spatrick     if (finalized() ||
350*3cab2bb3Spatrick         !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
351*3cab2bb3Spatrick         recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
352*3cab2bb3Spatrick       return returnBuffer();
353*3cab2bb3Spatrick 
354*3cab2bb3Spatrick     auto Delta = TSC - LatestTSC;
355*3cab2bb3Spatrick     LatestTSC = TSC;
356*3cab2bb3Spatrick     UndoableFunctionEnters = 0;
357*3cab2bb3Spatrick     UndoableTailExits = 0;
358*3cab2bb3Spatrick     return W.writeTypedEvent(Delta, EventType, Event, EventSize);
359*3cab2bb3Spatrick   }
360*3cab2bb3Spatrick 
flush()361*3cab2bb3Spatrick   bool flush() XRAY_NEVER_INSTRUMENT {
362*3cab2bb3Spatrick     if (finalized()) {
363*3cab2bb3Spatrick       returnBuffer(); // ignore result.
364*3cab2bb3Spatrick       return true;
365*3cab2bb3Spatrick     }
366*3cab2bb3Spatrick     return returnBuffer();
367*3cab2bb3Spatrick   }
368*3cab2bb3Spatrick };
369*3cab2bb3Spatrick 
370*3cab2bb3Spatrick } // namespace __xray
371*3cab2bb3Spatrick 
372*3cab2bb3Spatrick #endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
373