FDRRecordProducer.cpp revision 344779
1//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "llvm/XRay/FDRRecordProducer.h"
10#include "llvm/Support/DataExtractor.h"
11
12#include <cstdint>
13
14namespace llvm {
15namespace xray {
16
17namespace {
18
19// Keep this in sync with the values written in the XRay FDR mode runtime in
20// compiler-rt.
21enum MetadataRecordKinds : uint8_t {
22  NewBufferKind,
23  EndOfBufferKind,
24  NewCPUIdKind,
25  TSCWrapKind,
26  WalltimeMarkerKind,
27  CustomEventMarkerKind,
28  CallArgumentKind,
29  BufferExtentsKind,
30  TypedEventMarkerKind,
31  PidKind,
32  // This is an end marker, used to identify the upper bound for this enum.
33  EnumEndMarker,
34};
35
36Expected<std::unique_ptr<Record>>
37metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
38
39  if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
40    return createStringError(std::make_error_code(std::errc::invalid_argument),
41                             "Invalid metadata record type: %d", T);
42  switch (T) {
43  case MetadataRecordKinds::NewBufferKind:
44    return make_unique<NewBufferRecord>();
45  case MetadataRecordKinds::EndOfBufferKind:
46    if (Header.Version >= 2)
47      return createStringError(
48          std::make_error_code(std::errc::executable_format_error),
49          "End of buffer records are no longer supported starting version "
50          "2 of the log.");
51    return make_unique<EndBufferRecord>();
52  case MetadataRecordKinds::NewCPUIdKind:
53    return make_unique<NewCPUIDRecord>();
54  case MetadataRecordKinds::TSCWrapKind:
55    return make_unique<TSCWrapRecord>();
56  case MetadataRecordKinds::WalltimeMarkerKind:
57    return make_unique<WallclockRecord>();
58  case MetadataRecordKinds::CustomEventMarkerKind:
59    if (Header.Version >= 5)
60      return make_unique<CustomEventRecordV5>();
61    return make_unique<CustomEventRecord>();
62  case MetadataRecordKinds::CallArgumentKind:
63    return make_unique<CallArgRecord>();
64  case MetadataRecordKinds::BufferExtentsKind:
65    return make_unique<BufferExtents>();
66  case MetadataRecordKinds::TypedEventMarkerKind:
67    return make_unique<TypedEventRecord>();
68  case MetadataRecordKinds::PidKind:
69    return make_unique<PIDRecord>();
70  case MetadataRecordKinds::EnumEndMarker:
71    llvm_unreachable("Invalid MetadataRecordKind");
72  }
73  llvm_unreachable("Unhandled MetadataRecordKinds enum value");
74}
75
76constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
77  return FirstByte & 0x01u;
78}
79
80} // namespace
81
82Expected<std::unique_ptr<Record>>
83FileBasedRecordProducer::findNextBufferExtent() {
84  // We seek one byte at a time until we find a suitable buffer extents metadata
85  // record introducer.
86  std::unique_ptr<Record> R;
87  while (!R) {
88    auto PreReadOffset = OffsetPtr;
89    uint8_t FirstByte = E.getU8(&OffsetPtr);
90    if (OffsetPtr == PreReadOffset)
91      return createStringError(
92          std::make_error_code(std::errc::executable_format_error),
93          "Failed reading one byte from offset %d.", OffsetPtr);
94
95    if (isMetadataIntroducer(FirstByte)) {
96      auto LoadedType = FirstByte >> 1;
97      if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {
98        auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
99        if (!MetadataRecordOrErr)
100          return MetadataRecordOrErr.takeError();
101
102        R = std::move(MetadataRecordOrErr.get());
103        RecordInitializer RI(E, OffsetPtr);
104        if (auto Err = R->apply(RI))
105          return std::move(Err);
106        return std::move(R);
107      }
108    }
109  }
110  llvm_unreachable("Must always terminate with either an error or a record.");
111}
112
113Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
114  // First, we set up our result record.
115  std::unique_ptr<Record> R;
116
117  // Before we do any further reading, we should check whether we're at the end
118  // of the current buffer we're been consuming. In FDR logs version >= 3, we
119  // rely on the buffer extents record to determine how many bytes we should be
120  // considering as valid records.
121  if (Header.Version >= 3 && CurrentBufferBytes == 0) {
122    // Find the next buffer extents record.
123    auto BufferExtentsOrError = findNextBufferExtent();
124    if (!BufferExtentsOrError)
125      return joinErrors(
126          BufferExtentsOrError.takeError(),
127          createStringError(
128              std::make_error_code(std::errc::executable_format_error),
129              "Failed to find the next BufferExtents record."));
130
131    R = std::move(BufferExtentsOrError.get());
132    assert(R != nullptr);
133    assert(isa<BufferExtents>(R.get()));
134    auto BE = dyn_cast<BufferExtents>(R.get());
135    CurrentBufferBytes = BE->size();
136    return std::move(R);
137  }
138
139  //
140  // At the top level, we read one byte to determine the type of the record to
141  // create. This byte will comprise of the following bits:
142  //
143  //   - offset 0: A '1' indicates a metadata record, a '0' indicates a function
144  //     record.
145  //   - offsets 1-7: For metadata records, this will indicate the kind of
146  //     metadata record should be loaded.
147  //
148  // We read first byte, then create the appropriate type of record to consume
149  // the rest of the bytes.
150  auto PreReadOffset = OffsetPtr;
151  uint8_t FirstByte = E.getU8(&OffsetPtr);
152  if (OffsetPtr == PreReadOffset)
153    return createStringError(
154        std::make_error_code(std::errc::executable_format_error),
155        "Failed reading one byte from offset %d.", OffsetPtr);
156
157  // For metadata records, handle especially here.
158  if (isMetadataIntroducer(FirstByte)) {
159    auto LoadedType = FirstByte >> 1;
160    auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
161    if (!MetadataRecordOrErr)
162      return joinErrors(
163          MetadataRecordOrErr.takeError(),
164          createStringError(
165              std::make_error_code(std::errc::executable_format_error),
166              "Encountered an unsupported metadata record (%d) at offset %d.",
167              LoadedType, PreReadOffset));
168    R = std::move(MetadataRecordOrErr.get());
169  } else {
170    R = llvm::make_unique<FunctionRecord>();
171  }
172  RecordInitializer RI(E, OffsetPtr);
173
174  if (auto Err = R->apply(RI))
175    return std::move(Err);
176
177  // If we encountered a BufferExtents record, we should record the remaining
178  // bytes for the current buffer, to determine when we should start ignoring
179  // potentially malformed data and looking for buffer extents records.
180  if (auto BE = dyn_cast<BufferExtents>(R.get())) {
181    CurrentBufferBytes = BE->size();
182  } else if (Header.Version >= 3) {
183    if (OffsetPtr - PreReadOffset > CurrentBufferBytes)
184      return createStringError(
185          std::make_error_code(std::errc::executable_format_error),
186          "Buffer over-read at offset %d (over-read by %d bytes); Record Type "
187          "= %s.",
188          OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,
189          Record::kindToString(R->getRecordType()).data());
190
191    CurrentBufferBytes -= OffsetPtr - PreReadOffset;
192  }
193  assert(R != nullptr);
194  return std::move(R);
195}
196
197} // namespace xray
198} // namespace llvm
199