aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm/lib/XRay/Trace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/lib/XRay/Trace.cpp')
-rw-r--r--contrib/llvm/lib/XRay/Trace.cpp306
1 files changed, 231 insertions, 75 deletions
diff --git a/contrib/llvm/lib/XRay/Trace.cpp b/contrib/llvm/lib/XRay/Trace.cpp
index 6677063f944f..d1fcf1c35b36 100644
--- a/contrib/llvm/lib/XRay/Trace.cpp
+++ b/contrib/llvm/lib/XRay/Trace.cpp
@@ -21,6 +21,7 @@ using namespace llvm;
using namespace llvm::xray;
using llvm::yaml::Input;
+namespace {
using XRayRecordStorage =
std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type;
@@ -47,7 +48,7 @@ Error readBinaryFormatHeader(StringRef Data, XRayFileHeader &FileHeader) {
FileHeader.NonstopTSC = Bitfield & 1uL << 1;
FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr);
std::memcpy(&FileHeader.FreeFormData, Data.bytes_begin() + OffsetPtr, 16);
- if (FileHeader.Version != 1)
+ if (FileHeader.Version != 1 && FileHeader.Version != 2)
return make_error<StringError>(
Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
std::make_error_code(std::errc::invalid_argument));
@@ -56,7 +57,6 @@ Error readBinaryFormatHeader(StringRef Data, XRayFileHeader &FileHeader) {
Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader,
std::vector<XRayRecord> &Records) {
- // Check that there is at least a header
if (Data.size() < 32)
return make_error<StringError>(
"Not enough bytes for an XRay log.",
@@ -82,26 +82,59 @@ Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader,
for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(32)) {
DataExtractor RecordExtractor(S, true, 8);
uint32_t OffsetPtr = 0;
- Records.emplace_back();
- auto &Record = Records.back();
- Record.RecordType = RecordExtractor.getU16(&OffsetPtr);
- Record.CPU = RecordExtractor.getU8(&OffsetPtr);
- auto Type = RecordExtractor.getU8(&OffsetPtr);
- switch (Type) {
- case 0:
- Record.Type = RecordTypes::ENTER;
+ switch (auto RecordType = RecordExtractor.getU16(&OffsetPtr)) {
+ case 0: { // Normal records.
+ Records.emplace_back();
+ auto &Record = Records.back();
+ Record.RecordType = RecordType;
+ Record.CPU = RecordExtractor.getU8(&OffsetPtr);
+ auto Type = RecordExtractor.getU8(&OffsetPtr);
+ switch (Type) {
+ case 0:
+ Record.Type = RecordTypes::ENTER;
+ break;
+ case 1:
+ Record.Type = RecordTypes::EXIT;
+ break;
+ case 2:
+ Record.Type = RecordTypes::TAIL_EXIT;
+ break;
+ case 3:
+ Record.Type = RecordTypes::ENTER_ARG;
+ break;
+ default:
+ return make_error<StringError>(
+ Twine("Unknown record type '") + Twine(int{Type}) + "'",
+ std::make_error_code(std::errc::executable_format_error));
+ }
+ Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t));
+ Record.TSC = RecordExtractor.getU64(&OffsetPtr);
+ Record.TId = RecordExtractor.getU32(&OffsetPtr);
break;
- case 1:
- Record.Type = RecordTypes::EXIT;
+ }
+ case 1: { // Arg payload record.
+ auto &Record = Records.back();
+ // Advance two bytes to avoid padding.
+ OffsetPtr += 2;
+ int32_t FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t));
+ auto TId = RecordExtractor.getU32(&OffsetPtr);
+ if (Record.FuncId != FuncId || Record.TId != TId)
+ return make_error<StringError>(
+ Twine("Corrupted log, found payload following non-matching "
+ "function + thread record. Record for ") +
+ Twine(Record.FuncId) + " != " + Twine(FuncId),
+ std::make_error_code(std::errc::executable_format_error));
+ // Advance another four bytes to avoid padding.
+ OffsetPtr += 4;
+ auto Arg = RecordExtractor.getU64(&OffsetPtr);
+ Record.CallArgs.push_back(Arg);
break;
+ }
default:
return make_error<StringError>(
- Twine("Unknown record type '") + Twine(int{Type}) + "'",
+ Twine("Unknown record type == ") + Twine(RecordType),
std::make_error_code(std::errc::executable_format_error));
}
- Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t));
- Record.TSC = RecordExtractor.getU64(&OffsetPtr);
- Record.TId = RecordExtractor.getU32(&OffsetPtr);
}
return Error::success();
}
@@ -125,6 +158,8 @@ struct FDRState {
FUNCTION_SEQUENCE,
SCAN_TO_END_OF_THREAD_BUF,
CUSTOM_EVENT_DATA,
+ CALL_ARGUMENT,
+ BUFFER_EXTENTS,
};
Token Expects;
@@ -134,7 +169,7 @@ struct FDRState {
uint64_t CurrentBufferConsumed;
};
-Twine fdrStateToTwine(const FDRState::Token &state) {
+const char *fdrStateToTwine(const FDRState::Token &state) {
switch (state) {
case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF:
return "NEW_BUFFER_RECORD_OR_EOF";
@@ -148,6 +183,10 @@ Twine fdrStateToTwine(const FDRState::Token &state) {
return "SCAN_TO_END_OF_THREAD_BUF";
case FDRState::Token::CUSTOM_EVENT_DATA:
return "CUSTOM_EVENT_DATA";
+ case FDRState::Token::CALL_ARGUMENT:
+ return "CALL_ARGUMENT";
+ case FDRState::Token::BUFFER_EXTENTS:
+ return "BUFFER_EXTENTS";
}
return "UNKNOWN";
}
@@ -158,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);
@@ -171,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();
@@ -183,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);
@@ -197,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);
@@ -209,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();
}
@@ -222,27 +272,63 @@ Error processCustomEventMarker(FDRState &State, uint8_t RecordFirstByte,
DataExtractor &RecordExtractor,
size_t &RecordSize) {
// We can encounter a CustomEventMarker anywhere in the log, so we can handle
- // it regardless of the expectation. However, we do se the expectation to read
- // a set number of fixed bytes, as described in the metadata.
+ // it regardless of the expectation. However, we do set the expectation to
+ // read a set number of fixed bytes, as described in the metadata.
uint32_t OffsetPtr = 1; // Read after the first byte.
uint32_t DataSize = RecordExtractor.getU32(&OffsetPtr);
uint64_t TSC = RecordExtractor.getU64(&OffsetPtr);
- // FIXME: Actually represent the record through the API. For now we only skip
- // through the data.
+ // FIXME: Actually represent the record through the API. For now we only
+ // skip through the data.
(void)TSC;
RecordSize = 16 + DataSize;
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,
+ std::vector<XRayRecord> &Records) {
+ uint32_t OffsetPtr = 1; // Read starting after the first byte.
+ auto &Enter = Records.back();
+
+ if (Enter.Type != RecordTypes::ENTER)
+ return make_error<StringError>(
+ "CallArgument needs to be right after a function entry",
+ std::make_error_code(std::errc::executable_format_error));
+ Enter.Type = RecordTypes::ENTER_ARG;
+ Enter.CallArgs.emplace_back(RecordExtractor.getU64(&OffsetPtr));
+ return Error::success();
+}
+
/// Advances the state machine for reading the FDR record type by reading one
/// Metadata Record and updating the State appropriately based on the kind of
/// 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) {
+ size_t &RecordSize,
+ std::vector<XRayRecord> &Records,
+ uint16_t Version) {
// The remaining 7 bits are the RecordKind enum.
uint8_t RecordKind = RecordFirstByte >> 1;
switch (RecordKind) {
@@ -252,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;
@@ -276,6 +366,15 @@ Error processFDRMetadataRecord(FDRState &State, uint8_t RecordFirstByte,
RecordExtractor, RecordSize))
return E;
break;
+ case 6: // CallArgument
+ if (auto E = processFDRCallArgumentRecord(State, 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>(
@@ -320,12 +419,13 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
Record.Type = RecordTypes::ENTER;
break;
case static_cast<uint8_t>(RecordTypes::EXIT):
- case 2: // TAIL_EXIT is not yet defined in RecordTypes.
Record.Type = RecordTypes::EXIT;
break;
+ case static_cast<uint8_t>(RecordTypes::TAIL_EXIT):
+ Record.Type = RecordTypes::TAIL_EXIT;
+ break;
default:
- // When initializing the error, convert to uint16_t so that the record
- // type isn't interpreted as a char.
+ // Cast to an unsigned integer to not interpret the record type as a char.
return make_error<StringError>(
Twine("Illegal function record type: ")
.concat(Twine(static_cast<unsigned>(RecordType))),
@@ -333,7 +433,7 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
}
Record.CPU = State.CPUId;
Record.TId = State.ThreadId;
- // Back up to read first 32 bits, including the 8 we pulled RecordType
+ // Back up to read first 32 bits, including the 4 we pulled RecordType
// and RecordKind out of. The remaining 28 are FunctionId.
uint32_t OffsetPtr = 0;
// Despite function Id being a signed int on XRayRecord,
@@ -346,9 +446,9 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
// FunctionRecords have a 32 bit delta from the previous absolute TSC
// or TSC delta. If this would overflow, we should read a TSCWrap record
// with an absolute TSC reading.
- uint64_t new_tsc = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr);
- State.BaseTSC = new_tsc;
- Record.TSC = new_tsc;
+ uint64_t NewTSC = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr);
+ State.BaseTSC = NewTSC;
+ Record.TSC = NewTSC;
}
return Error::success();
}
@@ -360,17 +460,19 @@ Error processFDRFunctionRecord(FDRState &State, uint8_t RecordFirstByte,
///
/// The following is an attempt to document the grammar of the format, which is
/// parsed by this function for little-endian machines. Since the format makes
-/// use of BitFields, when we support big-Endian architectures, we will need to
+/// use of BitFields, when we support big-endian architectures, we will need to
/// adjust not only the endianness parameter to llvm's RecordExtractor, but also
/// the bit twiddling logic, which is consistent with the little-endian
/// 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 bits to identify the log as FDR with machine metadata.
-/// ThreadBuffer: BufSize NewBuffer WallClockTime NewCPUId FunctionSequence EOB
+/// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata.
+/// Includes BufferSize
+/// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB
/// BufSize: 8 byte unsigned integer indicating how large the buffer is.
/// NewBuffer: 16 byte metadata record with Thread Id.
/// WallClockTime: 16 byte metadata record with human readable time.
@@ -379,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)
@@ -404,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;
@@ -414,11 +539,10 @@ Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
uint32_t OffsetPtr = 0;
if (State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF) {
RecordSize = State.CurrentBufferSize - State.CurrentBufferConsumed;
- if (S.size() < State.CurrentBufferSize - State.CurrentBufferConsumed) {
+ if (S.size() < RecordSize) {
return make_error<StringError>(
- Twine("Incomplete thread buffer. Expected ") +
- Twine(State.CurrentBufferSize - State.CurrentBufferConsumed) +
- " remaining bytes but found " + Twine(S.size()),
+ Twine("Incomplete thread buffer. Expected at least ") +
+ Twine(RecordSize) + " bytes but found " + Twine(S.size()),
make_error_code(std::errc::invalid_argument));
}
State.CurrentBufferConsumed = 0;
@@ -427,24 +551,43 @@ 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))
+ if (auto E =
+ processFDRMetadataRecord(State, BitField, RecordExtractor,
+ RecordSize, Records, FileHeader.Version))
return E;
- State.CurrentBufferConsumed += RecordSize;
} else { // Process Function Record
RecordSize = 8;
if (auto E = processFDRFunctionRecord(State, BitField, RecordExtractor,
Records))
return E;
+ }
+
+ // 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;
}
}
- // There are two conditions
- if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF &&
- !(State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF &&
- State.CurrentBufferSize == State.CurrentBufferConsumed))
+
+ // 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 &&
+ State.Expects != FDRState::Token::BUFFER_EXTENTS) &&
+ !Finished)
return make_error<StringError>(
Twine("Encountered EOF with unexpected state expectation ") +
fdrStateToTwine(State.Expects) +
@@ -457,7 +600,6 @@ Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader,
Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader,
std::vector<XRayRecord> &Records) {
- // Load the documents from the MappedFile.
YAMLXRayTrace Trace;
Input In(Data);
In >> Trace;
@@ -478,11 +620,12 @@ Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader,
Records.clear();
std::transform(Trace.Records.begin(), Trace.Records.end(),
std::back_inserter(Records), [&](const YAMLXRayRecord &R) {
- return XRayRecord{R.RecordType, R.CPU, R.Type,
- R.FuncId, R.TSC, R.TId};
+ return XRayRecord{R.RecordType, R.CPU, R.Type, R.FuncId,
+ R.TSC, R.TId, R.CallArgs};
});
return Error::success();
}
+} // namespace
Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
int Fd;
@@ -491,7 +634,6 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
Twine("Cannot read log from '") + Filename + "'", EC);
}
- // Attempt to get the filesize.
uint64_t FileSize;
if (auto EC = sys::fs::file_size(Filename, FileSize)) {
return make_error<StringError>(
@@ -503,7 +645,7 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
std::make_error_code(std::errc::executable_format_error));
}
- // Attempt to mmap the file.
+ // Map the opened file into memory and use a StringRef to access it later.
std::error_code EC;
sys::fs::mapped_file_region MappedFile(
Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
@@ -511,16 +653,18 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
return make_error<StringError>(
Twine("Cannot read log from '") + Filename + "'", EC);
}
+ auto Data = StringRef(MappedFile.data(), MappedFile.size());
// Attempt to detect the file type using file magic. We have a slight bias
// towards the binary format, and we do this by making sure that the first 4
// bytes of the binary file is some combination of the following byte
- // patterns:
+ // patterns: (observe the code loading them assumes they're little endian)
//
- // 0x0001 0x0000 - version 1, "naive" format
- // 0x0001 0x0001 - version 1, "flight data recorder" format
+ // 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 dont' typically have those first four bytes as valid text so we
+ // 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.
//
// Only if we can't load either the binary or the YAML format will we yield an
@@ -534,23 +678,35 @@ Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 };
Trace T;
- if (Version == 1 && Type == NAIVE_FORMAT) {
- if (auto E =
- loadNaiveFormatLog(StringRef(MappedFile.data(), MappedFile.size()),
- T.FileHeader, T.Records))
- return std::move(E);
- } else if (Version == 1 && Type == FLIGHT_DATA_RECORDER_FORMAT) {
- if (auto E = loadFDRLog(StringRef(MappedFile.data(), MappedFile.size()),
- T.FileHeader, T.Records))
- return std::move(E);
- } else {
- if (auto E = loadYAMLLog(StringRef(MappedFile.data(), MappedFile.size()),
- T.FileHeader, T.Records))
+ 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);
}
if (Sort)
- std::sort(T.Records.begin(), T.Records.end(),
+ std::stable_sort(T.Records.begin(), T.Records.end(),
[&](const XRayRecord &L, const XRayRecord &R) {
return L.TSC < R.TSC;
});