123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- //===- 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 <cstdint>
- 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<std::unique_ptr<Record>>
- metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
- if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
- return createStringError(std::make_error_code(std::errc::invalid_argument),
- "Invalid metadata record type: %d", T);
- switch (T) {
- case MetadataRecordKinds::NewBufferKind:
- return std::make_unique<NewBufferRecord>();
- 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 std::make_unique<EndBufferRecord>();
- case MetadataRecordKinds::NewCPUIdKind:
- return std::make_unique<NewCPUIDRecord>();
- case MetadataRecordKinds::TSCWrapKind:
- return std::make_unique<TSCWrapRecord>();
- case MetadataRecordKinds::WalltimeMarkerKind:
- return std::make_unique<WallclockRecord>();
- case MetadataRecordKinds::CustomEventMarkerKind:
- if (Header.Version >= 5)
- return std::make_unique<CustomEventRecordV5>();
- return std::make_unique<CustomEventRecord>();
- case MetadataRecordKinds::CallArgumentKind:
- return std::make_unique<CallArgRecord>();
- case MetadataRecordKinds::BufferExtentsKind:
- return std::make_unique<BufferExtents>();
- case MetadataRecordKinds::TypedEventMarkerKind:
- return std::make_unique<TypedEventRecord>();
- case MetadataRecordKinds::PidKind:
- return std::make_unique<PIDRecord>();
- case MetadataRecordKinds::EnumEndMarker:
- llvm_unreachable("Invalid MetadataRecordKind");
- }
- llvm_unreachable("Unhandled MetadataRecordKinds enum value");
- }
- constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
- return FirstByte & 0x01u;
- }
- } // namespace
- Expected<std::unique_ptr<Record>>
- FileBasedRecordProducer::findNextBufferExtent() {
- // We seek one byte at a time until we find a suitable buffer extents metadata
- // record introducer.
- std::unique_ptr<Record> 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 %" PRId64 ".", 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<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
- // First, we set up our result record.
- std::unique_ptr<Record> 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<BufferExtents>(R.get()));
- auto BE = cast<BufferExtents>(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 %" PRId64 ".", 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 %" PRId64 ".",
- LoadedType, PreReadOffset));
- R = std::move(MetadataRecordOrErr.get());
- } else {
- R = std::make_unique<FunctionRecord>();
- }
- 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<BufferExtents>(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 %" PRId64 " (over-read by %" PRId64
- " 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
|