blob: 68ab3db06208d7e58be1c7d523d9fea4fde6657a [file] [log] [blame]
//===- 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/FDRRecords.h"
namespace llvm {
namespace xray {
Error RecordInitializer::visit(BufferExtents &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr, sizeof(uint64_t)))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a buffer extent (%" PRId64 ").", OffsetPtr);
auto PreReadOffset = OffsetPtr;
R.Size = E.getU64(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(std::make_error_code(std::errc::invalid_argument),
"Cannot read buffer extent at offset %" PRId64 ".",
OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
return Error::success();
}
Error RecordInitializer::visit(WallclockRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a wallclock record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = OffsetPtr;
R.Seconds = E.getU64(&OffsetPtr);
if (OffsetPtr == PreReadOffset)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read wall clock 'seconds' field at offset %" PRId64 ".",
OffsetPtr);
PreReadOffset = OffsetPtr;
R.Nanos = E.getU32(&OffsetPtr);
if (OffsetPtr == PreReadOffset)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read wall clock 'nanos' field at offset %" PRId64 ".",
OffsetPtr);
// Align to metadata record size boundary.
assert(OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
return Error::success();
}
Error RecordInitializer::visit(NewCPUIDRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a new cpu id record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = OffsetPtr;
R.CPUId = E.getU16(&OffsetPtr);
if (OffsetPtr == PreReadOffset)
return createStringError(std::make_error_code(std::errc::invalid_argument),
"Cannot read CPU id at offset %" PRId64 ".",
OffsetPtr);
PreReadOffset = OffsetPtr;
R.TSC = E.getU64(&OffsetPtr);
if (OffsetPtr == PreReadOffset)
return createStringError(std::make_error_code(std::errc::invalid_argument),
"Cannot read CPU TSC at offset %" PRId64 ".",
OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
return Error::success();
}
Error RecordInitializer::visit(TSCWrapRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a new TSC wrap record (%" PRId64 ").", OffsetPtr);
auto PreReadOffset = OffsetPtr;
R.BaseTSC = E.getU64(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read TSC wrap record at offset %" PRId64 ".", OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
return Error::success();
}
Error RecordInitializer::visit(CustomEventRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = OffsetPtr;
R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a custom event record size field offset %" PRId64 ".",
OffsetPtr);
if (R.Size <= 0)
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid size for custom event (size = %d) at offset %" PRId64 ".",
R.Size, OffsetPtr);
PreReadOffset = OffsetPtr;
R.TSC = E.getU64(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a custom event TSC field at offset %" PRId64 ".",
OffsetPtr);
// For version 4 onwards, of the FDR log, we want to also capture the CPU ID
// of the custom event.
if (Version >= 4) {
PreReadOffset = OffsetPtr;
R.CPU = E.getU16(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Missing CPU field at offset %" PRId64 ".", OffsetPtr);
}
assert(OffsetPtr > BeginOffset &&
OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
// Next we read in a fixed chunk of data from the given offset.
if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Cannot read %d bytes of custom event data from offset %" PRId64 ".",
R.Size, OffsetPtr);
std::vector<uint8_t> Buffer;
Buffer.resize(R.Size);
PreReadOffset = OffsetPtr;
if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading data into buffer of size %d at offset %" PRId64 ".",
R.Size, OffsetPtr);
assert(OffsetPtr >= PreReadOffset);
if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading enough bytes for the custom event payload -- read "
"%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
R.Data.assign(Buffer.begin(), Buffer.end());
return Error::success();
}
Error RecordInitializer::visit(CustomEventRecordV5 &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a custom event record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = OffsetPtr;
R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a custom event record size field offset %" PRId64 ".",
OffsetPtr);
if (R.Size <= 0)
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid size for custom event (size = %d) at offset %" PRId64 ".",
R.Size, OffsetPtr);
PreReadOffset = OffsetPtr;
R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a custom event record TSC delta field at offset "
"%" PRId64 ".",
OffsetPtr);
assert(OffsetPtr > BeginOffset &&
OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
// Next we read in a fixed chunk of data from the given offset.
if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Cannot read %d bytes of custom event data from offset %" PRId64 ".",
R.Size, OffsetPtr);
std::vector<uint8_t> Buffer;
Buffer.resize(R.Size);
PreReadOffset = OffsetPtr;
if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading data into buffer of size %d at offset %" PRId64 ".",
R.Size, OffsetPtr);
assert(OffsetPtr >= PreReadOffset);
if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading enough bytes for the custom event payload -- read "
"%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
R.Data.assign(Buffer.begin(), Buffer.end());
return Error::success();
}
Error RecordInitializer::visit(TypedEventRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a typed event record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = OffsetPtr;
R.Size = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a typed event record size field offset %" PRId64 ".",
OffsetPtr);
if (R.Size <= 0)
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid size for typed event (size = %d) at offset %" PRId64 ".",
R.Size, OffsetPtr);
PreReadOffset = OffsetPtr;
R.Delta = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a typed event record TSC delta field at offset "
"%" PRId64 ".",
OffsetPtr);
PreReadOffset = OffsetPtr;
R.EventType = E.getU16(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a typed event record type field at offset %" PRId64 ".",
OffsetPtr);
assert(OffsetPtr > BeginOffset &&
OffsetPtr - BeginOffset <= MetadataRecord::kMetadataBodySize);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - BeginOffset);
// Next we read in a fixed chunk of data from the given offset.
if (!E.isValidOffsetForDataOfSize(OffsetPtr, R.Size))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Cannot read %d bytes of custom event data from offset %" PRId64 ".",
R.Size, OffsetPtr);
std::vector<uint8_t> Buffer;
Buffer.resize(R.Size);
PreReadOffset = OffsetPtr;
if (E.getU8(&OffsetPtr, Buffer.data(), R.Size) != Buffer.data())
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading data into buffer of size %d at offset %" PRId64 ".",
R.Size, OffsetPtr);
assert(OffsetPtr >= PreReadOffset);
if (OffsetPtr - PreReadOffset != static_cast<uint32_t>(R.Size))
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading enough bytes for the typed event payload -- read "
"%" PRId64 " expecting %d bytes at offset %" PRId64 ".",
OffsetPtr - PreReadOffset, R.Size, PreReadOffset);
R.Data.assign(Buffer.begin(), Buffer.end());
return Error::success();
}
Error RecordInitializer::visit(CallArgRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a call argument record (%" PRId64 ").",
OffsetPtr);
auto PreReadOffset = OffsetPtr;
R.Arg = E.getU64(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a call arg record at offset %" PRId64 ".", OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
return Error::success();
}
Error RecordInitializer::visit(PIDRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a process ID record (%" PRId64 ").", OffsetPtr);
auto PreReadOffset = OffsetPtr;
R.PID = E.getSigned(&OffsetPtr, 4);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a process ID record at offset %" PRId64 ".", OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
return Error::success();
}
Error RecordInitializer::visit(NewBufferRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a new buffer record (%" PRId64 ").", OffsetPtr);
auto PreReadOffset = OffsetPtr;
R.TID = E.getSigned(&OffsetPtr, sizeof(int32_t));
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Cannot read a new buffer record at offset %" PRId64 ".", OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize - (OffsetPtr - PreReadOffset);
return Error::success();
}
Error RecordInitializer::visit(EndBufferRecord &R) {
if (!E.isValidOffsetForDataOfSize(OffsetPtr,
MetadataRecord::kMetadataBodySize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for an end-of-buffer record (%" PRId64 ").",
OffsetPtr);
OffsetPtr += MetadataRecord::kMetadataBodySize;
return Error::success();
}
Error RecordInitializer::visit(FunctionRecord &R) {
// For function records, we need to retreat one byte back to read a full
// unsigned 32-bit value. The first four bytes will have the following
// layout:
//
// bit 0 : function record indicator (must be 0)
// bits 1..3 : function record type
// bits 4..32 : function id
//
if (OffsetPtr == 0 || !E.isValidOffsetForDataOfSize(
--OffsetPtr, FunctionRecord::kFunctionRecordSize))
return createStringError(
std::make_error_code(std::errc::bad_address),
"Invalid offset for a function record (%" PRId64 ").", OffsetPtr);
auto BeginOffset = OffsetPtr;
auto PreReadOffset = BeginOffset;
uint32_t Buffer = E.getU32(&OffsetPtr);
if (PreReadOffset == OffsetPtr)
return createStringError(
std::make_error_code(std::errc::bad_address),
"Cannot read function id field from offset %" PRId64 ".", OffsetPtr);
// To get the function record type, we shift the buffer one to the right
// (truncating the function record indicator) then take the three bits
// (0b0111) to get the record type as an unsigned value.
unsigned FunctionType = (Buffer >> 1) & 0x07u;
switch (FunctionType) {
case static_cast<unsigned>(RecordTypes::ENTER):
case static_cast<unsigned>(RecordTypes::ENTER_ARG):
case static_cast<unsigned>(RecordTypes::EXIT):
case static_cast<unsigned>(RecordTypes::TAIL_EXIT):
R.Kind = static_cast<RecordTypes>(FunctionType);
break;
default:
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Unknown function record type '%d' at offset %" PRId64 ".",
FunctionType, BeginOffset);
}
R.FuncId = Buffer >> 4;
PreReadOffset = OffsetPtr;
R.Delta = E.getU32(&OffsetPtr);
if (OffsetPtr == PreReadOffset)
return createStringError(
std::make_error_code(std::errc::invalid_argument),
"Failed reading TSC delta from offset %" PRId64 ".", OffsetPtr);
assert(FunctionRecord::kFunctionRecordSize == (OffsetPtr - BeginOffset));
return Error::success();
}
} // namespace xray
} // namespace llvm