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 | (const XRayFileHeader &, uint8_t T) { |
37 | |
38 | if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker)) |
39 | return createStringError(EC: std::make_error_code(e: std::errc::invalid_argument), |
40 | Fmt: "Invalid metadata record type: %d" , Vals: 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 | EC: std::make_error_code(e: std::errc::executable_format_error), |
48 | S: "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(offset_ptr: &OffsetPtr); |
89 | if (OffsetPtr == PreReadOffset) |
90 | return createStringError( |
91 | EC: std::make_error_code(e: std::errc::executable_format_error), |
92 | Fmt: "Failed reading one byte from offset %" PRId64 "." , Vals: OffsetPtr); |
93 | |
94 | if (isMetadataIntroducer(FirstByte)) { |
95 | auto LoadedType = FirstByte >> 1; |
96 | if (LoadedType == MetadataRecordKinds::BufferExtentsKind) { |
97 | auto MetadataRecordOrErr = metadataRecordType(Header, T: 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(V&: 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 | E1: BufferExtentsOrError.takeError(), |
126 | E2: createStringError( |
127 | EC: std::make_error_code(e: std::errc::executable_format_error), |
128 | S: "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>(Val: 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(offset_ptr: &OffsetPtr); |
151 | if (OffsetPtr == PreReadOffset) |
152 | return createStringError( |
153 | EC: std::make_error_code(e: std::errc::executable_format_error), |
154 | Fmt: "Failed reading one byte from offset %" PRId64 "." , Vals: OffsetPtr); |
155 | |
156 | // For metadata records, handle especially here. |
157 | if (isMetadataIntroducer(FirstByte)) { |
158 | auto LoadedType = FirstByte >> 1; |
159 | auto MetadataRecordOrErr = metadataRecordType(Header, T: LoadedType); |
160 | if (!MetadataRecordOrErr) |
161 | return joinErrors( |
162 | E1: MetadataRecordOrErr.takeError(), |
163 | E2: createStringError( |
164 | EC: std::make_error_code(e: std::errc::executable_format_error), |
165 | Fmt: "Encountered an unsupported metadata record (%d) " |
166 | "at offset %" PRId64 "." , |
167 | Vals: LoadedType, Vals: 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(V&: 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>(Val: R.get())) { |
181 | CurrentBufferBytes = BE->size(); |
182 | } else if (Header.Version >= 3) { |
183 | if (OffsetPtr - PreReadOffset > CurrentBufferBytes) |
184 | return createStringError( |
185 | EC: std::make_error_code(e: std::errc::executable_format_error), |
186 | Fmt: "Buffer over-read at offset %" PRId64 " (over-read by %" PRId64 |
187 | " bytes); Record Type = %s." , |
188 | Vals: OffsetPtr, Vals: (OffsetPtr - PreReadOffset) - CurrentBufferBytes, |
189 | Vals: Record::kindToString(K: 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 | |