[XRay] FDR Record Producer/Consumer Implementation

Summary:
This patch defines two new base types called `RecordProducer` and
`RecordConsumer` which have default implementations for convenience
(particularly for testing).

A `RecordProducer` implementation has one member function called
`produce()` which serves as a factory constructor for `Record`
instances. This code exercises the `RecordInitializer` code path in the
implementation for `FileBasedRecordProducer`.

A `RecordConsumer` has a single member function called `consume(...)`
which, as the name implies, consumes instances of
`std::unique_ptr<Record>`. We have two implementations, one of which is
used in the test to generate a vector of `std::unique_ptr<Record>`
similar to how the `LogBuilder` implementation works.

We introduce a test in `FDRProducerConsumerTest` which ensures that
records we write through the `FDRTraceWriter` can be loaded by the
`FileBasedRecordProducer`. The record(s) loaded this way are written
again through the `FDRTraceWriter` into a separate string, which we then
compare. This ensures that the read-in bytes to create the `Record`
instances in memory can be replicated when written out through the
`FDRTraceWriter`.

This change depends on D51210 and is part of the refactoring of D50441
into smaller, more focused changes.

Reviewers: eizan, kpw

Subscribers: mgorny, hiraditya, llvm-commits

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

llvm-svn: 341180
diff --git a/llvm/lib/XRay/CMakeLists.txt b/llvm/lib/XRay/CMakeLists.txt
index 5a09a24..a726db2 100644
--- a/llvm/lib/XRay/CMakeLists.txt
+++ b/llvm/lib/XRay/CMakeLists.txt
@@ -1,8 +1,10 @@
 add_llvm_library(LLVMXRay
+  FDRRecordProducer.cpp
   FDRRecords.cpp
   FDRTraceWriter.cpp
   FileHeaderReader.cpp
   InstrumentationMap.cpp
+  LogBuilderConsumer.cpp
   Profile.cpp
   RecordInitializer.cpp
   Trace.cpp
diff --git a/llvm/lib/XRay/FDRRecordProducer.cpp b/llvm/lib/XRay/FDRRecordProducer.cpp
new file mode 100644
index 0000000..84d75cc
--- /dev/null
+++ b/llvm/lib/XRay/FDRRecordProducer.cpp
@@ -0,0 +1,125 @@
+//===- 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/FDRRecordProducer.h"
+#include "llvm/Support/DataExtractor.h"
+
+namespace llvm {
+namespace xray {
+
+namespace {
+
+// Keep this in sync with the values written in the XRay FDR mode runtime in
+// compiler-rt.
+enum class MetadataRecordKinds : uint8_t {
+  NewBuffer,
+  EndOfBuffer,
+  NewCPUId,
+  TSCWrap,
+  WalltimeMarker,
+  CustomEventMarker,
+  CallArgument,
+  BufferExtents,
+  TypedEventMarker,
+  Pid,
+  // This is an end marker, used to identify the upper bound for this enum.
+  EnumEndMarker,
+};
+
+Expected<std::unique_ptr<Record>>
+metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
+  if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
+    return createStringError(std::make_error_code(std::errc::invalid_argument),
+                             "Invalid metadata record type: %d", T);
+  static constexpr MetadataRecordKinds Mapping[] = {
+      MetadataRecordKinds::NewBuffer,
+      MetadataRecordKinds::EndOfBuffer,
+      MetadataRecordKinds::NewCPUId,
+      MetadataRecordKinds::TSCWrap,
+      MetadataRecordKinds::WalltimeMarker,
+      MetadataRecordKinds::CustomEventMarker,
+      MetadataRecordKinds::CallArgument,
+      MetadataRecordKinds::BufferExtents,
+      MetadataRecordKinds::TypedEventMarker,
+      MetadataRecordKinds::Pid,
+  };
+  switch (Mapping[T]) {
+  case MetadataRecordKinds::NewBuffer:
+    return make_unique<NewBufferRecord>();
+  case MetadataRecordKinds::EndOfBuffer:
+    if (Header.Version >= 2)
+      return createStringError(
+          std::make_error_code(std::errc::executable_format_error),
+          "End of buffer records are no longer supported starting version "
+          "2 of the log.");
+    return make_unique<EndBufferRecord>();
+  case MetadataRecordKinds::NewCPUId:
+    return make_unique<NewCPUIDRecord>();
+  case MetadataRecordKinds::TSCWrap:
+    return make_unique<TSCWrapRecord>();
+  case MetadataRecordKinds::WalltimeMarker:
+    return make_unique<WallclockRecord>();
+  case MetadataRecordKinds::CustomEventMarker:
+    return make_unique<CustomEventRecord>();
+  case MetadataRecordKinds::CallArgument:
+    return make_unique<CallArgRecord>();
+  case MetadataRecordKinds::BufferExtents:
+    return make_unique<BufferExtents>();
+  case MetadataRecordKinds::TypedEventMarker:
+    return createStringError(std::make_error_code(std::errc::invalid_argument),
+                             "Encountered an unsupported TypedEventMarker.");
+  case MetadataRecordKinds::Pid:
+    return make_unique<PIDRecord>();
+  case MetadataRecordKinds::EnumEndMarker:
+    llvm_unreachable("Invalid MetadataRecordKind");
+  }
+}
+
+} // namespace
+
+Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
+  // At the top level, we read one byte to determine the type of the record to
+  // create. This byte will comprise of the following bits:
+  //
+  //   - offset 0: A '1' indicates a metadata record, a '0' indicates a function
+  //     record.
+  //   - offsets 1-7: For metadata records, this will indicate the kind of
+  //     metadata record should be loaded.
+  //
+  // We read first byte, then create the appropriate type of record to consume
+  // the rest of the bytes.
+  auto PreReadOffset = OffsetPtr;
+  uint8_t FirstByte = E.getU8(&OffsetPtr);
+  std::unique_ptr<Record> R;
+
+  // For metadata records, handle especially here.
+  if (FirstByte & 0x01) {
+    auto LoadedType = FirstByte >> 1;
+    auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
+    if (!MetadataRecordOrErr)
+      return joinErrors(
+          MetadataRecordOrErr.takeError(),
+          createStringError(
+              std::make_error_code(std::errc::executable_format_error),
+              "Encountered an unsupported metadata record (%d) at offset %d.",
+              LoadedType, PreReadOffset));
+    R = std::move(MetadataRecordOrErr.get());
+  } else {
+    R = llvm::make_unique<FunctionRecord>();
+  }
+  RecordInitializer RI(E, OffsetPtr);
+
+  if (auto Err = R->apply(RI))
+    return std::move(Err);
+
+  assert(R != nullptr);
+  return std::move(R);
+}
+
+} // namespace xray
+} // namespace llvm
diff --git a/llvm/lib/XRay/LogBuilderConsumer.cpp b/llvm/lib/XRay/LogBuilderConsumer.cpp
new file mode 100644
index 0000000..88b7d2d
--- /dev/null
+++ b/llvm/lib/XRay/LogBuilderConsumer.cpp
@@ -0,0 +1,38 @@
+//===- FDRRecordConsumer.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.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/XRay/FDRRecordConsumer.h"
+
+namespace llvm {
+namespace xray {
+
+Error LogBuilderConsumer::consume(std::unique_ptr<Record> R) {
+  if (!R)
+    return createStringError(
+        std::make_error_code(std::errc::invalid_argument),
+        "Must not call RecordConsumer::consume() with a null pointer.");
+  Records.push_back(std::move(R));
+  return Error::success();
+}
+
+Error PipelineConsumer::consume(std::unique_ptr<Record> R) {
+  if (!R)
+    return createStringError(
+        std::make_error_code(std::errc::invalid_argument),
+        "Must not call RecordConsumer::consume() with a null pointer.");
+
+  // We apply all of the visitors in order, and concatenate errors
+  // appropriately.
+  Error Result = Error::success();
+  for (auto *V : Visitors)
+    Result = joinErrors(std::move(Result), R->apply(*V));
+  return Result;
+}
+
+} // namespace xray
+} // namespace llvm