#include "llvm/XRay/FDRRecordProducer.h"
#include "llvm/Support/DataExtractor.h"
#include <cstdint>
namespace llvm {
namespace xray {
namespace {
enum MetadataRecordKinds : uint8_t {
NewBufferKind,
EndOfBufferKind,
NewCPUIdKind,
TSCWrapKind,
WalltimeMarkerKind,
CustomEventMarkerKind,
CallArgumentKind,
BufferExtentsKind,
TypedEventMarkerKind,
PidKind,
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;
}
}
Expected<std::unique_ptr<Record>>
FileBasedRecordProducer::findNextBufferExtent() {
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() {
std::unique_ptr<Record> R;
if (Header.Version >= 3 && CurrentBufferBytes == 0) {
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);
}
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;
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 (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);
}
} }