1 //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // XRay log reader implementation. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "llvm/XRay/Trace.h" 14 #include "llvm/ADT/STLExtras.h" 15 #include "llvm/Support/DataExtractor.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/FileSystem.h" 18 #include "llvm/XRay/FileHeaderReader.h" 19 #include "llvm/XRay/YAMLXRayRecord.h" 20 21 using namespace llvm; 22 using namespace llvm::xray; 23 using llvm::yaml::Input; 24 25 namespace { 26 using XRayRecordStorage = 27 std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type; 28 29 // This is the number of bytes in the "body" of a MetadataRecord in FDR Mode. 30 // This already excludes the first byte, which indicates the type of metadata 31 // record it is. 32 constexpr auto kFDRMetadataBodySize = 15; 33 34 Error loadNaiveFormatLog(StringRef Data, XRayFileHeader &FileHeader, 35 std::vector<XRayRecord> &Records) { 36 if (Data.size() < 32) 37 return make_error<StringError>( 38 "Not enough bytes for an XRay log.", 39 std::make_error_code(std::errc::invalid_argument)); 40 41 if (Data.size() - 32 == 0 || Data.size() % 32 != 0) 42 return make_error<StringError>( 43 "Invalid-sized XRay data.", 44 std::make_error_code(std::errc::invalid_argument)); 45 46 DataExtractor Reader(Data, true, 8); 47 uint32_t OffsetPtr = 0; 48 auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 49 if (!FileHeaderOrError) 50 return FileHeaderOrError.takeError(); 51 FileHeader = std::move(FileHeaderOrError.get()); 52 53 // Each record after the header will be 32 bytes, in the following format: 54 // 55 // (2) uint16 : record type 56 // (1) uint8 : cpu id 57 // (1) uint8 : type 58 // (4) sint32 : function id 59 // (8) uint64 : tsc 60 // (4) uint32 : thread id 61 // (4) uint32 : process id 62 // (8) - : padding 63 while (Reader.isValidOffset(OffsetPtr)) { 64 if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) 65 return createStringError( 66 std::make_error_code(std::errc::executable_format_error), 67 "Not enough bytes to read a full record at offset %d.", OffsetPtr); 68 auto PreReadOffset = OffsetPtr; 69 auto RecordType = Reader.getU16(&OffsetPtr); 70 if (OffsetPtr == PreReadOffset) 71 return createStringError( 72 std::make_error_code(std::errc::executable_format_error), 73 "Failed reading record type at offset %d.", OffsetPtr); 74 75 switch (RecordType) { 76 case 0: { // Normal records. 77 Records.emplace_back(); 78 auto &Record = Records.back(); 79 Record.RecordType = RecordType; 80 81 PreReadOffset = OffsetPtr; 82 Record.CPU = Reader.getU8(&OffsetPtr); 83 if (OffsetPtr == PreReadOffset) 84 return createStringError( 85 std::make_error_code(std::errc::executable_format_error), 86 "Failed reading CPU field at offset %d.", OffsetPtr); 87 88 PreReadOffset = OffsetPtr; 89 auto Type = Reader.getU8(&OffsetPtr); 90 if (OffsetPtr == PreReadOffset) 91 return createStringError( 92 std::make_error_code(std::errc::executable_format_error), 93 "Failed reading record type field at offset %d.", OffsetPtr); 94 95 switch (Type) { 96 case 0: 97 Record.Type = RecordTypes::ENTER; 98 break; 99 case 1: 100 Record.Type = RecordTypes::EXIT; 101 break; 102 case 2: 103 Record.Type = RecordTypes::TAIL_EXIT; 104 break; 105 case 3: 106 Record.Type = RecordTypes::ENTER_ARG; 107 break; 108 default: 109 return createStringError( 110 std::make_error_code(std::errc::executable_format_error), 111 "Unknown record type '%d' at offset %d.", Type, OffsetPtr); 112 } 113 114 PreReadOffset = OffsetPtr; 115 Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 116 if (OffsetPtr == PreReadOffset) 117 return createStringError( 118 std::make_error_code(std::errc::executable_format_error), 119 "Failed reading function id field at offset %d.", OffsetPtr); 120 121 PreReadOffset = OffsetPtr; 122 Record.TSC = Reader.getU64(&OffsetPtr); 123 if (OffsetPtr == PreReadOffset) 124 return createStringError( 125 std::make_error_code(std::errc::executable_format_error), 126 "Failed reading TSC field at offset %d.", OffsetPtr); 127 128 PreReadOffset = OffsetPtr; 129 Record.TId = Reader.getU32(&OffsetPtr); 130 if (OffsetPtr == PreReadOffset) 131 return createStringError( 132 std::make_error_code(std::errc::executable_format_error), 133 "Failed reading thread id field at offset %d.", OffsetPtr); 134 135 PreReadOffset = OffsetPtr; 136 Record.PId = Reader.getU32(&OffsetPtr); 137 if (OffsetPtr == PreReadOffset) 138 return createStringError( 139 std::make_error_code(std::errc::executable_format_error), 140 "Failed reading process id at offset %d.", OffsetPtr); 141 142 break; 143 } 144 case 1: { // Arg payload record. 145 auto &Record = Records.back(); 146 147 // We skip the next two bytes of the record, because we don't need the 148 // type and the CPU record for arg payloads. 149 OffsetPtr += 2; 150 PreReadOffset = OffsetPtr; 151 int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 152 if (OffsetPtr == PreReadOffset) 153 return createStringError( 154 std::make_error_code(std::errc::executable_format_error), 155 "Failed reading function id field at offset %d.", OffsetPtr); 156 157 PreReadOffset = OffsetPtr; 158 auto TId = Reader.getU32(&OffsetPtr); 159 if (OffsetPtr == PreReadOffset) 160 return createStringError( 161 std::make_error_code(std::errc::executable_format_error), 162 "Failed reading thread id field at offset %d.", OffsetPtr); 163 164 PreReadOffset = OffsetPtr; 165 auto PId = Reader.getU32(&OffsetPtr); 166 if (OffsetPtr == PreReadOffset) 167 return createStringError( 168 std::make_error_code(std::errc::executable_format_error), 169 "Failed reading process id field at offset %d.", OffsetPtr); 170 171 // Make a check for versions above 3 for the Pid field 172 if (Record.FuncId != FuncId || Record.TId != TId || 173 (FileHeader.Version >= 3 ? Record.PId != PId : false)) 174 return createStringError( 175 std::make_error_code(std::errc::executable_format_error), 176 "Corrupted log, found arg payload following non-matching " 177 "function+thread record. Record for function %d != %d at offset " 178 "%d", 179 Record.FuncId, FuncId, OffsetPtr); 180 181 PreReadOffset = OffsetPtr; 182 auto Arg = Reader.getU64(&OffsetPtr); 183 if (OffsetPtr == PreReadOffset) 184 return createStringError( 185 std::make_error_code(std::errc::executable_format_error), 186 "Failed reading argument payload at offset %d.", OffsetPtr); 187 188 Record.CallArgs.push_back(Arg); 189 break; 190 } 191 default: 192 return createStringError( 193 std::make_error_code(std::errc::executable_format_error), 194 "Unknown record type '%d' at offset %d.", RecordType, OffsetPtr); 195 } 196 // Advance the offset pointer enough bytes to align to 32-byte records for 197 // basic mode logs. 198 OffsetPtr += 8; 199 } 200 return Error::success(); 201 } 202 203 /// When reading from a Flight Data Recorder mode log, metadata records are 204 /// sparse compared to packed function records, so we must maintain state as we 205 /// read through the sequence of entries. This allows the reader to denormalize 206 /// the CPUId and Thread Id onto each Function Record and transform delta 207 /// encoded TSC values into absolute encodings on each record. 208 struct FDRState { 209 uint16_t CPUId; 210 uint16_t ThreadId; 211 int32_t ProcessId; 212 uint64_t BaseTSC; 213 214 /// Encode some of the state transitions for the FDR log reader as explicit 215 /// checks. These are expectations for the next Record in the stream. 216 enum class Token { 217 NEW_BUFFER_RECORD_OR_EOF, 218 WALLCLOCK_RECORD, 219 NEW_CPU_ID_RECORD, 220 FUNCTION_SEQUENCE, 221 SCAN_TO_END_OF_THREAD_BUF, 222 CUSTOM_EVENT_DATA, 223 CALL_ARGUMENT, 224 BUFFER_EXTENTS, 225 PID_RECORD, 226 }; 227 Token Expects; 228 229 // Each threads buffer may have trailing garbage to scan over, so we track our 230 // progress. 231 uint64_t CurrentBufferSize; 232 uint64_t CurrentBufferConsumed; 233 }; 234 235 const char *fdrStateToTwine(const FDRState::Token &state) { 236 switch (state) { 237 case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 238 return "NEW_BUFFER_RECORD_OR_EOF"; 239 case FDRState::Token::WALLCLOCK_RECORD: 240 return "WALLCLOCK_RECORD"; 241 case FDRState::Token::NEW_CPU_ID_RECORD: 242 return "NEW_CPU_ID_RECORD"; 243 case FDRState::Token::FUNCTION_SEQUENCE: 244 return "FUNCTION_SEQUENCE"; 245 case FDRState::Token::SCAN_TO_END_OF_THREAD_BUF: 246 return "SCAN_TO_END_OF_THREAD_BUF"; 247 case FDRState::Token::CUSTOM_EVENT_DATA: 248 return "CUSTOM_EVENT_DATA"; 249 case FDRState::Token::CALL_ARGUMENT: 250 return "CALL_ARGUMENT"; 251 case FDRState::Token::BUFFER_EXTENTS: 252 return "BUFFER_EXTENTS"; 253 case FDRState::Token::PID_RECORD: 254 return "PID_RECORD"; 255 } 256 return "UNKNOWN"; 257 } 258 259 /// State transition when a NewBufferRecord is encountered. 260 Error processFDRNewBufferRecord(FDRState &State, DataExtractor &RecordExtractor, 261 uint32_t &OffsetPtr) { 262 if (State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 263 return createStringError( 264 std::make_error_code(std::errc::executable_format_error), 265 "Malformed log: Read New Buffer record kind out of sequence; expected: " 266 "%s at offset %d.", 267 fdrStateToTwine(State.Expects), OffsetPtr); 268 269 auto PreReadOffset = OffsetPtr; 270 State.ThreadId = RecordExtractor.getU16(&OffsetPtr); 271 if (OffsetPtr == PreReadOffset) 272 return createStringError( 273 std::make_error_code(std::errc::executable_format_error), 274 "Failed reading the thread id at offset %d.", OffsetPtr); 275 State.Expects = FDRState::Token::WALLCLOCK_RECORD; 276 277 // Advance the offset pointer by enough bytes representing the remaining 278 // padding in a metadata record. 279 OffsetPtr += kFDRMetadataBodySize - 2; 280 assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 281 return Error::success(); 282 } 283 284 /// State transition when an EndOfBufferRecord is encountered. 285 Error processFDREndOfBufferRecord(FDRState &State, uint32_t &OffsetPtr) { 286 if (State.Expects == FDRState::Token::NEW_BUFFER_RECORD_OR_EOF) 287 return createStringError( 288 std::make_error_code(std::errc::executable_format_error), 289 "Malformed log: Received EOB message without current buffer; expected: " 290 "%s at offset %d.", 291 fdrStateToTwine(State.Expects), OffsetPtr); 292 293 State.Expects = FDRState::Token::SCAN_TO_END_OF_THREAD_BUF; 294 295 // Advance the offset pointer by enough bytes representing the remaining 296 // padding in a metadata record. 297 OffsetPtr += kFDRMetadataBodySize; 298 return Error::success(); 299 } 300 301 /// State transition when a NewCPUIdRecord is encountered. 302 Error processFDRNewCPUIdRecord(FDRState &State, DataExtractor &RecordExtractor, 303 uint32_t &OffsetPtr) { 304 if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE && 305 State.Expects != FDRState::Token::NEW_CPU_ID_RECORD) 306 return make_error<StringError>( 307 Twine("Malformed log. Read NewCPUId record kind out of sequence; " 308 "expected: ") + 309 fdrStateToTwine(State.Expects), 310 std::make_error_code(std::errc::executable_format_error)); 311 auto BeginOffset = OffsetPtr; 312 auto PreReadOffset = OffsetPtr; 313 State.CPUId = RecordExtractor.getU16(&OffsetPtr); 314 if (OffsetPtr == PreReadOffset) 315 return createStringError( 316 std::make_error_code(std::errc::executable_format_error), 317 "Failed reading the CPU field at offset %d.", OffsetPtr); 318 319 PreReadOffset = OffsetPtr; 320 State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 321 if (OffsetPtr == PreReadOffset) 322 return createStringError( 323 std::make_error_code(std::errc::executable_format_error), 324 "Failed reading the base TSC field at offset %d.", OffsetPtr); 325 326 State.Expects = FDRState::Token::FUNCTION_SEQUENCE; 327 328 // Advance the offset pointer by a few bytes, to account for the padding in 329 // CPU ID metadata records that we've already advanced through. 330 OffsetPtr += kFDRMetadataBodySize - (OffsetPtr - BeginOffset); 331 assert(OffsetPtr - BeginOffset == kFDRMetadataBodySize); 332 return Error::success(); 333 } 334 335 /// State transition when a TSCWrapRecord (overflow detection) is encountered. 336 Error processFDRTSCWrapRecord(FDRState &State, DataExtractor &RecordExtractor, 337 uint32_t &OffsetPtr) { 338 if (State.Expects != FDRState::Token::FUNCTION_SEQUENCE) 339 return make_error<StringError>( 340 Twine("Malformed log. Read TSCWrap record kind out of sequence; " 341 "expecting: ") + 342 fdrStateToTwine(State.Expects), 343 std::make_error_code(std::errc::executable_format_error)); 344 auto PreReadOffset = OffsetPtr; 345 State.BaseTSC = RecordExtractor.getU64(&OffsetPtr); 346 if (OffsetPtr == PreReadOffset) 347 return createStringError( 348 std::make_error_code(std::errc::executable_format_error), 349 "Failed reading the base TSC field at offset %d.", OffsetPtr); 350 351 // Advance the offset pointer by a few more bytes, accounting for the padding 352 // in the metadata record after reading the base TSC. 353 OffsetPtr += kFDRMetadataBodySize - 8; 354 assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 355 return Error::success(); 356 } 357 358 /// State transition when a WallTimeMarkerRecord is encountered. 359 Error processFDRWallTimeRecord(FDRState &State, DataExtractor &RecordExtractor, 360 uint32_t &OffsetPtr) { 361 if (State.Expects != FDRState::Token::WALLCLOCK_RECORD) 362 return make_error<StringError>( 363 Twine("Malformed log. Read Wallclock record kind out of sequence; " 364 "expecting: ") + 365 fdrStateToTwine(State.Expects), 366 std::make_error_code(std::errc::executable_format_error)); 367 368 // Read in the data from the walltime record. 369 auto PreReadOffset = OffsetPtr; 370 auto WallTime = RecordExtractor.getU64(&OffsetPtr); 371 if (OffsetPtr == PreReadOffset) 372 return createStringError( 373 std::make_error_code(std::errc::executable_format_error), 374 "Failed reading the walltime record at offset %d.", OffsetPtr); 375 376 // TODO: Someday, reconcile the TSC ticks to wall clock time for presentation 377 // purposes. For now, we're ignoring these records. 378 (void)WallTime; 379 State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 380 381 // Advance the offset pointer by a few more bytes, accounting for the padding 382 // in the metadata record after reading in the walltime data. 383 OffsetPtr += kFDRMetadataBodySize - 8; 384 assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 385 return Error::success(); 386 } 387 388 /// State transition when a PidRecord is encountered. 389 Error processFDRPidRecord(FDRState &State, DataExtractor &RecordExtractor, 390 uint32_t &OffsetPtr) { 391 if (State.Expects != FDRState::Token::PID_RECORD) 392 return make_error<StringError>( 393 Twine("Malformed log. Read Pid record kind out of sequence; " 394 "expected: ") + 395 fdrStateToTwine(State.Expects), 396 std::make_error_code(std::errc::executable_format_error)); 397 auto PreReadOffset = OffsetPtr; 398 State.ProcessId = RecordExtractor.getU32(&OffsetPtr); 399 if (OffsetPtr == PreReadOffset) 400 return createStringError( 401 std::make_error_code(std::errc::executable_format_error), 402 "Failed reading the process ID at offset %d.", OffsetPtr); 403 State.Expects = FDRState::Token::NEW_CPU_ID_RECORD; 404 405 // Advance the offset pointer by a few more bytes, accounting for the padding 406 // in the metadata record after reading in the PID. 407 OffsetPtr += kFDRMetadataBodySize - 4; 408 assert(OffsetPtr - PreReadOffset == kFDRMetadataBodySize); 409 return Error::success(); 410 } 411 412 /// State transition when a CustomEventMarker is encountered. 413 Error processCustomEventMarker(FDRState &State, DataExtractor &RecordExtractor, 414 uint32_t &OffsetPtr) { 415 // We can encounter a CustomEventMarker anywhere in the log, so we can handle 416 // it regardless of the expectation. However, we do set the expectation to 417 // read a set number of fixed bytes, as described in the metadata. 418 auto BeginOffset = OffsetPtr; 419 auto PreReadOffset = OffsetPtr; 420 uint32_t DataSize = RecordExtractor.getU32(&OffsetPtr); 421 if (OffsetPtr == PreReadOffset) 422 return createStringError( 423 std::make_error_code(std::errc::executable_format_error), 424 "Failed reading a custom event marker at offset %d.", OffsetPtr); 425 426 PreReadOffset = OffsetPtr; 427 uint64_t TSC = RecordExtractor.getU64(&OffsetPtr); 428 if (OffsetPtr == PreReadOffset) 429 return createStringError( 430 std::make_error_code(std::errc::executable_format_error), 431 "Failed reading the TSC at offset %d.", OffsetPtr); 432 433 // FIXME: Actually represent the record through the API. For now we only 434 // skip through the data. 435 (void)TSC; 436 // Advance the offset ptr by the size of the data associated with the custom 437 // event, as well as the padding associated with the remainder of the metadata 438 // record. 439 OffsetPtr += (kFDRMetadataBodySize - (OffsetPtr - BeginOffset)) + DataSize; 440 if (!RecordExtractor.isValidOffset(OffsetPtr)) 441 return createStringError( 442 std::make_error_code(std::errc::executable_format_error), 443 "Reading custom event data moves past addressable trace data (starting " 444 "at offset %d, advancing to offset %d).", 445 BeginOffset, OffsetPtr); 446 return Error::success(); 447 } 448 449 /// State transition when an BufferExtents record is encountered. 450 Error processBufferExtents(FDRState &State, DataExtractor &RecordExtractor, 451 uint32_t &OffsetPtr) { 452 if (State.Expects != FDRState::Token::BUFFER_EXTENTS) 453 return make_error<StringError>( 454 Twine("Malformed log. Buffer Extents unexpected; expected: ") + 455 fdrStateToTwine(State.Expects), 456 std::make_error_code(std::errc::executable_format_error)); 457 458 auto PreReadOffset = OffsetPtr; 459 State.CurrentBufferSize = RecordExtractor.getU64(&OffsetPtr); 460 if (OffsetPtr == PreReadOffset) 461 return createStringError( 462 std::make_error_code(std::errc::executable_format_error), 463 "Failed to read current buffer size at offset %d.", OffsetPtr); 464 465 State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 466 467 // Advance the offset pointer by enough bytes accounting for the padding in a 468 // metadata record, after we read in the buffer extents. 469 OffsetPtr += kFDRMetadataBodySize - 8; 470 return Error::success(); 471 } 472 473 /// State transition when a CallArgumentRecord is encountered. 474 Error processFDRCallArgumentRecord(FDRState &State, 475 DataExtractor &RecordExtractor, 476 std::vector<XRayRecord> &Records, 477 uint32_t &OffsetPtr) { 478 auto &Enter = Records.back(); 479 if (Enter.Type != RecordTypes::ENTER && Enter.Type != RecordTypes::ENTER_ARG) 480 return make_error<StringError>( 481 "CallArgument needs to be right after a function entry", 482 std::make_error_code(std::errc::executable_format_error)); 483 484 auto PreReadOffset = OffsetPtr; 485 auto Arg = RecordExtractor.getU64(&OffsetPtr); 486 if (OffsetPtr == PreReadOffset) 487 return createStringError( 488 std::make_error_code(std::errc::executable_format_error), 489 "Failed to read argument record at offset %d.", OffsetPtr); 490 491 Enter.Type = RecordTypes::ENTER_ARG; 492 Enter.CallArgs.emplace_back(Arg); 493 494 // Advance the offset pointer by enough bytes accounting for the padding in a 495 // metadata record, after reading the payload. 496 OffsetPtr += kFDRMetadataBodySize - 8; 497 return Error::success(); 498 } 499 500 /// Advances the state machine for reading the FDR record type by reading one 501 /// Metadata Record and updating the State appropriately based on the kind of 502 /// record encountered. The RecordKind is encoded in the first byte of the 503 /// Record, which the caller should pass in because they have already read it 504 /// to determine that this is a metadata record as opposed to a function record. 505 /// 506 /// Beginning with Version 2 of the FDR log, we do not depend on the size of the 507 /// buffer, but rather use the extents to determine how far to read in the log 508 /// for this particular buffer. 509 /// 510 /// In Version 3, FDR log now includes a pid metadata record after 511 /// WallTimeMarker 512 Error processFDRMetadataRecord(FDRState &State, DataExtractor &RecordExtractor, 513 uint32_t &OffsetPtr, 514 std::vector<XRayRecord> &Records, 515 uint16_t Version, uint8_t FirstByte) { 516 // The remaining 7 bits of the first byte are the RecordKind enum for each 517 // Metadata Record. 518 switch (FirstByte >> 1) { 519 case 0: // NewBuffer 520 if (auto E = processFDRNewBufferRecord(State, RecordExtractor, OffsetPtr)) 521 return E; 522 break; 523 case 1: // EndOfBuffer 524 if (Version >= 2) 525 return make_error<StringError>( 526 "Since Version 2 of FDR logging, we no longer support EOB records.", 527 std::make_error_code(std::errc::executable_format_error)); 528 if (auto E = processFDREndOfBufferRecord(State, OffsetPtr)) 529 return E; 530 break; 531 case 2: // NewCPUId 532 if (auto E = processFDRNewCPUIdRecord(State, RecordExtractor, OffsetPtr)) 533 return E; 534 break; 535 case 3: // TSCWrap 536 if (auto E = processFDRTSCWrapRecord(State, RecordExtractor, OffsetPtr)) 537 return E; 538 break; 539 case 4: // WallTimeMarker 540 if (auto E = processFDRWallTimeRecord(State, RecordExtractor, OffsetPtr)) 541 return E; 542 // In Version 3 and and above, a PidRecord is expected after WallTimeRecord 543 if (Version >= 3) 544 State.Expects = FDRState::Token::PID_RECORD; 545 break; 546 case 5: // CustomEventMarker 547 if (auto E = processCustomEventMarker(State, RecordExtractor, OffsetPtr)) 548 return E; 549 break; 550 case 6: // CallArgument 551 if (auto E = processFDRCallArgumentRecord(State, RecordExtractor, Records, 552 OffsetPtr)) 553 return E; 554 break; 555 case 7: // BufferExtents 556 if (auto E = processBufferExtents(State, RecordExtractor, OffsetPtr)) 557 return E; 558 break; 559 case 9: // Pid 560 if (auto E = processFDRPidRecord(State, RecordExtractor, OffsetPtr)) 561 return E; 562 break; 563 default: 564 return createStringError( 565 std::make_error_code(std::errc::executable_format_error), 566 "Illegal metadata record type: '%d' at offset %d.", FirstByte >> 1, 567 OffsetPtr); 568 } 569 return Error::success(); 570 } 571 572 /// Reads a function record from an FDR format log, appending a new XRayRecord 573 /// to the vector being populated and updating the State with a new value 574 /// reference value to interpret TSC deltas. 575 /// 576 /// The XRayRecord constructed includes information from the function record 577 /// processed here as well as Thread ID and CPU ID formerly extracted into 578 /// State. 579 Error processFDRFunctionRecord(FDRState &State, DataExtractor &RecordExtractor, 580 uint32_t &OffsetPtr, uint8_t FirstByte, 581 std::vector<XRayRecord> &Records) { 582 switch (State.Expects) { 583 case FDRState::Token::NEW_BUFFER_RECORD_OR_EOF: 584 return make_error<StringError>( 585 "Malformed log. Received Function Record before new buffer setup.", 586 std::make_error_code(std::errc::executable_format_error)); 587 case FDRState::Token::WALLCLOCK_RECORD: 588 return make_error<StringError>( 589 "Malformed log. Received Function Record when expecting wallclock.", 590 std::make_error_code(std::errc::executable_format_error)); 591 case FDRState::Token::PID_RECORD: 592 return make_error<StringError>( 593 "Malformed log. Received Function Record when expecting pid.", 594 std::make_error_code(std::errc::executable_format_error)); 595 case FDRState::Token::NEW_CPU_ID_RECORD: 596 return make_error<StringError>( 597 "Malformed log. Received Function Record before first CPU record.", 598 std::make_error_code(std::errc::executable_format_error)); 599 default: 600 Records.emplace_back(); 601 auto &Record = Records.back(); 602 Record.RecordType = 0; // Record is type NORMAL. 603 // Strip off record type bit and use the next three bits. 604 auto T = (FirstByte >> 1) & 0x07; 605 switch (T) { 606 case static_cast<decltype(T)>(RecordTypes::ENTER): 607 Record.Type = RecordTypes::ENTER; 608 break; 609 case static_cast<decltype(T)>(RecordTypes::EXIT): 610 Record.Type = RecordTypes::EXIT; 611 break; 612 case static_cast<decltype(T)>(RecordTypes::TAIL_EXIT): 613 Record.Type = RecordTypes::TAIL_EXIT; 614 break; 615 case static_cast<decltype(T)>(RecordTypes::ENTER_ARG): 616 Record.Type = RecordTypes::ENTER_ARG; 617 State.Expects = FDRState::Token::CALL_ARGUMENT; 618 break; 619 default: 620 return createStringError( 621 std::make_error_code(std::errc::executable_format_error), 622 "Illegal function record type '%d' at offset %d.", T, OffsetPtr); 623 } 624 Record.CPU = State.CPUId; 625 Record.TId = State.ThreadId; 626 Record.PId = State.ProcessId; 627 628 // Back up one byte to re-read the first byte, which is important for 629 // computing the function id for a record. 630 --OffsetPtr; 631 632 // Despite function Id being a signed int on XRayRecord, 633 // when it is written to an FDR format, the top bits are truncated, 634 // so it is effectively an unsigned value. When we shift off the 635 // top four bits, we want the shift to be logical, so we read as 636 // uint32_t. 637 auto PreReadOffset = OffsetPtr; 638 uint32_t FuncIdBitField = RecordExtractor.getU32(&OffsetPtr); 639 if (OffsetPtr == PreReadOffset) 640 return createStringError( 641 std::make_error_code(std::errc::executable_format_error), 642 "Failed reading truncated function id field at offset %d.", 643 OffsetPtr); 644 645 Record.FuncId = FuncIdBitField >> 4; 646 647 // FunctionRecords have a 32 bit delta from the previous absolute TSC 648 // or TSC delta. If this would overflow, we should read a TSCWrap record 649 // with an absolute TSC reading. 650 PreReadOffset = OffsetPtr; 651 uint64_t NewTSC = State.BaseTSC + RecordExtractor.getU32(&OffsetPtr); 652 if (OffsetPtr == PreReadOffset) 653 return createStringError( 654 std::make_error_code(std::errc::executable_format_error), 655 "Failed reading TSC delta at offset %d.", OffsetPtr); 656 657 State.BaseTSC = NewTSC; 658 Record.TSC = NewTSC; 659 } 660 return Error::success(); 661 } 662 663 /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is 664 /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such 665 /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of 666 /// of interspersed 16 byte Metadata Records and 8 byte Function Records. 667 /// 668 /// The following is an attempt to document the grammar of the format, which is 669 /// parsed by this function for little-endian machines. Since the format makes 670 /// use of BitFields, when we support big-endian architectures, we will need to 671 /// adjust not only the endianness parameter to llvm's RecordExtractor, but also 672 /// the bit twiddling logic, which is consistent with the little-endian 673 /// convention that BitFields within a struct will first be packed into the 674 /// least significant bits the address they belong to. 675 /// 676 /// We expect a format complying with the grammar in the following pseudo-EBNF 677 /// in Version 1 of the FDR log. 678 /// 679 /// FDRLog: XRayFileHeader ThreadBuffer* 680 /// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. 681 /// Includes BufferSize 682 /// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB 683 /// BufSize: 8 byte unsigned integer indicating how large the buffer is. 684 /// NewBuffer: 16 byte metadata record with Thread Id. 685 /// WallClockTime: 16 byte metadata record with human readable time. 686 /// Pid: 16 byte metadata record with Pid 687 /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. 688 /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. 689 /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord 690 /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. 691 /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. 692 /// 693 /// In Version 2, we make the following changes: 694 /// 695 /// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId 696 /// FunctionSequence 697 /// BufferExtents: 16 byte metdata record describing how many usable bytes are 698 /// in the buffer. This is measured from the start of the buffer 699 /// and must always be at least 48 (bytes). 700 /// 701 /// In Version 3, we make the following changes: 702 /// 703 /// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId 704 /// FunctionSequence 705 /// EOB: *deprecated* 706 Error loadFDRLog(StringRef Data, XRayFileHeader &FileHeader, 707 std::vector<XRayRecord> &Records) { 708 709 if (Data.size() < 32) 710 return make_error<StringError>( 711 "Not enough bytes for an XRay log.", 712 std::make_error_code(std::errc::invalid_argument)); 713 714 DataExtractor Reader(Data, true, 8); 715 uint32_t OffsetPtr = 0; 716 717 auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 718 if (!FileHeaderOrError) 719 return FileHeaderOrError.takeError(); 720 FileHeader = std::move(FileHeaderOrError.get()); 721 722 uint64_t BufferSize = 0; 723 { 724 StringRef ExtraDataRef(FileHeader.FreeFormData, 16); 725 DataExtractor ExtraDataExtractor(ExtraDataRef, true, 8); 726 uint32_t ExtraDataOffset = 0; 727 BufferSize = ExtraDataExtractor.getU64(&ExtraDataOffset); 728 } 729 730 FDRState::Token InitialExpectation; 731 switch (FileHeader.Version) { 732 case 1: 733 InitialExpectation = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 734 break; 735 case 2: 736 case 3: 737 InitialExpectation = FDRState::Token::BUFFER_EXTENTS; 738 break; 739 default: 740 return make_error<StringError>( 741 Twine("Unsupported version '") + Twine(FileHeader.Version) + "'", 742 std::make_error_code(std::errc::executable_format_error)); 743 } 744 FDRState State{0, 0, 0, 0, InitialExpectation, BufferSize, 0}; 745 746 // RecordSize will tell the loop how far to seek ahead based on the record 747 // type that we have just read. 748 while (Reader.isValidOffset(OffsetPtr)) { 749 auto BeginOffset = OffsetPtr; 750 if (State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF) { 751 OffsetPtr += State.CurrentBufferSize - State.CurrentBufferConsumed; 752 State.CurrentBufferConsumed = 0; 753 State.Expects = FDRState::Token::NEW_BUFFER_RECORD_OR_EOF; 754 continue; 755 } 756 auto PreReadOffset = OffsetPtr; 757 uint8_t BitField = Reader.getU8(&OffsetPtr); 758 if (OffsetPtr == PreReadOffset) 759 return createStringError( 760 std::make_error_code(std::errc::executable_format_error), 761 "Failed reading first byte of record at offset %d.", OffsetPtr); 762 bool isMetadataRecord = BitField & 0x01uL; 763 bool isBufferExtents = 764 (BitField >> 1) == 7; // BufferExtents record kind == 7 765 if (isMetadataRecord) { 766 if (auto E = processFDRMetadataRecord(State, Reader, OffsetPtr, Records, 767 FileHeader.Version, BitField)) 768 return E; 769 } else { // Process Function Record 770 if (auto E = processFDRFunctionRecord(State, Reader, OffsetPtr, BitField, 771 Records)) 772 return E; 773 } 774 775 // The BufferExtents record is technically not part of the buffer, so we 776 // don't count the size of that record against the buffer's actual size. 777 if (!isBufferExtents) 778 State.CurrentBufferConsumed += OffsetPtr - BeginOffset; 779 780 assert(State.CurrentBufferConsumed <= State.CurrentBufferSize); 781 782 if ((FileHeader.Version == 2 || FileHeader.Version == 3) && 783 State.CurrentBufferSize == State.CurrentBufferConsumed) { 784 // In Version 2 of the log, we don't need to scan to the end of the thread 785 // buffer if we've already consumed all the bytes we need to. 786 State.Expects = FDRState::Token::BUFFER_EXTENTS; 787 State.CurrentBufferSize = BufferSize; 788 State.CurrentBufferConsumed = 0; 789 } 790 } 791 792 // Having iterated over everything we've been given, we've either consumed 793 // everything and ended up in the end state, or were told to skip the rest. 794 bool Finished = State.Expects == FDRState::Token::SCAN_TO_END_OF_THREAD_BUF && 795 State.CurrentBufferSize == State.CurrentBufferConsumed; 796 if ((State.Expects != FDRState::Token::NEW_BUFFER_RECORD_OR_EOF && 797 State.Expects != FDRState::Token::BUFFER_EXTENTS) && 798 !Finished) 799 return make_error<StringError>( 800 Twine("Encountered EOF with unexpected state expectation ") + 801 fdrStateToTwine(State.Expects) + 802 ". Remaining expected bytes in thread buffer total " + 803 Twine(State.CurrentBufferSize - State.CurrentBufferConsumed), 804 std::make_error_code(std::errc::executable_format_error)); 805 806 return Error::success(); 807 } 808 809 Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader, 810 std::vector<XRayRecord> &Records) { 811 YAMLXRayTrace Trace; 812 Input In(Data); 813 In >> Trace; 814 if (In.error()) 815 return make_error<StringError>("Failed loading YAML Data.", In.error()); 816 817 FileHeader.Version = Trace.Header.Version; 818 FileHeader.Type = Trace.Header.Type; 819 FileHeader.ConstantTSC = Trace.Header.ConstantTSC; 820 FileHeader.NonstopTSC = Trace.Header.NonstopTSC; 821 FileHeader.CycleFrequency = Trace.Header.CycleFrequency; 822 823 if (FileHeader.Version != 1) 824 return make_error<StringError>( 825 Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), 826 std::make_error_code(std::errc::invalid_argument)); 827 828 Records.clear(); 829 std::transform(Trace.Records.begin(), Trace.Records.end(), 830 std::back_inserter(Records), [&](const YAMLXRayRecord &R) { 831 return XRayRecord{R.RecordType, R.CPU, R.Type, R.FuncId, 832 R.TSC, R.TId, R.PId, R.CallArgs}; 833 }); 834 return Error::success(); 835 } 836 } // namespace 837 838 Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { 839 int Fd; 840 if (auto EC = sys::fs::openFileForRead(Filename, Fd)) { 841 return make_error<StringError>( 842 Twine("Cannot read log from '") + Filename + "'", EC); 843 } 844 845 uint64_t FileSize; 846 if (auto EC = sys::fs::file_size(Filename, FileSize)) { 847 return make_error<StringError>( 848 Twine("Cannot read log from '") + Filename + "'", EC); 849 } 850 if (FileSize < 4) { 851 return make_error<StringError>( 852 Twine("File '") + Filename + "' too small for XRay.", 853 std::make_error_code(std::errc::executable_format_error)); 854 } 855 856 // Map the opened file into memory and use a StringRef to access it later. 857 std::error_code EC; 858 sys::fs::mapped_file_region MappedFile( 859 Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); 860 if (EC) { 861 return make_error<StringError>( 862 Twine("Cannot read log from '") + Filename + "'", EC); 863 } 864 auto Data = StringRef(MappedFile.data(), MappedFile.size()); 865 866 // Attempt to detect the file type using file magic. We have a slight bias 867 // towards the binary format, and we do this by making sure that the first 4 868 // bytes of the binary file is some combination of the following byte 869 // patterns: (observe the code loading them assumes they're little endian) 870 // 871 // 0x01 0x00 0x00 0x00 - version 1, "naive" format 872 // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format 873 // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format 874 // 875 // YAML files don't typically have those first four bytes as valid text so we 876 // try loading assuming YAML if we don't find these bytes. 877 // 878 // Only if we can't load either the binary or the YAML format will we yield an 879 // error. 880 StringRef Magic(MappedFile.data(), 4); 881 DataExtractor HeaderExtractor(Magic, true, 8); 882 uint32_t OffsetPtr = 0; 883 uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); 884 uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); 885 886 enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; 887 888 Trace T; 889 switch (Type) { 890 case NAIVE_FORMAT: 891 if (Version == 1 || Version == 2 || Version == 3) { 892 if (auto E = loadNaiveFormatLog(Data, T.FileHeader, T.Records)) 893 return std::move(E); 894 } else { 895 return make_error<StringError>( 896 Twine("Unsupported version for Basic/Naive Mode logging: ") + 897 Twine(Version), 898 std::make_error_code(std::errc::executable_format_error)); 899 } 900 break; 901 case FLIGHT_DATA_RECORDER_FORMAT: 902 if (Version == 1 || Version == 2 || Version == 3) { 903 if (auto E = loadFDRLog(Data, T.FileHeader, T.Records)) 904 return std::move(E); 905 } else { 906 return make_error<StringError>( 907 Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), 908 std::make_error_code(std::errc::executable_format_error)); 909 } 910 break; 911 default: 912 if (auto E = loadYAMLLog(Data, T.FileHeader, T.Records)) 913 return std::move(E); 914 } 915 916 if (Sort) 917 std::stable_sort(T.Records.begin(), T.Records.end(), 918 [&](const XRayRecord &L, const XRayRecord &R) { 919 return L.TSC < R.TSC; 920 }); 921 922 return std::move(T); 923 } 924