Index: llvm/trunk/include/llvm/XRay/BlockPrinter.h =================================================================== --- llvm/trunk/include/llvm/XRay/BlockPrinter.h +++ llvm/trunk/include/llvm/XRay/BlockPrinter.h @@ -0,0 +1,60 @@ +//===- BlockPrinter.h - FDR Block Pretty Printer -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An implementation of the RecordVisitor which formats a block of records for +// easier human consumption. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_INCLUDE_LLVM_XRAY_BLOCKPRINTER_H_ +#define LLVM_INCLUDE_LLVM_XRAY_BLOCKPRINTER_H_ + +#include "llvm/Support/raw_ostream.h" +#include "llvm/XRay/FDRRecords.h" +#include "llvm/XRay/RecordPrinter.h" + +namespace llvm { +namespace xray { + +class BlockPrinter : public RecordVisitor { + enum class State { + Start, + Preamble, + Metadata, + Function, + Arg, + CustomEvent, + End, + }; + + raw_ostream &OS; + RecordPrinter &RP; + State CurrentState = State::Start; + +public: + explicit BlockPrinter(raw_ostream &O, RecordPrinter &P) + : RecordVisitor(), OS(O), RP(P) {} + + 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; + + void reset() { CurrentState = State::Start; } +}; + +} // namespace xray +} // namespace llvm + +#endif // LLVM_INCLUDE_LLVM_XRAY_BLOCKPRINTER_H_ Index: llvm/trunk/lib/XRay/BlockPrinter.cpp =================================================================== --- llvm/trunk/lib/XRay/BlockPrinter.cpp +++ llvm/trunk/lib/XRay/BlockPrinter.cpp @@ -0,0 +1,96 @@ +//===- BlockPrinter.cpp - FDR Block Pretty Printer Implementation --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "llvm/XRay/BlockPrinter.h" + +namespace llvm { +namespace xray { + +Error BlockPrinter::visit(BufferExtents &R) { + OS << "\n[New Block]\n"; + CurrentState = State::Preamble; + return RP.visit(R); +} + +// Preamble printing. +Error BlockPrinter::visit(NewBufferRecord &R) { + if (CurrentState == State::Start) + OS << "\n[New Block]\n"; + + OS << "Preamble: \n"; + CurrentState = State::Preamble; + return RP.visit(R); +} + +Error BlockPrinter::visit(WallclockRecord &R) { + CurrentState = State::Preamble; + return RP.visit(R); +} + +Error BlockPrinter::visit(PIDRecord &R) { + CurrentState = State::Preamble; + return RP.visit(R); +} + +// Metadata printing. +Error BlockPrinter::visit(NewCPUIDRecord &R) { + if (CurrentState == State::Preamble) + OS << "\nBody:\n"; + if (CurrentState == State::Function) + OS << "\nMetadata: "; + CurrentState = State::Metadata; + OS << " "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(TSCWrapRecord &R) { + if (CurrentState == State::Function) + OS << "\nMetadata:"; + CurrentState = State::Metadata; + OS << " "; + auto E = RP.visit(R); + return E; +} + +// Custom events will be rendered like "function" events. +Error BlockPrinter::visit(CustomEventRecord &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) + OS << "\n"; + CurrentState = State::Function; + OS << "- "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(CallArgRecord &R) { + CurrentState = State::Arg; + OS << " : "; + auto E = RP.visit(R); + return E; +} + +Error BlockPrinter::visit(EndBufferRecord &R) { + CurrentState = State::End; + OS << " *** "; + auto E = RP.visit(R); + return E; +} + +} // namespace xray +} // namespace llvm Index: llvm/trunk/lib/XRay/CMakeLists.txt =================================================================== --- llvm/trunk/lib/XRay/CMakeLists.txt +++ llvm/trunk/lib/XRay/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_library(LLVMXRay BlockIndexer.cpp + BlockPrinter.cpp BlockVerifier.cpp FDRRecordProducer.cpp FDRRecords.cpp Index: llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1-version-3.txt =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1-version-3.txt +++ llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1-version-3.txt @@ -0,0 +1,25 @@ +; RUN: llvm-xray fdr-dump -verify %S/Inputs/fdr-log-arg1-version-3.xray \ +; RUN: | FileCheck %s + +; CHECK: [New Block] +; CHECK-NEXT: Preamble: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-EMPTY: +; CHECK-NEXT: Body: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-EMPTY: +; CHECK-NEXT: - +; CHECK-NEXT: - +; CHECK-NEXT: - +; CHECK-NEXT: - +; CHECK-EMPTY: +; CHECK-NEXT: Metadata: +; CHECK-EMPTY: +; CHECK-NEXT: - +; CHECK-NEXT: : +; CHECK-NEXT: - + Index: llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1.txt =================================================================== --- llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1.txt +++ llvm/trunk/test/tools/llvm-xray/X86/fdr-dump-arg1.txt @@ -0,0 +1,16 @@ +; RUN: llvm-xray fdr-dump -verify %S/Inputs/fdr-log-arg1.xray | FileCheck %s + +; CHECK: [New Block] +; CHECK-NEXT: Preamble: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-EMPTY: +; CHECK-NEXT: Body: +; CHECK-NEXT: +; CHECK-NEXT: +; CHECK-EMPTY: +; CHECK-NEXT: - +; CHECK-NEXT: : +; CHECK-NEXT: - +; CHECK-NEXT: *** + Index: llvm/trunk/tools/llvm-xray/CMakeLists.txt =================================================================== --- llvm/trunk/tools/llvm-xray/CMakeLists.txt +++ llvm/trunk/tools/llvm-xray/CMakeLists.txt @@ -14,6 +14,7 @@ xray-color-helper.cpp xray-converter.cpp xray-extract.cpp + xray-fdr-dump.cpp xray-graph-diff.cpp xray-graph.cpp xray-registry.cpp Index: llvm/trunk/tools/llvm-xray/xray-fdr-dump.cpp =================================================================== --- llvm/trunk/tools/llvm-xray/xray-fdr-dump.cpp +++ llvm/trunk/tools/llvm-xray/xray-fdr-dump.cpp @@ -0,0 +1,119 @@ +//===- xray-fdr-dump.cpp: XRay FDR Trace Dump Tool ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the FDR trace dumping tool, using the libraries for handling FDR +// mode traces specifically. +// +//===----------------------------------------------------------------------===// +#include "xray-registry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/XRay/BlockIndexer.h" +#include "llvm/XRay/BlockPrinter.h" +#include "llvm/XRay/BlockVerifier.h" +#include "llvm/XRay/FDRRecordConsumer.h" +#include "llvm/XRay/FDRRecordProducer.h" +#include "llvm/XRay/FDRRecords.h" +#include "llvm/XRay/FileHeaderReader.h" +#include "llvm/XRay/RecordPrinter.h" + +using namespace llvm; +using namespace xray; + +static cl::SubCommand Dump("fdr-dump", "FDR Trace Dump"); +static cl::opt DumpInput(cl::Positional, + cl::desc(""), + cl::Required, cl::sub(Dump)); +static cl::opt DumpVerify("verify", + cl::desc("verify structure of the log"), + cl::init(false), cl::sub(Dump)); + +static CommandRegistration Unused(&Dump, []() -> Error { + // Open the file provided. + int Fd; + if (auto EC = sys::fs::openFileForRead(DumpInput, Fd)) + return createStringError(EC, "Cannot open file '%s' for read.", + DumpInput.c_str()); + + uint64_t FileSize; + if (auto EC = sys::fs::file_size(DumpInput, FileSize)) + return createStringError(EC, "Failed to get file size for '%s'.", + DumpInput.c_str()); + + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + + DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8); + uint32_t OffsetPtr = 0; + + auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr); + if (!FileHeaderOrError) + return FileHeaderOrError.takeError(); + auto &H = FileHeaderOrError.get(); + + FileBasedRecordProducer P(H, DE, OffsetPtr); + + RecordPrinter RP(outs(), "\n"); + if (!DumpVerify) { + PipelineConsumer C({&RP}); + while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { + auto R = P.produce(); + if (!R) + return R.takeError(); + if (auto E = C.consume(std::move(R.get()))) + return E; + } + return Error::success(); + } + + BlockPrinter BP(outs(), RP); + std::vector> Records; + LogBuilderConsumer C(Records); + while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { + auto R = P.produce(); + if (!R) { + // Print records we've found so far. + for (auto &Ptr : Records) + if (auto E = Ptr->apply(RP)) + return joinErrors(std::move(E), R.takeError()); + return R.takeError(); + } + if (auto E = C.consume(std::move(R.get()))) + return E; + } + + // Once we have a trace, we then index the blocks. + BlockIndexer::Index Index; + BlockIndexer BI(Index); + for (auto &Ptr : Records) + if (auto E = Ptr->apply(BI)) + return E; + + if (auto E = BI.flush()) + return E; + + // Then we validate while printing each block. + BlockVerifier BV; + for (auto ProcessThreadBlocks : Index) { + auto &Blocks = ProcessThreadBlocks.second; + for (auto &B : Blocks) { + for (auto *R : B.Records) { + if (auto E = R->apply(BV)) + return E; + if (auto E = R->apply(BP)) + return E; + } + BV.reset(); + BP.reset(); + } + } + outs().flush(); + return Error::success(); +}); Index: llvm/trunk/unittests/XRay/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/XRay/CMakeLists.txt +++ llvm/trunk/unittests/XRay/CMakeLists.txt @@ -5,13 +5,14 @@ ) add_llvm_unittest(XRayTests - ProfileTest.cpp FDRBlockIndexerTest.cpp FDRBlockVerifierTest.cpp FDRProducerConsumerTest.cpp FDRRecordPrinterTest.cpp + FDRRecordsTest.cpp FDRTraceWriterTest.cpp GraphTest.cpp + ProfileTest.cpp ) add_dependencies(XRayTests intrinsics_gen) Index: llvm/trunk/unittests/XRay/FDRRecordsTest.cpp =================================================================== --- llvm/trunk/unittests/XRay/FDRRecordsTest.cpp +++ llvm/trunk/unittests/XRay/FDRRecordsTest.cpp @@ -0,0 +1,156 @@ +//===- FDRRecords.cpp - Unit Tests for XRay FDR Record Loading ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "llvm/XRay/BlockIndexer.h" +#include "llvm/XRay/BlockPrinter.h" +#include "llvm/XRay/BlockVerifier.h" +#include "llvm/XRay/FDRLogBuilder.h" +#include "llvm/XRay/FDRRecords.h" +#include "llvm/XRay/RecordPrinter.h" + +namespace llvm { +namespace xray { +namespace { + +using ::testing::Eq; +using ::testing::Not; + +TEST(XRayFDRTest, BuilderAndBlockIndexer) { + // We recreate a single block of valid records, then ensure that we find all + // of them belonging in the same index. We do this for three blocks, and + // ensure we find the same records in the blocks we deduce. + auto Block0 = LogBuilder() + .add(100) + .add(1) + .add(1, 1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block1 = LogBuilder() + .add(100) + .add(1) + .add(1, 2) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block2 = LogBuilder() + .add(100) + .add(2) + .add(1, 3) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + BlockIndexer::Index Index; + BlockIndexer Indexer(Index); + for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) { + for (auto &R : B.get()) + ASSERT_FALSE(errorToBool(R->apply(Indexer))); + ASSERT_FALSE(errorToBool(Indexer.flush())); + } + + // We have two threads worth of blocks. + ASSERT_THAT(Index.size(), Eq(2u)); + auto T1Blocks = Index.find({1, 1}); + ASSERT_THAT(T1Blocks, Not(Eq(Index.end()))); + ASSERT_THAT(T1Blocks->second.size(), Eq(2u)); + auto T2Blocks = Index.find({1, 2}); + ASSERT_THAT(T2Blocks, Not(Eq(Index.end()))); + ASSERT_THAT(T2Blocks->second.size(), Eq(1u)); +} + +TEST(XRayFDRTest, BuilderAndBlockVerifier) { + auto Block = LogBuilder() + .add(48) + .add(1) + .add(1, 1) + .add(1) + .add(1) + .consume(); + BlockVerifier Verifier; + for (auto &R : Block) + ASSERT_FALSE(errorToBool(R->apply(Verifier))); + ASSERT_FALSE(errorToBool(Verifier.verify())); +} + +TEST(XRayFDRTest, IndexAndVerifyBlocks) { + auto Block0 = LogBuilder() + .add(64) + .add(1) + .add(1, 1) + .add(1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block1 = LogBuilder() + .add(64) + .add(1) + .add(1, 1) + .add(1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + auto Block2 = LogBuilder() + .add(64) + .add(1) + .add(1, 1) + .add(1) + .add(1) + .add(RecordTypes::ENTER, 1, 1) + .add(RecordTypes::EXIT, 1, 100) + .consume(); + + // First, index the records in different blocks. + BlockIndexer::Index Index; + BlockIndexer Indexer(Index); + for (auto B : {std::ref(Block0), std::ref(Block1), std::ref(Block2)}) { + for (auto &R : B.get()) + ASSERT_FALSE(errorToBool(R->apply(Indexer))); + ASSERT_FALSE(errorToBool(Indexer.flush())); + } + + // Next, verify that each block is consistently defined. + BlockVerifier Verifier; + for (auto &ProcessThreadBlocks : Index) { + auto &Blocks = ProcessThreadBlocks.second; + for (auto &B : Blocks) { + for (auto *R : B.Records) + ASSERT_FALSE(errorToBool(R->apply(Verifier))); + ASSERT_FALSE(errorToBool(Verifier.verify())); + Verifier.reset(); + } + } + + // Then set up the printing mechanisms. + std::string Output; + raw_string_ostream OS(Output); + RecordPrinter RP(OS); + BlockPrinter BP(OS, RP); + for (auto &ProcessThreadBlocks : Index) { + auto &Blocks = ProcessThreadBlocks.second; + for (auto &B : Blocks) { + for (auto *R : B.Records) + ASSERT_FALSE(errorToBool(R->apply(BP))); + BP.reset(); + } + } + + OS.flush(); + EXPECT_THAT(Output, Not(Eq(""))); +} + +} // namespace +} // namespace xray +} // namespace llvm