199 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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
 |