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" 18*d764c1b6SDean 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 344f83c4d1SDean Michael Berris Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader, 354f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 364f83c4d1SDean Michael Berris if (Data.size() < 32) 374f83c4d1SDean Michael Berris return make_error<StringError>( 384f83c4d1SDean Michael Berris "Not enough bytes for an XRay log.", 394f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 404f83c4d1SDean Michael Berris 414f83c4d1SDean Michael Berris if (Data.size() - 32 == 0 || Data.size() % 32 != 0) 424f83c4d1SDean Michael Berris return make_error<StringError>( 434f83c4d1SDean Michael Berris "Invalid-sized XRay data.", 444f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 454f83c4d1SDean Michael Berris 46a9d477a6SDean Michael Berris DataExtractor Reader(Data, true, 8); 47a9d477a6SDean Michael Berris uint32_t OffsetPtr = 0; 48*d764c1b6SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 49*d764c1b6SDean Michael Berris if (!FileHeaderOrError) 50*d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 51*d764c1b6SDean Michael Berris FileHeader = std::move(FileHeaderOrError.get()); 52d6c18657SDean Michael Berris 53d6c18657SDean Michael Berris // Each record after the header will be 32 bytes, in the following format: 54d6c18657SDean Michael Berris // 55d6c18657SDean Michael Berris // (2) uint16 : record type 56d6c18657SDean Michael Berris // (1) uint8 : cpu id 57d6c18657SDean Michael Berris // (1) uint8 : type 58d6c18657SDean Michael Berris // (4) sint32 : function id 59d6c18657SDean Michael Berris // (8) uint64 : tsc 60d6c18657SDean Michael Berris // (4) uint32 : thread id 6110141261SDean Michael Berris // (4) uint32 : process id 6210141261SDean Michael Berris // (8) - : padding 63a9d477a6SDean Michael Berris while (Reader.isValidOffset(OffsetPtr)) { 64a9d477a6SDean Michael Berris if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) 65a9d477a6SDean Michael Berris return createStringError( 66a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 67a9d477a6SDean Michael Berris "Not enough bytes to read a full record at offset %d.", OffsetPtr); 68a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 69a9d477a6SDean Michael Berris auto RecordType = Reader.getU16(&OffsetPtr); 70a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 71a9d477a6SDean Michael Berris return createStringError( 72a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 73a9d477a6SDean Michael Berris "Failed reading record type at offset %d.", OffsetPtr); 74a9d477a6SDean Michael Berris 75a9d477a6SDean Michael Berris switch (RecordType) { 760a465d7aSDean Michael Berris case 0: { // Normal records. 77d6c18657SDean Michael Berris Records.emplace_back(); 78d6c18657SDean Michael Berris auto &Record = Records.back(); 790a465d7aSDean Michael Berris Record.RecordType = RecordType; 80a9d477a6SDean Michael Berris 81a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 82a9d477a6SDean Michael Berris Record.CPU = Reader.getU8(&OffsetPtr); 83a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 84a9d477a6SDean Michael Berris return createStringError( 85a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 86a9d477a6SDean Michael Berris "Failed reading CPU field at offset %d.", OffsetPtr); 87a9d477a6SDean Michael Berris 88a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 89a9d477a6SDean Michael Berris auto Type = Reader.getU8(&OffsetPtr); 90a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 91a9d477a6SDean Michael Berris return createStringError( 92a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 93a9d477a6SDean Michael Berris "Failed reading record type field at offset %d.", OffsetPtr); 94a9d477a6SDean Michael Berris 95d6c18657SDean Michael Berris switch (Type) { 96d6c18657SDean Michael Berris case 0: 97d6c18657SDean Michael Berris Record.Type = RecordTypes::ENTER; 98d6c18657SDean Michael Berris break; 99d6c18657SDean Michael Berris case 1: 100d6c18657SDean Michael Berris Record.Type = RecordTypes::EXIT; 101d6c18657SDean Michael Berris break; 1020f84a7d3SDean Michael Berris case 2: 1030f84a7d3SDean Michael Berris Record.Type = RecordTypes::TAIL_EXIT; 1040f84a7d3SDean Michael Berris break; 1050a465d7aSDean Michael Berris case 3: 1060a465d7aSDean Michael Berris Record.Type = RecordTypes::ENTER_ARG; 1070a465d7aSDean Michael Berris break; 108d6c18657SDean Michael Berris default: 109a9d477a6SDean Michael Berris return createStringError( 110a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 111a9d477a6SDean Michael Berris "Unknown record type '%d' at offset %d.", Type, OffsetPtr); 112d6c18657SDean Michael Berris } 113a9d477a6SDean Michael Berris 114a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 115a9d477a6SDean Michael Berris Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 116a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 117a9d477a6SDean Michael Berris return createStringError( 118a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 119a9d477a6SDean Michael Berris "Failed reading function id field at offset %d.", OffsetPtr); 120a9d477a6SDean Michael Berris 121a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 122a9d477a6SDean Michael Berris Record.TSC = Reader.getU64(&OffsetPtr); 123a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 124a9d477a6SDean Michael Berris return createStringError( 125a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 126a9d477a6SDean Michael Berris "Failed reading TSC field at offset %d.", OffsetPtr); 127a9d477a6SDean Michael Berris 128a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 129a9d477a6SDean Michael Berris Record.TId = Reader.getU32(&OffsetPtr); 130a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 131a9d477a6SDean Michael Berris return createStringError( 132a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 133a9d477a6SDean Michael Berris "Failed reading thread id field at offset %d.", OffsetPtr); 134a9d477a6SDean Michael Berris 135a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 136a9d477a6SDean Michael Berris Record.PId = Reader.getU32(&OffsetPtr); 137a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 138a9d477a6SDean Michael Berris return createStringError( 139a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 140a9d477a6SDean Michael Berris "Failed reading process id at offset %d.", OffsetPtr); 141a9d477a6SDean Michael Berris 1420a465d7aSDean Michael Berris break; 1430a465d7aSDean Michael Berris } 1440a465d7aSDean Michael Berris case 1: { // Arg payload record. 1450a465d7aSDean Michael Berris auto &Record = Records.back(); 146a9d477a6SDean Michael Berris 147a9d477a6SDean Michael Berris // We skip the next two bytes of the record, because we don't need the 148a9d477a6SDean Michael Berris // type and the CPU record for arg payloads. 1490a465d7aSDean Michael Berris OffsetPtr += 2; 150a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 151a9d477a6SDean Michael Berris int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 152a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 153a9d477a6SDean Michael Berris return createStringError( 154a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 155a9d477a6SDean Michael Berris "Failed reading function id field at offset %d.", OffsetPtr); 156a9d477a6SDean Michael Berris 157a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 158a9d477a6SDean Michael Berris auto TId = Reader.getU32(&OffsetPtr); 159a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 160a9d477a6SDean Michael Berris return createStringError( 161a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 162a9d477a6SDean Michael Berris "Failed reading thread id field at offset %d.", OffsetPtr); 163a9d477a6SDean Michael Berris 164a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 165a9d477a6SDean Michael Berris auto PId = Reader.getU32(&OffsetPtr); 166a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 167a9d477a6SDean Michael Berris return createStringError( 168a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 169a9d477a6SDean Michael Berris "Failed reading process id field at offset %d.", OffsetPtr); 17010141261SDean Michael Berris 17110141261SDean Michael Berris // Make a check for versions above 3 for the Pid field 17210141261SDean Michael Berris if (Record.FuncId != FuncId || Record.TId != TId || 17310141261SDean Michael Berris (FileHeader.Version >= 3 ? Record.PId != PId : false)) 174a9d477a6SDean Michael Berris return createStringError( 175a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 176a9d477a6SDean Michael Berris "Corrupted log, found arg payload following non-matching " 177a9d477a6SDean Michael Berris "function+thread record. Record for function %d != %d at offset " 178a9d477a6SDean Michael Berris "%d", 179a9d477a6SDean Michael Berris Record.FuncId, FuncId, OffsetPtr); 18010141261SDean Michael Berris 181a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 182a9d477a6SDean Michael Berris auto Arg = Reader.getU64(&OffsetPtr); 183a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 184a9d477a6SDean Michael Berris return createStringError( 185a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 186a9d477a6SDean Michael Berris "Failed reading argument payload at offset %d.", OffsetPtr); 187a9d477a6SDean Michael Berris 1880a465d7aSDean Michael Berris Record.CallArgs.push_back(Arg); 1890a465d7aSDean Michael Berris break; 1900a465d7aSDean Michael Berris } 1910a465d7aSDean Michael Berris default: 192a9d477a6SDean Michael Berris return createStringError( 193a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 194a9d477a6SDean Michael Berris "Unknown record type '%d' at offset %d.", RecordType, OffsetPtr); 1950a465d7aSDean Michael Berris } 196a9d477a6SDean Michael Berris // Advance the offset pointer enough bytes to align to 32-byte records for 197a9d477a6SDean Michael Berris // basic mode logs. 198a9d477a6SDean Michael Berris OffsetPtr += 8; 199d6c18657SDean Michael Berris } 200d6c18657SDean Michael Berris return Error::success(); 201d6c18657SDean Michael Berris } 202d6c18657SDean Michael Berris 2034f83c4d1SDean Michael Berris /// When reading from a Flight Data Recorder mode log, metadata records are 2044f83c4d1SDean Michael Berris /// sparse compared to packed function records, so we must maintain state as we 2054f83c4d1SDean Michael Berris /// read through the sequence of entries. This allows the reader to denormalize 2064f83c4d1SDean Michael Berris /// the CPUId and Thread Id onto each Function Record and transform delta 2074f83c4d1SDean Michael Berris /// encoded TSC values into absolute encodings on each record. 2084f83c4d1SDean Michael Berris struct FDRState { 2094f83c4d1SDean Michael Berris uint16_t CPUId; 2104f83c4d1SDean Michael Berris uint16_t ThreadId; 21110141261SDean Michael Berris int32_t ProcessId; 2124f83c4d1SDean Michael Berris uint64_t BaseTSC; 213a7bbe448SDean Michael Berris 2144f83c4d1SDean Michael Berris /// Encode some of the state transitions for the FDR log reader as explicit 2154f83c4d1SDean Michael Berris /// checks. These are expectations for the next Record in the stream. 2164f83c4d1SDean Michael Berris enum class Token { 2174f83c4d1SDean Michael Berris NEW_BUFFER_RECORD_OR_EOF, 2184f83c4d1SDean Michael Berris WALLCLOCK_RECORD, 2194f83c4d1SDean Michael Berris NEW_CPU_ID_RECORD, 22060c24878SDean Michael Berris FUNCTION_SEQUENCE, 22160c24878SDean Michael Berris SCAN_TO_END_OF_THREAD_BUF, 222a7bbe448SDean Michael Berris CUSTOM_EVENT_DATA, 22310c873f1SMartin Pelikan CALL_ARGUMENT, 2246ec72625SDean Michael Berris BUFFER_EXTENTS, 22510141261SDean Michael Berris PID_RECORD, 2264f83c4d1SDean Michael Berris }; 2274f83c4d1SDean Michael Berris Token Expects; 228a7bbe448SDean Michael Berris 22960c24878SDean Michael Berris // Each threads buffer may have trailing garbage to scan over, so we track our 23060c24878SDean Michael Berris // progress. 23160c24878SDean Michael Berris uint64_t CurrentBufferSize; 23260c24878SDean Michael Berris uint64_t CurrentBufferConsumed; 2334f83c4d1SDean Michael Berris }; 234d6c18657SDean Michael Berris 23549a49fe8SBenjamin Kramer const char *fdrStateToTwine(const FDRState::Token &state) { 23660c24878SDean Michael Berris switch (state) { 23760c24878SDean Michael Berris case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 23860c24878SDean Michael Berris return "NEW_BUFFER_RECORD_OR_EOF"; 23960c24878SDean Michael Berris case FDRState::Token::WALLCLOCK_RECORD: 24060c24878SDean Michael Berris return "WALLCLOCK_RECORD"; 24160c24878SDean Michael Berris case FDRState::Token::NEW_CPU_ID_RECORD: 24260c24878SDean Michael Berris return "NEW_CPU_ID_RECORD"; 24360c24878SDean Michael Berris case FDRState::Token::FUNCTION_SEQUENCE: 24460c24878SDean Michael Berris return "FUNCTION_SEQUENCE"; 24560c24878SDean Michael Berris case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF: 24660c24878SDean Michael Berris return "SCAN_TO_END_OF_THREAD_BUF"; 247a7bbe448SDean Michael Berris case FDRState::Token::CUSTOM_EVENT_DATA: 248a7bbe448SDean Michael Berris return "CUSTOM_EVENT_DATA"; 24910c873f1SMartin Pelikan case FDRState::Token::CALL_ARGUMENT: 25010c873f1SMartin Pelikan return "CALL_ARGUMENT"; 2516ec72625SDean Michael Berris case FDRState::Token::BUFFER_EXTENTS: 2526ec72625SDean Michael Berris return "BUFFER_EXTENTS"; 25310141261SDean Michael Berris case FDRState::Token::PID_RECORD: 25410141261SDean Michael Berris return "PID_RECORD"; 25560c24878SDean Michael Berris } 25660c24878SDean Michael Berris return "UNKNOWN"; 25760c24878SDean Michael Berris } 25860c24878SDean Michael Berris 2594f83c4d1SDean Michael Berris /// State transition when a NewBufferRecord is encountered. 260a9d477a6SDean Michael Berris Error processFDRNewBufferRecord(FDRState &State, DataExtractor &RecordExtractor, 261a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 26260c24878SDean Michael Berris if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 263a9d477a6SDean Michael Berris return createStringError( 264a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 265a9d477a6SDean Michael Berris "Malformed log: Read New Buffer record kind out of sequence; expected: " 266a9d477a6SDean Michael Berris "%s at offset %d.", 267a9d477a6SDean Michael Berris fdrStateToTwine(State.Expects), OffsetPtr); 268a9d477a6SDean Michael Berris 269a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 2704f83c4d1SDean Michael Berris State.ThreadId = RecordExtractor.getU16(&OffsetPtr); 271a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 272a9d477a6SDean Michael Berris return createStringError( 273a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 274a9d477a6SDean Michael Berris "Failed reading the thread id at offset %d.", OffsetPtr); 2754f83c4d1SDean Michael Berris State.Expects = FDRState::Token::WALLCLOCK_RECORD; 276a9d477a6SDean Michael Berris 277a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes representing the remaining 278a9d477a6SDean Michael Berris // padding in a metadata record. 279a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 2; 280a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 2814f83c4d1SDean Michael Berris return Error::success(); 2824f83c4d1SDean Michael Berris } 2834f83c4d1SDean Michael Berris 2844f83c4d1SDean Michael Berris /// State transition when an EndOfBufferRecord is encountered. 285a9d477a6SDean Michael Berris Error processFDREndOfBufferRecord(FDRState &State, uint32_t &OffsetPtr) { 28660c24878SDean Michael Berris if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 287a9d477a6SDean Michael Berris return createStringError( 288a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 289a9d477a6SDean Michael Berris "Malformed log: Received EOB message without current buffer; expected: " 290a9d477a6SDean Michael Berris "%s at offset %d.", 291a9d477a6SDean Michael Berris fdrStateToTwine(State.Expects), OffsetPtr); 292a9d477a6SDean Michael Berris 29360c24878SDean Michael Berris State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF; 294a9d477a6SDean Michael Berris 295a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes representing the remaining 296a9d477a6SDean Michael Berris // padding in a metadata record. 297a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize; 2984f83c4d1SDean Michael Berris return Error::success(); 2994f83c4d1SDean Michael Berris } 3004f83c4d1SDean Michael Berris 3014f83c4d1SDean Michael Berris /// State transition when a NewCPUIdRecord is encountered. 302a9d477a6SDean Michael Berris Error processFDRNewCPUIdRecord(FDRState &State, DataExtractor &RecordExtractor, 303a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 3044f83c4d1SDean Michael Berris if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE && 30560c24878SDean Michael Berris State.Expects != FDRState::Token::NEW_CPU_ID_RECORD) 3064f83c4d1SDean Michael Berris return make_error<StringError>( 3076ec72625SDean Michael Berris Twine("Malformed log. Read NewCPUId record kind out of sequence; " 3086ec72625SDean Michael Berris "expected: ") + 3096ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3104f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 311a9d477a6SDean Michael Berris auto BeginOffset = OffsetPtr; 312a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 3134f83c4d1SDean Michael Berris State.CPUId = RecordExtractor.getU16(&OffsetPtr); 314a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 315a9d477a6SDean Michael Berris return createStringError( 316a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 317a9d477a6SDean Michael Berris "Failed reading the CPU field at offset %d.", OffsetPtr); 318a9d477a6SDean Michael Berris 319a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 3204f83c4d1SDean Michael Berris State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 321a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 322a9d477a6SDean Michael Berris return createStringError( 323a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 324a9d477a6SDean Michael Berris "Failed reading the base TSC field at offset %d.", OffsetPtr); 325a9d477a6SDean Michael Berris 3264f83c4d1SDean Michael Berris State.Expects = FDRState::Token::FUNCTION_SEQUENCE; 327a9d477a6SDean Michael Berris 328a9d477a6SDean Michael Berris // Advance the offset pointer by a few bytes, to account for the padding in 329a9d477a6SDean Michael Berris // CPU ID metadata records that we've already advanced through. 330a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - (OffsetPtr - BeginOffset); 331a9d477a6SDean Michael Berris assert(OffsetPtr - BeginOffset == kFDRMetadataBodySize); 3324f83c4d1SDean Michael Berris return Error::success(); 3334f83c4d1SDean Michael Berris } 3344f83c4d1SDean Michael Berris 3354f83c4d1SDean Michael Berris /// State transition when a TSCWrapRecord (overflow detection) is encountered. 336a9d477a6SDean Michael Berris Error processFDRTSCWrapRecord(FDRState &State, DataExtractor &RecordExtractor, 337a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 33860c24878SDean Michael Berris if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE) 3394f83c4d1SDean Michael Berris return make_error<StringError>( 3406ec72625SDean Michael Berris Twine("Malformed log. Read TSCWrap record kind out of sequence; " 3416ec72625SDean Michael Berris "expecting: ") + 3426ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3434f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 344a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 3454f83c4d1SDean Michael Berris State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 346a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 347a9d477a6SDean Michael Berris return createStringError( 348a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 349a9d477a6SDean Michael Berris "Failed reading the base TSC field at offset %d.", OffsetPtr); 350a9d477a6SDean Michael Berris 351a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 352a9d477a6SDean Michael Berris // in the metadata record after reading the base TSC. 353a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 354a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 3554f83c4d1SDean Michael Berris return Error::success(); 3564f83c4d1SDean Michael Berris } 3574f83c4d1SDean Michael Berris 3584f83c4d1SDean Michael Berris /// State transition when a WallTimeMarkerRecord is encountered. 359a9d477a6SDean Michael Berris Error processFDRWallTimeRecord(FDRState &State, DataExtractor &RecordExtractor, 360a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 36160c24878SDean Michael Berris if (State.Expects != FDRState::Token::WALLCLOCK_RECORD) 3624f83c4d1SDean Michael Berris return make_error<StringError>( 3636ec72625SDean Michael Berris Twine("Malformed log. Read Wallclock record kind out of sequence; " 3646ec72625SDean Michael Berris "expecting: ") + 3656ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 3664f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 3676ec72625SDean Michael Berris 368a9d477a6SDean Michael Berris // Read in the data from the walltime record. 369a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 370a9d477a6SDean Michael Berris auto WallTime = RecordExtractor.getU64(&OffsetPtr); 371a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 372a9d477a6SDean Michael Berris return createStringError( 373a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 374a9d477a6SDean Michael Berris "Failed reading the walltime record at offset %d.", OffsetPtr); 375a9d477a6SDean Michael Berris 3766ec72625SDean Michael Berris // TODO: Someday, reconcile the TSC ticks to wall clock time for presentation 3776ec72625SDean Michael Berris // purposes. For now, we're ignoring these records. 378a9d477a6SDean Michael Berris (void)WallTime; 3794f83c4d1SDean Michael Berris State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 380a9d477a6SDean Michael Berris 381a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 382a9d477a6SDean Michael Berris // in the metadata record after reading in the walltime data. 383a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 384a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 3854f83c4d1SDean Michael Berris return Error::success(); 3864f83c4d1SDean Michael Berris } 3874f83c4d1SDean Michael Berris 38810141261SDean Michael Berris /// State transition when a PidRecord is encountered. 389a9d477a6SDean Michael Berris Error processFDRPidRecord(FDRState &State, DataExtractor &RecordExtractor, 390a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 39110141261SDean Michael Berris if (State.Expects != FDRState::Token::PID_RECORD) 39210141261SDean Michael Berris return make_error<StringError>( 39310141261SDean Michael Berris Twine("Malformed log. Read Pid record kind out of sequence; " 39410141261SDean Michael Berris "expected: ") + 39510141261SDean Michael Berris fdrStateToTwine(State.Expects), 39610141261SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 397a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 39810141261SDean Michael Berris State.ProcessId = RecordExtractor.getU32(&OffsetPtr); 399a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 400a9d477a6SDean Michael Berris return createStringError( 401a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 402a9d477a6SDean Michael Berris "Failed reading the process ID at offset %d.", OffsetPtr); 40310141261SDean Michael Berris State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 404a9d477a6SDean Michael Berris 405a9d477a6SDean Michael Berris // Advance the offset pointer by a few more bytes, accounting for the padding 406a9d477a6SDean Michael Berris // in the metadata record after reading in the PID. 407a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 4; 408a9d477a6SDean Michael Berris assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 40910141261SDean Michael Berris return Error::success(); 41010141261SDean Michael Berris } 41110141261SDean Michael Berris 412a7bbe448SDean Michael Berris /// State transition when a CustomEventMarker is encountered. 413a9d477a6SDean Michael Berris Error processCustomEventMarker(FDRState &State, DataExtractor &RecordExtractor, 414a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 415a7bbe448SDean Michael Berris // We can encounter a CustomEventMarker anywhere in the log, so we can handle 4163d0bc9efSKeith Wyss // it regardless of the expectation. However, we do set the expectation to 4173d0bc9efSKeith Wyss // read a set number of fixed bytes, as described in the metadata. 418a9d477a6SDean Michael Berris auto BeginOffset = OffsetPtr; 419a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 420a7bbe448SDean Michael Berris uint32_t DataSize = RecordExtractor.getU32(&OffsetPtr); 421a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 422a9d477a6SDean Michael Berris return createStringError( 423a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 424a9d477a6SDean Michael Berris "Failed reading a custom event marker at offset %d.", OffsetPtr); 425a9d477a6SDean Michael Berris 426a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 427a7bbe448SDean Michael Berris uint64_t TSC = RecordExtractor.getU64(&OffsetPtr); 428a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 429a9d477a6SDean Michael Berris return createStringError( 430a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 431a9d477a6SDean Michael Berris "Failed reading the TSC at offset %d.", OffsetPtr); 432a7bbe448SDean Michael Berris 4330a465d7aSDean Michael Berris // FIXME: Actually represent the record through the API. For now we only 4340a465d7aSDean Michael Berris // skip through the data. 435a7bbe448SDean Michael Berris (void)TSC; 436a9d477a6SDean Michael Berris // Advance the offset ptr by the size of the data associated with the custom 437a9d477a6SDean Michael Berris // event, as well as the padding associated with the remainder of the metadata 438a9d477a6SDean Michael Berris // record. 439a9d477a6SDean Michael Berris OffsetPtr += (kFDRMetadataBodySize - (OffsetPtr - BeginOffset)) + DataSize; 440a9d477a6SDean Michael Berris if (!RecordExtractor.isValidOffset(OffsetPtr)) 441a9d477a6SDean Michael Berris return createStringError( 442a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 443a9d477a6SDean Michael Berris "Reading custom event data moves past addressable trace data (starting " 444a9d477a6SDean Michael Berris "at offset %d, advancing to offset %d).", 445a9d477a6SDean Michael Berris BeginOffset, OffsetPtr); 446a7bbe448SDean Michael Berris return Error::success(); 447a7bbe448SDean Michael Berris } 448a7bbe448SDean Michael Berris 4496ec72625SDean Michael Berris /// State transition when an BufferExtents record is encountered. 450a9d477a6SDean Michael Berris Error processBufferExtents(FDRState &State, DataExtractor &RecordExtractor, 451a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 4526ec72625SDean Michael Berris if (State.Expects != FDRState::Token::BUFFER_EXTENTS) 4536ec72625SDean Michael Berris return make_error<StringError>( 4546ec72625SDean Michael Berris Twine("Malformed log. Buffer Extents unexpected; expected: ") + 4556ec72625SDean Michael Berris fdrStateToTwine(State.Expects), 4566ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 457a9d477a6SDean Michael Berris 458a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 4596ec72625SDean Michael Berris State.CurrentBufferSize = RecordExtractor.getU64(&OffsetPtr); 460a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 461a9d477a6SDean Michael Berris return createStringError( 462a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 463a9d477a6SDean Michael Berris "Failed to read current buffer size at offset %d.", OffsetPtr); 464a9d477a6SDean Michael Berris 4656ec72625SDean Michael Berris State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 466a9d477a6SDean Michael Berris 467a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes accounting for the padding in a 468a9d477a6SDean Michael Berris // metadata record, after we read in the buffer extents. 469a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 4706ec72625SDean Michael Berris return Error::success(); 4716ec72625SDean Michael Berris } 4726ec72625SDean Michael Berris 47310c873f1SMartin Pelikan /// State transition when a CallArgumentRecord is encountered. 474a9d477a6SDean Michael Berris Error processFDRCallArgumentRecord(FDRState &State, 47510c873f1SMartin Pelikan DataExtractor &RecordExtractor, 476a9d477a6SDean Michael Berris std::vector<XRayRecord> &Records, 477a9d477a6SDean Michael Berris uint32_t &OffsetPtr) { 47810c873f1SMartin Pelikan auto &Enter = Records.back(); 479a9d477a6SDean Michael Berris if (Enter.Type != RecordTypes::ENTER && Enter.Type != RecordTypes::ENTER_ARG) 48010c873f1SMartin Pelikan return make_error<StringError>( 48110c873f1SMartin Pelikan "CallArgument needs to be right after a function entry", 48210c873f1SMartin Pelikan std::make_error_code(std::errc::executable_format_error)); 483a9d477a6SDean Michael Berris 484a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 485a9d477a6SDean Michael Berris auto Arg = RecordExtractor.getU64(&OffsetPtr); 486a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 487a9d477a6SDean Michael Berris return createStringError( 488a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 489a9d477a6SDean Michael Berris "Failed to read argument record at offset %d.", OffsetPtr); 490a9d477a6SDean Michael Berris 49110c873f1SMartin Pelikan Enter.Type = RecordTypes::ENTER_ARG; 492a9d477a6SDean Michael Berris Enter.CallArgs.emplace_back(Arg); 493a9d477a6SDean Michael Berris 494a9d477a6SDean Michael Berris // Advance the offset pointer by enough bytes accounting for the padding in a 495a9d477a6SDean Michael Berris // metadata record, after reading the payload. 496a9d477a6SDean Michael Berris OffsetPtr += kFDRMetadataBodySize - 8; 49710c873f1SMartin Pelikan return Error::success(); 49810c873f1SMartin Pelikan } 49910c873f1SMartin Pelikan 5004f83c4d1SDean Michael Berris /// Advances the state machine for reading the FDR record type by reading one 501e96152a9SKeith Wyss /// Metadata Record and updating the State appropriately based on the kind of 5024f83c4d1SDean Michael Berris /// record encountered. The RecordKind is encoded in the first byte of the 5034f83c4d1SDean Michael Berris /// Record, which the caller should pass in because they have already read it 5044f83c4d1SDean Michael Berris /// to determine that this is a metadata record as opposed to a function record. 5056ec72625SDean Michael Berris /// 5066ec72625SDean Michael Berris /// Beginning with Version 2 of the FDR log, we do not depend on the size of the 5076ec72625SDean Michael Berris /// buffer, but rather use the extents to determine how far to read in the log 5086ec72625SDean Michael Berris /// for this particular buffer. 50910141261SDean Michael Berris /// 51010141261SDean Michael Berris /// In Version 3, FDR log now includes a pid metadata record after 51110141261SDean Michael Berris /// WallTimeMarker 512a9d477a6SDean Michael Berris Error processFDRMetadataRecord(FDRState &State, DataExtractor &RecordExtractor, 513a9d477a6SDean Michael Berris uint32_t &OffsetPtr, 5146ec72625SDean Michael Berris std::vector<XRayRecord> &Records, 515a9d477a6SDean Michael Berris uint16_t Version, uint8_t FirstByte) { 516a9d477a6SDean Michael Berris // The remaining 7 bits of the first byte are the RecordKind enum for each 517a9d477a6SDean Michael Berris // Metadata Record. 518a9d477a6SDean Michael Berris switch (FirstByte >> 1) { 5194f83c4d1SDean Michael Berris case 0: // NewBuffer 520a9d477a6SDean Michael Berris if (auto E = processFDRNewBufferRecord(State, RecordExtractor, OffsetPtr)) 5214f83c4d1SDean Michael Berris return E; 5224f83c4d1SDean Michael Berris break; 5234f83c4d1SDean Michael Berris case 1: // EndOfBuffer 5246ec72625SDean Michael Berris if (Version >= 2) 5256ec72625SDean Michael Berris return make_error<StringError>( 5266ec72625SDean Michael Berris "Since Version 2 of FDR logging, we no longer support EOB records.", 5276ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 528a9d477a6SDean Michael Berris if (auto E = processFDREndOfBufferRecord(State, OffsetPtr)) 5294f83c4d1SDean Michael Berris return E; 5304f83c4d1SDean Michael Berris break; 5314f83c4d1SDean Michael Berris case 2: // NewCPUId 532a9d477a6SDean Michael Berris if (auto E = processFDRNewCPUIdRecord(State, RecordExtractor, OffsetPtr)) 5334f83c4d1SDean Michael Berris return E; 5344f83c4d1SDean Michael Berris break; 5354f83c4d1SDean Michael Berris case 3: // TSCWrap 536a9d477a6SDean Michael Berris if (auto E = processFDRTSCWrapRecord(State, RecordExtractor, OffsetPtr)) 5374f83c4d1SDean Michael Berris return E; 5384f83c4d1SDean Michael Berris break; 5394f83c4d1SDean Michael Berris case 4: // WallTimeMarker 540a9d477a6SDean Michael Berris if (auto E = processFDRWallTimeRecord(State, RecordExtractor, OffsetPtr)) 5414f83c4d1SDean Michael Berris return E; 54210141261SDean Michael Berris // In Version 3 and and above, a PidRecord is expected after WallTimeRecord 54310141261SDean Michael Berris if (Version >= 3) 54410141261SDean Michael Berris State.Expects = FDRState::Token::PID_RECORD; 5454f83c4d1SDean Michael Berris break; 546a7bbe448SDean Michael Berris case 5: // CustomEventMarker 547a9d477a6SDean Michael Berris if (auto E = processCustomEventMarker(State, RecordExtractor, OffsetPtr)) 548a7bbe448SDean Michael Berris return E; 549a7bbe448SDean Michael Berris break; 55010c873f1SMartin Pelikan case 6: // CallArgument 551a9d477a6SDean Michael Berris if (auto E = processFDRCallArgumentRecord(State, RecordExtractor, Records, 552a9d477a6SDean Michael Berris OffsetPtr)) 55310c873f1SMartin Pelikan return E; 55410c873f1SMartin Pelikan break; 5556ec72625SDean Michael Berris case 7: // BufferExtents 556a9d477a6SDean Michael Berris if (auto E = processBufferExtents(State, RecordExtractor, OffsetPtr)) 5576ec72625SDean Michael Berris return E; 5586ec72625SDean Michael Berris break; 55910141261SDean Michael Berris case 9: // Pid 560a9d477a6SDean Michael Berris if (auto E = processFDRPidRecord(State, RecordExtractor, OffsetPtr)) 56110141261SDean Michael Berris return E; 56210141261SDean Michael Berris break; 5634f83c4d1SDean Michael Berris default: 564a9d477a6SDean Michael Berris return createStringError( 565a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 566a9d477a6SDean Michael Berris "Illegal metadata record type: '%d' at offset %d.", FirstByte >> 1, 567a9d477a6SDean Michael Berris OffsetPtr); 5684f83c4d1SDean Michael Berris } 5694f83c4d1SDean Michael Berris return Error::success(); 5704f83c4d1SDean Michael Berris } 5714f83c4d1SDean Michael Berris 5724f83c4d1SDean Michael Berris /// Reads a function record from an FDR format log, appending a new XRayRecord 5734f83c4d1SDean Michael Berris /// to the vector being populated and updating the State with a new value 5744f83c4d1SDean Michael Berris /// reference value to interpret TSC deltas. 5754f83c4d1SDean Michael Berris /// 5764f83c4d1SDean Michael Berris /// The XRayRecord constructed includes information from the function record 5774f83c4d1SDean Michael Berris /// processed here as well as Thread ID and CPU ID formerly extracted into 5784f83c4d1SDean Michael Berris /// State. 579a9d477a6SDean Michael Berris Error processFDRFunctionRecord(FDRState &State, DataExtractor &RecordExtractor, 580a9d477a6SDean Michael Berris uint32_t &OffsetPtr, uint8_t FirstByte, 5814f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 5824f83c4d1SDean Michael Berris switch (State.Expects) { 5834f83c4d1SDean Michael Berris case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 5844f83c4d1SDean Michael Berris return make_error<StringError>( 5854f83c4d1SDean Michael Berris "Malformed log. Received Function Record before new buffer setup.", 5864f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 5874f83c4d1SDean Michael Berris case FDRState::Token::WALLCLOCK_RECORD: 5884f83c4d1SDean Michael Berris return make_error<StringError>( 5894f83c4d1SDean Michael Berris "Malformed log. Received Function Record when expecting wallclock.", 5904f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 59110141261SDean Michael Berris case FDRState::Token::PID_RECORD: 59210141261SDean Michael Berris return make_error<StringError>( 59310141261SDean Michael Berris "Malformed log. Received Function Record when expecting pid.", 59410141261SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 5954f83c4d1SDean Michael Berris case FDRState::Token::NEW_CPU_ID_RECORD: 5964f83c4d1SDean Michael Berris return make_error<StringError>( 5974f83c4d1SDean Michael Berris "Malformed log. Received Function Record before first CPU record.", 5984f83c4d1SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 5994f83c4d1SDean Michael Berris default: 6004f83c4d1SDean Michael Berris Records.emplace_back(); 6014f83c4d1SDean Michael Berris auto &Record = Records.back(); 6024f83c4d1SDean Michael Berris Record.RecordType = 0; // Record is type NORMAL. 6034f83c4d1SDean Michael Berris // Strip off record type bit and use the next three bits. 604a9d477a6SDean Michael Berris auto T = (FirstByte >> 1) & 0x07; 605a9d477a6SDean Michael Berris switch (T) { 606a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::ENTER): 6074f83c4d1SDean Michael Berris Record.Type = RecordTypes::ENTER; 6084f83c4d1SDean Michael Berris break; 609a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::EXIT): 6104f83c4d1SDean Michael Berris Record.Type = RecordTypes::EXIT; 6114f83c4d1SDean Michael Berris break; 612a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::TAIL_EXIT): 6130f84a7d3SDean Michael Berris Record.Type = RecordTypes::TAIL_EXIT; 6140f84a7d3SDean Michael Berris break; 615a9d477a6SDean Michael Berris case static_cast<decltype(T)>(RecordTypes::ENTER_ARG): 616a9d477a6SDean Michael Berris Record.Type = RecordTypes::ENTER_ARG; 617a9d477a6SDean Michael Berris State.Expects = FDRState::Token::CALL_ARGUMENT; 618a9d477a6SDean Michael Berris break; 6194f83c4d1SDean Michael Berris default: 620a9d477a6SDean Michael Berris return createStringError( 621a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 622a9d477a6SDean Michael Berris "Illegal function record type '%d' at offset %d.", T, OffsetPtr); 6234f83c4d1SDean Michael Berris } 6244f83c4d1SDean Michael Berris Record.CPU = State.CPUId; 6254f83c4d1SDean Michael Berris Record.TId = State.ThreadId; 62610141261SDean Michael Berris Record.PId = State.ProcessId; 627a9d477a6SDean Michael Berris 628a9d477a6SDean Michael Berris // Back up one byte to re-read the first byte, which is important for 629a9d477a6SDean Michael Berris // computing the function id for a record. 630a9d477a6SDean Michael Berris --OffsetPtr; 631a9d477a6SDean Michael Berris 6324f83c4d1SDean Michael Berris // Despite function Id being a signed int on XRayRecord, 6334f83c4d1SDean Michael Berris // when it is written to an FDR format, the top bits are truncated, 6344f83c4d1SDean Michael Berris // so it is effectively an unsigned value. When we shift off the 6354f83c4d1SDean Michael Berris // top four bits, we want the shift to be logical, so we read as 6364f83c4d1SDean Michael Berris // uint32_t. 637a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 6384f83c4d1SDean Michael Berris uint32_t FuncIdBitField = RecordExtractor.getU32(&OffsetPtr); 639a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 640a9d477a6SDean Michael Berris return createStringError( 641a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 642a9d477a6SDean Michael Berris "Failed reading truncated function id field at offset %d.", 643a9d477a6SDean Michael Berris OffsetPtr); 644a9d477a6SDean Michael Berris 6454f83c4d1SDean Michael Berris Record.FuncId = FuncIdBitField >> 4; 646a9d477a6SDean Michael Berris 6474f83c4d1SDean Michael Berris // FunctionRecords have a 32 bit delta from the previous absolute TSC 6484f83c4d1SDean Michael Berris // or TSC delta. If this would overflow, we should read a TSCWrap record 6494f83c4d1SDean Michael Berris // with an absolute TSC reading. 650a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 651d78db156SMartin Pelikan uint64_t NewTSC = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr); 652a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 653a9d477a6SDean Michael Berris return createStringError( 654a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 655a9d477a6SDean Michael Berris "Failed reading TSC delta at offset %d.", OffsetPtr); 656a9d477a6SDean Michael Berris 657d78db156SMartin Pelikan State.BaseTSC = NewTSC; 658d78db156SMartin Pelikan Record.TSC = NewTSC; 6594f83c4d1SDean Michael Berris } 6604f83c4d1SDean Michael Berris return Error::success(); 6614f83c4d1SDean Michael Berris } 6624f83c4d1SDean Michael Berris 6634f83c4d1SDean Michael Berris /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is 6644f83c4d1SDean Michael Berris /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such 6654f83c4d1SDean Michael Berris /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of 6664f83c4d1SDean Michael Berris /// of interspersed 16 byte Metadata Records and 8 byte Function Records. 6674f83c4d1SDean Michael Berris /// 6684f83c4d1SDean Michael Berris /// The following is an attempt to document the grammar of the format, which is 6694f83c4d1SDean Michael Berris /// parsed by this function for little-endian machines. Since the format makes 670d78db156SMartin Pelikan /// use of BitFields, when we support big-endian architectures, we will need to 67168168d17SSimon Pilgrim /// adjust not only the endianness parameter to llvm's RecordExtractor, but also 6724f83c4d1SDean Michael Berris /// the bit twiddling logic, which is consistent with the little-endian 6734f83c4d1SDean Michael Berris /// convention that BitFields within a struct will first be packed into the 6744f83c4d1SDean Michael Berris /// least significant bits the address they belong to. 6754f83c4d1SDean Michael Berris /// 6766ec72625SDean Michael Berris /// We expect a format complying with the grammar in the following pseudo-EBNF 6776ec72625SDean Michael Berris /// in Version 1 of the FDR log. 6784f83c4d1SDean Michael Berris /// 6794f83c4d1SDean Michael Berris /// FDRLog: XRayFileHeader ThreadBuffer* 6803d0bc9efSKeith Wyss /// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. 6813d0bc9efSKeith Wyss /// Includes BufferSize 6823d0bc9efSKeith Wyss /// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB 68360c24878SDean Michael Berris /// BufSize: 8 byte unsigned integer indicating how large the buffer is. 6844f83c4d1SDean Michael Berris /// NewBuffer: 16 byte metadata record with Thread Id. 6854f83c4d1SDean Michael Berris /// WallClockTime: 16 byte metadata record with human readable time. 68610141261SDean Michael Berris /// Pid: 16 byte metadata record with Pid 6874f83c4d1SDean Michael Berris /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. 68860c24878SDean Michael Berris /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. 6894f83c4d1SDean Michael Berris /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord 6904f83c4d1SDean Michael Berris /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. 6914f83c4d1SDean Michael Berris /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. 6926ec72625SDean Michael Berris /// 6936ec72625SDean Michael Berris /// In Version 2, we make the following changes: 6946ec72625SDean Michael Berris /// 6956ec72625SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId 6966ec72625SDean Michael Berris /// FunctionSequence 6976ec72625SDean Michael Berris /// BufferExtents: 16 byte metdata record describing how many usable bytes are 6986ec72625SDean Michael Berris /// in the buffer. This is measured from the start of the buffer 6996ec72625SDean Michael Berris /// and must always be at least 48 (bytes). 70010141261SDean Michael Berris /// 70110141261SDean Michael Berris /// In Version 3, we make the following changes: 70210141261SDean Michael Berris /// 70310141261SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId 70410141261SDean Michael Berris /// FunctionSequence 7056ec72625SDean Michael Berris /// EOB: *deprecated* 7064f83c4d1SDean Michael Berris Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader, 7074f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 708*d764c1b6SDean Michael Berris 7094f83c4d1SDean Michael Berris if (Data.size() < 32) 7104f83c4d1SDean Michael Berris return make_error<StringError>( 7114f83c4d1SDean Michael Berris "Not enough bytes for an XRay log.", 7124f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 7134f83c4d1SDean Michael Berris 714a9d477a6SDean Michael Berris DataExtractor Reader(Data, true, 8); 715a9d477a6SDean Michael Berris uint32_t OffsetPtr = 0; 7164f83c4d1SDean Michael Berris 717*d764c1b6SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 718*d764c1b6SDean Michael Berris if (!FileHeaderOrError) 719*d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 720*d764c1b6SDean 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); 72560c24878SDean Michael Berris DataExtractor ExtraDataExtractor(ExtraDataRef, true, 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()); 865d6c18657SDean Michael Berris 866d6c18657SDean Michael Berris // Attempt to detect the file type using file magic. We have a slight bias 867d6c18657SDean Michael Berris // towards the binary format, and we do this by making sure that the first 4 868d6c18657SDean Michael Berris // bytes of the binary file is some combination of the following byte 869d78db156SMartin Pelikan // patterns: (observe the code loading them assumes they're little endian) 870d6c18657SDean Michael Berris // 871d78db156SMartin Pelikan // 0x01 0x00 0x00 0x00 - version 1, "naive" format 872d78db156SMartin Pelikan // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format 8736ec72625SDean Michael Berris // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format 874d6c18657SDean Michael Berris // 875d78db156SMartin Pelikan // YAML files don't typically have those first four bytes as valid text so we 876d6c18657SDean Michael Berris // try loading assuming YAML if we don't find these bytes. 877d6c18657SDean Michael Berris // 878d6c18657SDean Michael Berris // Only if we can't load either the binary or the YAML format will we yield an 879d6c18657SDean Michael Berris // error. 880d6c18657SDean Michael Berris StringRef Magic(MappedFile.data(), 4); 881d6c18657SDean Michael Berris DataExtractor HeaderExtractor(Magic, true, 8); 882d6c18657SDean Michael Berris uint32_t OffsetPtr = 0; 883d6c18657SDean Michael Berris uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); 884d6c18657SDean Michael Berris uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); 885d6c18657SDean Michael Berris 8864f83c4d1SDean Michael Berris enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; 8874f83c4d1SDean Michael Berris 888d6c18657SDean Michael Berris Trace T; 8896ec72625SDean Michael Berris switch (Type) { 8906ec72625SDean Michael Berris case NAIVE_FORMAT: 89110141261SDean Michael Berris if (Version == 1 || Version == 2 || Version == 3) { 8920f84a7d3SDean Michael Berris if (auto E = loadNaiveFormatLog(Data, T.FileHeader, T.Records)) 8934f83c4d1SDean Michael Berris return std::move(E); 8946ec72625SDean Michael Berris } else { 8956ec72625SDean Michael Berris return make_error<StringError>( 8966ec72625SDean Michael Berris Twine("Unsupported version for Basic/Naive Mode logging: ") + 8976ec72625SDean Michael Berris Twine(Version), 8986ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 8996ec72625SDean Michael Berris } 9006ec72625SDean Michael Berris break; 9016ec72625SDean Michael Berris case FLIGHT_DATA_RECORDER_FORMAT: 90210141261SDean Michael Berris if (Version == 1 || Version == 2 || Version == 3) { 903d78db156SMartin Pelikan if (auto E = loadFDRLog(Data, T.FileHeader, T.Records)) 904d6c18657SDean Michael Berris return std::move(E); 905d6c18657SDean Michael Berris } else { 9066ec72625SDean Michael Berris return make_error<StringError>( 9076ec72625SDean Michael Berris Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), 9086ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 9096ec72625SDean Michael Berris } 9106ec72625SDean Michael Berris break; 9116ec72625SDean Michael Berris default: 912d78db156SMartin Pelikan if (auto E = loadYAMLLog(Data, T.FileHeader, T.Records)) 913d6c18657SDean Michael Berris return std::move(E); 914d6c18657SDean Michael Berris } 915d6c18657SDean Michael Berris 916d6c18657SDean Michael Berris if (Sort) 91728f3d5cbSMandeep Singh Grang std::stable_sort(T.Records.begin(), T.Records.end(), 918d6c18657SDean Michael Berris [&](const XRayRecord &L, const XRayRecord &R) { 919d6c18657SDean Michael Berris return L.TSC < R.TSC; 920d6c18657SDean Michael Berris }); 921d6c18657SDean Michael Berris 922d6c18657SDean Michael Berris return std::move(T); 923d6c18657SDean Michael Berris } 924