namespace __xray {
-template <size_t Version = 3> class FDRController {
+template <size_t Version = 5> class FDRController {
BufferQueue *BQ;
BufferQueue::Buffer &B;
FDRLogWriter &W;
}
enum class PreambleResult { NoChange, WroteMetadata, InvalidBuffer };
- PreambleResult functionPreamble(uint64_t TSC,
- uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ PreambleResult recordPreamble(uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
if (UNLIKELY(LatestCPU != CPU || LatestTSC == 0)) {
// We update our internal tracking state for the Latest TSC and CPU we've
// seen, then write out the appropriate metadata and function records.
!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
return returnBuffer();
- auto PreambleStatus = functionPreamble(TSC, CPU);
+ auto PreambleStatus = recordPreamble(TSC, CPU);
if (PreambleStatus == PreambleResult::InvalidBuffer)
return returnBuffer();
if (!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
return returnBuffer();
- auto PreambleStatus = functionPreamble(TSC, CPU);
+ auto PreambleStatus = recordPreamble(TSC, CPU);
if (PreambleStatus == PreambleResult::InvalidBuffer)
return returnBuffer();
uint64_t Arg) XRAY_NEVER_INSTRUMENT {
if (finalized() ||
!prepareBuffer((2 * sizeof(MetadataRecord)) + sizeof(FunctionRecord)) ||
- functionPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
return returnBuffer();
auto Delta = TSC - LatestTSC;
!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
return returnBuffer();
- auto PreambleStatus = functionPreamble(TSC, CPU);
+ auto PreambleStatus = recordPreamble(TSC, CPU);
if (PreambleStatus == PreambleResult::InvalidBuffer)
return returnBuffer();
int32_t EventSize) XRAY_NEVER_INSTRUMENT {
if (finalized() ||
!prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
- functionPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
return returnBuffer();
- LatestTSC = 0;
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
UndoableFunctionEnters = 0;
UndoableTailExits = 0;
- return W.writeCustomEvent(TSC, CPU, Event, EventSize);
+ return W.writeCustomEvent(Delta, Event, EventSize);
}
bool typedEvent(uint64_t TSC, uint16_t CPU, uint16_t EventType,
const void *Event, int32_t EventSize) XRAY_NEVER_INSTRUMENT {
if (finalized() ||
!prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
- functionPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
return returnBuffer();
- LatestTSC = 0;
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
UndoableFunctionEnters = 0;
UndoableTailExits = 0;
- return W.writeTypedEvent(TSC, EventType, Event, EventSize);
+ return W.writeTypedEvent(Delta, EventType, Event, EventSize);
}
bool flush() XRAY_NEVER_INSTRUMENT {
return true;
}
- bool writeCustomEvent(uint64_t TSC, uint16_t CPU, const void *Event,
- int32_t EventSize) {
+ bool writeCustomEvent(int32_t Delta, const void *Event, int32_t EventSize) {
// We write the metadata record and the custom event data into the buffer
// first, before we atomically update the extents for the buffer. This
// allows us to ensure that any threads reading the extents of the buffer
// (no partial writes accounted).
MetadataRecord R =
createMetadataRecord<MetadataRecord::RecordKinds::CustomEventMarker>(
- EventSize, TSC, CPU);
+ EventSize, Delta);
NextRecord = reinterpret_cast<char *>(internal_memcpy(
NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +
sizeof(R);
return true;
}
- bool writeTypedEvent(uint64_t TSC, uint16_t EventType, const void *Event,
+ bool writeTypedEvent(int32_t Delta, uint16_t EventType, const void *Event,
int32_t EventSize) {
// We do something similar when writing out typed events, see
// writeCustomEvent(...) above for details.
MetadataRecord R =
createMetadataRecord<MetadataRecord::RecordKinds::TypedEventMarker>(
- EventSize, TSC, EventType);
+ EventSize, Delta, EventType);
NextRecord = reinterpret_cast<char *>(internal_memcpy(
NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +
sizeof(R);
XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
namespace {
+
// Group together thread-local-data in a struct, then hide it behind a function
// call so that it can be initialized on first use instead of as a global. We
// force the alignment to 64-bytes for x86 cache line alignment, as this
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
static bool TSCSupported = true;
static uint64_t CycleFrequency = NanosecondsPerSecond;
- pthread_once(&OnceInit, +[] {
- XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
- // Version 2 of the log writes the extents of the buffer, instead of
- // relying on an end-of-buffer record.
- // Version 3 includes PID metadata record
- // Version 4 includes CPU data in the custom event records
- H.Version = 4;
- H.Type = FileTypes::FDR_LOG;
-
- // Test for required CPU features and cache the cycle frequency
- TSCSupported = probeRequiredCPUFeatures();
- if (TSCSupported)
- CycleFrequency = getTSCFrequency();
- H.CycleFrequency = CycleFrequency;
-
- // FIXME: Actually check whether we have 'constant_tsc' and
- // 'nonstop_tsc' before setting the values in the header.
- H.ConstantTSC = 1;
- H.NonstopTSC = 1;
- });
+ pthread_once(
+ &OnceInit, +[] {
+ XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
+ // Version 2 of the log writes the extents of the buffer, instead of
+ // relying on an end-of-buffer record.
+ // Version 3 includes PID metadata record.
+ // Version 4 includes CPU data in the custom event records.
+ // Version 5 uses relative deltas for custom and typed event records,
+ // and removes the CPU data in custom event records (similar to how
+ // function records use deltas instead of full TSCs and rely on other
+ // metadata records for TSC wraparound and CPU migration).
+ H.Version = 5;
+ H.Type = FileTypes::FDR_LOG;
+
+ // Test for required CPU features and cache the cycle frequency
+ TSCSupported = probeRequiredCPUFeatures();
+ if (TSCSupported)
+ CycleFrequency = getTSCFrequency();
+ H.CycleFrequency = CycleFrequency;
+
+ // FIXME: Actually check whether we have 'constant_tsc' and
+ // 'nonstop_tsc' before setting the values in the header.
+ H.ConstantTSC = 1;
+ H.NonstopTSC = 1;
+ });
return reinterpret_cast<XRayFileHeader &>(HStorage);
}
// buffers to expect).
static std::aligned_storage<sizeof(XRayFileHeader)>::type HeaderStorage;
static pthread_once_t HeaderOnce = PTHREAD_ONCE_INIT;
- pthread_once(&HeaderOnce, +[] {
- reinterpret_cast<XRayFileHeader &>(HeaderStorage) = fdrCommonHeaderInfo();
- });
+ pthread_once(
+ &HeaderOnce, +[] {
+ reinterpret_cast<XRayFileHeader &>(HeaderStorage) =
+ fdrCommonHeaderInfo();
+ });
// We use a convenience alias for code referring to Header from here on out.
auto &Header = reinterpret_cast<XRayFileHeader &>(HeaderStorage);
// Test once for required CPU features
static pthread_once_t OnceProbe = PTHREAD_ONCE_INIT;
static bool TSCSupported = true;
- pthread_once(&OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
+ pthread_once(
+ &OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
if (TSCSupported) {
Result.TSC = __xray::readTSC(Result.CPU);
if (EventSize >
static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] {
- Report("Custom event size too large; truncating to %d.\n",
- std::numeric_limits<int32_t>::max());
- });
+ pthread_once(
+ &Once, +[] {
+ Report("Custom event size too large; truncating to %d.\n",
+ std::numeric_limits<int32_t>::max());
+ });
}
auto &TLD = getThreadLocalData();
if (EventSize >
static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] {
- Report("Typed event size too large; truncating to %d.\n",
- std::numeric_limits<int32_t>::max());
- });
+ pthread_once(
+ &Once, +[] {
+ Report("Typed event size too large; truncating to %d.\n",
+ std::numeric_limits<int32_t>::max());
+ });
}
auto &TLD = getThreadLocalData();
}
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
- pthread_once(&OnceInit, +[] {
- atomic_store(&TicksPerSec,
- probeRequiredCPUFeatures() ? getTSCFrequency()
- : __xray::NanosecondsPerSecond,
- memory_order_release);
- pthread_key_create(&Key, +[](void *TLDPtr) {
- if (TLDPtr == nullptr)
- return;
- auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
- if (TLD.BQ == nullptr)
- return;
- if (TLD.Buffer.Data == nullptr)
- return;
- auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
- if (EC != BufferQueue::ErrorCode::Ok)
- Report("At thread exit, failed to release buffer at %p; error=%s\n",
- TLD.Buffer.Data, BufferQueue::getErrorString(EC));
- });
- });
+ pthread_once(
+ &OnceInit, +[] {
+ atomic_store(&TicksPerSec,
+ probeRequiredCPUFeatures() ? getTSCFrequency()
+ : __xray::NanosecondsPerSecond,
+ memory_order_release);
+ pthread_key_create(
+ &Key, +[](void *TLDPtr) {
+ if (TLDPtr == nullptr)
+ return;
+ auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
+ if (TLD.BQ == nullptr)
+ return;
+ if (TLD.Buffer.Data == nullptr)
+ return;
+ auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
+ if (EC != BufferQueue::ErrorCode::Ok)
+ Report("At thread exit, failed to release buffer at %p; "
+ "error=%s\n",
+ TLD.Buffer.Data, BufferQueue::getErrorString(EC));
+ });
+ });
atomic_store(&ThresholdTicks,
atomic_load_relaxed(&TicksPerSec) *
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
/// The flush() function will clear out the current state of the visitor, to
/// allow for explicitly flushing a block's records to the currently
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
void reset() { CurrentState = State::Start; }
};
NewCPUId,
TSCWrap,
CustomEvent,
+ TypedEvent,
Function,
CallArg,
EndOfBuffer,
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
Error verify();
void reset();
PIDEntry,
NewBuffer,
EndOfBuffer,
+ TypedEvent,
};
Type type() const override { return Type::Metadata; }
Error apply(RecordVisitor &V) override;
};
+class CustomEventRecordV5 : public MetadataRecord {
+ int32_t Size = 0;
+ int32_t Delta = 0;
+ std::string Data{};
+ friend class RecordInitializer;
+
+public:
+ CustomEventRecordV5() = default;
+ explicit CustomEventRecordV5(int32_t S, int32_t D, std::string P)
+ : MetadataRecord(), Size(S), Delta(D), Data(std::move(P)) {}
+
+ MetadataType metadataType() const override {
+ return MetadataType::CustomEvent;
+ }
+
+ int32_t size() const { return Size; }
+ int32_t delta() const { return Delta; }
+ StringRef data() const { return Data; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class TypedEventRecord : public MetadataRecord {
+ int32_t Size = 0;
+ int32_t Delta = 0;
+ uint16_t EventType = 0;
+ std::string Data{};
+ friend class RecordInitializer;
+
+public:
+ TypedEventRecord() = default;
+ explicit TypedEventRecord(int32_t S, int32_t D, uint16_t E, std::string P)
+ : MetadataRecord(), Size(S), Delta(D), Data(std::move(P)) {}
+
+ MetadataType metadataType() const override {
+ return MetadataType::TypedEvent;
+ }
+
+ int32_t size() const { return Size; }
+ int32_t delta() const { return Delta; }
+ uint16_t eventType() const { return EventType; }
+ StringRef data() const { return Data; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
class CallArgRecord : public MetadataRecord {
uint64_t Arg;
friend class RecordInitializer;
virtual Error visit(NewBufferRecord &) = 0;
virtual Error visit(EndBufferRecord &) = 0;
virtual Error visit(FunctionRecord &) = 0;
+ virtual Error visit(CustomEventRecordV5 &) = 0;
+ virtual Error visit(TypedEventRecord &) = 0;
};
class RecordInitializer : public RecordVisitor {
uint16_t Version;
public:
- static constexpr uint16_t DefaultVersion = 4u;
+ static constexpr uint16_t DefaultVersion = 5u;
explicit RecordInitializer(DataExtractor &DE, uint32_t &OP, uint16_t V)
: RecordVisitor(), E(DE), OffsetPtr(OP), Version(V) {}
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
};
} // namespace xray
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
// Must be called after all the records have been processed, to handle the
// most recent record generated.
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
private:
support::endian::Writer OS;
Error visit(NewBufferRecord &) override;
Error visit(EndBufferRecord &) override;
Error visit(FunctionRecord &) override;
+ Error visit(CustomEventRecordV5 &) override;
+ Error visit(TypedEventRecord &) override;
};
} // namespace xray
return Error::success();
}
+Error BlockIndexer::visit(CustomEventRecordV5 &R) {
+ CurrentBlock.Records.push_back(&R);
+ return Error::success();
+}
+
+Error BlockIndexer::visit(TypedEventRecord &R) {
+ CurrentBlock.Records.push_back(&R);
+ return Error::success();
+}
+
Error BlockIndexer::visit(CallArgRecord &R) {
CurrentBlock.Records.push_back(&R);
return Error::success();
return E;
}
+Error BlockPrinter::visit(CustomEventRecordV5 &R) {
+ if (CurrentState == State::Metadata)
+ OS << "\n";
+ CurrentState = State::CustomEvent;
+ OS << "* ";
+ auto E = RP.visit(R);
+ return E;
+}
+
+Error BlockPrinter::visit(TypedEventRecord &R) {
+ if (CurrentState == State::Metadata)
+ OS << "\n";
+ CurrentState = State::CustomEvent;
+ OS << "* ";
+ auto E = RP.visit(R);
+ return E;
+}
+
// Function call printing.
Error BlockPrinter::visit(FunctionRecord &R) {
if (CurrentState == State::Metadata)
return "CallArg";
case BlockVerifier::State::EndOfBuffer:
return "EndOfBuffer";
+ case BlockVerifier::State::TypedEvent:
+ return "TypedEvent";
case BlockVerifier::State::StateMax:
case BlockVerifier::State::Unknown:
return "Unknown";
{State::NewCPUId,
{mask(State::NewCPUId) | mask(State::TSCWrap) |
mask(State::CustomEvent) | mask(State::Function) |
- mask(State::EndOfBuffer)}},
+ mask(State::EndOfBuffer) | mask(State::TypedEvent)}},
{State::TSCWrap,
{mask(State::TSCWrap) | mask(State::NewCPUId) |
mask(State::CustomEvent) | mask(State::Function) |
- mask(State::EndOfBuffer)}},
+ mask(State::EndOfBuffer) | mask(State::TypedEvent)}},
{State::CustomEvent,
{mask(State::CustomEvent) | mask(State::TSCWrap) |
mask(State::NewCPUId) | mask(State::Function) |
- mask(State::EndOfBuffer)}},
+ mask(State::EndOfBuffer) | mask(State::TypedEvent)}},
+
+ {State::TypedEvent,
+ {mask(State::TypedEvent) | mask(State::TSCWrap) |
+ mask(State::NewCPUId) | mask(State::Function) |
+ mask(State::EndOfBuffer) | mask(State::CustomEvent)}},
{State::Function,
{mask(State::Function) | mask(State::TSCWrap) |
mask(State::NewCPUId) | mask(State::CustomEvent) |
- mask(State::CallArg) | mask(State::EndOfBuffer)}},
+ mask(State::CallArg) | mask(State::EndOfBuffer) |
+ mask(State::TypedEvent)}},
{State::CallArg,
{mask(State::CallArg) | mask(State::Function) |
mask(State::TSCWrap) | mask(State::NewCPUId) |
- mask(State::CustomEvent) | mask(State::EndOfBuffer)}},
+ mask(State::CustomEvent) | mask(State::EndOfBuffer) |
+ mask(State::TypedEvent)}},
{State::EndOfBuffer, {}}}};
return transition(State::CustomEvent);
}
+Error BlockVerifier::visit(CustomEventRecordV5 &) {
+ return transition(State::CustomEvent);
+}
+
+Error BlockVerifier::visit(TypedEventRecord &) {
+ return transition(State::TypedEvent);
+}
+
Error BlockVerifier::visit(CallArgRecord &) {
return transition(State::CallArg);
}
case State::EndOfBuffer:
case State::NewCPUId:
case State::CustomEvent:
+ case State::TypedEvent:
case State::Function:
case State::CallArg:
case State::TSCWrap:
case MetadataRecordKinds::WalltimeMarkerKind:
return make_unique<WallclockRecord>();
case MetadataRecordKinds::CustomEventMarkerKind:
+ if (Header.Version >= 5)
+ return make_unique<CustomEventRecordV5>();
return make_unique<CustomEventRecord>();
case MetadataRecordKinds::CallArgumentKind:
return make_unique<CallArgRecord>();
case MetadataRecordKinds::BufferExtentsKind:
return make_unique<BufferExtents>();
case MetadataRecordKinds::TypedEventMarkerKind:
- return createStringError(std::make_error_code(std::errc::invalid_argument),
- "Encountered an unsupported TypedEventMarker.");
+ return make_unique<TypedEventRecord>();
case MetadataRecordKinds::PidKind:
return make_unique<PIDRecord>();
case MetadataRecordKinds::EnumEndMarker:
Error NewBufferRecord::apply(RecordVisitor &V) { return V.visit(*this); }
Error EndBufferRecord::apply(RecordVisitor &V) { return V.visit(*this); }
Error FunctionRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error CustomEventRecordV5::apply(RecordVisitor &V) { return V.visit(*this); }
+Error TypedEventRecord::apply(RecordVisitor &V) { return V.visit(*this); }
} // namespace xray
} // namespace llvm
return Error::success();
}
+Error TraceExpander::visit(CustomEventRecordV5 &R) {
+ resetCurrentRecord();
+ if (!IgnoringRecords) {
+ BaseTSC += R.delta();
+ CurrentRecord.TSC = BaseTSC;
+ CurrentRecord.CPU = CPUId;
+ CurrentRecord.PId = PID;
+ CurrentRecord.TId = TID;
+ CurrentRecord.Type = RecordTypes::CUSTOM_EVENT;
+ std::copy(R.data().begin(), R.data().end(),
+ std::back_inserter(CurrentRecord.Data));
+ BuildingRecord = true;
+ }
+ return Error::success();
+}
+
+Error TraceExpander::visit(TypedEventRecord &R) {
+ resetCurrentRecord();
+ if (!IgnoringRecords) {
+ BaseTSC += R.delta();
+ CurrentRecord.TSC = BaseTSC;
+ CurrentRecord.CPU = CPUId;
+ CurrentRecord.PId = PID;
+ CurrentRecord.TId = TID;
+ CurrentRecord.RecordType = R.eventType();
+ CurrentRecord.Type = RecordTypes::TYPED_EVENT;
+ std::copy(R.data().begin(), R.data().end(),
+ std::back_inserter(CurrentRecord.Data));
+ BuildingRecord = true;
+ }
+ return Error::success();
+}
+
Error TraceExpander::visit(CallArgRecord &R) {
CurrentRecord.CallArgs.push_back(R.arg());
CurrentRecord.Type = RecordTypes::ENTER_ARG;
return Error::success();
}
+Error FDRTraceWriter::visit(CustomEventRecordV5 &R) {
+ if (auto E = writeMetadata<5u>(OS, R.size(), R.delta()))
+ return E;
+ auto D = R.data();
+ ArrayRef<char> Bytes(D.data(), D.size());
+ OS.write(Bytes);
+ return Error::success();
+}
+
+Error FDRTraceWriter::visit(TypedEventRecord &R) {
+ if (auto E = writeMetadata<7u>(OS, R.size(), R.delta(), R.eventType()))
+ return E;
+ auto D = R.data();
+ ArrayRef<char> Bytes(D.data(), D.size());
+ OS.write(Bytes);
+ return Error::success();
+}
+
Error FDRTraceWriter::visit(CallArgRecord &R) {
return writeMetadata<6u>(OS, R.arg());
}
// Manually advance the offset pointer 16 bytes, after getting a raw memcpy
// from the underlying data.
OffsetPtr += 16;
- if (FileHeader.Version < 1 || FileHeader.Version > 4)
- return createStringError(std::make_error_code(std::errc::invalid_argument),
- "Unsupported XRay file version: %d at offset %d",
- FileHeader.Version, OffsetPtr);
return std::move(FileHeader);
}
return Error::success();
}
+Error RecordInitializer::visit(CustomEventRecordV5 &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a custom event record (%d).",
+ OffsetPtr);
+
+ auto BeginOffset = OffsetPtr;
+ auto PreReadOffset = OffsetPtr;
+
+ R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Cannot read a custom event record size field offset %d.", OffsetPtr);
+
+ PreReadOffset = OffsetPtr;
+ R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Cannot read a custom event record TSC delta field at offset %d.",
+ OffsetPtr);
+
+ assert(OffsetPtr > BeginOffset &&
+ OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
+
+ // Next we read in a fixed chunk of data from the given offset.
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size))
+ return createStringError(
+ std::make_error_code(std::errc::bad_address),
+ "Cannot read %d bytes of custom event data from offset %d.", R.Size,
+ OffsetPtr);
+
+ std::vector<uint8_t> Buffer;
+ Buffer.resize(R.Size);
+ if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Failed reading data into buffer of size %d at offset %d.", R.Size,
+ OffsetPtr);
+ R.Data.assign(Buffer.begin(), Buffer.end());
+ return Error::success();
+}
+
+Error RecordInitializer::visit(TypedEventRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a typed event record (%d).",
+ OffsetPtr);
+
+ auto BeginOffset = OffsetPtr;
+ auto PreReadOffset = OffsetPtr;
+
+ R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Cannot read a typed event record size field offset %d.", OffsetPtr);
+
+ PreReadOffset = OffsetPtr;
+ R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Cannot read a typed event record TSC delta field at offset %d.",
+ OffsetPtr);
+
+ PreReadOffset = OffsetPtr;
+ R.EventType = E.getU16(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Cannot read a typed event record type field at offset %d.", OffsetPtr);
+
+ assert(OffsetPtr > BeginOffset &&
+ OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
+
+ // Next we read in a fixed chunk of data from the given offset.
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size))
+ return createStringError(
+ std::make_error_code(std::errc::bad_address),
+ "Cannot read %d bytes of custom event data from offset %d.", R.Size,
+ OffsetPtr);
+
+ std::vector<uint8_t> Buffer;
+ Buffer.resize(R.Size);
+ if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "Failed reading data into buffer of size %d at offset %d.", R.Size,
+ OffsetPtr);
+ R.Data.assign(Buffer.begin(), Buffer.end());
+ return Error::success();
+}
+
Error RecordInitializer::visit(CallArgRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return Error::success();
}
+Error RecordPrinter::visit(CustomEventRecordV5 &R) {
+ OS << formatv("<Custom Event: delta = +{0}, size = {1}, data = '{2}'>",
+ R.delta(), R.size(), R.data())
+ << Delim;
+ return Error::success();
+}
+
+Error RecordPrinter::visit(TypedEventRecord &R) {
+ OS << formatv(
+ "<Typed Event: delta = +{0}, type = {1}, size = {2}, data = '{3}'",
+ R.delta(), R.eventType(), R.size(), R.data())
+ << Delim;
+ return Error::success();
+}
+
Error RecordPrinter::visit(CallArgRecord &R) {
OS << formatv("<Call Argument: data = {0} (hex = {0:x})>", R.arg()) << Delim;
return Error::success();
/// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId
/// FunctionSequence
/// EOB: *deprecated*
+///
+/// In Version 4, we make the following changes:
+///
+/// CustomEventRecord now includes the CPU data.
+///
+/// In Version 5, we make the following changes:
+///
+/// CustomEventRecord and TypedEventRecord now use TSC delta encoding similar to
+/// what FunctionRecord instances use, and we no longer need to include the CPU
+/// id in the CustomEventRecord.
+///
Error loadFDRLog(StringRef Data, bool IsLittleEndian,
XRayFileHeader &FileHeader, std::vector<XRayRecord> &Records) {
}
break;
case FLIGHT_DATA_RECORDER_FORMAT:
- if (Version >= 1 && Version <= 4) {
+ if (Version >= 1 && Version <= 5) {
if (auto E = loadFDRLog(DE.getData(), DE.isLittleEndian(), T.FileHeader,
T.Records))
return std::move(E);
.add<PIDRecord>(1)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
auto Block1 = LogBuilder()
.add<BufferExtents>(100)
.add<PIDRecord>(1)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
auto Block2 = LogBuilder()
.add<BufferExtents>(100)
.add<PIDRecord>(1)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
BlockIndexer::Index Index;
BlockIndexer Indexer(Index);
.add<NewCPUIDRecord>(1, 2)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
auto Block1 = LogBuilder()
.add<BufferExtents>(64)
.add<NewCPUIDRecord>(1, 2)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
auto Block2 = LogBuilder()
.add<BufferExtents>(64)
.add<NewCPUIDRecord>(1, 2)
.add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
.add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<CustomEventRecordV5>(1, 4, "XRAY")
+ .add<TypedEventRecord>(1, 4, 2, "XRAY")
.consume();
// First, index the records in different blocks.