[XRay] Add the `llvm-xray fdr-dump` implementation

Summary:
In this change, we implement a `BlockPrinter` which orders records in a
Block that's been indexed by the `BlockIndexer`. This is used in the
`llvm-xray fdr-dump` tool which ties together the various types and
utilities we've been working on, to allow for inspection of XRay FDR
mode traces both with and without verification.

This change is the final step of the refactoring of D50441.

Reviewers: mboerger, eizan

Subscribers: mgorny, hiraditya, llvm-commits

Differential Revision: https://reviews.llvm.org/D51846

llvm-svn: 341887
diff --git a/llvm/unittests/XRay/FDRRecordsTest.cpp b/llvm/unittests/XRay/FDRRecordsTest.cpp
new file mode 100644
index 0000000..24ff7a5
--- /dev/null
+++ b/llvm/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<BufferExtents>(100)
+                    .add<NewBufferRecord>(1)
+                    .add<WallclockRecord>(1, 1)
+                    .add<PIDRecord>(1)
+                    .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+                    .add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+                    .consume();
+  auto Block1 = LogBuilder()
+                    .add<BufferExtents>(100)
+                    .add<NewBufferRecord>(1)
+                    .add<WallclockRecord>(1, 2)
+                    .add<PIDRecord>(1)
+                    .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+                    .add<FunctionRecord>(RecordTypes::EXIT, 1, 100)
+                    .consume();
+  auto Block2 = LogBuilder()
+                    .add<BufferExtents>(100)
+                    .add<NewBufferRecord>(2)
+                    .add<WallclockRecord>(1, 3)
+                    .add<PIDRecord>(1)
+                    .add<FunctionRecord>(RecordTypes::ENTER, 1, 1)
+                    .add<FunctionRecord>(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<BufferExtents>(48)
+                   .add<NewBufferRecord>(1)
+                   .add<WallclockRecord>(1, 1)
+                   .add<PIDRecord>(1)
+                   .add<NewCPUIDRecord>(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<BufferExtents>(64)
+                    .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();
+  auto Block1 = LogBuilder()
+                    .add<BufferExtents>(64)
+                    .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();
+  auto Block2 = LogBuilder()
+                    .add<BufferExtents>(64)
+                    .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();
+
+  // 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