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