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