summaryrefslogtreecommitdiffstats
path: root/llvm/lib/XRay/Trace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/XRay/Trace.cpp')
-rw-r--r--llvm/lib/XRay/Trace.cpp144
1 files changed, 122 insertions, 22 deletions
diff --git a/llvm/lib/XRay/Trace.cpp b/llvm/lib/XRay/Trace.cpp
index d4ce6b2ffeb..d1fcf1c35b3 100644
--- a/llvm/lib/XRay/Trace.cpp
+++ b/llvm/lib/XRay/Trace.cpp
@@ -159,6 +159,7 @@ struct FDRState {
SCAN_TO_END_OF_THREAD_BUF,
CUSTOM_EVENT_DATA,
CALL_ARGUMENT,
+ BUFFER_EXTENTS,
};
Token Expects;
@@ -184,6 +185,8 @@ const char *fdrStateToTwine(const FDRState::Token &state) {
return "CUSTOM_EVENT_DATA";
case FDRState::Token::CALL_ARGUMENT:
return "CALL_ARGUMENT";
+ case FDRState::Token::BUFFER_EXTENTS:
+ return "BUFFER_EXTENTS";
}
return "UNKNOWN";
}
@@ -194,7 +197,9 @@ Error processFDRNewBufferRecord(FDRState &State, uint8_t RecordFirstByte,
if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF)
return make_error<StringError>(
- "Malformed log. Read New Buffer record kind out of sequence",
+ Twine("Malformed log. Read New Buffer record kind out of sequence; "
+ "expected: ") +
+ fdrStateToTwine(State.Expects),
std::make_error_code(std::errc::executable_format_error));
uint32_t OffsetPtr = 1; // 1 byte into record.
State.ThreadId = RecordExtractor.getU16(&OffsetPtr);
@@ -207,7 +212,9 @@ Error processFDREndOfBufferRecord(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor) {
if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF)
return make_error<StringError>(
- "Malformed log. Received EOB message without current buffer.",
+ Twine("Malformed log. Received EOB message without current buffer; "
+ "expected: ") +
+ fdrStateToTwine(State.Expects),
std::make_error_code(std::errc::executable_format_error));
State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF;
return Error::success();
@@ -219,7 +226,9 @@ Error processFDRNewCPUIdRecord(FDRState &State, uint8_t RecordFirstByte,
if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE &&
State.Expects != FDRState::Token::NEW_CPU_ID_RECORD)
return make_error<StringError>(
- "Malformed log. Read NewCPUId record kind out of sequence",
+ Twine("Malformed log. Read NewCPUId record kind out of sequence; "
+ "expected: ") +
+ fdrStateToTwine(State.Expects),
std::make_error_code(std::errc::executable_format_error));
uint32_t OffsetPtr = 1; // Read starting after the first byte.
State.CPUId = RecordExtractor.getU16(&OffsetPtr);
@@ -233,7 +242,9 @@ Error processFDRTSCWrapRecord(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor) {
if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE)
return make_error<StringError>(
- "Malformed log. Read TSCWrap record kind out of sequence",
+ Twine("Malformed log. Read TSCWrap record kind out of sequence; "
+ "expecting: ") +
+ fdrStateToTwine(State.Expects),
std::make_error_code(std::errc::executable_format_error));
uint32_t OffsetPtr = 1; // Read starting after the first byte.
State.BaseTSC = RecordExtractor.getU64(&OffsetPtr);
@@ -245,10 +256,13 @@ Error processFDRWallTimeRecord(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor) {
if (State.Expects != FDRState::Token::WALLCLOCK_RECORD)
return make_error<StringError>(
- "Malformed log. Read Wallclock record kind out of sequence",
+ Twine("Malformed log. Read Wallclock record kind out of sequence; "
+ "expecting: ") +
+ fdrStateToTwine(State.Expects),
std::make_error_code(std::errc::executable_format_error));
- // We don't encode the wall time into any of the records.
- // XRayRecords are concerned with the TSC instead.
+
+ // TODO: Someday, reconcile the TSC ticks to wall clock time for presentation
+ // purposes. For now, we're ignoring these records.
State.Expects = FDRState::Token::NEW_CPU_ID_RECORD;
return Error::success();
}
@@ -271,6 +285,20 @@ Error processCustomEventMarker(FDRState &State, uint8_t RecordFirstByte,
return Error::success();
}
+/// State transition when an BufferExtents record is encountered.
+Error processBufferExtents(FDRState &State, uint8_t RecordFirstByte,
+ DataExtractor &RecordExtractor) {
+ if (State.Expects != FDRState::Token::BUFFER_EXTENTS)
+ return make_error<StringError>(
+ Twine("Malformed log. Buffer Extents unexpected; expected: ") +
+ fdrStateToTwine(State.Expects),
+ std::make_error_code(std::errc::executable_format_error));
+ uint32_t OffsetPtr = 1; // Read after the first byte.
+ State.CurrentBufferSize = RecordExtractor.getU64(&OffsetPtr);
+ State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF;
+ return Error::success();
+}
+
/// State transition when a CallArgumentRecord is encountered.
Error processFDRCallArgumentRecord(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor,
@@ -292,10 +320,15 @@ Error processFDRCallArgumentRecord(FDRState &State, uint8_t RecordFirstByte,
/// record encountered. The RecordKind is encoded in the first byte of the
/// Record, which the caller should pass in because they have already read it
/// to determine that this is a metadata record as opposed to a function record.
+///
+/// Beginning with Version 2 of the FDR log, we do not depend on the size of the
+/// buffer, but rather use the extents to determine how far to read in the log
+/// for this particular buffer.
Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor,
size_t &RecordSize,
- std::vector<XRayRecord> &Records) {
+ std::vector<XRayRecord> &Records,
+ uint16_t Version) {
// The remaining 7 bits are the RecordKind enum.
uint8_t RecordKind = RecordFirstByte >> 1;
switch (RecordKind) {
@@ -305,6 +338,10 @@ Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte,
return E;
break;
case 1: // EndOfBuffer
+ if (Version >= 2)
+ return make_error<StringError>(
+ "Since Version 2 of FDR logging, we no longer support EOB records.",
+ std::make_error_code(std::errc::executable_format_error));
if (auto E = processFDREndOfBufferRecord(State, RecordFirstByte,
RecordExtractor))
return E;
@@ -334,6 +371,10 @@ Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte,
RecordExtractor, Records))
return E;
break;
+ case 7: // BufferExtents
+ if (auto E = processBufferExtents(State, RecordFirstByte, RecordExtractor))
+ return E;
+ break;
default:
// Widen the record type to uint16_t to prevent conversion to char.
return make_error<StringError>(
@@ -425,7 +466,8 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
/// convention that BitFields within a struct will first be packed into the
/// least significant bits the address they belong to.
///
-/// We expect a format complying with the grammar in the following pseudo-EBNF.
+/// We expect a format complying with the grammar in the following pseudo-EBNF
+/// in Version 1 of the FDR log.
///
/// FDRLog: XRayFileHeader ThreadBuffer*
/// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata.
@@ -439,6 +481,15 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
/// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord
/// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading.
/// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta.
+///
+/// In Version 2, we make the following changes:
+///
+/// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId
+/// FunctionSequence
+/// BufferExtents: 16 byte metdata record describing how many usable bytes are
+/// in the buffer. This is measured from the start of the buffer
+/// and must always be at least 48 (bytes).
+/// EOB: *deprecated*
Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
std::vector<XRayRecord> &Records) {
if (Data.size() < 32)
@@ -464,8 +515,22 @@ Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
uint32_t ExtraDataOffset = 0;
BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset);
}
- FDRState State{0, 0, 0, FDRState::Token::NEW_BUFFER_RECORD_OR_EOF,
- BufferSize, 0};
+
+ FDRState::Token InitialExpectation;
+ switch (FileHeader.Version) {
+ case 1:
+ InitialExpectation = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF;
+ break;
+ case 2:
+ InitialExpectation = FDRState::Token::BUFFER_EXTENTS;
+ break;
+ default:
+ return make_error<StringError>(
+ Twine("Unsupported version '") + Twine(FileHeader.Version) + "'",
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ FDRState State{0, 0, 0, InitialExpectation, BufferSize, 0};
+
// RecordSize will tell the loop how far to seek ahead based on the record
// type that we have just read.
size_t RecordSize = 0;
@@ -486,10 +551,13 @@ Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
}
uint8_t BitField = RecordExtractor.getU8(&OffsetPtr);
bool isMetadataRecord = BitField & 0x01uL;
+ bool isBufferExtents =
+ (BitField >> 1) == 7; // BufferExtents record kind == 7
if (isMetadataRecord) {
RecordSize = 16;
- if (auto E = processFDRMetadataRecord(State, BitField, RecordExtractor,
- RecordSize, Records))
+ if (auto E =
+ processFDRMetadataRecord(State, BitField, RecordExtractor,
+ RecordSize, Records, FileHeader.Version))
return E;
} else { // Process Function Record
RecordSize = 8;
@@ -497,14 +565,29 @@ Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
Records))
return E;
}
- State.CurrentBufferConsumed += RecordSize;
+
+ // The BufferExtents record is technically not part of the buffer, so we
+ // don't count the size of that record against the buffer's actual size.
+ if (!isBufferExtents)
+ State.CurrentBufferConsumed += RecordSize;
+ assert(State.CurrentBufferConsumed <= State.CurrentBufferSize);
+ if (FileHeader.Version == 2 &&
+ State.CurrentBufferSize == State.CurrentBufferConsumed) {
+ // In Version 2 of the log, we don't need to scan to the end of the thread
+ // buffer if we've already consumed all the bytes we need to.
+ State.Expects = FDRState::Token::BUFFER_EXTENTS;
+ State.CurrentBufferSize = BufferSize;
+ State.CurrentBufferConsumed = 0;
+ }
}
// Having iterated over everything we've been given, we've either consumed
// everything and ended up in the end state, or were told to skip the rest.
bool Finished = State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF &&
State.CurrentBufferSize == State.CurrentBufferConsumed;
- if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF && !Finished)
+ if ((State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF &&
+ State.Expects != FDRState::Token::BUFFER_EXTENTS) &&
+ !Finished)
return make_error<StringError>(
Twine("Encountered EOF with unexpected state expectation ") +
fdrStateToTwine(State.Expects) +
@@ -579,6 +662,7 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
//
// 0x01 0x00 0x00 0x00 - version 1, "naive" format
// 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format
+ // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format
//
// YAML files don't typically have those first four bytes as valid text so we
// try loading assuming YAML if we don't find these bytes.
@@ -594,13 +678,29 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 };
Trace T;
- if (Type == NAIVE_FORMAT && (Version == 1 || Version == 2)) {
- if (auto E = loadNaiveFormatLog(Data, T.FileHeader, T.Records))
- return std::move(E);
- } else if (Version == 1 && Type == FLIGHT_DATA_RECORDER_FORMAT) {
- if (auto E = loadFDRLog(Data, T.FileHeader, T.Records))
- return std::move(E);
- } else {
+ switch (Type) {
+ case NAIVE_FORMAT:
+ if (Version == 1 || Version == 2) {
+ if (auto E = loadNaiveFormatLog(Data, T.FileHeader, T.Records))
+ return std::move(E);
+ } else {
+ return make_error<StringError>(
+ Twine("Unsupported version for Basic/Naive Mode logging: ") +
+ Twine(Version),
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ break;
+ case FLIGHT_DATA_RECORDER_FORMAT:
+ if (Version == 1 || Version == 2) {
+ if (auto E = loadFDRLog(Data, T.FileHeader, T.Records))
+ return std::move(E);
+ } else {
+ return make_error<StringError>(
+ Twine("Unsupported version for FDR Mode logging: ") + Twine(Version),
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ break;
+ default:
if (auto E = loadYAMLLog(Data, T.FileHeader, T.Records))
return std::move(E);
}
OpenPOWER on IntegriCloud