1d6c18657SDean Michael Berris //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// 2d6c18657SDean Michael Berris // 3d6c18657SDean Michael Berris // The LLVM Compiler Infrastructure 4d6c18657SDean Michael Berris // 5d6c18657SDean Michael Berris // This file is distributed under the University of Illinois Open Source 6d6c18657SDean Michael Berris // License. See LICENSE.TXT for details. 7d6c18657SDean Michael Berris // 8d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 9d6c18657SDean Michael Berris // 10d6c18657SDean Michael Berris // XRay log reader implementation. 11d6c18657SDean Michael Berris // 12d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 13d6c18657SDean Michael Berris #include "llvm/XRay/Trace.h" 14d6c18657SDean Michael Berris #include "llvm/ADT/STLExtras.h" 15d6c18657SDean Michael Berris #include "llvm/Support/DataExtractor.h" 16d6c18657SDean Michael Berris #include "llvm/Support/Error.h" 17d6c18657SDean Michael Berris #include "llvm/Support/FileSystem.h" 18d764c1b6SDean Michael Berris #include "llvm/XRay/FileHeaderReader.h" 19d6c18657SDean Michael Berris #include "llvm/XRay/YAMLXRayRecord.h" 20d6c18657SDean Michael Berris 21d6c18657SDean Michael Berris using namespace llvm; 22d6c18657SDean Michael Berris using namespace llvm::xray; 23d6c18657SDean Michael Berris using llvm::yaml::Input; 24d6c18657SDean Michael Berris 2549a49fe8SBenjamin Kramer namespace { 26d6c18657SDean Michael Berris using XRayRecordStorage = 27d6c18657SDean Michael Berris std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type; 28d6c18657SDean Michael Berris 29a9d477a6SDean Michael Berris // This is the number of bytes in the "body" of a MetadataRecord in FDR Mode. 30a9d477a6SDean Michael Berris // This already excludes the first byte, which indicates the type of metadata 31a9d477a6SDean Michael Berris // record it is. 32a9d477a6SDean Michael Berris constexpr auto kFDRMetadataBodySize = 15; 33a9d477a6SDean Michael Berris 34*5b7548c6SDean Michael Berris Error loadNaiveFormatLog(StringRef Data, bool IsLittleEndian, 35*5b7548c6SDean Michael Berris XRayFileHeader &FileHeader, 364f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 374f83c4d1SDean Michael Berris if (Data.size() < 32) 384f83c4d1SDean Michael Berris return make_error<StringError>( 394f83c4d1SDean Michael Berris "Not enough bytes for an XRay log.", 404f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 414f83c4d1SDean Michael Berris 424f83c4d1SDean Michael Berris if (Data.size() - 32 == 0 || Data.size() % 32 != 0) 434f83c4d1SDean Michael Berris return make_error<StringError>( 444f83c4d1SDean Michael Berris "Invalid-sized XRay data.", 454f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 464f83c4d1SDean Michael Berris 47*5b7548c6SDean Michael Berris DataExtractor Reader(Data, IsLittleEndian, 8); 48a9d477a6SDean Michael Berris uint32_t OffsetPtr = 0; 49d764c1b6SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 50d764c1b6SDean Michael Berris if (!FileHeaderOrError) 51d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 52d764c1b6SDean Michael Berris FileHeader = std::move(FileHeaderOrError.get()); 53d6c18657SDean Michael Berris 54d6c18657SDean Michael Berris // Each record after the header will be 32 bytes, in the following format: 55d6c18657SDean Michael Berris // 56d6c18657SDean Michael Berris // (2) uint16 : record type 57d6c18657SDean Michael Berris // (1) uint8 : cpu id 58d6c18657SDean Michael Berris // (1) uint8 : type 59d6c18657SDean Michael Berris // (4) sint32 : function id 60d6c18657SDean Michael Berris // (8) uint64 : tsc 61d6c18657SDean Michael Berris // (4) uint32 : thread id 6210141261SDean Michael Berris // (4) uint32 : process id 6310141261SDean Michael Berris // (8) - : padding 64a9d477a6SDean Michael Berris while (Reader.isValidOffset(OffsetPtr)) { 65a9d477a6SDean Michael Berris if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) 66a9d477a6SDean Michael Berris return createStringError( 67a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 68a9d477a6SDean Michael Berris "Not enough bytes to read a full record at offset %d.", OffsetPtr); 69a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 70a9d477a6SDean Michael Berris auto RecordType = Reader.getU16(&OffsetPtr); 71a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 72a9d477a6SDean Michael Berris return createStringError( 73a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 74a9d477a6SDean Michael Berris "Failed reading record type at offset %d.", OffsetPtr); 75a9d477a6SDean Michael Berris 76a9d477a6SDean Michael Berris switch (RecordType) { 770a465d7aSDean Michael Berris case 0: { // Normal records. 78d6c18657SDean Michael Berris Records.emplace_back(); 79d6c18657SDean Michael Berris auto &Record = Records.back(); 800a465d7aSDean Michael Berris Record.RecordType = RecordType; 81a9d477a6SDean Michael Berris 82a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 83a9d477a6SDean Michael Berris Record.CPU = Reader.getU8(&OffsetPtr); 84a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 85a9d477a6SDean Michael Berris return createStringError( 86a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 87a9d477a6SDean Michael Berris "Failed reading CPU field at offset %d.", OffsetPtr); 88a9d477a6SDean Michael Berris 89a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 90a9d477a6SDean Michael Berris auto Type = Reader.getU8(&OffsetPtr); 91a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 92a9d477a6SDean Michael Berris return createStringError( 93a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 94a9d477a6SDean Michael Berris "Failed reading record type field at offset %d.", OffsetPtr); 95a9d477a6SDean Michael Berris 96d6c18657SDean Michael Berris switch (Type) { 97d6c18657SDean Michael Berris case 0: 98d6c18657SDean Michael Berris Record.Type = RecordTypes::ENTER; 99d6c18657SDean Michael Berris break; 100d6c18657SDean Michael Berris case 1: 101d6c18657SDean Michael Berris Record.Type = RecordTypes::EXIT; 102d6c18657SDean Michael Berris break; 1030f84a7d3SDean Michael Berris case 2: 1040f84a7d3SDean Michael Berris Record.Type = RecordTypes::TAIL_EXIT; 1050f84a7d3SDean Michael Berris break; 1060a465d7aSDean Michael Berris case 3: 1070a465d7aSDean Michael Berris Record.Type = RecordTypes::ENTER_ARG; 1080a465d7aSDean Michael Berris break; 109d6c18657SDean Michael Berris default: 110a9d477a6SDean Michael Berris return createStringError( 111a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 112a9d477a6SDean Michael Berris "Unknown record type '%d' at offset %d.", Type, OffsetPtr); 113d6c18657SDean Michael Berris } 114a9d477a6SDean Michael Berris 115a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 116a9d477a6SDean Michael Berris Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 117a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 118a9d477a6SDean Michael Berris return createStringError( 119a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 120a9d477a6SDean Michael Berris "Failed reading function id field at offset %d.", OffsetPtr); 121a9d477a6SDean Michael Berris 122a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 123a9d477a6SDean Michael Berris Record.TSC = Reader.getU64(&OffsetPtr); 124a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 125a9d477a6SDean Michael Berris return createStringError( 126a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 127a9d477a6SDean Michael Berris "Failed reading TSC field at offset %d.", OffsetPtr); 128a9d477a6SDean Michael Berris 129a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 130a9d477a6SDean Michael Berris Record.TId = Reader.getU32(&OffsetPtr); 131a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 132a9d477a6SDean Michael Berris return createStringError( 133a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 134a9d477a6SDean Michael Berris "Failed reading thread id field at offset %d.", OffsetPtr); 135a9d477a6SDean Michael Berris 136a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 137a9d477a6SDean Michael Berris Record.PId = Reader.getU32(&OffsetPtr); 138a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 139a9d477a6SDean Michael Berris return createStringError( 140a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 141a9d477a6SDean Michael Berris "Failed reading process id at offset %d.", OffsetPtr); 142a9d477a6SDean Michael Berris 1430a465d7aSDean Michael Berris break; 1440a465d7aSDean Michael Berris } 1450a465d7aSDean Michael Berris case 1: { // Arg payload record. 1460a465d7aSDean Michael Berris auto &Record = Records.back(); 147a9d477a6SDean Michael Berris 148a9d477a6SDean Michael Berris // We skip the next two bytes of the record, because we don't need the 149a9d477a6SDean Michael Berris // type and the CPU record for arg payloads. 1500a465d7aSDean Michael Berris OffsetPtr += 2; 151a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 152a9d477a6SDean Michael Berris int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 153a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 154a9d477a6SDean Michael Berris return createStringError( 155a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 156a9d477a6SDean Michael Berris "Failed reading function id field at offset %d.", OffsetPtr); 157a9d477a6SDean Michael Berris 158a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 159a9d477a6SDean Michael Berris auto TId = Reader.getU32(&OffsetPtr); 160a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 161a9d477a6SDean Michael Berris return createStringError( 162a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 163a9d477a6SDean Michael Berris "Failed reading thread id field at offset %d.", OffsetPtr); 164a9d477a6SDean Michael Berris 165a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 166a9d477a6SDean Michael Berris auto PId = Reader.getU32(&OffsetPtr); 167a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 168a9d477a6SDean Michael Berris return createStringError( 169a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 170a9d477a6SDean Michael Berris "Failed reading process id field at offset %d.", OffsetPtr); 17110141261SDean Michael Berris 17210141261SDean Michael Berris // Make a check for versions above 3 for the Pid field 17310141261SDean Michael Berris if (Record.FuncId != FuncId || Record.TId != TId || 17410141261SDean Michael Berris (FileHeader.Version >= 3 ? Record.PId != PId : false)) 175a9d477a6SDean Michael Berris return createStringError( 176a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 177a9d477a6SDean Michael Berris "Corrupted log, found arg payload following non-matching " 178a9d477a6SDean Michael Berris "function+thread record. Record for function %d != %d at offset " 179a9d477a6SDean Michael Berris "%d", 180a9d477a6SDean Michael Berris Record.FuncId, FuncId, OffsetPtr); 18110141261SDean Michael Berris 182a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 183a9d477a6SDean Michael Berris auto Arg = Reader.getU64(&OffsetPtr); 184a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 185a9d477a6SDean Michael Berris return createStringError( 186a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 187a9d477a6SDean Michael Berris "Failed reading argument payload at offset %d.", OffsetPtr); 188a9d477a6SDean Michael Berris 1890a465d7aSDean Michael Berris Record.CallArgs.push_back(Arg); 1900a465d7aSDean Michael Berris break; 1910a465d7aSDean Michael Berris } 1920a465d7aSDean Michael Berris default: 193a9d477a6SDean Michael Berris return createStringError( 194a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 195a9d477a6SDean Michael Berris "Unknown record type '%d' at offset %d.", RecordType, OffsetPtr); 1960a465d7aSDean Michael Berris } 197a9d477a6SDean Michael Berris // Advance the offset pointer enough bytes to align to 32-byte records for 198a9d477a6SDean Michael Berris // basic mode logs. 199a9d477a6SDean Michael Berris OffsetPtr += 8; 200d6c18657SDean Michael Berris } 201d6c18657SDean Michael Berris return Error::success(); 202d6c18657SDean Michael Berris } 203d6c18657SDean Michael Berris 2044f83c4d1SDean Michael Berris /// When reading from a Flight Data Recorder mode log, metadata records are 2054f83c4d1SDean Michael Berris /// sparse compared to packed function records, so we must maintain state as we 2064f83c4d1SDean Michael Berris /// read through the sequence of entries. This allows the reader to denormalize 2074f83c4d1SDean Michael Berris /// the CPUId and Thread Id onto each Function Record and transform delta 2084f83c4d1SDean Michael Berris /// encoded TSC values into absolute encodings on each record. 2094f83c4d1SDean Michael Berris struct FDRState { 2104f83c4d1SDean Michael Berris uint16_t CPUId; 2114f83c4d1SDean Michael Berris uint16_t ThreadId; 21210141261SDean Michael Berris int32_t ProcessId; 2134f83c4d1SDean Michael Berris uint64_t BaseTSC; 214a7bbe448SDean Michael Berris 2154f83c4d1SDean Michael Berris /// Encode some of the state transitions for the FDR log reader as explicit 2164f83c4d1SDean Michael Berris /// checks. These are expectations for the next Record in the stream. 2174f83c4d1SDean Michael Berris enum class Token { 2184f83c4d1SDean Michael Berris NEW_BUFFER_RECORD_OR_EOF, 2194f83c4d1SDean Michael Berris WALLCLOCK_RECORD, 2204f83c4d1SDean Michael Berris NEW_CPU_ID_RECORD, 22160c24878SDean Michael Berris FUNCTION_SEQUENCE, 22260c24878SDean Michael Berris SCAN_TO_END_OF_THREAD_BUF, 223a7bbe448SDean Michael Berris CUSTOM_EVENT_DATA, 22410c873f1SMartin Pelikan CALL_ARGUMENT, 2256ec72625SDean Michael Berris BUFFER_EXTENTS, 22610141261SDean Michael Berris PID_RECORD, 2274f83c4d1SDean Michael Berris }; 2284f83c4d1SDean Michael Berris Token Expects; 229a7bbe448SDean Michael Berris 23060c24878SDean Michael Berris // Each threads buffer may have trailing garbage to scan over, so we track our 23160c24878SDean Michael Berris // progress. 23260c24878SDean Michael Berris uint64_t CurrentBufferSize; 23360c24878SDean Michael Berris uint64_t CurrentBufferConsumed; 2344f83c4d1SDean Michael Berris }; 235d6c18657SDean Michael Berris 23649a49fe8SBenjamin Kramer const char *fdrStateToTwine(const FDRState::Token &state) { 23760c24878SDean Michael Berris switch (state) { 23860c24878SDean Michael Berris case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 23960c24878SDean Michael Berris return "NEW_BUFFER_RECORD_OR_EOF"; 24060c24878SDean Michael Berris case FDRState::Token::WALLCLOCK_RECORD: 24160c24878SDean Michael Berris return "WALLCLOCK_RECORD"; 24260c24878SDean Michael Berris case FDRState::Token::NEW_CPU_ID_RECORD: 24360c24878SDean Michael Berris return "NEW_CPU_ID_RECORD"; 24460c24878SDean Michael Berris case FDRState::Token::FUNCTION_SEQUENCE: 24560c24878SDean Michael Berris return "FUNCTION_SEQUENCE"; 24660c24878SDean Michael Berris case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF: 24760c24878SDean Michael Berris return "SCAN_TO_END_OF_THREAD_BUF"; 248a7bbe448SDean Michael Berris case FDRState::Token::CUSTOM_EVENT_DATA: 249a7bbe448SDean Michael Berris return "CUSTOM_EVENT_DATA"; 25010c873f1SMartin Pelikan case FDRState::Token::CALL_ARGUMENT: 25110c873f1SMartin Pelikan return "CALL_ARGUMENT"; 2526ec72625SDean Michael Berris case FDRState::Token::BUFFER_EXTENTS: 2536ec72625SDean Michael Berris return "BUFFER_EXTENTS"; 25410141261SDean Michael Berris case FDRState::Token::PID_RECORD: 25510141261SDean Michael Berris return "PID_RECORD"; 25660c24878SDean Michael Berris } 25760c24878SDean Michael Berris return "UNKNOWN"; 25860c24878SDean Michael Berris } 25960c24878SDean Michael Berris 2604f83c4d1SDean Michael Berris /// State transition when a NewBufferRecord is encountered. 261a9d477a6SDean Michael Berris Error processFDRNewBufferRecord(FDRState &State, DataExtractor &RecordExtractor, 262a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 26360c24878SDean Michael Berris if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 264a9d477a6SDean Michael Berris return createStringError( 265a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 266a9d477a6SDean Michael Berris "Malformed log: Read New Buffer record kind out of sequence; expected: " 267a9d477a6SDean Michael Berris "%s at offset %d.", 268a9d477a6SDean Michael Berris fdrStateToTwine(State.Expects), OffsetPtr); 269a9d477a6SDean Michael Berris 270a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 2714f83c4d1SDean Michael Berris State.ThreadId = RecordExtractor.getU16(&OffsetPtr); 272a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 273a9d477a6SDean Michael Berris return createStringError( 274a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 275a9d477a6SDean Michael Berris "Failed reading the thread id at offset %d.", OffsetPtr); 2764f83c4d1SDean Michael Berris State.Expects = FDRState::Token::WALLCLOCK_RECORD; 277a9d477a6SDean Michael Berris 278a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes representing the remaining 279a9d477a6SDean Michael Berris // padding in a metadata record. 280a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 2; 281a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 2824f83c4d1SDean Michael Berris return Error::success(); 2834f83c4d1SDean Michael Berris } 2844f83c4d1SDean Michael Berris 2854f83c4d1SDean Michael Berris /// State transition when an EndOfBufferRecord is encountered. 286a9d477a6SDean Michael Berris Error processFDREndOfBufferRecord(FDRState &State, uint32_t &OffsetPtr) { 28760c24878SDean Michael Berris if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 288a9d477a6SDean Michael Berris return createStringError( 289a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 290a9d477a6SDean Michael Berris "Malformed log: Received EOB message without current buffer; expected: " 291a9d477a6SDean Michael Berris "%s at offset %d.", 292a9d477a6SDean Michael Berris fdrStateToTwine(State.Expects), OffsetPtr); 293a9d477a6SDean Michael Berris 29460c24878SDean Michael Berris State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF; 295a9d477a6SDean Michael Berris 296a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes representing the remaining 297a9d477a6SDean Michael Berris // padding in a metadata record. 298a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize; 2994f83c4d1SDean Michael Berris return Error::success(); 3004f83c4d1SDean Michael Berris } 3014f83c4d1SDean Michael Berris 3024f83c4d1SDean Michael Berris /// State transition when a NewCPUIdRecord is encountered. 303a9d477a6SDean Michael Berris Error processFDRNewCPUIdRecord(FDRState &State, DataExtractor &RecordExtractor, 304a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 3054f83c4d1SDean Michael Berris if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE && 30660c24878SDean Michael Berris State.Expects != FDRState::Token::NEW_CPU_ID_RECORD) 3074f83c4d1SDean Michael Berris return make_error<StringError>( 3086ec72625SDean Michael Berris Twine("Malformed log. Read NewCPUId record kind out of sequence; " 3096ec72625SDean Michael Berris "expected: ") + 3106ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3114f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 312a9d477a6SDean Michael Berris auto BeginOffset = OffsetPtr; 313a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 3144f83c4d1SDean Michael Berris State.CPUId = RecordExtractor.getU16(&OffsetPtr); 315a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 316a9d477a6SDean Michael Berris return createStringError( 317a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 318a9d477a6SDean Michael Berris "Failed reading the CPU field at offset %d.", OffsetPtr); 319a9d477a6SDean Michael Berris 320a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 3214f83c4d1SDean Michael Berris State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 322a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 323a9d477a6SDean Michael Berris return createStringError( 324a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 325a9d477a6SDean Michael Berris "Failed reading the base TSC field at offset %d.", OffsetPtr); 326a9d477a6SDean Michael Berris 3274f83c4d1SDean Michael Berris State.Expects = FDRState::Token::FUNCTION_SEQUENCE; 328a9d477a6SDean Michael Berris 329a9d477a6SDean Michael Berris // Advance the offset pointer by a few bytes, to account for the padding in 330a9d477a6SDean Michael Berris // CPU ID metadata records that we've already advanced through. 331a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - (OffsetPtr - BeginOffset); 332a9d477a6SDean Michael Berris assert(OffsetPtr - BeginOffset == kFDRMetadataBodySize); 3334f83c4d1SDean Michael Berris return Error::success(); 3344f83c4d1SDean Michael Berris } 3354f83c4d1SDean Michael Berris 3364f83c4d1SDean Michael Berris /// State transition when a TSCWrapRecord (overflow detection) is encountered. 337a9d477a6SDean Michael Berris Error processFDRTSCWrapRecord(FDRState &State, DataExtractor &RecordExtractor, 338a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 33960c24878SDean Michael Berris if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE) 3404f83c4d1SDean Michael Berris return make_error<StringError>( 3416ec72625SDean Michael Berris Twine("Malformed log. Read TSCWrap record kind out of sequence; " 3426ec72625SDean Michael Berris "expecting: ") + 3436ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3444f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 345a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 3464f83c4d1SDean Michael Berris State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 347a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 348a9d477a6SDean Michael Berris return createStringError( 349a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 350a9d477a6SDean Michael Berris "Failed reading the base TSC field at offset %d.", OffsetPtr); 351a9d477a6SDean Michael Berris 352a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 353a9d477a6SDean Michael Berris // in the metadata record after reading the base TSC. 354a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 355a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 3564f83c4d1SDean Michael Berris return Error::success(); 3574f83c4d1SDean Michael Berris } 3584f83c4d1SDean Michael Berris 3594f83c4d1SDean Michael Berris /// State transition when a WallTimeMarkerRecord is encountered. 360a9d477a6SDean Michael Berris Error processFDRWallTimeRecord(FDRState &State, DataExtractor &RecordExtractor, 361a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 36260c24878SDean Michael Berris if (State.Expects != FDRState::Token::WALLCLOCK_RECORD) 3634f83c4d1SDean Michael Berris return make_error<StringError>( 3646ec72625SDean Michael Berris Twine("Malformed log. Read Wallclock record kind out of sequence; " 3656ec72625SDean Michael Berris "expecting: ") + 3666ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3674f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 3686ec72625SDean Michael Berris 369a9d477a6SDean Michael Berris // Read in the data from the walltime record. 370a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 371a9d477a6SDean Michael Berris auto WallTime = RecordExtractor.getU64(&OffsetPtr); 372a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 373a9d477a6SDean Michael Berris return createStringError( 374a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 375a9d477a6SDean Michael Berris "Failed reading the walltime record at offset %d.", OffsetPtr); 376a9d477a6SDean Michael Berris 3776ec72625SDean Michael Berris // TODO: Someday, reconcile the TSC ticks to wall clock time for presentation 3786ec72625SDean Michael Berris // purposes. For now, we're ignoring these records. 379a9d477a6SDean Michael Berris (void)WallTime; 3804f83c4d1SDean Michael Berris State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 381a9d477a6SDean Michael Berris 382a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 383a9d477a6SDean Michael Berris // in the metadata record after reading in the walltime data. 384a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 385a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 3864f83c4d1SDean Michael Berris return Error::success(); 3874f83c4d1SDean Michael Berris } 3884f83c4d1SDean Michael Berris 38910141261SDean Michael Berris /// State transition when a PidRecord is encountered. 390a9d477a6SDean Michael Berris Error processFDRPidRecord(FDRState &State, DataExtractor &RecordExtractor, 391a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 39210141261SDean Michael Berris if (State.Expects != FDRState::Token::PID_RECORD) 39310141261SDean Michael Berris return make_error<StringError>( 39410141261SDean Michael Berris Twine("Malformed log. Read Pid record kind out of sequence; " 39510141261SDean Michael Berris "expected: ") + 39610141261SDean Michael Berris fdrStateToTwine(State.Expects), 39710141261SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 398a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 39910141261SDean Michael Berris State.ProcessId = RecordExtractor.getU32(&OffsetPtr); 400a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 401a9d477a6SDean Michael Berris return createStringError( 402a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 403a9d477a6SDean Michael Berris "Failed reading the process ID at offset %d.", OffsetPtr); 40410141261SDean Michael Berris State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 405a9d477a6SDean Michael Berris 406a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 407a9d477a6SDean Michael Berris // in the metadata record after reading in the PID. 408a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 4; 409a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 41010141261SDean Michael Berris return Error::success(); 41110141261SDean Michael Berris } 41210141261SDean Michael Berris 413a7bbe448SDean Michael Berris /// State transition when a CustomEventMarker is encountered. 414a9d477a6SDean Michael Berris Error processCustomEventMarker(FDRState &State, DataExtractor &RecordExtractor, 415a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 416a7bbe448SDean Michael Berris // We can encounter a CustomEventMarker anywhere in the log, so we can handle 4173d0bc9efSKeith Wyss // it regardless of the expectation. However, we do set the expectation to 4183d0bc9efSKeith Wyss // read a set number of fixed bytes, as described in the metadata. 419a9d477a6SDean Michael Berris auto BeginOffset = OffsetPtr; 420a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 421a7bbe448SDean Michael Berris uint32_t DataSize = RecordExtractor.getU32(&OffsetPtr); 422a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 423a9d477a6SDean Michael Berris return createStringError( 424a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 425a9d477a6SDean Michael Berris "Failed reading a custom event marker at offset %d.", OffsetPtr); 426a9d477a6SDean Michael Berris 427a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 428a7bbe448SDean Michael Berris uint64_t TSC = RecordExtractor.getU64(&OffsetPtr); 429a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 430a9d477a6SDean Michael Berris return createStringError( 431a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 432a9d477a6SDean Michael Berris "Failed reading the TSC at offset %d.", OffsetPtr); 433a7bbe448SDean Michael Berris 4340a465d7aSDean Michael Berris // FIXME: Actually represent the record through the API. For now we only 4350a465d7aSDean Michael Berris // skip through the data. 436a7bbe448SDean Michael Berris (void)TSC; 437a9d477a6SDean Michael Berris // Advance the offset ptr by the size of the data associated with the custom 438a9d477a6SDean Michael Berris // event, as well as the padding associated with the remainder of the metadata 439a9d477a6SDean Michael Berris // record. 440a9d477a6SDean Michael Berris OffsetPtr += (kFDRMetadataBodySize - (OffsetPtr - BeginOffset)) + DataSize; 441a9d477a6SDean Michael Berris if (!RecordExtractor.isValidOffset(OffsetPtr)) 442a9d477a6SDean Michael Berris return createStringError( 443a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 444a9d477a6SDean Michael Berris "Reading custom event data moves past addressable trace data (starting " 445a9d477a6SDean Michael Berris "at offset %d, advancing to offset %d).", 446a9d477a6SDean Michael Berris BeginOffset, OffsetPtr); 447a7bbe448SDean Michael Berris return Error::success(); 448a7bbe448SDean Michael Berris } 449a7bbe448SDean Michael Berris 4506ec72625SDean Michael Berris /// State transition when an BufferExtents record is encountered. 451a9d477a6SDean Michael Berris Error processBufferExtents(FDRState &State, DataExtractor &RecordExtractor, 452a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 4536ec72625SDean Michael Berris if (State.Expects != FDRState::Token::BUFFER_EXTENTS) 4546ec72625SDean Michael Berris return make_error<StringError>( 4556ec72625SDean Michael Berris Twine("Malformed log. Buffer Extents unexpected; expected: ") + 4566ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 4576ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 458a9d477a6SDean Michael Berris 459a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 4606ec72625SDean Michael Berris State.CurrentBufferSize = RecordExtractor.getU64(&OffsetPtr); 461a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 462a9d477a6SDean Michael Berris return createStringError( 463a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 464a9d477a6SDean Michael Berris "Failed to read current buffer size at offset %d.", OffsetPtr); 465a9d477a6SDean Michael Berris 4666ec72625SDean Michael Berris State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 467a9d477a6SDean Michael Berris 468a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes accounting for the padding in a 469a9d477a6SDean Michael Berris // metadata record, after we read in the buffer extents. 470a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 4716ec72625SDean Michael Berris return Error::success(); 4726ec72625SDean Michael Berris } 4736ec72625SDean Michael Berris 47410c873f1SMartin Pelikan /// State transition when a CallArgumentRecord is encountered. 475a9d477a6SDean Michael Berris Error processFDRCallArgumentRecord(FDRState &State, 47610c873f1SMartin Pelikan DataExtractor &RecordExtractor, 477a9d477a6SDean Michael Berris std::vector<XRayRecord> &Records, 478a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 47910c873f1SMartin Pelikan auto &Enter = Records.back(); 480a9d477a6SDean Michael Berris if (Enter.Type != RecordTypes::ENTER && Enter.Type != RecordTypes::ENTER_ARG) 48110c873f1SMartin Pelikan return make_error<StringError>( 48210c873f1SMartin Pelikan "CallArgument needs to be right after a function entry", 48310c873f1SMartin Pelikan std::make_error_code(std::errc::executable_format_error)); 484a9d477a6SDean Michael Berris 485a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 486a9d477a6SDean Michael Berris auto Arg = RecordExtractor.getU64(&OffsetPtr); 487a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 488a9d477a6SDean Michael Berris return createStringError( 489a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 490a9d477a6SDean Michael Berris "Failed to read argument record at offset %d.", OffsetPtr); 491a9d477a6SDean Michael Berris 49210c873f1SMartin Pelikan Enter.Type = RecordTypes::ENTER_ARG; 493a9d477a6SDean Michael Berris Enter.CallArgs.emplace_back(Arg); 494a9d477a6SDean Michael Berris 495a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes accounting for the padding in a 496a9d477a6SDean Michael Berris // metadata record, after reading the payload. 497a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 49810c873f1SMartin Pelikan return Error::success(); 49910c873f1SMartin Pelikan } 50010c873f1SMartin Pelikan 5014f83c4d1SDean Michael Berris /// Advances the state machine for reading the FDR record type by reading one 502e96152a9SKeith Wyss /// Metadata Record and updating the State appropriately based on the kind of 5034f83c4d1SDean Michael Berris /// record encountered. The RecordKind is encoded in the first byte of the 5044f83c4d1SDean Michael Berris /// Record, which the caller should pass in because they have already read it 5054f83c4d1SDean Michael Berris /// to determine that this is a metadata record as opposed to a function record. 5066ec72625SDean Michael Berris /// 5076ec72625SDean Michael Berris /// Beginning with Version 2 of the FDR log, we do not depend on the size of the 5086ec72625SDean Michael Berris /// buffer, but rather use the extents to determine how far to read in the log 5096ec72625SDean Michael Berris /// for this particular buffer. 51010141261SDean Michael Berris /// 51110141261SDean Michael Berris /// In Version 3, FDR log now includes a pid metadata record after 51210141261SDean Michael Berris /// WallTimeMarker 513a9d477a6SDean Michael Berris Error processFDRMetadataRecord(FDRState &State, DataExtractor &RecordExtractor, 514a9d477a6SDean Michael Berris uint32_t &OffsetPtr, 5156ec72625SDean Michael Berris std::vector<XRayRecord> &Records, 516a9d477a6SDean Michael Berris uint16_t Version, uint8_t FirstByte) { 517a9d477a6SDean Michael Berris // The remaining 7 bits of the first byte are the RecordKind enum for each 518a9d477a6SDean Michael Berris // Metadata Record. 519a9d477a6SDean Michael Berris switch (FirstByte >> 1) { 5204f83c4d1SDean Michael Berris case 0: // NewBuffer 521a9d477a6SDean Michael Berris if (auto E = processFDRNewBufferRecord(State, RecordExtractor, OffsetPtr)) 5224f83c4d1SDean Michael Berris return E; 5234f83c4d1SDean Michael Berris break; 5244f83c4d1SDean Michael Berris case 1: // EndOfBuffer 5256ec72625SDean Michael Berris if (Version >= 2) 5266ec72625SDean Michael Berris return make_error<StringError>( 5276ec72625SDean Michael Berris "Since Version 2 of FDR logging, we no longer support EOB records.", 5286ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 529a9d477a6SDean Michael Berris if (auto E = processFDREndOfBufferRecord(State, OffsetPtr)) 5304f83c4d1SDean Michael Berris return E; 5314f83c4d1SDean Michael Berris break; 5324f83c4d1SDean Michael Berris case 2: // NewCPUId 533a9d477a6SDean Michael Berris if (auto E = processFDRNewCPUIdRecord(State, RecordExtractor, OffsetPtr)) 5344f83c4d1SDean Michael Berris return E; 5354f83c4d1SDean Michael Berris break; 5364f83c4d1SDean Michael Berris case 3: // TSCWrap 537a9d477a6SDean Michael Berris if (auto E = processFDRTSCWrapRecord(State, RecordExtractor, OffsetPtr)) 5384f83c4d1SDean Michael Berris return E; 5394f83c4d1SDean Michael Berris break; 5404f83c4d1SDean Michael Berris case 4: // WallTimeMarker 541a9d477a6SDean Michael Berris if (auto E = processFDRWallTimeRecord(State, RecordExtractor, OffsetPtr)) 5424f83c4d1SDean Michael Berris return E; 54310141261SDean Michael Berris // In Version 3 and and above, a PidRecord is expected after WallTimeRecord 54410141261SDean Michael Berris if (Version >= 3) 54510141261SDean Michael Berris State.Expects = FDRState::Token::PID_RECORD; 5464f83c4d1SDean Michael Berris break; 547a7bbe448SDean Michael Berris case 5: // CustomEventMarker 548a9d477a6SDean Michael Berris if (auto E = processCustomEventMarker(State, RecordExtractor, OffsetPtr)) 549a7bbe448SDean Michael Berris return E; 550a7bbe448SDean Michael Berris break; 55110c873f1SMartin Pelikan case 6: // CallArgument 552a9d477a6SDean Michael Berris if (auto E = processFDRCallArgumentRecord(State, RecordExtractor, Records, 553a9d477a6SDean Michael Berris OffsetPtr)) 55410c873f1SMartin Pelikan return E; 55510c873f1SMartin Pelikan break; 5566ec72625SDean Michael Berris case 7: // BufferExtents 557a9d477a6SDean Michael Berris if (auto E = processBufferExtents(State, RecordExtractor, OffsetPtr)) 5586ec72625SDean Michael Berris return E; 5596ec72625SDean Michael Berris break; 56010141261SDean Michael Berris case 9: // Pid 561a9d477a6SDean Michael Berris if (auto E = processFDRPidRecord(State, RecordExtractor, OffsetPtr)) 56210141261SDean Michael Berris return E; 56310141261SDean Michael Berris break; 5644f83c4d1SDean Michael Berris default: 565a9d477a6SDean Michael Berris return createStringError( 566a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 567a9d477a6SDean Michael Berris "Illegal metadata record type: '%d' at offset %d.", FirstByte >> 1, 568a9d477a6SDean Michael Berris OffsetPtr); 5694f83c4d1SDean Michael Berris } 5704f83c4d1SDean Michael Berris return Error::success(); 5714f83c4d1SDean Michael Berris } 5724f83c4d1SDean Michael Berris 5734f83c4d1SDean Michael Berris /// Reads a function record from an FDR format log, appending a new XRayRecord 5744f83c4d1SDean Michael Berris /// to the vector being populated and updating the State with a new value 5754f83c4d1SDean Michael Berris /// reference value to interpret TSC deltas. 5764f83c4d1SDean Michael Berris /// 5774f83c4d1SDean Michael Berris /// The XRayRecord constructed includes information from the function record 5784f83c4d1SDean Michael Berris /// processed here as well as Thread ID and CPU ID formerly extracted into 5794f83c4d1SDean Michael Berris /// State. 580a9d477a6SDean Michael Berris Error processFDRFunctionRecord(FDRState &State, DataExtractor &RecordExtractor, 581a9d477a6SDean Michael Berris uint32_t &OffsetPtr, uint8_t FirstByte, 5824f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 5834f83c4d1SDean Michael Berris switch (State.Expects) { 5844f83c4d1SDean Michael Berris case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 5854f83c4d1SDean Michael Berris return make_error<StringError>( 5864f83c4d1SDean Michael Berris "Malformed log. Received Function Record before new buffer setup.", 5874f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 5884f83c4d1SDean Michael Berris case FDRState::Token::WALLCLOCK_RECORD: 5894f83c4d1SDean Michael Berris return make_error<StringError>( 5904f83c4d1SDean Michael Berris "Malformed log. Received Function Record when expecting wallclock.", 5914f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 59210141261SDean Michael Berris case FDRState::Token::PID_RECORD: 59310141261SDean Michael Berris return make_error<StringError>( 59410141261SDean Michael Berris "Malformed log. Received Function Record when expecting pid.", 59510141261SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 5964f83c4d1SDean Michael Berris case FDRState::Token::NEW_CPU_ID_RECORD: 5974f83c4d1SDean Michael Berris return make_error<StringError>( 5984f83c4d1SDean Michael Berris "Malformed log. Received Function Record before first CPU record.", 5994f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 6004f83c4d1SDean Michael Berris default: 6014f83c4d1SDean Michael Berris Records.emplace_back(); 6024f83c4d1SDean Michael Berris auto &Record = Records.back(); 6034f83c4d1SDean Michael Berris Record.RecordType = 0; // Record is type NORMAL. 6044f83c4d1SDean Michael Berris // Strip off record type bit and use the next three bits. 605a9d477a6SDean Michael Berris auto T = (FirstByte >> 1) & 0x07; 606a9d477a6SDean Michael Berris switch (T) { 607a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::ENTER): 6084f83c4d1SDean Michael Berris Record.Type = RecordTypes::ENTER; 6094f83c4d1SDean Michael Berris break; 610a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::EXIT): 6114f83c4d1SDean Michael Berris Record.Type = RecordTypes::EXIT; 6124f83c4d1SDean Michael Berris break; 613a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::TAIL_EXIT): 6140f84a7d3SDean Michael Berris Record.Type = RecordTypes::TAIL_EXIT; 6150f84a7d3SDean Michael Berris break; 616a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::ENTER_ARG): 617a9d477a6SDean Michael Berris Record.Type = RecordTypes::ENTER_ARG; 618a9d477a6SDean Michael Berris State.Expects = FDRState::Token::CALL_ARGUMENT; 619a9d477a6SDean Michael Berris break; 6204f83c4d1SDean Michael Berris default: 621a9d477a6SDean Michael Berris return createStringError( 622a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 623a9d477a6SDean Michael Berris "Illegal function record type '%d' at offset %d.", T, OffsetPtr); 6244f83c4d1SDean Michael Berris } 6254f83c4d1SDean Michael Berris Record.CPU = State.CPUId; 6264f83c4d1SDean Michael Berris Record.TId = State.ThreadId; 62710141261SDean Michael Berris Record.PId = State.ProcessId; 628a9d477a6SDean Michael Berris 629a9d477a6SDean Michael Berris // Back up one byte to re-read the first byte, which is important for 630a9d477a6SDean Michael Berris // computing the function id for a record. 631a9d477a6SDean Michael Berris --OffsetPtr; 632a9d477a6SDean Michael Berris 6334f83c4d1SDean Michael Berris // Despite function Id being a signed int on XRayRecord, 6344f83c4d1SDean Michael Berris // when it is written to an FDR format, the top bits are truncated, 6354f83c4d1SDean Michael Berris // so it is effectively an unsigned value. When we shift off the 6364f83c4d1SDean Michael Berris // top four bits, we want the shift to be logical, so we read as 6374f83c4d1SDean Michael Berris // uint32_t. 638a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 6394f83c4d1SDean Michael Berris uint32_t FuncIdBitField = RecordExtractor.getU32(&OffsetPtr); 640a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 641a9d477a6SDean Michael Berris return createStringError( 642a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 643a9d477a6SDean Michael Berris "Failed reading truncated function id field at offset %d.", 644a9d477a6SDean Michael Berris OffsetPtr); 645a9d477a6SDean Michael Berris 6464f83c4d1SDean Michael Berris Record.FuncId = FuncIdBitField >> 4; 647a9d477a6SDean Michael Berris 6484f83c4d1SDean Michael Berris // FunctionRecords have a 32 bit delta from the previous absolute TSC 6494f83c4d1SDean Michael Berris // or TSC delta. If this would overflow, we should read a TSCWrap record 6504f83c4d1SDean Michael Berris // with an absolute TSC reading. 651a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 652d78db156SMartin Pelikan uint64_t NewTSC = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr); 653a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 654a9d477a6SDean Michael Berris return createStringError( 655a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 656a9d477a6SDean Michael Berris "Failed reading TSC delta at offset %d.", OffsetPtr); 657a9d477a6SDean Michael Berris 658d78db156SMartin Pelikan State.BaseTSC = NewTSC; 659d78db156SMartin Pelikan Record.TSC = NewTSC; 6604f83c4d1SDean Michael Berris } 6614f83c4d1SDean Michael Berris return Error::success(); 6624f83c4d1SDean Michael Berris } 6634f83c4d1SDean Michael Berris 6644f83c4d1SDean Michael Berris /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is 6654f83c4d1SDean Michael Berris /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such 6664f83c4d1SDean Michael Berris /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of 6674f83c4d1SDean Michael Berris /// of interspersed 16 byte Metadata Records and 8 byte Function Records. 6684f83c4d1SDean Michael Berris /// 6694f83c4d1SDean Michael Berris /// The following is an attempt to document the grammar of the format, which is 6704f83c4d1SDean Michael Berris /// parsed by this function for little-endian machines. Since the format makes 671d78db156SMartin Pelikan /// use of BitFields, when we support big-endian architectures, we will need to 67268168d17SSimon Pilgrim /// adjust not only the endianness parameter to llvm's RecordExtractor, but also 6734f83c4d1SDean Michael Berris /// the bit twiddling logic, which is consistent with the little-endian 6744f83c4d1SDean Michael Berris /// convention that BitFields within a struct will first be packed into the 6754f83c4d1SDean Michael Berris /// least significant bits the address they belong to. 6764f83c4d1SDean Michael Berris /// 6776ec72625SDean Michael Berris /// We expect a format complying with the grammar in the following pseudo-EBNF 6786ec72625SDean Michael Berris /// in Version 1 of the FDR log. 6794f83c4d1SDean Michael Berris /// 6804f83c4d1SDean Michael Berris /// FDRLog: XRayFileHeader ThreadBuffer* 6813d0bc9efSKeith Wyss /// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. 6823d0bc9efSKeith Wyss /// Includes BufferSize 6833d0bc9efSKeith Wyss /// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB 68460c24878SDean Michael Berris /// BufSize: 8 byte unsigned integer indicating how large the buffer is. 6854f83c4d1SDean Michael Berris /// NewBuffer: 16 byte metadata record with Thread Id. 6864f83c4d1SDean Michael Berris /// WallClockTime: 16 byte metadata record with human readable time. 68710141261SDean Michael Berris /// Pid: 16 byte metadata record with Pid 6884f83c4d1SDean Michael Berris /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. 68960c24878SDean Michael Berris /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. 6904f83c4d1SDean Michael Berris /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord 6914f83c4d1SDean Michael Berris /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. 6924f83c4d1SDean Michael Berris /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. 6936ec72625SDean Michael Berris /// 6946ec72625SDean Michael Berris /// In Version 2, we make the following changes: 6956ec72625SDean Michael Berris /// 6966ec72625SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId 6976ec72625SDean Michael Berris /// FunctionSequence 6986ec72625SDean Michael Berris /// BufferExtents: 16 byte metdata record describing how many usable bytes are 6996ec72625SDean Michael Berris /// in the buffer. This is measured from the start of the buffer 7006ec72625SDean Michael Berris /// and must always be at least 48 (bytes). 70110141261SDean Michael Berris /// 70210141261SDean Michael Berris /// In Version 3, we make the following changes: 70310141261SDean Michael Berris /// 70410141261SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId 70510141261SDean Michael Berris /// FunctionSequence 7066ec72625SDean Michael Berris /// EOB: *deprecated* 707*5b7548c6SDean Michael Berris Error loadFDRLog(StringRef Data, bool IsLittleEndian, 708*5b7548c6SDean Michael Berris XRayFileHeader &FileHeader, std::vector<XRayRecord> &Records) { 709d764c1b6SDean Michael Berris 7104f83c4d1SDean Michael Berris if (Data.size() < 32) 7114f83c4d1SDean Michael Berris return make_error<StringError>( 7124f83c4d1SDean Michael Berris "Not enough bytes for an XRay log.", 7134f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 7144f83c4d1SDean Michael Berris 715*5b7548c6SDean Michael Berris DataExtractor Reader(Data, IsLittleEndian, 8); 716a9d477a6SDean Michael Berris uint32_t OffsetPtr = 0; 717d764c1b6SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 718d764c1b6SDean Michael Berris if (!FileHeaderOrError) 719d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 720d764c1b6SDean Michael Berris FileHeader = std::move(FileHeaderOrError.get()); 7214f83c4d1SDean Michael Berris 72260c24878SDean Michael Berris uint64_t BufferSize = 0; 72360c24878SDean Michael Berris { 72460c24878SDean Michael Berris StringRef ExtraDataRef(FileHeader.FreeFormData, 16); 725*5b7548c6SDean Michael Berris DataExtractor ExtraDataExtractor(ExtraDataRef, IsLittleEndian, 8); 72660c24878SDean Michael Berris uint32_t ExtraDataOffset = 0; 72760c24878SDean Michael Berris BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset); 72860c24878SDean Michael Berris } 7296ec72625SDean Michael Berris 7306ec72625SDean Michael Berris FDRState::Token InitialExpectation; 7316ec72625SDean Michael Berris switch (FileHeader.Version) { 7326ec72625SDean Michael Berris case 1: 7336ec72625SDean Michael Berris InitialExpectation = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 7346ec72625SDean Michael Berris break; 7356ec72625SDean Michael Berris case 2: 73610141261SDean Michael Berris case 3: 7376ec72625SDean Michael Berris InitialExpectation = FDRState::Token::BUFFER_EXTENTS; 7386ec72625SDean Michael Berris break; 7396ec72625SDean Michael Berris default: 7406ec72625SDean Michael Berris return make_error<StringError>( 7416ec72625SDean Michael Berris Twine("Unsupported version '") + Twine(FileHeader.Version) + "'", 7426ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 7436ec72625SDean Michael Berris } 74410141261SDean Michael Berris FDRState State{0, 0, 0, 0, InitialExpectation, BufferSize, 0}; 7456ec72625SDean Michael Berris 7464f83c4d1SDean Michael Berris // RecordSize will tell the loop how far to seek ahead based on the record 7474f83c4d1SDean Michael Berris // type that we have just read. 748a9d477a6SDean Michael Berris while (Reader.isValidOffset(OffsetPtr)) { 749a9d477a6SDean Michael Berris auto BeginOffset = OffsetPtr; 75060c24878SDean Michael Berris if (State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF) { 751a9d477a6SDean Michael Berris OffsetPtr += State.CurrentBufferSize - State.CurrentBufferConsumed; 75260c24878SDean Michael Berris State.CurrentBufferConsumed = 0; 75360c24878SDean Michael Berris State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 75460c24878SDean Michael Berris continue; 75560c24878SDean Michael Berris } 756a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 757a9d477a6SDean Michael Berris uint8_t BitField = Reader.getU8(&OffsetPtr); 758a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 759a9d477a6SDean Michael Berris return createStringError( 760a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 761a9d477a6SDean Michael Berris "Failed reading first byte of record at offset %d.", OffsetPtr); 7624f83c4d1SDean Michael Berris bool isMetadataRecord = BitField & 0x01uL; 7636ec72625SDean Michael Berris bool isBufferExtents = 7646ec72625SDean Michael Berris (BitField >> 1) == 7; // BufferExtents record kind == 7 7654f83c4d1SDean Michael Berris if (isMetadataRecord) { 766a9d477a6SDean Michael Berris if (auto E = processFDRMetadataRecord(State, Reader, OffsetPtr, Records, 767a9d477a6SDean Michael Berris FileHeader.Version, BitField)) 7684f83c4d1SDean Michael Berris return E; 7694f83c4d1SDean Michael Berris } else { // Process Function Record 770a9d477a6SDean Michael Berris if (auto E = processFDRFunctionRecord(State, Reader, OffsetPtr, BitField, 7714f83c4d1SDean Michael Berris Records)) 7724f83c4d1SDean Michael Berris return E; 773d78db156SMartin Pelikan } 7746ec72625SDean Michael Berris 7756ec72625SDean Michael Berris // The BufferExtents record is technically not part of the buffer, so we 7766ec72625SDean Michael Berris // don't count the size of that record against the buffer's actual size. 7776ec72625SDean Michael Berris if (!isBufferExtents) 778a9d477a6SDean Michael Berris State.CurrentBufferConsumed += OffsetPtr - BeginOffset; 779a9d477a6SDean Michael Berris 7806ec72625SDean Michael Berris assert(State.CurrentBufferConsumed <= State.CurrentBufferSize); 781a9d477a6SDean Michael Berris 78210141261SDean Michael Berris if ((FileHeader.Version == 2 || FileHeader.Version == 3) && 7836ec72625SDean Michael Berris State.CurrentBufferSize == State.CurrentBufferConsumed) { 7846ec72625SDean Michael Berris // In Version 2 of the log, we don't need to scan to the end of the thread 7856ec72625SDean Michael Berris // buffer if we've already consumed all the bytes we need to. 7866ec72625SDean Michael Berris State.Expects = FDRState::Token::BUFFER_EXTENTS; 7876ec72625SDean Michael Berris State.CurrentBufferSize = BufferSize; 7886ec72625SDean Michael Berris State.CurrentBufferConsumed = 0; 7896ec72625SDean Michael Berris } 7904f83c4d1SDean Michael Berris } 791d78db156SMartin Pelikan 792d78db156SMartin Pelikan // Having iterated over everything we've been given, we've either consumed 793d78db156SMartin Pelikan // everything and ended up in the end state, or were told to skip the rest. 794d78db156SMartin Pelikan bool Finished = State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF && 795d78db156SMartin Pelikan State.CurrentBufferSize == State.CurrentBufferConsumed; 7966ec72625SDean Michael Berris if ((State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF && 7976ec72625SDean Michael Berris State.Expects != FDRState::Token::BUFFER_EXTENTS) && 7986ec72625SDean Michael Berris !Finished) 7994f83c4d1SDean Michael Berris return make_error<StringError>( 80060c24878SDean Michael Berris Twine("Encountered EOF with unexpected state expectation ") + 80160c24878SDean Michael Berris fdrStateToTwine(State.Expects) + 80260c24878SDean Michael Berris ". Remaining expected bytes in thread buffer total " + 80360c24878SDean Michael Berris Twine(State.CurrentBufferSize - State.CurrentBufferConsumed), 8044f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 8054f83c4d1SDean Michael Berris 8064f83c4d1SDean Michael Berris return Error::success(); 8074f83c4d1SDean Michael Berris } 8084f83c4d1SDean Michael Berris 8094f83c4d1SDean Michael Berris Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader, 8104f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 811d6c18657SDean Michael Berris YAMLXRayTrace Trace; 812d6c18657SDean Michael Berris Input In(Data); 813d6c18657SDean Michael Berris In >> Trace; 814d6c18657SDean Michael Berris if (In.error()) 815d6c18657SDean Michael Berris return make_error<StringError>("Failed loading YAML Data.", In.error()); 816d6c18657SDean Michael Berris 817d6c18657SDean Michael Berris FileHeader.Version = Trace.Header.Version; 818d6c18657SDean Michael Berris FileHeader.Type = Trace.Header.Type; 819d6c18657SDean Michael Berris FileHeader.ConstantTSC = Trace.Header.ConstantTSC; 820d6c18657SDean Michael Berris FileHeader.NonstopTSC = Trace.Header.NonstopTSC; 821d6c18657SDean Michael Berris FileHeader.CycleFrequency = Trace.Header.CycleFrequency; 822d6c18657SDean Michael Berris 823d6c18657SDean Michael Berris if (FileHeader.Version != 1) 824d6c18657SDean Michael Berris return make_error<StringError>( 825d6c18657SDean Michael Berris Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), 826d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 827d6c18657SDean Michael Berris 828d6c18657SDean Michael Berris Records.clear(); 829d6c18657SDean Michael Berris std::transform(Trace.Records.begin(), Trace.Records.end(), 830d6c18657SDean Michael Berris std::back_inserter(Records), [&](const YAMLXRayRecord &R) { 8310a465d7aSDean Michael Berris return XRayRecord{R.RecordType, R.CPU, R.Type, R.FuncId, 83210141261SDean Michael Berris R.TSC, R.TId, R.PId, R.CallArgs}; 833d6c18657SDean Michael Berris }); 834d6c18657SDean Michael Berris return Error::success(); 835d6c18657SDean Michael Berris } 83649a49fe8SBenjamin Kramer } // namespace 837d6c18657SDean Michael Berris 838d6c18657SDean Michael Berris Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { 839d6c18657SDean Michael Berris int Fd; 840d6c18657SDean Michael Berris if (auto EC = sys::fs::openFileForRead(Filename, Fd)) { 841d6c18657SDean Michael Berris return make_error<StringError>( 842d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 843d6c18657SDean Michael Berris } 844d6c18657SDean Michael Berris 845d6c18657SDean Michael Berris uint64_t FileSize; 846d6c18657SDean Michael Berris if (auto EC = sys::fs::file_size(Filename, FileSize)) { 847d6c18657SDean Michael Berris return make_error<StringError>( 848d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 849d6c18657SDean Michael Berris } 850d6c18657SDean Michael Berris if (FileSize < 4) { 851d6c18657SDean Michael Berris return make_error<StringError>( 852d6c18657SDean Michael Berris Twine("File '") + Filename + "' too small for XRay.", 85384da6615SHans Wennborg std::make_error_code(std::errc::executable_format_error)); 854d6c18657SDean Michael Berris } 855d6c18657SDean Michael Berris 856d78db156SMartin Pelikan // Map the opened file into memory and use a StringRef to access it later. 857d6c18657SDean Michael Berris std::error_code EC; 858d6c18657SDean Michael Berris sys::fs::mapped_file_region MappedFile( 859d6c18657SDean Michael Berris Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); 860d6c18657SDean Michael Berris if (EC) { 861d6c18657SDean Michael Berris return make_error<StringError>( 862d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 863d6c18657SDean Michael Berris } 864d78db156SMartin Pelikan auto Data = StringRef(MappedFile.data(), MappedFile.size()); 865*5b7548c6SDean Michael Berris 866*5b7548c6SDean Michael Berris // TODO: Lift the endianness and implementation selection here. 867*5b7548c6SDean Michael Berris DataExtractor LittleEndianDE(Data, true, 8); 868*5b7548c6SDean Michael Berris auto TraceOrError = loadTrace(LittleEndianDE, Sort); 869*5b7548c6SDean Michael Berris if (!TraceOrError) { 870*5b7548c6SDean Michael Berris DataExtractor BigEndianDE(Data, false, 8); 871*5b7548c6SDean Michael Berris TraceOrError = loadTrace(BigEndianDE, Sort); 872*5b7548c6SDean Michael Berris } 873*5b7548c6SDean Michael Berris return TraceOrError; 874f81b0800SDean Michael Berris } 875d6c18657SDean Michael Berris 876f81b0800SDean Michael Berris Expected<Trace> llvm::xray::loadTrace(const DataExtractor &DE, bool Sort) { 877d6c18657SDean Michael Berris // Attempt to detect the file type using file magic. We have a slight bias 878d6c18657SDean Michael Berris // towards the binary format, and we do this by making sure that the first 4 879d6c18657SDean Michael Berris // bytes of the binary file is some combination of the following byte 880d78db156SMartin Pelikan // patterns: (observe the code loading them assumes they're little endian) 881d6c18657SDean Michael Berris // 882d78db156SMartin Pelikan // 0x01 0x00 0x00 0x00 - version 1, "naive" format 883d78db156SMartin Pelikan // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format 8846ec72625SDean Michael Berris // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format 885d6c18657SDean Michael Berris // 886d78db156SMartin Pelikan // YAML files don't typically have those first four bytes as valid text so we 887d6c18657SDean Michael Berris // try loading assuming YAML if we don't find these bytes. 888d6c18657SDean Michael Berris // 889d6c18657SDean Michael Berris // Only if we can't load either the binary or the YAML format will we yield an 890d6c18657SDean Michael Berris // error. 891*5b7548c6SDean Michael Berris DataExtractor HeaderExtractor(DE.getData(), DE.isLittleEndian(), 8); 892d6c18657SDean Michael Berris uint32_t OffsetPtr = 0; 893d6c18657SDean Michael Berris uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); 894d6c18657SDean Michael Berris uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); 895d6c18657SDean Michael Berris 8964f83c4d1SDean Michael Berris enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; 8974f83c4d1SDean Michael Berris 898d6c18657SDean Michael Berris Trace T; 8996ec72625SDean Michael Berris switch (Type) { 9006ec72625SDean Michael Berris case NAIVE_FORMAT: 90110141261SDean Michael Berris if (Version == 1 || Version == 2 || Version == 3) { 902*5b7548c6SDean Michael Berris if (auto E = loadNaiveFormatLog(DE.getData(), DE.isLittleEndian(), 903*5b7548c6SDean Michael Berris T.FileHeader, T.Records)) 9044f83c4d1SDean Michael Berris return std::move(E); 9056ec72625SDean Michael Berris } else { 9066ec72625SDean Michael Berris return make_error<StringError>( 9076ec72625SDean Michael Berris Twine("Unsupported version for Basic/Naive Mode logging: ") + 9086ec72625SDean Michael Berris Twine(Version), 9096ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 9106ec72625SDean Michael Berris } 9116ec72625SDean Michael Berris break; 9126ec72625SDean Michael Berris case FLIGHT_DATA_RECORDER_FORMAT: 91310141261SDean Michael Berris if (Version == 1 || Version == 2 || Version == 3) { 914*5b7548c6SDean Michael Berris if (auto E = loadFDRLog(DE.getData(), DE.isLittleEndian(), T.FileHeader, 915*5b7548c6SDean Michael Berris T.Records)) 916d6c18657SDean Michael Berris return std::move(E); 917d6c18657SDean Michael Berris } else { 9186ec72625SDean Michael Berris return make_error<StringError>( 9196ec72625SDean Michael Berris Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), 9206ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 9216ec72625SDean Michael Berris } 9226ec72625SDean Michael Berris break; 9236ec72625SDean Michael Berris default: 924f81b0800SDean Michael Berris if (auto E = loadYAMLLog(DE.getData(), T.FileHeader, T.Records)) 925d6c18657SDean Michael Berris return std::move(E); 926d6c18657SDean Michael Berris } 927d6c18657SDean Michael Berris 928d6c18657SDean Michael Berris if (Sort) 92928f3d5cbSMandeep Singh Grang std::stable_sort(T.Records.begin(), T.Records.end(), 930d6c18657SDean Michael Berris [&](const XRayRecord &L, const XRayRecord &R) { 931d6c18657SDean Michael Berris return L.TSC < R.TSC; 932d6c18657SDean Michael Berris }); 933d6c18657SDean Michael Berris 934d6c18657SDean Michael Berris return std::move(T); 935d6c18657SDean Michael Berris } 936