--- /dev/null
+//===- FDRLogBuilder.h - XRay FDR Log Building Utility --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_INCLUDE_LLVM_XRAY_FDRLOGBUILDER_H_
+#define LLVM_INCLUDE_LLVM_XRAY_FDRLOGBUILDER_H_
+
+#include "llvm/XRay/FDRRecords.h"
+
+namespace llvm {
+namespace xray {
+
+/// The LogBuilder class allows for creating ad-hoc collections of records
+/// through the `add<...>(...)` function. An example use of this API is in
+/// crafting arbitrary sequences of records:
+///
+/// auto Records = LogBuilder()
+/// .add<BufferExtents>(256)
+/// .add<NewBufferRecord>(1)
+/// .consume();
+///
+class LogBuilder {
+ std::vector<std::unique_ptr<Record>> Records;
+
+public:
+ template <class R, class... T> LogBuilder &add(T &&... A) {
+ Records.emplace_back(new R(std::forward<T>(A)...));
+ return *this;
+ }
+
+ std::vector<std::unique_ptr<Record>> consume() { return std::move(Records); }
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_INCLUDE_LLVM_XRAY_FDRLOGBUILDER_H_
--- /dev/null
+//===- FDRRecords.h - XRay Flight Data Recorder Mode Records --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Define types and operations on these types that represent the different kinds
+// of records we encounter in XRay flight data recorder mode traces.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIB_XRAY_FDRRECORDS_H_
+#define LLVM_LIB_XRAY_FDRRECORDS_H_
+
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/Error.h"
+#include "llvm/XRay/XRayRecord.h"
+#include <cstdint>
+
+namespace llvm {
+namespace xray {
+
+class RecordVisitor;
+class RecordInitializer;
+
+class Record {
+protected:
+ enum class Type {
+ Unknown,
+ Function,
+ Metadata,
+ };
+
+public:
+ Record(const Record &) = delete;
+ Record(Record &&) = delete;
+ Record &operator=(const Record &) = delete;
+ Record &operator=(Record &&) = delete;
+ Record() = default;
+
+ virtual Type type() const = 0;
+
+ // Each Record should be able to apply an abstract visitor, and choose the
+ // appropriate function in the visitor to invoke, given its own type.
+ virtual Error apply(RecordVisitor &V) = 0;
+
+ virtual ~Record() = default;
+};
+
+class MetadataRecord : public Record {
+protected:
+ static constexpr int kMetadataBodySize = 15;
+ friend class RecordInitializer;
+
+public:
+ enum class MetadataType : unsigned {
+ Unknown,
+ BufferExtents,
+ WallClockTime,
+ NewCPUId,
+ TSCWrap,
+ CustomEvent,
+ CallArg,
+ PIDEntry,
+ NewBuffer,
+ EndOfBuffer,
+ };
+
+ Type type() const override { return Type::Metadata; }
+
+ // All metadata records must know to provide the type of their open
+ // metadata record.
+ virtual MetadataType metadataType() const = 0;
+
+ virtual ~MetadataRecord() = default;
+};
+
+// What follows are specific Metadata record types which encapsulate the
+// information associated with specific metadata record types in an FDR mode
+// log.
+class BufferExtents : public MetadataRecord {
+ uint64_t Size = 0;
+ friend class RecordInitializer;
+
+public:
+ BufferExtents() = default;
+ explicit BufferExtents(uint64_t S) : MetadataRecord(), Size(S) {}
+
+ MetadataType metadataType() const override {
+ return MetadataType::BufferExtents;
+ }
+
+ uint64_t size() const { return Size; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class WallclockRecord : public MetadataRecord {
+ uint64_t Seconds = 0;
+ uint32_t Nanos = 0;
+ friend class RecordInitializer;
+
+public:
+ WallclockRecord() = default;
+ explicit WallclockRecord(uint64_t S, uint32_t N)
+ : MetadataRecord(), Seconds(S), Nanos(N) {}
+
+ MetadataType metadataType() const override {
+ return MetadataType::WallClockTime;
+ }
+
+ uint64_t seconds() const { return Seconds; }
+ uint32_t nanos() const { return Nanos; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class NewCPUIDRecord : public MetadataRecord {
+ uint16_t CPUId = 0;
+ friend class RecordInitializer;
+
+public:
+ NewCPUIDRecord() = default;
+ explicit NewCPUIDRecord(uint16_t C) : MetadataRecord(), CPUId(C) {}
+
+ MetadataType metadataType() const override { return MetadataType::NewCPUId; }
+
+ uint16_t cpuid() const { return CPUId; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class TSCWrapRecord : public MetadataRecord {
+ uint64_t BaseTSC = 0;
+ friend class RecordInitializer;
+
+public:
+ TSCWrapRecord() = default;
+ explicit TSCWrapRecord(uint64_t B) : MetadataRecord(), BaseTSC(B) {}
+
+ MetadataType metadataType() const override { return MetadataType::TSCWrap; }
+
+ uint64_t tsc() const { return BaseTSC; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class CustomEventRecord : public MetadataRecord {
+ int32_t Size = 0;
+ uint64_t TSC = 0;
+ std::string Data{};
+ friend class RecordInitializer;
+
+public:
+ CustomEventRecord() = default;
+ explicit CustomEventRecord(uint64_t S, uint64_t T, std::string D)
+ : MetadataRecord(), Size(S), TSC(T), Data(std::move(D)) {}
+
+ MetadataType metadataType() const override {
+ return MetadataType::CustomEvent;
+ }
+
+ int32_t size() const { return Size; }
+ uint64_t tsc() const { return TSC; }
+ StringRef data() const { return Data; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class CallArgRecord : public MetadataRecord {
+ uint64_t Arg;
+ friend class RecordInitializer;
+
+public:
+ CallArgRecord() = default;
+ explicit CallArgRecord(uint64_t A) : MetadataRecord(), Arg(A) {}
+
+ MetadataType metadataType() const override { return MetadataType::CallArg; }
+
+ uint64_t arg() const { return Arg; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class PIDRecord : public MetadataRecord {
+ uint64_t PID = 0;
+ friend class RecordInitializer;
+
+public:
+ PIDRecord() = default;
+ explicit PIDRecord(uint64_t P) : MetadataRecord(), PID(P) {}
+
+ MetadataType metadataType() const override { return MetadataType::PIDEntry; }
+
+ uint64_t pid() const { return PID; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class NewBufferRecord : public MetadataRecord {
+ int32_t TID = 0;
+ friend class RecordInitializer;
+
+public:
+ NewBufferRecord() = default;
+ explicit NewBufferRecord(int32_t T) : MetadataRecord(), TID(T) {}
+
+ MetadataType metadataType() const override { return MetadataType::NewBuffer; }
+
+ int32_t tid() const { return TID; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class EndBufferRecord : public MetadataRecord {
+public:
+ EndBufferRecord() = default;
+
+ MetadataType metadataType() const override {
+ return MetadataType::EndOfBuffer;
+ }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class FunctionRecord : public Record {
+ RecordTypes Kind;
+ int32_t FuncId;
+ uint32_t Delta;
+ friend class RecordInitializer;
+
+ static constexpr unsigned kFunctionRecordSize = 8;
+
+public:
+ FunctionRecord() = default;
+ explicit FunctionRecord(RecordTypes K, int32_t F, uint32_t D)
+ : Record(), Kind(K), FuncId(F), Delta(D) {}
+
+ Type type() const override { return Type::Function; }
+
+ // A function record is a concrete record type which has a number of common
+ // properties.
+ RecordTypes recordType() const { return Kind; }
+ int32_t functionId() const { return FuncId; }
+ uint64_t delta() const { return Delta; }
+
+ Error apply(RecordVisitor &V) override;
+};
+
+class RecordVisitor {
+public:
+ virtual ~RecordVisitor() = default;
+
+ // Support all specific kinds of records:
+ virtual Error visit(BufferExtents &) = 0;
+ virtual Error visit(WallclockRecord &) = 0;
+ virtual Error visit(NewCPUIDRecord &) = 0;
+ virtual Error visit(TSCWrapRecord &) = 0;
+ virtual Error visit(CustomEventRecord &) = 0;
+ virtual Error visit(CallArgRecord &) = 0;
+ virtual Error visit(PIDRecord &) = 0;
+ virtual Error visit(NewBufferRecord &) = 0;
+ virtual Error visit(EndBufferRecord &) = 0;
+ virtual Error visit(FunctionRecord &) = 0;
+};
+
+class RecordInitializer : public RecordVisitor {
+ DataExtractor &E;
+ uint32_t &OffsetPtr;
+
+public:
+ explicit RecordInitializer(DataExtractor &DE, uint32_t &OP)
+ : RecordVisitor(), E(DE), OffsetPtr(OP) {}
+
+ Error visit(BufferExtents &) override;
+ Error visit(WallclockRecord &) override;
+ Error visit(NewCPUIDRecord &) override;
+ Error visit(TSCWrapRecord &) override;
+ Error visit(CustomEventRecord &) override;
+ Error visit(CallArgRecord &) override;
+ Error visit(PIDRecord &) override;
+ Error visit(NewBufferRecord &) override;
+ Error visit(EndBufferRecord &) override;
+ Error visit(FunctionRecord &) override;
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_LIB_XRAY_FDRRECORDS_H_
--- /dev/null
+//===- FDRTraceWriter.h - XRay FDR Trace Writer -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Test a utility that can write out XRay FDR Mode formatted trace files.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_INCLUDE_LLVM_XRAY_FDRTRACEWRITER_H_
+#define LLVM_INCLUDE_LLVM_XRAY_FDRTRACEWRITER_H_
+
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/FDRRecords.h"
+#include "llvm/XRay/XRayRecord.h"
+
+namespace llvm {
+namespace xray {
+
+/// The FDRTraceWriter allows us to hand-craft an XRay Flight Data Recorder
+/// (FDR) mode log file. This is used primarily for testing, generating
+/// sequences of FDR records that can be read/processed. It can also be used to
+/// generate various kinds of execution traces without using the XRay runtime.
+/// Note that this writer does not do any validation, but uses the types of
+/// records defined in the FDRRecords.h file.
+class FDRTraceWriter : public RecordVisitor {
+public:
+ // Construct an FDRTraceWriter associated with an output stream.
+ explicit FDRTraceWriter(raw_ostream &O, const XRayFileHeader &H);
+ ~FDRTraceWriter();
+
+ Error visit(BufferExtents &) override;
+ Error visit(WallclockRecord &) override;
+ Error visit(NewCPUIDRecord &) override;
+ Error visit(TSCWrapRecord &) override;
+ Error visit(CustomEventRecord &) override;
+ Error visit(CallArgRecord &) override;
+ Error visit(PIDRecord &) override;
+ Error visit(NewBufferRecord &) override;
+ Error visit(EndBufferRecord &) override;
+ Error visit(FunctionRecord &) override;
+
+private:
+ raw_ostream &OS;
+};
+
+} // namespace xray
+} // namespace llvm
+
+#endif // LLVM_INCLUDE_LLVM_XRAY_FDRTRACEWRITER_H_
add_llvm_library(LLVMXRay
+ FDRRecords.cpp
+ FDRTraceWriter.cpp
FileHeaderReader.cpp
InstrumentationMap.cpp
- Profile.cpp
+ Profile.cpp
+ RecordInitializer.cpp
Trace.cpp
ADDITIONAL_HEADER_DIRS
--- /dev/null
+//===- FDRRecords.cpp - XRay Flight Data Recorder Mode Records -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Define types and operations on these types that represent the different kinds
+// of records we encounter in XRay flight data recorder mode traces.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/XRay/FDRRecords.h"
+
+namespace llvm {
+namespace xray {
+
+Error BufferExtents::apply(RecordVisitor &V) { return V.visit(*this); }
+Error WallclockRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error NewCPUIDRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error TSCWrapRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error CustomEventRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error CallArgRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+Error PIDRecord::apply(RecordVisitor &V) { return V.visit(*this); }
+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); }
+
+} // namespace xray
+} // namespace llvm
--- /dev/null
+//===- FDRTraceWriter.cpp - XRay FDR Trace Writer ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Test a utility that can write out XRay FDR Mode formatted trace files.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/XRay/FDRTraceWriter.h"
+#include <tuple>
+
+namespace llvm {
+namespace xray {
+
+namespace {
+
+struct alignas(32) FileHeader {
+ uint16_t Version;
+ uint16_t Type;
+ bool ConstantTSC : 1;
+ bool NonstopTSC : 1;
+ alignas(8) uint64_t CycleFrequency;
+ char FreeForm[16];
+};
+
+struct MetadataBlob {
+ uint8_t Type : 1;
+ uint8_t RecordKind : 7;
+ char Data[15];
+} __attribute__((packed));
+
+struct FunctionDeltaBlob {
+ uint8_t Type : 1;
+ uint8_t RecordKind : 3;
+ int FuncId : 28;
+ uint32_t TSCDelta;
+} __attribute__((packed));
+
+template <size_t Index> struct IndexedMemcpy {
+ template <
+ class Tuple,
+ typename std::enable_if<
+ (Index <
+ std::tuple_size<typename std::remove_reference<Tuple>::type>::value),
+ int>::type = 0>
+ static void Copy(char *Dest, Tuple &&T) {
+ auto Next = static_cast<char *>(std::memcpy(
+ Dest, reinterpret_cast<const char *>(&std::get<Index>(T)),
+ sizeof(std::get<Index>(T)))) +
+ sizeof(std::get<Index>(T));
+ IndexedMemcpy<Index + 1>::Copy(Next, T);
+ }
+
+ template <
+ class Tuple,
+ typename std::enable_if<
+ (Index >=
+ std::tuple_size<typename std::remove_reference<Tuple>::type>::value),
+ int>::type = 0>
+ static void Copy(char *, Tuple &&) {}
+};
+
+template <uint8_t Kind, class... Data>
+Error writeMetadata(raw_ostream &OS, Data... Ds) {
+ MetadataBlob B;
+ B.Type = 1;
+ B.RecordKind = Kind;
+ IndexedMemcpy<0>::Copy(B.Data, std::make_tuple(Ds...));
+ OS.write(reinterpret_cast<const char *>(&B), sizeof(MetadataBlob));
+ return Error::success();
+}
+
+} // namespace
+
+FDRTraceWriter::FDRTraceWriter(raw_ostream &O, const XRayFileHeader &H)
+ : OS(O) {
+ // We need to re-construct a header, by writing the fields we care about for
+ // traces, in the format that the runtime would have written.
+ FileHeader Raw;
+ Raw.Version = H.Version;
+ Raw.Type = H.Type;
+ Raw.ConstantTSC = H.ConstantTSC;
+ Raw.NonstopTSC = H.NonstopTSC;
+ Raw.CycleFrequency = H.CycleFrequency;
+ memcpy(&Raw.FreeForm, H.FreeFormData, 16);
+ OS.write(reinterpret_cast<const char *>(&Raw), sizeof(XRayFileHeader));
+}
+
+FDRTraceWriter::~FDRTraceWriter() {}
+
+Error FDRTraceWriter::visit(BufferExtents &R) {
+ return writeMetadata<7u>(OS, R.size());
+}
+
+Error FDRTraceWriter::visit(WallclockRecord &R) {
+ return writeMetadata<4u>(OS, R.seconds(), R.nanos());
+}
+
+Error FDRTraceWriter::visit(NewCPUIDRecord &R) {
+ return writeMetadata<2u>(OS, R.cpuid());
+}
+
+Error FDRTraceWriter::visit(TSCWrapRecord &R) {
+ return writeMetadata<3u>(OS, R.tsc());
+}
+
+Error FDRTraceWriter::visit(CustomEventRecord &R) {
+ if (auto E = writeMetadata<5u>(OS, R.size(), R.tsc()))
+ return E;
+ OS.write(R.data().data(), R.data().size());
+ return Error::success();
+}
+
+Error FDRTraceWriter::visit(CallArgRecord &R) {
+ return writeMetadata<6u>(OS, R.arg());
+}
+
+Error FDRTraceWriter::visit(PIDRecord &R) {
+ return writeMetadata<9u>(OS, R.pid());
+}
+
+Error FDRTraceWriter::visit(NewBufferRecord &R) {
+ return writeMetadata<0u>(OS, R.tid());
+}
+
+Error FDRTraceWriter::visit(EndBufferRecord &R) {
+ return writeMetadata<1u>(OS, 0);
+}
+
+Error FDRTraceWriter::visit(FunctionRecord &R) {
+ FunctionDeltaBlob B;
+ B.Type = 0;
+ B.RecordKind = static_cast<uint8_t>(R.recordType());
+ B.FuncId = R.functionId();
+ B.TSCDelta = R.delta();
+ OS.write(reinterpret_cast<const char *>(&B), sizeof(FunctionDeltaBlob));
+ return Error::success();
+}
+
+} // namespace xray
+} // namespace llvm
--- /dev/null
+//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/XRay/FDRRecords.h"
+
+namespace llvm {
+namespace xray {
+
+Error RecordInitializer::visit(BufferExtents &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr, sizeof(uint64_t)))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a buffer extent (%d).",
+ OffsetPtr);
+
+ auto PreReadOffset = OffsetPtr;
+ R.Size = E.getU64(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read buffer extent at offset %d.",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(WallclockRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a wallclock record (%d).",
+ OffsetPtr);
+ auto BeginOffset = OffsetPtr;
+ auto PreReadOffset = OffsetPtr;
+ R.Seconds = E.getU64(&OffsetPtr);
+ if (OffsetPtr == PreReadOffset)
+ return createStringError(
+ std::make_error_code(std::errc::bad_message),
+ "Cannot read wall clock 'seconds' field at offset %d.", OffsetPtr);
+
+ PreReadOffset = OffsetPtr;
+ R.Nanos = E.getU32(&OffsetPtr);
+ if (OffsetPtr == PreReadOffset)
+ return createStringError(
+ std::make_error_code(std::errc::bad_message),
+ "Cannot read wall clock 'nanos' field at offset %d.", OffsetPtr);
+
+ // Align to metadata record size boundary.
+ assert(OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(NewCPUIDRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a new cpu id record (%d).",
+ OffsetPtr);
+ auto PreReadOffset = OffsetPtr;
+ R.CPUId = E.getU16(&OffsetPtr);
+ if (OffsetPtr == PreReadOffset)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read CPU id at offset %d.", OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(TSCWrapRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a new TSC wrap record (%d).",
+ OffsetPtr);
+
+ auto PreReadOffset = OffsetPtr;
+ R.BaseTSC = E.getU64(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read TSC wrap record at offset %d.",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(CustomEventRecord &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::bad_message),
+ "Cannot read a custom event record size field offset %d.", OffsetPtr);
+
+ PreReadOffset = OffsetPtr;
+ R.TSC = E.getU64(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(
+ std::make_error_code(std::errc::bad_message),
+ "Cannot read a custom event TSC field at offset %d.", OffsetPtr);
+
+ 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::bad_message),
+ "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 createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a call argument record (%d).",
+ OffsetPtr);
+
+ auto PreReadOffset = OffsetPtr;
+ R.Arg = E.getU64(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read a call arg record at offset %d.",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(PIDRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a process ID record (%d).",
+ OffsetPtr);
+
+ auto PreReadOffset = OffsetPtr;
+ R.PID = E.getU64(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read a process ID record at offset %d.",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(NewBufferRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a new buffer record (%d).",
+ OffsetPtr);
+
+ auto PreReadOffset = OffsetPtr;
+ R.TID = E.getSigned(&OffsetPtr, sizeof(int32_t));
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Cannot read a new buffer record at offset %d.",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
+ return Error::success();
+}
+
+Error RecordInitializer::visit(EndBufferRecord &R) {
+ if (!E.isValidOffsetForDataOfSize(OffsetPtr,
+ MetadataRecord::kMetadataBodySize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for an end-of-buffer record (%d).",
+ OffsetPtr);
+
+ OffsetPtr += MetadataRecord::kMetadataBodySize;
+ return Error::success();
+}
+
+Error RecordInitializer::visit(FunctionRecord &R) {
+ // For function records, we need to retreat one byte back to read a full
+ // unsigned 32-bit value. The first four bytes will have the following
+ // layout:
+ //
+ // bit 0 : function record indicator (must be 0)
+ // bits 1..3 : function record type
+ // bits 4..32 : function id
+ //
+ if (OffsetPtr == 0 || !E.isValidOffsetForDataOfSize(
+ --OffsetPtr, FunctionRecord::kFunctionRecordSize))
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Invalid offset for a function record (%d).",
+ OffsetPtr);
+
+ auto BeginOffset = OffsetPtr;
+ auto PreReadOffset = BeginOffset;
+ uint32_t Buffer = E.getU32(&OffsetPtr);
+ if (PreReadOffset == OffsetPtr)
+ return createStringError(std::make_error_code(std::errc::bad_address),
+ "Cannot read function id field from offset %d.",
+ OffsetPtr);
+ unsigned FunctionType = (Buffer >> 1) & 0x07;
+ switch (FunctionType) {
+ case static_cast<unsigned>(RecordTypes::ENTER):
+ case static_cast<unsigned>(RecordTypes::ENTER_ARG):
+ case static_cast<unsigned>(RecordTypes::EXIT):
+ case static_cast<unsigned>(RecordTypes::TAIL_EXIT):
+ R.Kind = static_cast<RecordTypes>(FunctionType);
+ break;
+ default:
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Unknown function record type '%d' at offset %d.",
+ FunctionType, BeginOffset);
+ }
+
+ R.FuncId = Buffer >> 4;
+ PreReadOffset = OffsetPtr;
+ R.Delta = E.getU32(&OffsetPtr);
+ if (OffsetPtr == PreReadOffset)
+ return createStringError(std::make_error_code(std::errc::bad_message),
+ "Failed reading TSC delta from offset %d.",
+ OffsetPtr);
+ assert(FunctionRecord::kFunctionRecordSize == (OffsetPtr - BeginOffset));
+ return Error::success();
+}
+
+} // namespace xray
+} // namespace llvm
set(LLVM_LINK_COMPONENTS
Support
- XRay
+ XRay
)
add_llvm_unittest(XRayTests
+ FDRTraceWriterTest.cpp
GraphTest.cpp
ProfileTest.cpp
)
--- /dev/null
+//===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Test a utility that can write out XRay FDR Mode formatted trace files.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/XRay/FDRTraceWriter.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/XRay/FDRLogBuilder.h"
+#include "llvm/XRay/FDRRecords.h"
+#include "llvm/XRay/Trace.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <string>
+
+namespace llvm {
+namespace xray {
+namespace {
+
+using testing::ElementsAre;
+using testing::Eq;
+using testing::Field;
+using testing::IsEmpty;
+using testing::Not;
+
+// We want to be able to create an instance of an FDRTraceWriter and associate
+// it with a stream, which could be loaded and turned into a Trace instance.
+// This test writes out version 3 trace logs.
+TEST(FDRTraceWriterTest, WriteToStringBufferVersion3) {
+ std::string Data;
+ raw_string_ostream OS(Data);
+ XRayFileHeader H;
+ H.Version = 3;
+ H.Type = 1;
+ H.ConstantTSC = true;
+ H.NonstopTSC = true;
+ H.CycleFrequency = 3e9;
+ FDRTraceWriter Writer(OS, H);
+ auto L = LogBuilder()
+ .add<BufferExtents>(80)
+ .add<NewBufferRecord>(1)
+ .add<WallclockRecord>(1, 1)
+ .add<PIDRecord>(1)
+ .add<NewCPUIDRecord>(1)
+ .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+ .add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .consume();
+ for (auto &P : L)
+ ASSERT_FALSE(errorToBool(P->apply(Writer)));
+ OS.flush();
+
+ // Then from here we load the Trace file.
+ DataExtractor DE(Data, true, 8);
+ auto TraceOrErr = loadTrace(DE, true);
+ if (!TraceOrErr)
+ FAIL() << TraceOrErr.takeError();
+ auto &Trace = TraceOrErr.get();
+
+ ASSERT_THAT(Trace, Not(IsEmpty()));
+ ASSERT_THAT(
+ Trace,
+ ElementsAre(AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::ENTER))),
+ AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))));
+}
+
+// This version is almost exactly the same as above, except writing version 2
+// logs, without the PID records.
+TEST(FDRTraceWriterTest, WriteToStringBufferVersion2) {
+ std::string Data;
+ raw_string_ostream OS(Data);
+ XRayFileHeader H;
+ H.Version = 2;
+ H.Type = 1;
+ H.ConstantTSC = true;
+ H.NonstopTSC = true;
+ H.CycleFrequency = 3e9;
+ FDRTraceWriter Writer(OS, H);
+ auto L = LogBuilder()
+ .add<BufferExtents>(64)
+ .add<NewBufferRecord>(1)
+ .add<WallclockRecord>(1, 1)
+ .add<NewCPUIDRecord>(1)
+ .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+ .add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .consume();
+ for (auto &P : L)
+ ASSERT_FALSE(errorToBool(P->apply(Writer)));
+ OS.flush();
+
+ // Then from here we load the Trace file.
+ DataExtractor DE(Data, true, 8);
+ auto TraceOrErr = loadTrace(DE, true);
+ if (!TraceOrErr)
+ FAIL() << TraceOrErr.takeError();
+ auto &Trace = TraceOrErr.get();
+
+ ASSERT_THAT(Trace, Not(IsEmpty()));
+ ASSERT_THAT(
+ Trace,
+ ElementsAre(AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::ENTER))),
+ AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))));
+}
+
+// This covers version 1 of the log, without a BufferExtents record but has an
+// explicit EndOfBuffer record.
+TEST(FDRTraceWriterTest, WriteToStringBufferVersion1) {
+ std::string Data;
+ raw_string_ostream OS(Data);
+ XRayFileHeader H;
+ H.Version = 1;
+ H.Type = 1;
+ H.ConstantTSC = true;
+ H.NonstopTSC = true;
+ H.CycleFrequency = 3e9;
+ // Write the size of buffers out, arbitrarily it's 4k.
+ constexpr uint64_t BufferSize = 4096;
+ std::memcpy(H.FreeFormData, reinterpret_cast<const char *>(&BufferSize),
+ sizeof(BufferSize));
+ FDRTraceWriter Writer(OS, H);
+ auto L = LogBuilder()
+ .add<NewBufferRecord>(1)
+ .add<WallclockRecord>(1, 1)
+ .add<NewCPUIDRecord>(1)
+ .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+ .add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+ .add<EndBufferRecord>()
+ .consume();
+ for (auto &P : L)
+ ASSERT_FALSE(errorToBool(P->apply(Writer)));
+
+ // We need to pad the buffer with 4016 (4096 - 80) bytes of zeros.
+ OS.write_zeros(4016);
+ OS.flush();
+
+ // Then from here we load the Trace file.
+ DataExtractor DE(Data, true, 8);
+ auto TraceOrErr = loadTrace(DE, true);
+ if (!TraceOrErr)
+ FAIL() << TraceOrErr.takeError();
+ auto &Trace = TraceOrErr.get();
+
+ ASSERT_THAT(Trace, Not(IsEmpty()));
+ ASSERT_THAT(
+ Trace,
+ ElementsAre(AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::ENTER))),
+ AllOf(Field(&XRayRecord::FuncId, Eq(1)),
+ Field(&XRayRecord::TId, Eq(1u)),
+ Field(&XRayRecord::CPU, Eq(1u)),
+ Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))));
+}
+
+} // namespace
+} // namespace xray
+} // namespace llvm