1d6c18657SDean Michael Berris //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===// 2d6c18657SDean Michael Berris // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6d6c18657SDean Michael Berris // 7d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 8d6c18657SDean Michael Berris // 9d6c18657SDean Michael Berris // XRay log reader implementation. 10d6c18657SDean Michael Berris // 11d6c18657SDean Michael Berris //===----------------------------------------------------------------------===// 12d6c18657SDean Michael Berris #include "llvm/XRay/Trace.h" 13d6c18657SDean Michael Berris #include "llvm/ADT/STLExtras.h" 14d6c18657SDean Michael Berris #include "llvm/Support/DataExtractor.h" 15d6c18657SDean Michael Berris #include "llvm/Support/Error.h" 16d6c18657SDean Michael Berris #include "llvm/Support/FileSystem.h" 17985c2b92SDean Michael Berris #include "llvm/XRay/BlockIndexer.h" 18985c2b92SDean Michael Berris #include "llvm/XRay/BlockVerifier.h" 19985c2b92SDean Michael Berris #include "llvm/XRay/FDRRecordConsumer.h" 20985c2b92SDean Michael Berris #include "llvm/XRay/FDRRecordProducer.h" 21985c2b92SDean Michael Berris #include "llvm/XRay/FDRRecords.h" 22985c2b92SDean Michael Berris #include "llvm/XRay/FDRTraceExpander.h" 23d764c1b6SDean Michael Berris #include "llvm/XRay/FileHeaderReader.h" 24d6c18657SDean Michael Berris #include "llvm/XRay/YAMLXRayRecord.h" 25985c2b92SDean Michael Berris #include <memory> 26985c2b92SDean Michael Berris #include <vector> 27d6c18657SDean Michael Berris 28d6c18657SDean Michael Berris using namespace llvm; 29d6c18657SDean Michael Berris using namespace llvm::xray; 30d6c18657SDean Michael Berris using llvm::yaml::Input; 31d6c18657SDean Michael Berris 3249a49fe8SBenjamin Kramer namespace { 33d6c18657SDean Michael Berris using XRayRecordStorage = 34d6c18657SDean Michael Berris std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type; 35d6c18657SDean Michael Berris 365b7548c6SDean Michael Berris Error loadNaiveFormatLog(StringRef Data, bool IsLittleEndian, 375b7548c6SDean Michael Berris XRayFileHeader &FileHeader, 384f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 394f83c4d1SDean Michael Berris if (Data.size() < 32) 404f83c4d1SDean Michael Berris return make_error<StringError>( 414f83c4d1SDean Michael Berris "Not enough bytes for an XRay log.", 424f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 434f83c4d1SDean Michael Berris 444f83c4d1SDean Michael Berris if (Data.size() - 32 == 0 || Data.size() % 32 != 0) 454f83c4d1SDean Michael Berris return make_error<StringError>( 464f83c4d1SDean Michael Berris "Invalid-sized XRay data.", 474f83c4d1SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 484f83c4d1SDean Michael Berris 495b7548c6SDean Michael Berris DataExtractor Reader(Data, IsLittleEndian, 8); 50*f26a70a5SIgor Kudrin uint64_t OffsetPtr = 0; 51d764c1b6SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr); 52d764c1b6SDean Michael Berris if (!FileHeaderOrError) 53d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 54d764c1b6SDean Michael Berris FileHeader = std::move(FileHeaderOrError.get()); 55d6c18657SDean Michael Berris 56d6c18657SDean Michael Berris // Each record after the header will be 32 bytes, in the following format: 57d6c18657SDean Michael Berris // 58d6c18657SDean Michael Berris // (2) uint16 : record type 59d6c18657SDean Michael Berris // (1) uint8 : cpu id 60d6c18657SDean Michael Berris // (1) uint8 : type 61d6c18657SDean Michael Berris // (4) sint32 : function id 62d6c18657SDean Michael Berris // (8) uint64 : tsc 63d6c18657SDean Michael Berris // (4) uint32 : thread id 6410141261SDean Michael Berris // (4) uint32 : process id 6510141261SDean Michael Berris // (8) - : padding 66a9d477a6SDean Michael Berris while (Reader.isValidOffset(OffsetPtr)) { 67a9d477a6SDean Michael Berris if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32)) 68a9d477a6SDean Michael Berris return createStringError( 69a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 70*f26a70a5SIgor Kudrin "Not enough bytes to read a full record at offset %" PRId64 ".", 71*f26a70a5SIgor Kudrin OffsetPtr); 72a9d477a6SDean Michael Berris auto PreReadOffset = OffsetPtr; 73a9d477a6SDean Michael Berris auto RecordType = Reader.getU16(&OffsetPtr); 74a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 75a9d477a6SDean Michael Berris return createStringError( 76a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 77*f26a70a5SIgor Kudrin "Failed reading record type at offset %" PRId64 ".", OffsetPtr); 78a9d477a6SDean Michael Berris 79a9d477a6SDean Michael Berris switch (RecordType) { 800a465d7aSDean Michael Berris case 0: { // Normal records. 81d6c18657SDean Michael Berris Records.emplace_back(); 82d6c18657SDean Michael Berris auto &Record = Records.back(); 830a465d7aSDean Michael Berris Record.RecordType = RecordType; 84a9d477a6SDean Michael Berris 85a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 86a9d477a6SDean Michael Berris Record.CPU = Reader.getU8(&OffsetPtr); 87a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 88a9d477a6SDean Michael Berris return createStringError( 89a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 90*f26a70a5SIgor Kudrin "Failed reading CPU field at offset %" PRId64 ".", OffsetPtr); 91a9d477a6SDean Michael Berris 92a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 93a9d477a6SDean Michael Berris auto Type = Reader.getU8(&OffsetPtr); 94a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 95a9d477a6SDean Michael Berris return createStringError( 96a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 97*f26a70a5SIgor Kudrin "Failed reading record type field at offset %" PRId64 ".", 98*f26a70a5SIgor Kudrin OffsetPtr); 99a9d477a6SDean Michael Berris 100d6c18657SDean Michael Berris switch (Type) { 101d6c18657SDean Michael Berris case 0: 102d6c18657SDean Michael Berris Record.Type = RecordTypes::ENTER; 103d6c18657SDean Michael Berris break; 104d6c18657SDean Michael Berris case 1: 105d6c18657SDean Michael Berris Record.Type = RecordTypes::EXIT; 106d6c18657SDean Michael Berris break; 1070f84a7d3SDean Michael Berris case 2: 1080f84a7d3SDean Michael Berris Record.Type = RecordTypes::TAIL_EXIT; 1090f84a7d3SDean Michael Berris break; 1100a465d7aSDean Michael Berris case 3: 1110a465d7aSDean Michael Berris Record.Type = RecordTypes::ENTER_ARG; 1120a465d7aSDean Michael Berris break; 113d6c18657SDean Michael Berris default: 114a9d477a6SDean Michael Berris return createStringError( 115a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 116*f26a70a5SIgor Kudrin "Unknown record type '%d' at offset %" PRId64 ".", Type, OffsetPtr); 117d6c18657SDean Michael Berris } 118a9d477a6SDean Michael Berris 119a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 120a9d477a6SDean Michael Berris Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 121a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 122a9d477a6SDean Michael Berris return createStringError( 123a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 124*f26a70a5SIgor Kudrin "Failed reading function id field at offset %" PRId64 ".", 125*f26a70a5SIgor Kudrin OffsetPtr); 126a9d477a6SDean Michael Berris 127a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 128a9d477a6SDean Michael Berris Record.TSC = Reader.getU64(&OffsetPtr); 129a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 130a9d477a6SDean Michael Berris return createStringError( 131a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 132*f26a70a5SIgor Kudrin "Failed reading TSC field at offset %" PRId64 ".", OffsetPtr); 133a9d477a6SDean Michael Berris 134a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 135a9d477a6SDean Michael Berris Record.TId = Reader.getU32(&OffsetPtr); 136a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 137a9d477a6SDean Michael Berris return createStringError( 138a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 139*f26a70a5SIgor Kudrin "Failed reading thread id field at offset %" PRId64 ".", OffsetPtr); 140a9d477a6SDean Michael Berris 141a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 142a9d477a6SDean Michael Berris Record.PId = Reader.getU32(&OffsetPtr); 143a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 144a9d477a6SDean Michael Berris return createStringError( 145a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 146*f26a70a5SIgor Kudrin "Failed reading process id at offset %" PRId64 ".", OffsetPtr); 147a9d477a6SDean Michael Berris 1480a465d7aSDean Michael Berris break; 1490a465d7aSDean Michael Berris } 1500a465d7aSDean Michael Berris case 1: { // Arg payload record. 1510a465d7aSDean Michael Berris auto &Record = Records.back(); 152a9d477a6SDean Michael Berris 153a9d477a6SDean Michael Berris // We skip the next two bytes of the record, because we don't need the 154a9d477a6SDean Michael Berris // type and the CPU record for arg payloads. 1550a465d7aSDean Michael Berris OffsetPtr += 2; 156a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 157a9d477a6SDean Michael Berris int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t)); 158a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 159a9d477a6SDean Michael Berris return createStringError( 160a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 161*f26a70a5SIgor Kudrin "Failed reading function id field at offset %" PRId64 ".", 162*f26a70a5SIgor Kudrin OffsetPtr); 163a9d477a6SDean Michael Berris 164a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 165a9d477a6SDean Michael Berris auto TId = 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), 169*f26a70a5SIgor Kudrin "Failed reading thread id field at offset %" PRId64 ".", OffsetPtr); 170a9d477a6SDean Michael Berris 171a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 172a9d477a6SDean Michael Berris auto PId = Reader.getU32(&OffsetPtr); 173a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 174a9d477a6SDean Michael Berris return createStringError( 175a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 176*f26a70a5SIgor Kudrin "Failed reading process id field at offset %" PRId64 ".", 177*f26a70a5SIgor Kudrin OffsetPtr); 17810141261SDean Michael Berris 17910141261SDean Michael Berris // Make a check for versions above 3 for the Pid field 18010141261SDean Michael Berris if (Record.FuncId != FuncId || Record.TId != TId || 18110141261SDean Michael Berris (FileHeader.Version >= 3 ? Record.PId != PId : false)) 182a9d477a6SDean Michael Berris return createStringError( 183a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 184a9d477a6SDean Michael Berris "Corrupted log, found arg payload following non-matching " 185a9d477a6SDean Michael Berris "function+thread record. Record for function %d != %d at offset " 186*f26a70a5SIgor Kudrin "%" PRId64 ".", 187a9d477a6SDean Michael Berris Record.FuncId, FuncId, OffsetPtr); 18810141261SDean Michael Berris 189a9d477a6SDean Michael Berris PreReadOffset = OffsetPtr; 190a9d477a6SDean Michael Berris auto Arg = Reader.getU64(&OffsetPtr); 191a9d477a6SDean Michael Berris if (OffsetPtr == PreReadOffset) 192a9d477a6SDean Michael Berris return createStringError( 193a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 194*f26a70a5SIgor Kudrin "Failed reading argument payload at offset %" PRId64 ".", 195*f26a70a5SIgor Kudrin OffsetPtr); 196a9d477a6SDean Michael Berris 1970a465d7aSDean Michael Berris Record.CallArgs.push_back(Arg); 1980a465d7aSDean Michael Berris break; 1990a465d7aSDean Michael Berris } 2000a465d7aSDean Michael Berris default: 201a9d477a6SDean Michael Berris return createStringError( 202a9d477a6SDean Michael Berris std::make_error_code(std::errc::executable_format_error), 203*f26a70a5SIgor Kudrin "Unknown record type '%d' at offset %" PRId64 ".", RecordType, 204*f26a70a5SIgor Kudrin OffsetPtr); 2050a465d7aSDean Michael Berris } 206a9d477a6SDean Michael Berris // Advance the offset pointer enough bytes to align to 32-byte records for 207a9d477a6SDean Michael Berris // basic mode logs. 208a9d477a6SDean Michael Berris OffsetPtr += 8; 209d6c18657SDean Michael Berris } 210d6c18657SDean Michael Berris return Error::success(); 211d6c18657SDean Michael Berris } 212d6c18657SDean Michael Berris 2134f83c4d1SDean Michael Berris /// Reads a log in FDR mode for version 1 of this binary format. FDR mode is 2144f83c4d1SDean Michael Berris /// defined as part of the compiler-rt project in xray_fdr_logging.h, and such 2154f83c4d1SDean Michael Berris /// a log consists of the familiar 32 bit XRayHeader, followed by sequences of 2164f83c4d1SDean Michael Berris /// of interspersed 16 byte Metadata Records and 8 byte Function Records. 2174f83c4d1SDean Michael Berris /// 2184f83c4d1SDean Michael Berris /// The following is an attempt to document the grammar of the format, which is 2194f83c4d1SDean Michael Berris /// parsed by this function for little-endian machines. Since the format makes 220d78db156SMartin Pelikan /// use of BitFields, when we support big-endian architectures, we will need to 22168168d17SSimon Pilgrim /// adjust not only the endianness parameter to llvm's RecordExtractor, but also 2224f83c4d1SDean Michael Berris /// the bit twiddling logic, which is consistent with the little-endian 2234f83c4d1SDean Michael Berris /// convention that BitFields within a struct will first be packed into the 2244f83c4d1SDean Michael Berris /// least significant bits the address they belong to. 2254f83c4d1SDean Michael Berris /// 2266ec72625SDean Michael Berris /// We expect a format complying with the grammar in the following pseudo-EBNF 2276ec72625SDean Michael Berris /// in Version 1 of the FDR log. 2284f83c4d1SDean Michael Berris /// 2294f83c4d1SDean Michael Berris /// FDRLog: XRayFileHeader ThreadBuffer* 2303d0bc9efSKeith Wyss /// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata. 2313d0bc9efSKeith Wyss /// Includes BufferSize 2323d0bc9efSKeith Wyss /// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB 23360c24878SDean Michael Berris /// BufSize: 8 byte unsigned integer indicating how large the buffer is. 2344f83c4d1SDean Michael Berris /// NewBuffer: 16 byte metadata record with Thread Id. 2354f83c4d1SDean Michael Berris /// WallClockTime: 16 byte metadata record with human readable time. 23610141261SDean Michael Berris /// Pid: 16 byte metadata record with Pid 2374f83c4d1SDean Michael Berris /// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading. 23860c24878SDean Michael Berris /// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize. 2394f83c4d1SDean Michael Berris /// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord 2404f83c4d1SDean Michael Berris /// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading. 2414f83c4d1SDean Michael Berris /// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta. 2426ec72625SDean Michael Berris /// 2436ec72625SDean Michael Berris /// In Version 2, we make the following changes: 2446ec72625SDean Michael Berris /// 2456ec72625SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId 2466ec72625SDean Michael Berris /// FunctionSequence 2476ec72625SDean Michael Berris /// BufferExtents: 16 byte metdata record describing how many usable bytes are 2486ec72625SDean Michael Berris /// in the buffer. This is measured from the start of the buffer 2496ec72625SDean Michael Berris /// and must always be at least 48 (bytes). 25010141261SDean Michael Berris /// 25110141261SDean Michael Berris /// In Version 3, we make the following changes: 25210141261SDean Michael Berris /// 25310141261SDean Michael Berris /// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId 25410141261SDean Michael Berris /// FunctionSequence 2556ec72625SDean Michael Berris /// EOB: *deprecated* 25659439dd0SDean Michael Berris /// 25759439dd0SDean Michael Berris /// In Version 4, we make the following changes: 25859439dd0SDean Michael Berris /// 25959439dd0SDean Michael Berris /// CustomEventRecord now includes the CPU data. 26059439dd0SDean Michael Berris /// 26159439dd0SDean Michael Berris /// In Version 5, we make the following changes: 26259439dd0SDean Michael Berris /// 26359439dd0SDean Michael Berris /// CustomEventRecord and TypedEventRecord now use TSC delta encoding similar to 26459439dd0SDean Michael Berris /// what FunctionRecord instances use, and we no longer need to include the CPU 26559439dd0SDean Michael Berris /// id in the CustomEventRecord. 26659439dd0SDean Michael Berris /// 2675b7548c6SDean Michael Berris Error loadFDRLog(StringRef Data, bool IsLittleEndian, 2685b7548c6SDean Michael Berris XRayFileHeader &FileHeader, std::vector<XRayRecord> &Records) { 269d764c1b6SDean Michael Berris 2704f83c4d1SDean Michael Berris if (Data.size() < 32) 271985c2b92SDean Michael Berris return createStringError(std::make_error_code(std::errc::invalid_argument), 272985c2b92SDean Michael Berris "Not enough bytes for an XRay FDR log."); 273985c2b92SDean Michael Berris DataExtractor DE(Data, IsLittleEndian, 8); 2744f83c4d1SDean Michael Berris 275*f26a70a5SIgor Kudrin uint64_t OffsetPtr = 0; 276985c2b92SDean Michael Berris auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr); 277d764c1b6SDean Michael Berris if (!FileHeaderOrError) 278d764c1b6SDean Michael Berris return FileHeaderOrError.takeError(); 279d764c1b6SDean Michael Berris FileHeader = std::move(FileHeaderOrError.get()); 2804f83c4d1SDean Michael Berris 281985c2b92SDean Michael Berris // First we load the records into memory. 282985c2b92SDean Michael Berris std::vector<std::unique_ptr<Record>> FDRRecords; 283985c2b92SDean Michael Berris 28460c24878SDean Michael Berris { 285985c2b92SDean Michael Berris FileBasedRecordProducer P(FileHeader, DE, OffsetPtr); 286985c2b92SDean Michael Berris LogBuilderConsumer C(FDRRecords); 287985c2b92SDean Michael Berris while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { 288985c2b92SDean Michael Berris auto R = P.produce(); 289985c2b92SDean Michael Berris if (!R) 290985c2b92SDean Michael Berris return R.takeError(); 291985c2b92SDean Michael Berris if (auto E = C.consume(std::move(R.get()))) 2924f83c4d1SDean Michael Berris return E; 293985c2b92SDean Michael Berris } 294985c2b92SDean Michael Berris } 295985c2b92SDean Michael Berris 296985c2b92SDean Michael Berris // Next we index the records into blocks. 297985c2b92SDean Michael Berris BlockIndexer::Index Index; 298985c2b92SDean Michael Berris { 299985c2b92SDean Michael Berris BlockIndexer Indexer(Index); 300985c2b92SDean Michael Berris for (auto &R : FDRRecords) 301985c2b92SDean Michael Berris if (auto E = R->apply(Indexer)) 302985c2b92SDean Michael Berris return E; 303985c2b92SDean Michael Berris if (auto E = Indexer.flush()) 3044f83c4d1SDean Michael Berris return E; 305d78db156SMartin Pelikan } 3066ec72625SDean Michael Berris 307985c2b92SDean Michael Berris // Then we verify the consistency of the blocks. 308985c2b92SDean Michael Berris { 309985c2b92SDean Michael Berris for (auto &PTB : Index) { 310985c2b92SDean Michael Berris auto &Blocks = PTB.second; 311985c2b92SDean Michael Berris for (auto &B : Blocks) { 31290a46bdeSDean Michael Berris BlockVerifier Verifier; 313985c2b92SDean Michael Berris for (auto *R : B.Records) 314985c2b92SDean Michael Berris if (auto E = R->apply(Verifier)) 315985c2b92SDean Michael Berris return E; 316985c2b92SDean Michael Berris if (auto E = Verifier.verify()) 317985c2b92SDean Michael Berris return E; 318985c2b92SDean Michael Berris } 3196ec72625SDean Michael Berris } 3204f83c4d1SDean Michael Berris } 321d78db156SMartin Pelikan 322985c2b92SDean Michael Berris // This is now the meat of the algorithm. Here we sort the blocks according to 323985c2b92SDean Michael Berris // the Walltime record in each of the blocks for the same thread. This allows 324985c2b92SDean Michael Berris // us to more consistently recreate the execution trace in temporal order. 325985c2b92SDean Michael Berris // After the sort, we then reconstitute `Trace` records using a stateful 326985c2b92SDean Michael Berris // visitor associated with a single process+thread pair. 327985c2b92SDean Michael Berris { 328985c2b92SDean Michael Berris for (auto &PTB : Index) { 329985c2b92SDean Michael Berris auto &Blocks = PTB.second; 3306b67ff03SDean Michael Berris llvm::sort(Blocks, [](const BlockIndexer::Block &L, 3316b67ff03SDean Michael Berris const BlockIndexer::Block &R) { 332985c2b92SDean Michael Berris return (L.WallclockTime->seconds() < R.WallclockTime->seconds() && 333985c2b92SDean Michael Berris L.WallclockTime->nanos() < R.WallclockTime->nanos()); 334985c2b92SDean Michael Berris }); 335174d2cf7SDean Michael Berris auto Adder = [&](const XRayRecord &R) { Records.push_back(R); }; 336174d2cf7SDean Michael Berris TraceExpander Expander(Adder, FileHeader.Version); 337985c2b92SDean Michael Berris for (auto &B : Blocks) { 338985c2b92SDean Michael Berris for (auto *R : B.Records) 339985c2b92SDean Michael Berris if (auto E = R->apply(Expander)) 340985c2b92SDean Michael Berris return E; 341985c2b92SDean Michael Berris } 342985c2b92SDean Michael Berris if (auto E = Expander.flush()) 343985c2b92SDean Michael Berris return E; 344985c2b92SDean Michael Berris } 345985c2b92SDean Michael Berris } 3464f83c4d1SDean Michael Berris 3474f83c4d1SDean Michael Berris return Error::success(); 3484f83c4d1SDean Michael Berris } 3494f83c4d1SDean Michael Berris 3504f83c4d1SDean Michael Berris Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader, 3514f83c4d1SDean Michael Berris std::vector<XRayRecord> &Records) { 352d6c18657SDean Michael Berris YAMLXRayTrace Trace; 353d6c18657SDean Michael Berris Input In(Data); 354d6c18657SDean Michael Berris In >> Trace; 355d6c18657SDean Michael Berris if (In.error()) 356d6c18657SDean Michael Berris return make_error<StringError>("Failed loading YAML Data.", In.error()); 357d6c18657SDean Michael Berris 358d6c18657SDean Michael Berris FileHeader.Version = Trace.Header.Version; 359d6c18657SDean Michael Berris FileHeader.Type = Trace.Header.Type; 360d6c18657SDean Michael Berris FileHeader.ConstantTSC = Trace.Header.ConstantTSC; 361d6c18657SDean Michael Berris FileHeader.NonstopTSC = Trace.Header.NonstopTSC; 362d6c18657SDean Michael Berris FileHeader.CycleFrequency = Trace.Header.CycleFrequency; 363d6c18657SDean Michael Berris 364d6c18657SDean Michael Berris if (FileHeader.Version != 1) 365d6c18657SDean Michael Berris return make_error<StringError>( 366d6c18657SDean Michael Berris Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version), 367d6c18657SDean Michael Berris std::make_error_code(std::errc::invalid_argument)); 368d6c18657SDean Michael Berris 369d6c18657SDean Michael Berris Records.clear(); 370d6c18657SDean Michael Berris std::transform(Trace.Records.begin(), Trace.Records.end(), 371d6c18657SDean Michael Berris std::back_inserter(Records), [&](const YAMLXRayRecord &R) { 37225f8d204SDean Michael Berris return XRayRecord{R.RecordType, R.CPU, R.Type, 37325f8d204SDean Michael Berris R.FuncId, R.TSC, R.TId, 37425f8d204SDean Michael Berris R.PId, R.CallArgs, R.Data}; 375d6c18657SDean Michael Berris }); 376d6c18657SDean Michael Berris return Error::success(); 377d6c18657SDean Michael Berris } 37849a49fe8SBenjamin Kramer } // namespace 379d6c18657SDean Michael Berris 380d6c18657SDean Michael Berris Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) { 381f002fcb2SReid Kleckner Expected<sys::fs::file_t> FdOrErr = sys::fs::openNativeFileForRead(Filename); 382f002fcb2SReid Kleckner if (!FdOrErr) 383f002fcb2SReid Kleckner return FdOrErr.takeError(); 384d6c18657SDean Michael Berris 385d6c18657SDean Michael Berris uint64_t FileSize; 386d6c18657SDean Michael Berris if (auto EC = sys::fs::file_size(Filename, FileSize)) { 387d6c18657SDean Michael Berris return make_error<StringError>( 388d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 389d6c18657SDean Michael Berris } 390d6c18657SDean Michael Berris if (FileSize < 4) { 391d6c18657SDean Michael Berris return make_error<StringError>( 392d6c18657SDean Michael Berris Twine("File '") + Filename + "' too small for XRay.", 39384da6615SHans Wennborg std::make_error_code(std::errc::executable_format_error)); 394d6c18657SDean Michael Berris } 395d6c18657SDean Michael Berris 396d78db156SMartin Pelikan // Map the opened file into memory and use a StringRef to access it later. 397d6c18657SDean Michael Berris std::error_code EC; 398d6c18657SDean Michael Berris sys::fs::mapped_file_region MappedFile( 399f002fcb2SReid Kleckner *FdOrErr, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, 400f002fcb2SReid Kleckner EC); 401f002fcb2SReid Kleckner sys::fs::closeFile(*FdOrErr); 402d6c18657SDean Michael Berris if (EC) { 403d6c18657SDean Michael Berris return make_error<StringError>( 404d6c18657SDean Michael Berris Twine("Cannot read log from '") + Filename + "'", EC); 405d6c18657SDean Michael Berris } 406d78db156SMartin Pelikan auto Data = StringRef(MappedFile.data(), MappedFile.size()); 4075b7548c6SDean Michael Berris 4085b7548c6SDean Michael Berris // TODO: Lift the endianness and implementation selection here. 4095b7548c6SDean Michael Berris DataExtractor LittleEndianDE(Data, true, 8); 4105b7548c6SDean Michael Berris auto TraceOrError = loadTrace(LittleEndianDE, Sort); 4115b7548c6SDean Michael Berris if (!TraceOrError) { 4125b7548c6SDean Michael Berris DataExtractor BigEndianDE(Data, false, 8); 4135b7548c6SDean Michael Berris TraceOrError = loadTrace(BigEndianDE, Sort); 4145b7548c6SDean Michael Berris } 4155b7548c6SDean Michael Berris return TraceOrError; 416f81b0800SDean Michael Berris } 417d6c18657SDean Michael Berris 418f81b0800SDean Michael Berris Expected<Trace> llvm::xray::loadTrace(const DataExtractor &DE, bool Sort) { 419d6c18657SDean Michael Berris // Attempt to detect the file type using file magic. We have a slight bias 420d6c18657SDean Michael Berris // towards the binary format, and we do this by making sure that the first 4 421d6c18657SDean Michael Berris // bytes of the binary file is some combination of the following byte 422d78db156SMartin Pelikan // patterns: (observe the code loading them assumes they're little endian) 423d6c18657SDean Michael Berris // 424d78db156SMartin Pelikan // 0x01 0x00 0x00 0x00 - version 1, "naive" format 425d78db156SMartin Pelikan // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format 4266ec72625SDean Michael Berris // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format 427d6c18657SDean Michael Berris // 428d78db156SMartin Pelikan // YAML files don't typically have those first four bytes as valid text so we 429d6c18657SDean Michael Berris // try loading assuming YAML if we don't find these bytes. 430d6c18657SDean Michael Berris // 431d6c18657SDean Michael Berris // Only if we can't load either the binary or the YAML format will we yield an 432d6c18657SDean Michael Berris // error. 4335b7548c6SDean Michael Berris DataExtractor HeaderExtractor(DE.getData(), DE.isLittleEndian(), 8); 434*f26a70a5SIgor Kudrin uint64_t OffsetPtr = 0; 435d6c18657SDean Michael Berris uint16_t Version = HeaderExtractor.getU16(&OffsetPtr); 436d6c18657SDean Michael Berris uint16_t Type = HeaderExtractor.getU16(&OffsetPtr); 437d6c18657SDean Michael Berris 4384f83c4d1SDean Michael Berris enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 }; 4394f83c4d1SDean Michael Berris 440d6c18657SDean Michael Berris Trace T; 4416ec72625SDean Michael Berris switch (Type) { 4426ec72625SDean Michael Berris case NAIVE_FORMAT: 44310141261SDean Michael Berris if (Version == 1 || Version == 2 || Version == 3) { 4445b7548c6SDean Michael Berris if (auto E = loadNaiveFormatLog(DE.getData(), DE.isLittleEndian(), 4455b7548c6SDean Michael Berris T.FileHeader, T.Records)) 4464f83c4d1SDean Michael Berris return std::move(E); 4476ec72625SDean Michael Berris } else { 4486ec72625SDean Michael Berris return make_error<StringError>( 4496ec72625SDean Michael Berris Twine("Unsupported version for Basic/Naive Mode logging: ") + 4506ec72625SDean Michael Berris Twine(Version), 4516ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 4526ec72625SDean Michael Berris } 4536ec72625SDean Michael Berris break; 4546ec72625SDean Michael Berris case FLIGHT_DATA_RECORDER_FORMAT: 45559439dd0SDean Michael Berris if (Version >= 1 && Version <= 5) { 4565b7548c6SDean Michael Berris if (auto E = loadFDRLog(DE.getData(), DE.isLittleEndian(), T.FileHeader, 4575b7548c6SDean Michael Berris T.Records)) 458d6c18657SDean Michael Berris return std::move(E); 459d6c18657SDean Michael Berris } else { 4606ec72625SDean Michael Berris return make_error<StringError>( 4616ec72625SDean Michael Berris Twine("Unsupported version for FDR Mode logging: ") + Twine(Version), 4626ec72625SDean Michael Berris std::make_error_code(std::errc::executable_format_error)); 4636ec72625SDean Michael Berris } 4646ec72625SDean Michael Berris break; 4656ec72625SDean Michael Berris default: 466f81b0800SDean Michael Berris if (auto E = loadYAMLLog(DE.getData(), T.FileHeader, T.Records)) 467d6c18657SDean Michael Berris return std::move(E); 468d6c18657SDean Michael Berris } 469d6c18657SDean Michael Berris 470d6c18657SDean Michael Berris if (Sort) 471efd94c56SFangrui Song llvm::stable_sort(T.Records, [&](const XRayRecord &L, const XRayRecord &R) { 472d6c18657SDean Michael Berris return L.TSC < R.TSC; 473d6c18657SDean Michael Berris }); 474d6c18657SDean Michael Berris 475d6c18657SDean Michael Berris return std::move(T); 476d6c18657SDean Michael Berris } 477