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