xref: /llvm-project-15.0.7/llvm/lib/XRay/Trace.cpp (revision c55cf4af)
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);
50f26a70a5SIgor 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),
70f26a70a5SIgor Kudrin           "Not enough bytes to read a full record at offset %" PRId64 ".",
71f26a70a5SIgor 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),
77f26a70a5SIgor 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),
90f26a70a5SIgor 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),
97f26a70a5SIgor Kudrin             "Failed reading record type field at offset %" PRId64 ".",
98f26a70a5SIgor 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),
116f26a70a5SIgor 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),
124f26a70a5SIgor Kudrin             "Failed reading function id field at offset %" PRId64 ".",
125f26a70a5SIgor 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),
132f26a70a5SIgor 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),
139f26a70a5SIgor 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),
146f26a70a5SIgor 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),
161f26a70a5SIgor Kudrin             "Failed reading function id field at offset %" PRId64 ".",
162f26a70a5SIgor 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),
169f26a70a5SIgor 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),
176f26a70a5SIgor Kudrin             "Failed reading process id field at offset %" PRId64 ".",
177f26a70a5SIgor 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 "
186f26a70a5SIgor 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),
194f26a70a5SIgor Kudrin             "Failed reading argument payload at offset %" PRId64 ".",
195f26a70a5SIgor 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),
203f26a70a5SIgor Kudrin           "Unknown record type '%d' at offset %" PRId64 ".", RecordType,
204f26a70a5SIgor 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 
275f26a70a5SIgor 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);
434f26a70a5SIgor 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))
446*c55cf4afSBill Wendling         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))
458*c55cf4afSBill Wendling         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))
467*c55cf4afSBill Wendling       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 
475*c55cf4afSBill Wendling   return std::move(T);
476d6c18657SDean Michael Berris }
477