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