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