195 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8#include "llvm/XRay/FDRRecordProducer.h"9#include "llvm/Support/DataExtractor.h"10 11#include <cstdint>12 13using namespace llvm;14using namespace llvm::xray;15 16namespace {17 18// Keep this in sync with the values written in the XRay FDR mode runtime in19// compiler-rt.20enum 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} // namespace35 36static Expected<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 std::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 std::make_unique<EndBufferRecord>();52 case MetadataRecordKinds::NewCPUIdKind:53 return std::make_unique<NewCPUIDRecord>();54 case MetadataRecordKinds::TSCWrapKind:55 return std::make_unique<TSCWrapRecord>();56 case MetadataRecordKinds::WalltimeMarkerKind:57 return std::make_unique<WallclockRecord>();58 case MetadataRecordKinds::CustomEventMarkerKind:59 if (Header.Version >= 5)60 return std::make_unique<CustomEventRecordV5>();61 return std::make_unique<CustomEventRecord>();62 case MetadataRecordKinds::CallArgumentKind:63 return std::make_unique<CallArgRecord>();64 case MetadataRecordKinds::BufferExtentsKind:65 return std::make_unique<BufferExtents>();66 case MetadataRecordKinds::TypedEventMarkerKind:67 return std::make_unique<TypedEventRecord>();68 case MetadataRecordKinds::PidKind:69 return std::make_unique<PIDRecord>();70 case MetadataRecordKinds::EnumEndMarker:71 llvm_unreachable("Invalid MetadataRecordKind");72 }73 llvm_unreachable("Unhandled MetadataRecordKinds enum value");74}75 76static constexpr bool isMetadataIntroducer(uint8_t FirstByte) {77 return FirstByte & 0x01u;78}79 80Expected<std::unique_ptr<Record>>81FileBasedRecordProducer::findNextBufferExtent() {82 // We seek one byte at a time until we find a suitable buffer extents metadata83 // record introducer.84 std::unique_ptr<Record> R;85 while (!R) {86 auto PreReadOffset = OffsetPtr;87 uint8_t FirstByte = E.getU8(&OffsetPtr);88 if (OffsetPtr == PreReadOffset)89 return createStringError(90 std::make_error_code(std::errc::executable_format_error),91 "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);92 93 if (isMetadataIntroducer(FirstByte)) {94 auto LoadedType = FirstByte >> 1;95 if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {96 auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);97 if (!MetadataRecordOrErr)98 return MetadataRecordOrErr.takeError();99 100 R = std::move(MetadataRecordOrErr.get());101 RecordInitializer RI(E, OffsetPtr);102 if (auto Err = R->apply(RI))103 return std::move(Err);104 return std::move(R);105 }106 }107 }108 llvm_unreachable("Must always terminate with either an error or a record.");109}110 111Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {112 // First, we set up our result record.113 std::unique_ptr<Record> R;114 115 // Before we do any further reading, we should check whether we're at the end116 // of the current buffer we're been consuming. In FDR logs version >= 3, we117 // rely on the buffer extents record to determine how many bytes we should be118 // considering as valid records.119 if (Header.Version >= 3 && CurrentBufferBytes == 0) {120 // Find the next buffer extents record.121 auto BufferExtentsOrError = findNextBufferExtent();122 if (!BufferExtentsOrError)123 return joinErrors(124 BufferExtentsOrError.takeError(),125 createStringError(126 std::make_error_code(std::errc::executable_format_error),127 "Failed to find the next BufferExtents record."));128 129 R = std::move(BufferExtentsOrError.get());130 assert(R != nullptr);131 assert(isa<BufferExtents>(R.get()));132 auto BE = cast<BufferExtents>(R.get());133 CurrentBufferBytes = BE->size();134 return std::move(R);135 }136 137 //138 // At the top level, we read one byte to determine the type of the record to139 // create. This byte will comprise of the following bits:140 //141 // - offset 0: A '1' indicates a metadata record, a '0' indicates a function142 // record.143 // - offsets 1-7: For metadata records, this will indicate the kind of144 // metadata record should be loaded.145 //146 // We read first byte, then create the appropriate type of record to consume147 // the rest of the bytes.148 auto PreReadOffset = OffsetPtr;149 uint8_t FirstByte = E.getU8(&OffsetPtr);150 if (OffsetPtr == PreReadOffset)151 return createStringError(152 std::make_error_code(std::errc::executable_format_error),153 "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);154 155 // For metadata records, handle especially here.156 if (isMetadataIntroducer(FirstByte)) {157 auto LoadedType = FirstByte >> 1;158 auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);159 if (!MetadataRecordOrErr)160 return joinErrors(161 MetadataRecordOrErr.takeError(),162 createStringError(163 std::make_error_code(std::errc::executable_format_error),164 "Encountered an unsupported metadata record (%d) "165 "at offset %" PRId64 ".",166 LoadedType, PreReadOffset));167 R = std::move(MetadataRecordOrErr.get());168 } else {169 R = std::make_unique<FunctionRecord>();170 }171 RecordInitializer RI(E, OffsetPtr);172 173 if (auto Err = R->apply(RI))174 return std::move(Err);175 176 // If we encountered a BufferExtents record, we should record the remaining177 // bytes for the current buffer, to determine when we should start ignoring178 // potentially malformed data and looking for buffer extents records.179 if (auto BE = dyn_cast<BufferExtents>(R.get())) {180 CurrentBufferBytes = BE->size();181 } else if (Header.Version >= 3) {182 if (OffsetPtr - PreReadOffset > CurrentBufferBytes)183 return createStringError(184 std::make_error_code(std::errc::executable_format_error),185 "Buffer over-read at offset %" PRId64 " (over-read by %" PRId64186 " bytes); Record Type = %s.",187 OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,188 Record::kindToString(R->getRecordType()).data());189 190 CurrentBufferBytes -= OffsetPtr - PreReadOffset;191 }192 assert(R != nullptr);193 return std::move(R);194}195