Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_fdr_controller.h
35265 views
//===-- xray_fdr_controller.h ---------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a function call tracing system.9//10//===----------------------------------------------------------------------===//11#ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_12#define COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_1314#include <limits>15#include <time.h>1617#include "xray/xray_interface.h"18#include "xray/xray_records.h"19#include "xray_buffer_queue.h"20#include "xray_fdr_log_writer.h"2122namespace __xray {2324template <size_t Version = 5> class FDRController {25BufferQueue *BQ;26BufferQueue::Buffer &B;27FDRLogWriter &W;28int (*WallClockReader)(clockid_t, struct timespec *) = 0;29uint64_t CycleThreshold = 0;3031uint64_t LastFunctionEntryTSC = 0;32uint64_t LatestTSC = 0;33uint16_t LatestCPU = 0;34tid_t TId = 0;35pid_t PId = 0;36bool First = true;3738uint32_t UndoableFunctionEnters = 0;39uint32_t UndoableTailExits = 0;4041bool finalized() const XRAY_NEVER_INSTRUMENT {42return BQ == nullptr || BQ->finalizing();43}4445bool hasSpace(size_t S) XRAY_NEVER_INSTRUMENT {46return B.Data != nullptr && B.Generation == BQ->generation() &&47W.getNextRecord() + S <= reinterpret_cast<char *>(B.Data) + B.Size;48}4950constexpr int32_t mask(int32_t FuncId) const XRAY_NEVER_INSTRUMENT {51return FuncId & ((1 << 29) - 1);52}5354bool getNewBuffer() XRAY_NEVER_INSTRUMENT {55if (BQ->getBuffer(B) != BufferQueue::ErrorCode::Ok)56return false;5758W.resetRecord();59DCHECK_EQ(W.getNextRecord(), B.Data);60LatestTSC = 0;61LatestCPU = 0;62First = true;63UndoableFunctionEnters = 0;64UndoableTailExits = 0;65atomic_store(B.Extents, 0, memory_order_release);66return true;67}6869bool setupNewBuffer() XRAY_NEVER_INSTRUMENT {70if (finalized())71return false;7273DCHECK(hasSpace(sizeof(MetadataRecord) * 3));74TId = GetTid();75PId = internal_getpid();76struct timespec TS {770, 078};79WallClockReader(CLOCK_MONOTONIC, &TS);8081MetadataRecord Metadata[] = {82// Write out a MetadataRecord to signify that this is the start of a new83// buffer, associated with a particular thread, with a new CPU. For the84// data, we have 15 bytes to squeeze as much information as we can. At85// this point we only write down the following bytes:86// - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 887// bytes)88createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(89static_cast<int32_t>(TId)),9091// Also write the WalltimeMarker record. We only really need microsecond92// precision here, and enforce across platforms that we need 64-bit93// seconds and 32-bit microseconds encoded in the Metadata record.94createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(95static_cast<int64_t>(TS.tv_sec),96static_cast<int32_t>(TS.tv_nsec / 1000)),9798// Also write the Pid record.99createMetadataRecord<MetadataRecord::RecordKinds::Pid>(100static_cast<int32_t>(PId)),101};102103if (finalized())104return false;105return W.writeMetadataRecords(Metadata);106}107108bool prepareBuffer(size_t S) XRAY_NEVER_INSTRUMENT {109if (finalized())110return returnBuffer();111112if (UNLIKELY(!hasSpace(S))) {113if (!returnBuffer())114return false;115if (!getNewBuffer())116return false;117if (!setupNewBuffer())118return false;119}120121if (First) {122First = false;123W.resetRecord();124atomic_store(B.Extents, 0, memory_order_release);125return setupNewBuffer();126}127128return true;129}130131bool returnBuffer() XRAY_NEVER_INSTRUMENT {132if (BQ == nullptr)133return false;134135First = true;136if (finalized()) {137BQ->releaseBuffer(B); // ignore result.138return false;139}140141return BQ->releaseBuffer(B) == BufferQueue::ErrorCode::Ok;142}143144enum class PreambleResult { NoChange, WroteMetadata, InvalidBuffer };145PreambleResult recordPreamble(uint64_t TSC,146uint16_t CPU) XRAY_NEVER_INSTRUMENT {147if (UNLIKELY(LatestCPU != CPU || LatestTSC == 0)) {148// We update our internal tracking state for the Latest TSC and CPU we've149// seen, then write out the appropriate metadata and function records.150LatestTSC = TSC;151LatestCPU = CPU;152153if (B.Generation != BQ->generation())154return PreambleResult::InvalidBuffer;155156W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);157return PreambleResult::WroteMetadata;158}159160DCHECK_EQ(LatestCPU, CPU);161162if (UNLIKELY(LatestTSC > TSC ||163TSC - LatestTSC >164uint64_t{std::numeric_limits<int32_t>::max()})) {165// Either the TSC has wrapped around from the last TSC we've seen or the166// delta is too large to fit in a 32-bit signed integer, so we write a167// wrap-around record.168LatestTSC = TSC;169170if (B.Generation != BQ->generation())171return PreambleResult::InvalidBuffer;172173W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);174return PreambleResult::WroteMetadata;175}176177return PreambleResult::NoChange;178}179180bool rewindRecords(int32_t FuncId, uint64_t TSC,181uint16_t CPU) XRAY_NEVER_INSTRUMENT {182// Undo one enter record, because at this point we are either at the state183// of:184// - We are exiting a function that we recently entered.185// - We are exiting a function that was the result of a sequence of tail186// exits, and we can check whether the tail exits can be re-wound.187//188FunctionRecord F;189W.undoWrites(sizeof(FunctionRecord));190if (B.Generation != BQ->generation())191return false;192internal_memcpy(&F, W.getNextRecord(), sizeof(FunctionRecord));193194DCHECK(F.RecordKind ==195uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&196"Expected to find function entry recording when rewinding.");197DCHECK_EQ(F.FuncId, FuncId & ~(0x0F << 28));198199LatestTSC -= F.TSCDelta;200if (--UndoableFunctionEnters != 0) {201LastFunctionEntryTSC -= F.TSCDelta;202return true;203}204205LastFunctionEntryTSC = 0;206auto RewindingTSC = LatestTSC;207auto RewindingRecordPtr = W.getNextRecord() - sizeof(FunctionRecord);208while (UndoableTailExits) {209if (B.Generation != BQ->generation())210return false;211internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));212DCHECK_EQ(F.RecordKind,213uint8_t(FunctionRecord::RecordKinds::FunctionTailExit));214RewindingTSC -= F.TSCDelta;215RewindingRecordPtr -= sizeof(FunctionRecord);216if (B.Generation != BQ->generation())217return false;218internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));219220// This tail call exceeded the threshold duration. It will not be erased.221if ((TSC - RewindingTSC) >= CycleThreshold) {222UndoableTailExits = 0;223return true;224}225226--UndoableTailExits;227W.undoWrites(sizeof(FunctionRecord) * 2);228LatestTSC = RewindingTSC;229}230return true;231}232233public:234template <class WallClockFunc>235FDRController(BufferQueue *BQ, BufferQueue::Buffer &B, FDRLogWriter &W,236WallClockFunc R, uint64_t C) XRAY_NEVER_INSTRUMENT237: BQ(BQ),238B(B),239W(W),240WallClockReader(R),241CycleThreshold(C) {}242243bool functionEnter(int32_t FuncId, uint64_t TSC,244uint16_t CPU) XRAY_NEVER_INSTRUMENT {245if (finalized() ||246!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))247return returnBuffer();248249auto PreambleStatus = recordPreamble(TSC, CPU);250if (PreambleStatus == PreambleResult::InvalidBuffer)251return returnBuffer();252253if (PreambleStatus == PreambleResult::WroteMetadata) {254UndoableFunctionEnters = 1;255UndoableTailExits = 0;256} else {257++UndoableFunctionEnters;258}259260auto Delta = TSC - LatestTSC;261LastFunctionEntryTSC = TSC;262LatestTSC = TSC;263return W.writeFunction(FDRLogWriter::FunctionRecordKind::Enter,264mask(FuncId), Delta);265}266267bool functionTailExit(int32_t FuncId, uint64_t TSC,268uint16_t CPU) XRAY_NEVER_INSTRUMENT {269if (finalized())270return returnBuffer();271272if (!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))273return returnBuffer();274275auto PreambleStatus = recordPreamble(TSC, CPU);276if (PreambleStatus == PreambleResult::InvalidBuffer)277return returnBuffer();278279if (PreambleStatus == PreambleResult::NoChange &&280UndoableFunctionEnters != 0 &&281TSC - LastFunctionEntryTSC < CycleThreshold)282return rewindRecords(FuncId, TSC, CPU);283284UndoableTailExits = UndoableFunctionEnters ? UndoableTailExits + 1 : 0;285UndoableFunctionEnters = 0;286auto Delta = TSC - LatestTSC;287LatestTSC = TSC;288return W.writeFunction(FDRLogWriter::FunctionRecordKind::TailExit,289mask(FuncId), Delta);290}291292bool functionEnterArg(int32_t FuncId, uint64_t TSC, uint16_t CPU,293uint64_t Arg) XRAY_NEVER_INSTRUMENT {294if (finalized() ||295!prepareBuffer((2 * sizeof(MetadataRecord)) + sizeof(FunctionRecord)) ||296recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)297return returnBuffer();298299auto Delta = TSC - LatestTSC;300LatestTSC = TSC;301LastFunctionEntryTSC = 0;302UndoableFunctionEnters = 0;303UndoableTailExits = 0;304305return W.writeFunctionWithArg(FDRLogWriter::FunctionRecordKind::EnterArg,306mask(FuncId), Delta, Arg);307}308309bool functionExit(int32_t FuncId, uint64_t TSC,310uint16_t CPU) XRAY_NEVER_INSTRUMENT {311if (finalized() ||312!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))313return returnBuffer();314315auto PreambleStatus = recordPreamble(TSC, CPU);316if (PreambleStatus == PreambleResult::InvalidBuffer)317return returnBuffer();318319if (PreambleStatus == PreambleResult::NoChange &&320UndoableFunctionEnters != 0 &&321TSC - LastFunctionEntryTSC < CycleThreshold)322return rewindRecords(FuncId, TSC, CPU);323324auto Delta = TSC - LatestTSC;325LatestTSC = TSC;326UndoableFunctionEnters = 0;327UndoableTailExits = 0;328return W.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, mask(FuncId),329Delta);330}331332bool customEvent(uint64_t TSC, uint16_t CPU, const void *Event,333int32_t EventSize) XRAY_NEVER_INSTRUMENT {334if (finalized() ||335!prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||336recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)337return returnBuffer();338339auto Delta = TSC - LatestTSC;340LatestTSC = TSC;341UndoableFunctionEnters = 0;342UndoableTailExits = 0;343return W.writeCustomEvent(Delta, Event, EventSize);344}345346bool typedEvent(uint64_t TSC, uint16_t CPU, uint16_t EventType,347const void *Event, int32_t EventSize) XRAY_NEVER_INSTRUMENT {348if (finalized() ||349!prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||350recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)351return returnBuffer();352353auto Delta = TSC - LatestTSC;354LatestTSC = TSC;355UndoableFunctionEnters = 0;356UndoableTailExits = 0;357return W.writeTypedEvent(Delta, EventType, Event, EventSize);358}359360bool flush() XRAY_NEVER_INSTRUMENT {361if (finalized()) {362returnBuffer(); // ignore result.363return true;364}365return returnBuffer();366}367};368369} // namespace __xray370371#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_372373374