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