Path: blob/main/contrib/llvm-project/llvm/lib/XRay/FDRRecordProducer.cpp
35234 views
//===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "llvm/XRay/FDRRecordProducer.h"8#include "llvm/Support/DataExtractor.h"910#include <cstdint>1112namespace llvm {13namespace xray {1415namespace {1617// Keep this in sync with the values written in the XRay FDR mode runtime in18// compiler-rt.19enum MetadataRecordKinds : uint8_t {20NewBufferKind,21EndOfBufferKind,22NewCPUIdKind,23TSCWrapKind,24WalltimeMarkerKind,25CustomEventMarkerKind,26CallArgumentKind,27BufferExtentsKind,28TypedEventMarkerKind,29PidKind,30// This is an end marker, used to identify the upper bound for this enum.31EnumEndMarker,32};3334Expected<std::unique_ptr<Record>>35metadataRecordType(const XRayFileHeader &Header, uint8_t T) {3637if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))38return createStringError(std::make_error_code(std::errc::invalid_argument),39"Invalid metadata record type: %d", T);40switch (T) {41case MetadataRecordKinds::NewBufferKind:42return std::make_unique<NewBufferRecord>();43case MetadataRecordKinds::EndOfBufferKind:44if (Header.Version >= 2)45return createStringError(46std::make_error_code(std::errc::executable_format_error),47"End of buffer records are no longer supported starting version "48"2 of the log.");49return std::make_unique<EndBufferRecord>();50case MetadataRecordKinds::NewCPUIdKind:51return std::make_unique<NewCPUIDRecord>();52case MetadataRecordKinds::TSCWrapKind:53return std::make_unique<TSCWrapRecord>();54case MetadataRecordKinds::WalltimeMarkerKind:55return std::make_unique<WallclockRecord>();56case MetadataRecordKinds::CustomEventMarkerKind:57if (Header.Version >= 5)58return std::make_unique<CustomEventRecordV5>();59return std::make_unique<CustomEventRecord>();60case MetadataRecordKinds::CallArgumentKind:61return std::make_unique<CallArgRecord>();62case MetadataRecordKinds::BufferExtentsKind:63return std::make_unique<BufferExtents>();64case MetadataRecordKinds::TypedEventMarkerKind:65return std::make_unique<TypedEventRecord>();66case MetadataRecordKinds::PidKind:67return std::make_unique<PIDRecord>();68case MetadataRecordKinds::EnumEndMarker:69llvm_unreachable("Invalid MetadataRecordKind");70}71llvm_unreachable("Unhandled MetadataRecordKinds enum value");72}7374constexpr bool isMetadataIntroducer(uint8_t FirstByte) {75return FirstByte & 0x01u;76}7778} // namespace7980Expected<std::unique_ptr<Record>>81FileBasedRecordProducer::findNextBufferExtent() {82// We seek one byte at a time until we find a suitable buffer extents metadata83// record introducer.84std::unique_ptr<Record> R;85while (!R) {86auto PreReadOffset = OffsetPtr;87uint8_t FirstByte = E.getU8(&OffsetPtr);88if (OffsetPtr == PreReadOffset)89return createStringError(90std::make_error_code(std::errc::executable_format_error),91"Failed reading one byte from offset %" PRId64 ".", OffsetPtr);9293if (isMetadataIntroducer(FirstByte)) {94auto LoadedType = FirstByte >> 1;95if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {96auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);97if (!MetadataRecordOrErr)98return MetadataRecordOrErr.takeError();99100R = std::move(MetadataRecordOrErr.get());101RecordInitializer RI(E, OffsetPtr);102if (auto Err = R->apply(RI))103return std::move(Err);104return std::move(R);105}106}107}108llvm_unreachable("Must always terminate with either an error or a record.");109}110111Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {112// First, we set up our result record.113std::unique_ptr<Record> R;114115// 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.119if (Header.Version >= 3 && CurrentBufferBytes == 0) {120// Find the next buffer extents record.121auto BufferExtentsOrError = findNextBufferExtent();122if (!BufferExtentsOrError)123return joinErrors(124BufferExtentsOrError.takeError(),125createStringError(126std::make_error_code(std::errc::executable_format_error),127"Failed to find the next BufferExtents record."));128129R = std::move(BufferExtentsOrError.get());130assert(R != nullptr);131assert(isa<BufferExtents>(R.get()));132auto BE = cast<BufferExtents>(R.get());133CurrentBufferBytes = BE->size();134return std::move(R);135}136137//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.148auto PreReadOffset = OffsetPtr;149uint8_t FirstByte = E.getU8(&OffsetPtr);150if (OffsetPtr == PreReadOffset)151return createStringError(152std::make_error_code(std::errc::executable_format_error),153"Failed reading one byte from offset %" PRId64 ".", OffsetPtr);154155// For metadata records, handle especially here.156if (isMetadataIntroducer(FirstByte)) {157auto LoadedType = FirstByte >> 1;158auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);159if (!MetadataRecordOrErr)160return joinErrors(161MetadataRecordOrErr.takeError(),162createStringError(163std::make_error_code(std::errc::executable_format_error),164"Encountered an unsupported metadata record (%d) "165"at offset %" PRId64 ".",166LoadedType, PreReadOffset));167R = std::move(MetadataRecordOrErr.get());168} else {169R = std::make_unique<FunctionRecord>();170}171RecordInitializer RI(E, OffsetPtr);172173if (auto Err = R->apply(RI))174return std::move(Err);175176// 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.179if (auto BE = dyn_cast<BufferExtents>(R.get())) {180CurrentBufferBytes = BE->size();181} else if (Header.Version >= 3) {182if (OffsetPtr - PreReadOffset > CurrentBufferBytes)183return createStringError(184std::make_error_code(std::errc::executable_format_error),185"Buffer over-read at offset %" PRId64 " (over-read by %" PRId64186" bytes); Record Type = %s.",187OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,188Record::kindToString(R->getRecordType()).data());189190CurrentBufferBytes -= OffsetPtr - PreReadOffset;191}192assert(R != nullptr);193return std::move(R);194}195196} // namespace xray197} // namespace llvm198199200