Home | History | Annotate | Line # | Download | only in XRay
      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-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 #include "llvm/XRay/FDRRecordProducer.h"
      9 #include "llvm/Support/DataExtractor.h"
     10 
     11 #include <cstdint>
     12 
     13 namespace llvm {
     14 namespace xray {
     15 
     16 namespace {
     17 
     18 // Keep this in sync with the values written in the XRay FDR mode runtime in
     19 // compiler-rt.
     20 enum 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 
     35 Expected<std::unique_ptr<Record>>
     36 metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
     37 
     38   if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
     39     return createStringError(std::make_error_code(std::errc::invalid_argument),
     40                              "Invalid metadata record type: %d", T);
     41   switch (T) {
     42   case MetadataRecordKinds::NewBufferKind:
     43     return std::make_unique<NewBufferRecord>();
     44   case MetadataRecordKinds::EndOfBufferKind:
     45     if (Header.Version >= 2)
     46       return createStringError(
     47           std::make_error_code(std::errc::executable_format_error),
     48           "End of buffer records are no longer supported starting version "
     49           "2 of the log.");
     50     return std::make_unique<EndBufferRecord>();
     51   case MetadataRecordKinds::NewCPUIdKind:
     52     return std::make_unique<NewCPUIDRecord>();
     53   case MetadataRecordKinds::TSCWrapKind:
     54     return std::make_unique<TSCWrapRecord>();
     55   case MetadataRecordKinds::WalltimeMarkerKind:
     56     return std::make_unique<WallclockRecord>();
     57   case MetadataRecordKinds::CustomEventMarkerKind:
     58     if (Header.Version >= 5)
     59       return std::make_unique<CustomEventRecordV5>();
     60     return std::make_unique<CustomEventRecord>();
     61   case MetadataRecordKinds::CallArgumentKind:
     62     return std::make_unique<CallArgRecord>();
     63   case MetadataRecordKinds::BufferExtentsKind:
     64     return std::make_unique<BufferExtents>();
     65   case MetadataRecordKinds::TypedEventMarkerKind:
     66     return std::make_unique<TypedEventRecord>();
     67   case MetadataRecordKinds::PidKind:
     68     return std::make_unique<PIDRecord>();
     69   case MetadataRecordKinds::EnumEndMarker:
     70     llvm_unreachable("Invalid MetadataRecordKind");
     71   }
     72   llvm_unreachable("Unhandled MetadataRecordKinds enum value");
     73 }
     74 
     75 constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
     76   return FirstByte & 0x01u;
     77 }
     78 
     79 } // namespace
     80 
     81 Expected<std::unique_ptr<Record>>
     82 FileBasedRecordProducer::findNextBufferExtent() {
     83   // We seek one byte at a time until we find a suitable buffer extents metadata
     84   // record introducer.
     85   std::unique_ptr<Record> R;
     86   while (!R) {
     87     auto PreReadOffset = OffsetPtr;
     88     uint8_t FirstByte = E.getU8(&OffsetPtr);
     89     if (OffsetPtr == PreReadOffset)
     90       return createStringError(
     91           std::make_error_code(std::errc::executable_format_error),
     92           "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
     93 
     94     if (isMetadataIntroducer(FirstByte)) {
     95       auto LoadedType = FirstByte >> 1;
     96       if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {
     97         auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
     98         if (!MetadataRecordOrErr)
     99           return MetadataRecordOrErr.takeError();
    100 
    101         R = std::move(MetadataRecordOrErr.get());
    102         RecordInitializer RI(E, OffsetPtr);
    103         if (auto Err = R->apply(RI))
    104           return std::move(Err);
    105         return std::move(R);
    106       }
    107     }
    108   }
    109   llvm_unreachable("Must always terminate with either an error or a record.");
    110 }
    111 
    112 Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
    113   // First, we set up our result record.
    114   std::unique_ptr<Record> R;
    115 
    116   // Before we do any further reading, we should check whether we're at the end
    117   // of the current buffer we're been consuming. In FDR logs version >= 3, we
    118   // rely on the buffer extents record to determine how many bytes we should be
    119   // considering as valid records.
    120   if (Header.Version >= 3 && CurrentBufferBytes == 0) {
    121     // Find the next buffer extents record.
    122     auto BufferExtentsOrError = findNextBufferExtent();
    123     if (!BufferExtentsOrError)
    124       return joinErrors(
    125           BufferExtentsOrError.takeError(),
    126           createStringError(
    127               std::make_error_code(std::errc::executable_format_error),
    128               "Failed to find the next BufferExtents record."));
    129 
    130     R = std::move(BufferExtentsOrError.get());
    131     assert(R != nullptr);
    132     assert(isa<BufferExtents>(R.get()));
    133     auto BE = cast<BufferExtents>(R.get());
    134     CurrentBufferBytes = BE->size();
    135     return std::move(R);
    136   }
    137 
    138   //
    139   // At the top level, we read one byte to determine the type of the record to
    140   // create. This byte will comprise of the following bits:
    141   //
    142   //   - offset 0: A '1' indicates a metadata record, a '0' indicates a function
    143   //     record.
    144   //   - offsets 1-7: For metadata records, this will indicate the kind of
    145   //     metadata record should be loaded.
    146   //
    147   // We read first byte, then create the appropriate type of record to consume
    148   // the rest of the bytes.
    149   auto PreReadOffset = OffsetPtr;
    150   uint8_t FirstByte = E.getU8(&OffsetPtr);
    151   if (OffsetPtr == PreReadOffset)
    152     return createStringError(
    153         std::make_error_code(std::errc::executable_format_error),
    154         "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
    155 
    156   // For metadata records, handle especially here.
    157   if (isMetadataIntroducer(FirstByte)) {
    158     auto LoadedType = FirstByte >> 1;
    159     auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
    160     if (!MetadataRecordOrErr)
    161       return joinErrors(
    162           MetadataRecordOrErr.takeError(),
    163           createStringError(
    164               std::make_error_code(std::errc::executable_format_error),
    165               "Encountered an unsupported metadata record (%d) "
    166               "at offset %" PRId64 ".",
    167               LoadedType, PreReadOffset));
    168     R = std::move(MetadataRecordOrErr.get());
    169   } else {
    170     R = std::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 %" PRId64 " (over-read by %" PRId64
    187           " bytes); Record Type = %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