//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/XRay/FDRRecordProducer.h" #include "llvm/Support/DataExtractor.h" #include namespace llvm { namespace xray { namespace { // Keep this in sync with the values written in the XRay FDR mode runtime in // compiler-rt. enum MetadataRecordKinds : uint8_t { NewBufferKind, EndOfBufferKind, NewCPUIdKind, TSCWrapKind, WalltimeMarkerKind, CustomEventMarkerKind, CallArgumentKind, BufferExtentsKind, TypedEventMarkerKind, PidKind, // This is an end marker, used to identify the upper bound for this enum. EnumEndMarker, }; Expected> metadataRecordType(const XRayFileHeader &Header, uint8_t T) { if (T >= static_cast(MetadataRecordKinds::EnumEndMarker)) return createStringError(std::make_error_code(std::errc::invalid_argument), "Invalid metadata record type: %d", T); switch (T) { case MetadataRecordKinds::NewBufferKind: return make_unique(); case MetadataRecordKinds::EndOfBufferKind: if (Header.Version >= 2) return createStringError( std::make_error_code(std::errc::executable_format_error), "End of buffer records are no longer supported starting version " "2 of the log."); return make_unique(); case MetadataRecordKinds::NewCPUIdKind: return make_unique(); case MetadataRecordKinds::TSCWrapKind: return make_unique(); case MetadataRecordKinds::WalltimeMarkerKind: return make_unique(); case MetadataRecordKinds::CustomEventMarkerKind: if (Header.Version >= 5) return make_unique(); return make_unique(); case MetadataRecordKinds::CallArgumentKind: return make_unique(); case MetadataRecordKinds::BufferExtentsKind: return make_unique(); case MetadataRecordKinds::TypedEventMarkerKind: return make_unique(); case MetadataRecordKinds::PidKind: return make_unique(); case MetadataRecordKinds::EnumEndMarker: llvm_unreachable("Invalid MetadataRecordKind"); } llvm_unreachable("Unhandled MetadataRecordKinds enum value"); } constexpr bool isMetadataIntroducer(uint8_t FirstByte) { return FirstByte & 0x01u; } } // namespace Expected> FileBasedRecordProducer::findNextBufferExtent() { // We seek one byte at a time until we find a suitable buffer extents metadata // record introducer. std::unique_ptr R; while (!R) { auto PreReadOffset = OffsetPtr; uint8_t FirstByte = E.getU8(&OffsetPtr); if (OffsetPtr == PreReadOffset) return createStringError( std::make_error_code(std::errc::executable_format_error), "Failed reading one byte from offset %d.", OffsetPtr); if (isMetadataIntroducer(FirstByte)) { auto LoadedType = FirstByte >> 1; if (LoadedType == MetadataRecordKinds::BufferExtentsKind) { auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType); if (!MetadataRecordOrErr) return MetadataRecordOrErr.takeError(); R = std::move(MetadataRecordOrErr.get()); RecordInitializer RI(E, OffsetPtr); if (auto Err = R->apply(RI)) return std::move(Err); return std::move(R); } } } llvm_unreachable("Must always terminate with either an error or a record."); } Expected> FileBasedRecordProducer::produce() { // First, we set up our result record. std::unique_ptr R; // Before we do any further reading, we should check whether we're at the end // of the current buffer we're been consuming. In FDR logs version >= 3, we // rely on the buffer extents record to determine how many bytes we should be // considering as valid records. if (Header.Version >= 3 && CurrentBufferBytes == 0) { // Find the next buffer extents record. auto BufferExtentsOrError = findNextBufferExtent(); if (!BufferExtentsOrError) return joinErrors( BufferExtentsOrError.takeError(), createStringError( std::make_error_code(std::errc::executable_format_error), "Failed to find the next BufferExtents record.")); R = std::move(BufferExtentsOrError.get()); assert(R != nullptr); assert(isa(R.get())); auto BE = dyn_cast(R.get()); CurrentBufferBytes = BE->size(); return std::move(R); } // // At the top level, we read one byte to determine the type of the record to // create. This byte will comprise of the following bits: // // - offset 0: A '1' indicates a metadata record, a '0' indicates a function // record. // - offsets 1-7: For metadata records, this will indicate the kind of // metadata record should be loaded. // // We read first byte, then create the appropriate type of record to consume // the rest of the bytes. auto PreReadOffset = OffsetPtr; uint8_t FirstByte = E.getU8(&OffsetPtr); if (OffsetPtr == PreReadOffset) return createStringError( std::make_error_code(std::errc::executable_format_error), "Failed reading one byte from offset %d.", OffsetPtr); // For metadata records, handle especially here. if (isMetadataIntroducer(FirstByte)) { auto LoadedType = FirstByte >> 1; auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType); if (!MetadataRecordOrErr) return joinErrors( MetadataRecordOrErr.takeError(), createStringError( std::make_error_code(std::errc::executable_format_error), "Encountered an unsupported metadata record (%d) at offset %d.", LoadedType, PreReadOffset)); R = std::move(MetadataRecordOrErr.get()); } else { R = llvm::make_unique(); } RecordInitializer RI(E, OffsetPtr); if (auto Err = R->apply(RI)) return std::move(Err); // If we encountered a BufferExtents record, we should record the remaining // bytes for the current buffer, to determine when we should start ignoring // potentially malformed data and looking for buffer extents records. if (auto BE = dyn_cast(R.get())) { CurrentBufferBytes = BE->size(); } else if (Header.Version >= 3) { if (OffsetPtr - PreReadOffset > CurrentBufferBytes) return createStringError( std::make_error_code(std::errc::executable_format_error), "Buffer over-read at offset %d (over-read by %d bytes); Record Type " "= %s.", OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes, Record::kindToString(R->getRecordType()).data()); CurrentBufferBytes -= OffsetPtr - PreReadOffset; } assert(R != nullptr); return std::move(R); } } // namespace xray } // namespace llvm