xref: /freebsd/contrib/llvm-project/llvm/lib/XRay/FDRRecordProducer.cpp (revision 5b27928474e6a4103d65b347544705c40c9618fd)
10b57cec5SDimitry Andric //===- FDRRecordProducer.cpp - XRay FDR Mode Record Producer --------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric #include "llvm/XRay/FDRRecordProducer.h"
90b57cec5SDimitry Andric #include "llvm/Support/DataExtractor.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include <cstdint>
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric namespace llvm {
140b57cec5SDimitry Andric namespace xray {
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric namespace {
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric // Keep this in sync with the values written in the XRay FDR mode runtime in
190b57cec5SDimitry Andric // compiler-rt.
200b57cec5SDimitry Andric enum MetadataRecordKinds : uint8_t {
210b57cec5SDimitry Andric   NewBufferKind,
220b57cec5SDimitry Andric   EndOfBufferKind,
230b57cec5SDimitry Andric   NewCPUIdKind,
240b57cec5SDimitry Andric   TSCWrapKind,
250b57cec5SDimitry Andric   WalltimeMarkerKind,
260b57cec5SDimitry Andric   CustomEventMarkerKind,
270b57cec5SDimitry Andric   CallArgumentKind,
280b57cec5SDimitry Andric   BufferExtentsKind,
290b57cec5SDimitry Andric   TypedEventMarkerKind,
300b57cec5SDimitry Andric   PidKind,
310b57cec5SDimitry Andric   // This is an end marker, used to identify the upper bound for this enum.
320b57cec5SDimitry Andric   EnumEndMarker,
330b57cec5SDimitry Andric };
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric Expected<std::unique_ptr<Record>>
metadataRecordType(const XRayFileHeader & Header,uint8_t T)360b57cec5SDimitry Andric metadataRecordType(const XRayFileHeader &Header, uint8_t T) {
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric   if (T >= static_cast<uint8_t>(MetadataRecordKinds::EnumEndMarker))
390b57cec5SDimitry Andric     return createStringError(std::make_error_code(std::errc::invalid_argument),
400b57cec5SDimitry Andric                              "Invalid metadata record type: %d", T);
410b57cec5SDimitry Andric   switch (T) {
420b57cec5SDimitry Andric   case MetadataRecordKinds::NewBufferKind:
43*8bcb0991SDimitry Andric     return std::make_unique<NewBufferRecord>();
440b57cec5SDimitry Andric   case MetadataRecordKinds::EndOfBufferKind:
450b57cec5SDimitry Andric     if (Header.Version >= 2)
460b57cec5SDimitry Andric       return createStringError(
470b57cec5SDimitry Andric           std::make_error_code(std::errc::executable_format_error),
480b57cec5SDimitry Andric           "End of buffer records are no longer supported starting version "
490b57cec5SDimitry Andric           "2 of the log.");
50*8bcb0991SDimitry Andric     return std::make_unique<EndBufferRecord>();
510b57cec5SDimitry Andric   case MetadataRecordKinds::NewCPUIdKind:
52*8bcb0991SDimitry Andric     return std::make_unique<NewCPUIDRecord>();
530b57cec5SDimitry Andric   case MetadataRecordKinds::TSCWrapKind:
54*8bcb0991SDimitry Andric     return std::make_unique<TSCWrapRecord>();
550b57cec5SDimitry Andric   case MetadataRecordKinds::WalltimeMarkerKind:
56*8bcb0991SDimitry Andric     return std::make_unique<WallclockRecord>();
570b57cec5SDimitry Andric   case MetadataRecordKinds::CustomEventMarkerKind:
580b57cec5SDimitry Andric     if (Header.Version >= 5)
59*8bcb0991SDimitry Andric       return std::make_unique<CustomEventRecordV5>();
60*8bcb0991SDimitry Andric     return std::make_unique<CustomEventRecord>();
610b57cec5SDimitry Andric   case MetadataRecordKinds::CallArgumentKind:
62*8bcb0991SDimitry Andric     return std::make_unique<CallArgRecord>();
630b57cec5SDimitry Andric   case MetadataRecordKinds::BufferExtentsKind:
64*8bcb0991SDimitry Andric     return std::make_unique<BufferExtents>();
650b57cec5SDimitry Andric   case MetadataRecordKinds::TypedEventMarkerKind:
66*8bcb0991SDimitry Andric     return std::make_unique<TypedEventRecord>();
670b57cec5SDimitry Andric   case MetadataRecordKinds::PidKind:
68*8bcb0991SDimitry Andric     return std::make_unique<PIDRecord>();
690b57cec5SDimitry Andric   case MetadataRecordKinds::EnumEndMarker:
700b57cec5SDimitry Andric     llvm_unreachable("Invalid MetadataRecordKind");
710b57cec5SDimitry Andric   }
720b57cec5SDimitry Andric   llvm_unreachable("Unhandled MetadataRecordKinds enum value");
730b57cec5SDimitry Andric }
740b57cec5SDimitry Andric 
isMetadataIntroducer(uint8_t FirstByte)750b57cec5SDimitry Andric constexpr bool isMetadataIntroducer(uint8_t FirstByte) {
760b57cec5SDimitry Andric   return FirstByte & 0x01u;
770b57cec5SDimitry Andric }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric } // namespace
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric Expected<std::unique_ptr<Record>>
findNextBufferExtent()820b57cec5SDimitry Andric FileBasedRecordProducer::findNextBufferExtent() {
830b57cec5SDimitry Andric   // We seek one byte at a time until we find a suitable buffer extents metadata
840b57cec5SDimitry Andric   // record introducer.
850b57cec5SDimitry Andric   std::unique_ptr<Record> R;
860b57cec5SDimitry Andric   while (!R) {
870b57cec5SDimitry Andric     auto PreReadOffset = OffsetPtr;
880b57cec5SDimitry Andric     uint8_t FirstByte = E.getU8(&OffsetPtr);
890b57cec5SDimitry Andric     if (OffsetPtr == PreReadOffset)
900b57cec5SDimitry Andric       return createStringError(
910b57cec5SDimitry Andric           std::make_error_code(std::errc::executable_format_error),
92*8bcb0991SDimitry Andric           "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric     if (isMetadataIntroducer(FirstByte)) {
950b57cec5SDimitry Andric       auto LoadedType = FirstByte >> 1;
960b57cec5SDimitry Andric       if (LoadedType == MetadataRecordKinds::BufferExtentsKind) {
970b57cec5SDimitry Andric         auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
980b57cec5SDimitry Andric         if (!MetadataRecordOrErr)
990b57cec5SDimitry Andric           return MetadataRecordOrErr.takeError();
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric         R = std::move(MetadataRecordOrErr.get());
1020b57cec5SDimitry Andric         RecordInitializer RI(E, OffsetPtr);
1030b57cec5SDimitry Andric         if (auto Err = R->apply(RI))
1040b57cec5SDimitry Andric           return std::move(Err);
1050b57cec5SDimitry Andric         return std::move(R);
1060b57cec5SDimitry Andric       }
1070b57cec5SDimitry Andric     }
1080b57cec5SDimitry Andric   }
1090b57cec5SDimitry Andric   llvm_unreachable("Must always terminate with either an error or a record.");
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric 
produce()1120b57cec5SDimitry Andric Expected<std::unique_ptr<Record>> FileBasedRecordProducer::produce() {
1130b57cec5SDimitry Andric   // First, we set up our result record.
1140b57cec5SDimitry Andric   std::unique_ptr<Record> R;
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   // Before we do any further reading, we should check whether we're at the end
1170b57cec5SDimitry Andric   // of the current buffer we're been consuming. In FDR logs version >= 3, we
1180b57cec5SDimitry Andric   // rely on the buffer extents record to determine how many bytes we should be
1190b57cec5SDimitry Andric   // considering as valid records.
1200b57cec5SDimitry Andric   if (Header.Version >= 3 && CurrentBufferBytes == 0) {
1210b57cec5SDimitry Andric     // Find the next buffer extents record.
1220b57cec5SDimitry Andric     auto BufferExtentsOrError = findNextBufferExtent();
1230b57cec5SDimitry Andric     if (!BufferExtentsOrError)
1240b57cec5SDimitry Andric       return joinErrors(
1250b57cec5SDimitry Andric           BufferExtentsOrError.takeError(),
1260b57cec5SDimitry Andric           createStringError(
1270b57cec5SDimitry Andric               std::make_error_code(std::errc::executable_format_error),
1280b57cec5SDimitry Andric               "Failed to find the next BufferExtents record."));
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric     R = std::move(BufferExtentsOrError.get());
1310b57cec5SDimitry Andric     assert(R != nullptr);
1320b57cec5SDimitry Andric     assert(isa<BufferExtents>(R.get()));
133*8bcb0991SDimitry Andric     auto BE = cast<BufferExtents>(R.get());
1340b57cec5SDimitry Andric     CurrentBufferBytes = BE->size();
1350b57cec5SDimitry Andric     return std::move(R);
1360b57cec5SDimitry Andric   }
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   //
1390b57cec5SDimitry Andric   // At the top level, we read one byte to determine the type of the record to
1400b57cec5SDimitry Andric   // create. This byte will comprise of the following bits:
1410b57cec5SDimitry Andric   //
1420b57cec5SDimitry Andric   //   - offset 0: A '1' indicates a metadata record, a '0' indicates a function
1430b57cec5SDimitry Andric   //     record.
1440b57cec5SDimitry Andric   //   - offsets 1-7: For metadata records, this will indicate the kind of
1450b57cec5SDimitry Andric   //     metadata record should be loaded.
1460b57cec5SDimitry Andric   //
1470b57cec5SDimitry Andric   // We read first byte, then create the appropriate type of record to consume
1480b57cec5SDimitry Andric   // the rest of the bytes.
1490b57cec5SDimitry Andric   auto PreReadOffset = OffsetPtr;
1500b57cec5SDimitry Andric   uint8_t FirstByte = E.getU8(&OffsetPtr);
1510b57cec5SDimitry Andric   if (OffsetPtr == PreReadOffset)
1520b57cec5SDimitry Andric     return createStringError(
1530b57cec5SDimitry Andric         std::make_error_code(std::errc::executable_format_error),
154*8bcb0991SDimitry Andric         "Failed reading one byte from offset %" PRId64 ".", OffsetPtr);
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   // For metadata records, handle especially here.
1570b57cec5SDimitry Andric   if (isMetadataIntroducer(FirstByte)) {
1580b57cec5SDimitry Andric     auto LoadedType = FirstByte >> 1;
1590b57cec5SDimitry Andric     auto MetadataRecordOrErr = metadataRecordType(Header, LoadedType);
1600b57cec5SDimitry Andric     if (!MetadataRecordOrErr)
1610b57cec5SDimitry Andric       return joinErrors(
1620b57cec5SDimitry Andric           MetadataRecordOrErr.takeError(),
1630b57cec5SDimitry Andric           createStringError(
1640b57cec5SDimitry Andric               std::make_error_code(std::errc::executable_format_error),
165*8bcb0991SDimitry Andric               "Encountered an unsupported metadata record (%d) "
166*8bcb0991SDimitry Andric               "at offset %" PRId64 ".",
1670b57cec5SDimitry Andric               LoadedType, PreReadOffset));
1680b57cec5SDimitry Andric     R = std::move(MetadataRecordOrErr.get());
1690b57cec5SDimitry Andric   } else {
170*8bcb0991SDimitry Andric     R = std::make_unique<FunctionRecord>();
1710b57cec5SDimitry Andric   }
1720b57cec5SDimitry Andric   RecordInitializer RI(E, OffsetPtr);
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   if (auto Err = R->apply(RI))
1750b57cec5SDimitry Andric     return std::move(Err);
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric   // If we encountered a BufferExtents record, we should record the remaining
1780b57cec5SDimitry Andric   // bytes for the current buffer, to determine when we should start ignoring
1790b57cec5SDimitry Andric   // potentially malformed data and looking for buffer extents records.
1800b57cec5SDimitry Andric   if (auto BE = dyn_cast<BufferExtents>(R.get())) {
1810b57cec5SDimitry Andric     CurrentBufferBytes = BE->size();
1820b57cec5SDimitry Andric   } else if (Header.Version >= 3) {
1830b57cec5SDimitry Andric     if (OffsetPtr - PreReadOffset > CurrentBufferBytes)
1840b57cec5SDimitry Andric       return createStringError(
1850b57cec5SDimitry Andric           std::make_error_code(std::errc::executable_format_error),
186*8bcb0991SDimitry Andric           "Buffer over-read at offset %" PRId64 " (over-read by %" PRId64
187*8bcb0991SDimitry Andric           " bytes); Record Type = %s.",
1880b57cec5SDimitry Andric           OffsetPtr, (OffsetPtr - PreReadOffset) - CurrentBufferBytes,
1890b57cec5SDimitry Andric           Record::kindToString(R->getRecordType()).data());
1900b57cec5SDimitry Andric 
1910b57cec5SDimitry Andric     CurrentBufferBytes -= OffsetPtr - PreReadOffset;
1920b57cec5SDimitry Andric   }
1930b57cec5SDimitry Andric   assert(R != nullptr);
1940b57cec5SDimitry Andric   return std::move(R);
1950b57cec5SDimitry Andric }
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric } // namespace xray
1980b57cec5SDimitry Andric } // namespace llvm
199