1146d5791SDean Michael Berris //===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
2146d5791SDean Michael Berris //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6146d5791SDean Michael Berris //
7146d5791SDean Michael Berris //===----------------------------------------------------------------------===//
8146d5791SDean Michael Berris #include "llvm/XRay/FDRRecordProducer.h"
9146d5791SDean Michael Berris #include "llvm/Support/DataExtractor.h"
10146d5791SDean Michael Berris
11da375a67SDean Michael Berris #include <cstdint>
12da375a67SDean Michael Berris
13146d5791SDean Michael Berris namespace llvm {
14146d5791SDean Michael Berris namespace xray {
15146d5791SDean Michael Berris
16146d5791SDean Michael Berris namespace {
17146d5791SDean Michael Berris
18146d5791SDean Michael Berris // Keep this in sync with the values written in the XRay FDR mode runtime in
19146d5791SDean Michael Berris // compiler-rt.
20fc774e29SDean Michael Berris enum MetadataRecordKinds : uint8_t {
21fc774e29SDean Michael Berris NewBufferKind,
22fc774e29SDean Michael Berris EndOfBufferKind,
23fc774e29SDean Michael Berris NewCPUIdKind,
24fc774e29SDean Michael Berris TSCWrapKind,
25fc774e29SDean Michael Berris WalltimeMarkerKind,
26fc774e29SDean Michael Berris CustomEventMarkerKind,
27fc774e29SDean Michael Berris CallArgumentKind,
28fc774e29SDean Michael Berris BufferExtentsKind,
29fc774e29SDean Michael Berris TypedEventMarkerKind,
30fc774e29SDean Michael Berris PidKind,
31146d5791SDean Michael Berris // This is an end marker, used to identify the upper bound for this enum.
32146d5791SDean Michael Berris EnumEndMarker,
33146d5791SDean Michael Berris };
34146d5791SDean Michael Berris
35da375a67SDean Michael Berris Expected<std::unique_ptr<Record>>
metadataRecordType(const XRayFileHeader & Header,uint8_t T)36da375a67SDean Michael Berris metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
37da375a67SDean Michael Berris
38146d5791SDean Michael Berris if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
39146d5791SDean Michael Berris return createStringError(std::make_error_code(std::errc::invalid_argument),
40146d5791SDean Michael Berris "Invalid metadata record type: %d", T);
41fc774e29SDean Michael Berris switch (T) {
42fc774e29SDean Michael Berris case MetadataRecordKinds::NewBufferKind:
430eaee545SJonas Devlieghere return std::make_unique<NewBufferRecord>();
44fc774e29SDean Michael Berris case MetadataRecordKinds::EndOfBufferKind:
45146d5791SDean Michael Berris if (Header.Version >= 2)
46146d5791SDean Michael Berris return createStringError(
47146d5791SDean Michael Berris std::make_error_code(std::errc::executable_format_error),
48146d5791SDean Michael Berris "End of buffer records are no longer supported starting version "
49146d5791SDean Michael Berris "2 of the log.");
500eaee545SJonas Devlieghere return std::make_unique<EndBufferRecord>();
51fc774e29SDean Michael Berris case MetadataRecordKinds::NewCPUIdKind:
520eaee545SJonas Devlieghere return std::make_unique<NewCPUIDRecord>();
53fc774e29SDean Michael Berris case MetadataRecordKinds::TSCWrapKind:
540eaee545SJonas Devlieghere return std::make_unique<TSCWrapRecord>();
55fc774e29SDean Michael Berris case MetadataRecordKinds::WalltimeMarkerKind:
560eaee545SJonas Devlieghere return std::make_unique<WallclockRecord>();
57fc774e29SDean Michael Berris case MetadataRecordKinds::CustomEventMarkerKind:
5859439dd0SDean Michael Berris if (Header.Version >= 5)
590eaee545SJonas Devlieghere return std::make_unique<CustomEventRecordV5>();
600eaee545SJonas Devlieghere return std::make_unique<CustomEventRecord>();
61fc774e29SDean Michael Berris case MetadataRecordKinds::CallArgumentKind:
620eaee545SJonas Devlieghere return std::make_unique<CallArgRecord>();
63fc774e29SDean Michael Berris case MetadataRecordKinds::BufferExtentsKind:
640eaee545SJonas Devlieghere return std::make_unique<BufferExtents>();
65fc774e29SDean Michael Berris case MetadataRecordKinds::TypedEventMarkerKind:
660eaee545SJonas Devlieghere return std::make_unique<TypedEventRecord>();
67fc774e29SDean Michael Berris case MetadataRecordKinds::PidKind:
680eaee545SJonas Devlieghere return std::make_unique<PIDRecord>();
69146d5791SDean Michael Berris case MetadataRecordKinds::EnumEndMarker:
70146d5791SDean Michael Berris llvm_unreachable("Invalid MetadataRecordKind");
71146d5791SDean Michael Berris }
7295f4120fSSimon Pilgrim llvm_unreachable("Unhandled MetadataRecordKinds enum value");
73146d5791SDean Michael Berris }
74146d5791SDean Michael Berris
isMetadataIntroducer(uint8_t FirstByte)75da375a67SDean Michael Berris constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
76da375a67SDean Michael Berris return FirstByte & 0x01u;
77da375a67SDean Michael Berris }
78da375a67SDean Michael Berris
79146d5791SDean Michael Berris } // namespace
80146d5791SDean Michael Berris
81da375a67SDean Michael Berris Expected<std::unique_ptr<Record>>
findNextBufferExtent()82da375a67SDean Michael Berris FileBasedRecordProducer::findNextBufferExtent() {
83da375a67SDean Michael Berris // We seek one byte at a time until we find a suitable buffer extents metadata
84da375a67SDean Michael Berris // record introducer.
85da375a67SDean Michael Berris std::unique_ptr<Record> R;
86da375a67SDean Michael Berris while (!R) {
87da375a67SDean Michael Berris auto PreReadOffset = OffsetPtr;
88da375a67SDean Michael Berris uint8_t FirstByte = E.getU8(&OffsetPtr);
89da375a67SDean Michael Berris if (OffsetPtr == PreReadOffset)
90da375a67SDean Michael Berris return createStringError(
91da375a67SDean Michael Berris std::make_error_code(std::errc::executable_format_error),
92f26a70a5SIgor Kudrin "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
93da375a67SDean Michael Berris
94da375a67SDean Michael Berris if (isMetadataIntroducer(FirstByte)) {
95da375a67SDean Michael Berris auto LoadedType = FirstByte >> 1;
96da375a67SDean Michael Berris if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {
97da375a67SDean Michael Berris auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
98da375a67SDean Michael Berris if (!MetadataRecordOrErr)
99da375a67SDean Michael Berris return MetadataRecordOrErr.takeError();
100da375a67SDean Michael Berris
101da375a67SDean Michael Berris R = std::move(MetadataRecordOrErr.get());
102da375a67SDean Michael Berris RecordInitializer RI(E, OffsetPtr);
103da375a67SDean Michael Berris if (auto Err = R->apply(RI))
104*c55cf4afSBill Wendling return std::move(Err);
105*c55cf4afSBill Wendling return std::move(R);
106da375a67SDean Michael Berris }
107da375a67SDean Michael Berris }
108da375a67SDean Michael Berris }
109da375a67SDean Michael Berris llvm_unreachable("Must always terminate with either an error or a record.");
110da375a67SDean Michael Berris }
111da375a67SDean Michael Berris
produce()112146d5791SDean Michael Berris Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
113da375a67SDean Michael Berris // First, we set up our result record.
114da375a67SDean Michael Berris std::unique_ptr<Record> R;
115da375a67SDean Michael Berris
116da375a67SDean Michael Berris // Before we do any further reading, we should check whether we're at the end
117da375a67SDean Michael Berris // of the current buffer we're been consuming. In FDR logs version >= 3, we
118da375a67SDean Michael Berris // rely on the buffer extents record to determine how many bytes we should be
119da375a67SDean Michael Berris // considering as valid records.
120da375a67SDean Michael Berris if (Header.Version >= 3 && CurrentBufferBytes == 0) {
121da375a67SDean Michael Berris // Find the next buffer extents record.
122da375a67SDean Michael Berris auto BufferExtentsOrError = findNextBufferExtent();
123da375a67SDean Michael Berris if (!BufferExtentsOrError)
124da375a67SDean Michael Berris return joinErrors(
125da375a67SDean Michael Berris BufferExtentsOrError.takeError(),
126da375a67SDean Michael Berris createStringError(
127da375a67SDean Michael Berris std::make_error_code(std::errc::executable_format_error),
128da375a67SDean Michael Berris "Failed to find the next BufferExtents record."));
129da375a67SDean Michael Berris
130da375a67SDean Michael Berris R = std::move(BufferExtentsOrError.get());
131da375a67SDean Michael Berris assert(R != nullptr);
132da375a67SDean Michael Berris assert(isa<BufferExtents>(R.get()));
1332c558bd8SSimon Pilgrim auto BE = cast<BufferExtents>(R.get());
134da375a67SDean Michael Berris CurrentBufferBytes = BE->size();
135*c55cf4afSBill Wendling return std::move(R);
136da375a67SDean Michael Berris }
137da375a67SDean Michael Berris
138da375a67SDean Michael Berris //
139146d5791SDean Michael Berris // At the top level, we read one byte to determine the type of the record to
140146d5791SDean Michael Berris // create. This byte will comprise of the following bits:
141146d5791SDean Michael Berris //
142146d5791SDean Michael Berris // - offset 0: A '1' indicates a metadata record, a '0' indicates a function
143146d5791SDean Michael Berris // record.
144146d5791SDean Michael Berris // - offsets 1-7: For metadata records, this will indicate the kind of
145146d5791SDean Michael Berris // metadata record should be loaded.
146146d5791SDean Michael Berris //
147146d5791SDean Michael Berris // We read first byte, then create the appropriate type of record to consume
148146d5791SDean Michael Berris // the rest of the bytes.
149146d5791SDean Michael Berris auto PreReadOffset = OffsetPtr;
150146d5791SDean Michael Berris uint8_t FirstByte = E.getU8(&OffsetPtr);
151e8c650abSDean Michael Berris if (OffsetPtr == PreReadOffset)
152e8c650abSDean Michael Berris return createStringError(
153e8c650abSDean Michael Berris std::make_error_code(std::errc::executable_format_error),
154f26a70a5SIgor Kudrin "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
155e8c650abSDean Michael Berris
156146d5791SDean Michael Berris // For metadata records, handle especially here.
157da375a67SDean Michael Berris if (isMetadataIntroducer(FirstByte)) {
158146d5791SDean Michael Berris auto LoadedType = FirstByte >> 1;
159146d5791SDean Michael Berris auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
160146d5791SDean Michael Berris if (!MetadataRecordOrErr)
161146d5791SDean Michael Berris return joinErrors(
162146d5791SDean Michael Berris MetadataRecordOrErr.takeError(),
163146d5791SDean Michael Berris createStringError(
164146d5791SDean Michael Berris std::make_error_code(std::errc::executable_format_error),
165f26a70a5SIgor Kudrin "Encountered an unsupported metadata record (%d) "
166f26a70a5SIgor Kudrin "at offset %" PRId64 ".",
167146d5791SDean Michael Berris LoadedType, PreReadOffset));
168146d5791SDean Michael Berris R = std::move(MetadataRecordOrErr.get());
169146d5791SDean Michael Berris } else {
1700eaee545SJonas Devlieghere R = std::make_unique<FunctionRecord>();
171146d5791SDean Michael Berris }
172146d5791SDean Michael Berris RecordInitializer RI(E, OffsetPtr);
173146d5791SDean Michael Berris
174146d5791SDean Michael Berris if (auto Err = R->apply(RI))
175*c55cf4afSBill Wendling return std::move(Err);
176146d5791SDean Michael Berris
177da375a67SDean Michael Berris // If we encountered a BufferExtents record, we should record the remaining
178da375a67SDean Michael Berris // bytes for the current buffer, to determine when we should start ignoring
179da375a67SDean Michael Berris // potentially malformed data and looking for buffer extents records.
180da375a67SDean Michael Berris if (auto BE = dyn_cast<BufferExtents>(R.get())) {
181da375a67SDean Michael Berris CurrentBufferBytes = BE->size();
182da375a67SDean Michael Berris } else if (Header.Version >= 3) {
183da375a67SDean Michael Berris if (OffsetPtr - PreReadOffset > CurrentBufferBytes)
184da375a67SDean Michael Berris return createStringError(
185da375a67SDean Michael Berris std::make_error_code(std::errc::executable_format_error),
186f26a70a5SIgor Kudrin "Buffer over-read at offset %" PRId64 " (over-read by %" PRId64
187f26a70a5SIgor Kudrin " bytes); Record Type = %s.",
188da375a67SDean Michael Berris OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,
189da375a67SDean Michael Berris Record::kindToString(R->getRecordType()).data());
190da375a67SDean Michael Berris
191da375a67SDean Michael Berris CurrentBufferBytes -= OffsetPtr - PreReadOffset;
192da375a67SDean Michael Berris }
193146d5791SDean Michael Berris assert(R != nullptr);
194*c55cf4afSBill Wendling return std::move(R);
195146d5791SDean Michael Berris }
196146d5791SDean Michael Berris
197146d5791SDean Michael Berris } // namespace xray
198146d5791SDean Michael Berris } // namespace llvm
199