blob: eba6ff2c7fb37776d7466513973310d9d71c14c2 [file] [log] [blame]
Dean Michael Berrisd6c18652017-01-11 06:39:09 +00001//===- Trace.cpp - XRay Trace Loading implementation. ---------------------===//
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +00006//
7//===----------------------------------------------------------------------===//
8//
9// XRay log reader implementation.
10//
11//===----------------------------------------------------------------------===//
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000012#include "llvm/XRay/Trace.h"
13#include "llvm/ADT/STLExtras.h"
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000014#include "llvm/Support/DataExtractor.h"
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000015#include "llvm/Support/Error.h"
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000016#include "llvm/Support/FileSystem.h"
Dean Michael Berris985c2b92018-09-11 06:45:59 +000017#include "llvm/XRay/BlockIndexer.h"
18#include "llvm/XRay/BlockVerifier.h"
19#include "llvm/XRay/FDRRecordConsumer.h"
20#include "llvm/XRay/FDRRecordProducer.h"
21#include "llvm/XRay/FDRRecords.h"
22#include "llvm/XRay/FDRTraceExpander.h"
Dean Michael Berrisd764c1b2018-08-22 07:37:55 +000023#include "llvm/XRay/FileHeaderReader.h"
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000024#include "llvm/XRay/YAMLXRayRecord.h"
Dean Michael Berris985c2b92018-09-11 06:45:59 +000025#include <memory>
26#include <vector>
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000027
28using namespace llvm;
29using namespace llvm::xray;
30using llvm::yaml::Input;
31
Benjamin Kramer49a49fe2017-08-20 13:03:48 +000032namespace {
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000033using XRayRecordStorage =
34 std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type;
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000035
Dean Michael Berris5b7548c2018-08-31 17:06:28 +000036Error loadNaiveFormatLog(StringRef Data, bool IsLittleEndian,
37 XRayFileHeader &FileHeader,
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +000038 std::vector<XRayRecord> &Records) {
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +000039 if (Data.size() < 32)
40 return make_error<StringError>(
41 "Not enough bytes for an XRay log.",
42 std::make_error_code(std::errc::invalid_argument));
43
44 if (Data.size() - 32 == 0 || Data.size() % 32 != 0)
45 return make_error<StringError>(
46 "Invalid-sized XRay data.",
47 std::make_error_code(std::errc::invalid_argument));
48
Dean Michael Berris5b7548c2018-08-31 17:06:28 +000049 DataExtractor Reader(Data, IsLittleEndian, 8);
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +000050 uint32_t OffsetPtr = 0;
Dean Michael Berrisd764c1b2018-08-22 07:37:55 +000051 auto FileHeaderOrError = readBinaryFormatHeader(Reader, OffsetPtr);
52 if (!FileHeaderOrError)
53 return FileHeaderOrError.takeError();
54 FileHeader = std::move(FileHeaderOrError.get());
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000055
56 // Each record after the header will be 32 bytes, in the following format:
57 //
58 // (2) uint16 : record type
59 // (1) uint8 : cpu id
60 // (1) uint8 : type
61 // (4) sint32 : function id
62 // (8) uint64 : tsc
63 // (4) uint32 : thread id
Dean Michael Berris10141262018-07-13 05:38:22 +000064 // (4) uint32 : process id
65 // (8) - : padding
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +000066 while (Reader.isValidOffset(OffsetPtr)) {
67 if (!Reader.isValidOffsetForDataOfSize(OffsetPtr, 32))
68 return createStringError(
69 std::make_error_code(std::errc::executable_format_error),
70 "Not enough bytes to read a full record at offset %d.", OffsetPtr);
71 auto PreReadOffset = OffsetPtr;
72 auto RecordType = Reader.getU16(&OffsetPtr);
73 if (OffsetPtr == PreReadOffset)
74 return createStringError(
75 std::make_error_code(std::errc::executable_format_error),
76 "Failed reading record type at offset %d.", OffsetPtr);
77
78 switch (RecordType) {
Dean Michael Berris0a465d72017-10-05 05:18:17 +000079 case 0: { // Normal records.
80 Records.emplace_back();
81 auto &Record = Records.back();
82 Record.RecordType = RecordType;
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +000083
84 PreReadOffset = OffsetPtr;
85 Record.CPU = Reader.getU8(&OffsetPtr);
86 if (OffsetPtr == PreReadOffset)
87 return createStringError(
88 std::make_error_code(std::errc::executable_format_error),
89 "Failed reading CPU field at offset %d.", OffsetPtr);
90
91 PreReadOffset = OffsetPtr;
92 auto Type = Reader.getU8(&OffsetPtr);
93 if (OffsetPtr == PreReadOffset)
94 return createStringError(
95 std::make_error_code(std::errc::executable_format_error),
96 "Failed reading record type field at offset %d.", OffsetPtr);
97
Dean Michael Berris0a465d72017-10-05 05:18:17 +000098 switch (Type) {
99 case 0:
100 Record.Type = RecordTypes::ENTER;
101 break;
102 case 1:
103 Record.Type = RecordTypes::EXIT;
104 break;
105 case 2:
106 Record.Type = RecordTypes::TAIL_EXIT;
107 break;
108 case 3:
109 Record.Type = RecordTypes::ENTER_ARG;
110 break;
111 default:
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000112 return createStringError(
113 std::make_error_code(std::errc::executable_format_error),
114 "Unknown record type '%d' at offset %d.", Type, OffsetPtr);
Dean Michael Berris0a465d72017-10-05 05:18:17 +0000115 }
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000116
117 PreReadOffset = OffsetPtr;
118 Record.FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t));
119 if (OffsetPtr == PreReadOffset)
120 return createStringError(
121 std::make_error_code(std::errc::executable_format_error),
122 "Failed reading function id field at offset %d.", OffsetPtr);
123
124 PreReadOffset = OffsetPtr;
125 Record.TSC = Reader.getU64(&OffsetPtr);
126 if (OffsetPtr == PreReadOffset)
127 return createStringError(
128 std::make_error_code(std::errc::executable_format_error),
129 "Failed reading TSC field at offset %d.", OffsetPtr);
130
131 PreReadOffset = OffsetPtr;
132 Record.TId = Reader.getU32(&OffsetPtr);
133 if (OffsetPtr == PreReadOffset)
134 return createStringError(
135 std::make_error_code(std::errc::executable_format_error),
136 "Failed reading thread id field at offset %d.", OffsetPtr);
137
138 PreReadOffset = OffsetPtr;
139 Record.PId = Reader.getU32(&OffsetPtr);
140 if (OffsetPtr == PreReadOffset)
141 return createStringError(
142 std::make_error_code(std::errc::executable_format_error),
143 "Failed reading process id at offset %d.", OffsetPtr);
144
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000145 break;
Dean Michael Berris0a465d72017-10-05 05:18:17 +0000146 }
147 case 1: { // Arg payload record.
148 auto &Record = Records.back();
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000149
150 // We skip the next two bytes of the record, because we don't need the
151 // type and the CPU record for arg payloads.
Dean Michael Berris0a465d72017-10-05 05:18:17 +0000152 OffsetPtr += 2;
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000153 PreReadOffset = OffsetPtr;
154 int32_t FuncId = Reader.getSigned(&OffsetPtr, sizeof(int32_t));
155 if (OffsetPtr == PreReadOffset)
156 return createStringError(
157 std::make_error_code(std::errc::executable_format_error),
158 "Failed reading function id field at offset %d.", OffsetPtr);
159
160 PreReadOffset = OffsetPtr;
161 auto TId = Reader.getU32(&OffsetPtr);
162 if (OffsetPtr == PreReadOffset)
163 return createStringError(
164 std::make_error_code(std::errc::executable_format_error),
165 "Failed reading thread id field at offset %d.", OffsetPtr);
166
167 PreReadOffset = OffsetPtr;
168 auto PId = Reader.getU32(&OffsetPtr);
169 if (OffsetPtr == PreReadOffset)
170 return createStringError(
171 std::make_error_code(std::errc::executable_format_error),
172 "Failed reading process id field at offset %d.", OffsetPtr);
Dean Michael Berris10141262018-07-13 05:38:22 +0000173
174 // Make a check for versions above 3 for the Pid field
175 if (Record.FuncId != FuncId || Record.TId != TId ||
176 (FileHeader.Version >= 3 ? Record.PId != PId : false))
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000177 return createStringError(
178 std::make_error_code(std::errc::executable_format_error),
179 "Corrupted log, found arg payload following non-matching "
180 "function+thread record. Record for function %d != %d at offset "
181 "%d",
182 Record.FuncId, FuncId, OffsetPtr);
Dean Michael Berris10141262018-07-13 05:38:22 +0000183
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000184 PreReadOffset = OffsetPtr;
185 auto Arg = Reader.getU64(&OffsetPtr);
186 if (OffsetPtr == PreReadOffset)
187 return createStringError(
188 std::make_error_code(std::errc::executable_format_error),
189 "Failed reading argument payload at offset %d.", OffsetPtr);
190
Dean Michael Berris0a465d72017-10-05 05:18:17 +0000191 Record.CallArgs.push_back(Arg);
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000192 break;
Dean Michael Berris0a465d72017-10-05 05:18:17 +0000193 }
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000194 default:
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000195 return createStringError(
196 std::make_error_code(std::errc::executable_format_error),
197 "Unknown record type '%d' at offset %d.", RecordType, OffsetPtr);
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000198 }
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000199 // Advance the offset pointer enough bytes to align to 32-byte records for
200 // basic mode logs.
201 OffsetPtr += 8;
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000202 }
203 return Error::success();
204}
205
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000206/// Reads a log in FDR mode for version 1 of this binary format. FDR mode is
207/// defined as part of the compiler-rt project in xray_fdr_logging.h, and such
208/// a log consists of the familiar 32 bit XRayHeader, followed by sequences of
209/// of interspersed 16 byte Metadata Records and 8 byte Function Records.
210///
211/// The following is an attempt to document the grammar of the format, which is
212/// parsed by this function for little-endian machines. Since the format makes
Martin Pelikand78db152017-09-15 04:22:16 +0000213/// use of BitFields, when we support big-endian architectures, we will need to
Simon Pilgrim68168d12017-03-30 12:59:53 +0000214/// adjust not only the endianness parameter to llvm's RecordExtractor, but also
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000215/// the bit twiddling logic, which is consistent with the little-endian
216/// convention that BitFields within a struct will first be packed into the
217/// least significant bits the address they belong to.
218///
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000219/// We expect a format complying with the grammar in the following pseudo-EBNF
220/// in Version 1 of the FDR log.
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000221///
222/// FDRLog: XRayFileHeader ThreadBuffer*
Keith Wyss3d0bc9e2017-08-02 21:47:27 +0000223/// XRayFileHeader: 32 bytes to identify the log as FDR with machine metadata.
224/// Includes BufferSize
225/// ThreadBuffer: NewBuffer WallClockTime NewCPUId FunctionSequence EOB
Dean Michael Berris60c24872017-03-29 06:10:12 +0000226/// BufSize: 8 byte unsigned integer indicating how large the buffer is.
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000227/// NewBuffer: 16 byte metadata record with Thread Id.
228/// WallClockTime: 16 byte metadata record with human readable time.
Dean Michael Berris10141262018-07-13 05:38:22 +0000229/// Pid: 16 byte metadata record with Pid
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000230/// NewCPUId: 16 byte metadata record with CPUId and a 64 bit TSC reading.
Dean Michael Berris60c24872017-03-29 06:10:12 +0000231/// EOB: 16 byte record in a thread buffer plus mem garbage to fill BufSize.
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000232/// FunctionSequence: NewCPUId | TSCWrap | FunctionRecord
233/// TSCWrap: 16 byte metadata record with a full 64 bit TSC reading.
234/// FunctionRecord: 8 byte record with FunctionId, entry/exit, and TSC delta.
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000235///
236/// In Version 2, we make the following changes:
237///
238/// ThreadBuffer: BufferExtents NewBuffer WallClockTime NewCPUId
239/// FunctionSequence
240/// BufferExtents: 16 byte metdata record describing how many usable bytes are
241/// in the buffer. This is measured from the start of the buffer
242/// and must always be at least 48 (bytes).
Dean Michael Berris10141262018-07-13 05:38:22 +0000243///
244/// In Version 3, we make the following changes:
245///
246/// ThreadBuffer: BufferExtents NewBuffer WallClockTime Pid NewCPUId
247/// FunctionSequence
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000248/// EOB: *deprecated*
Dean Michael Berris59439dd2018-11-07 04:37:42 +0000249///
250/// In Version 4, we make the following changes:
251///
252/// CustomEventRecord now includes the CPU data.
253///
254/// In Version 5, we make the following changes:
255///
256/// CustomEventRecord and TypedEventRecord now use TSC delta encoding similar to
257/// what FunctionRecord instances use, and we no longer need to include the CPU
258/// id in the CustomEventRecord.
259///
Dean Michael Berris5b7548c2018-08-31 17:06:28 +0000260Error loadFDRLog(StringRef Data, bool IsLittleEndian,
261 XRayFileHeader &FileHeader, std::vector<XRayRecord> &Records) {
Dean Michael Berrisd764c1b2018-08-22 07:37:55 +0000262
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000263 if (Data.size() < 32)
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000264 return createStringError(std::make_error_code(std::errc::invalid_argument),
265 "Not enough bytes for an XRay FDR log.");
266 DataExtractor DE(Data, IsLittleEndian, 8);
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000267
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000268 uint32_t OffsetPtr = 0;
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000269 auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr);
Dean Michael Berrisd764c1b2018-08-22 07:37:55 +0000270 if (!FileHeaderOrError)
271 return FileHeaderOrError.takeError();
272 FileHeader = std::move(FileHeaderOrError.get());
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000273
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000274 // First we load the records into memory.
275 std::vector<std::unique_ptr<Record>> FDRRecords;
276
Dean Michael Berris60c24872017-03-29 06:10:12 +0000277 {
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000278 FileBasedRecordProducer P(FileHeader, DE, OffsetPtr);
279 LogBuilderConsumer C(FDRRecords);
280 while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) {
281 auto R = P.produce();
282 if (!R)
283 return R.takeError();
284 if (auto E = C.consume(std::move(R.get())))
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000285 return E;
286 }
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000287 }
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000288
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000289 // Next we index the records into blocks.
290 BlockIndexer::Index Index;
291 {
292 BlockIndexer Indexer(Index);
293 for (auto &R : FDRRecords)
294 if (auto E = R->apply(Indexer))
295 return E;
296 if (auto E = Indexer.flush())
297 return E;
298 }
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000299
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000300 // Then we verify the consistency of the blocks.
301 {
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000302 for (auto &PTB : Index) {
303 auto &Blocks = PTB.second;
304 for (auto &B : Blocks) {
Dean Michael Berris90a46bd2018-09-13 09:25:42 +0000305 BlockVerifier Verifier;
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000306 for (auto *R : B.Records)
307 if (auto E = R->apply(Verifier))
308 return E;
309 if (auto E = Verifier.verify())
310 return E;
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000311 }
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000312 }
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000313 }
Martin Pelikand78db152017-09-15 04:22:16 +0000314
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000315 // This is now the meat of the algorithm. Here we sort the blocks according to
316 // the Walltime record in each of the blocks for the same thread. This allows
317 // us to more consistently recreate the execution trace in temporal order.
318 // After the sort, we then reconstitute `Trace` records using a stateful
319 // visitor associated with a single process+thread pair.
320 {
321 for (auto &PTB : Index) {
322 auto &Blocks = PTB.second;
Dean Michael Berris6b67ff02018-11-01 00:18:52 +0000323 llvm::sort(Blocks, [](const BlockIndexer::Block &L,
324 const BlockIndexer::Block &R) {
325 return (L.WallclockTime->seconds() < R.WallclockTime->seconds() &&
326 L.WallclockTime->nanos() < R.WallclockTime->nanos());
327 });
Dean Michael Berris174d2cf2018-09-11 08:03:30 +0000328 auto Adder = [&](const XRayRecord &R) { Records.push_back(R); };
329 TraceExpander Expander(Adder, FileHeader.Version);
Dean Michael Berris985c2b92018-09-11 06:45:59 +0000330 for (auto &B : Blocks) {
331 for (auto *R : B.Records)
332 if (auto E = R->apply(Expander))
333 return E;
334 }
335 if (auto E = Expander.flush())
336 return E;
337 }
338 }
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000339
340 return Error::success();
341}
342
343Error loadYAMLLog(StringRef Data, XRayFileHeader &FileHeader,
344 std::vector<XRayRecord> &Records) {
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000345 YAMLXRayTrace Trace;
346 Input In(Data);
347 In >> Trace;
348 if (In.error())
349 return make_error<StringError>("Failed loading YAML Data.", In.error());
350
351 FileHeader.Version = Trace.Header.Version;
352 FileHeader.Type = Trace.Header.Type;
353 FileHeader.ConstantTSC = Trace.Header.ConstantTSC;
354 FileHeader.NonstopTSC = Trace.Header.NonstopTSC;
355 FileHeader.CycleFrequency = Trace.Header.CycleFrequency;
356
357 if (FileHeader.Version != 1)
358 return make_error<StringError>(
359 Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
360 std::make_error_code(std::errc::invalid_argument));
361
362 Records.clear();
363 std::transform(Trace.Records.begin(), Trace.Records.end(),
364 std::back_inserter(Records), [&](const YAMLXRayRecord &R) {
Dean Michael Berris25f8d202018-11-06 08:51:37 +0000365 return XRayRecord{R.RecordType, R.CPU, R.Type,
366 R.FuncId, R.TSC, R.TId,
367 R.PId, R.CallArgs, R.Data};
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000368 });
369 return Error::success();
370}
Benjamin Kramer49a49fe2017-08-20 13:03:48 +0000371} // namespace
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000372
373Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
374 int Fd;
375 if (auto EC = sys::fs::openFileForRead(Filename, Fd)) {
376 return make_error<StringError>(
377 Twine("Cannot read log from '") + Filename + "'", EC);
378 }
379
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000380 uint64_t FileSize;
381 if (auto EC = sys::fs::file_size(Filename, FileSize)) {
382 return make_error<StringError>(
383 Twine("Cannot read log from '") + Filename + "'", EC);
384 }
385 if (FileSize < 4) {
386 return make_error<StringError>(
387 Twine("File '") + Filename + "' too small for XRay.",
Hans Wennborg84da6612017-01-12 18:33:14 +0000388 std::make_error_code(std::errc::executable_format_error));
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000389 }
390
Martin Pelikand78db152017-09-15 04:22:16 +0000391 // Map the opened file into memory and use a StringRef to access it later.
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000392 std::error_code EC;
393 sys::fs::mapped_file_region MappedFile(
394 Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
395 if (EC) {
396 return make_error<StringError>(
397 Twine("Cannot read log from '") + Filename + "'", EC);
398 }
Martin Pelikand78db152017-09-15 04:22:16 +0000399 auto Data = StringRef(MappedFile.data(), MappedFile.size());
Dean Michael Berris5b7548c2018-08-31 17:06:28 +0000400
401 // TODO: Lift the endianness and implementation selection here.
402 DataExtractor LittleEndianDE(Data, true, 8);
403 auto TraceOrError = loadTrace(LittleEndianDE, Sort);
404 if (!TraceOrError) {
405 DataExtractor BigEndianDE(Data, false, 8);
406 TraceOrError = loadTrace(BigEndianDE, Sort);
407 }
408 return TraceOrError;
Dean Michael Berrisf81b0802018-08-24 10:30:37 +0000409}
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000410
Dean Michael Berrisf81b0802018-08-24 10:30:37 +0000411Expected<Trace> llvm::xray::loadTrace(const DataExtractor &DE, bool Sort) {
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000412 // Attempt to detect the file type using file magic. We have a slight bias
413 // towards the binary format, and we do this by making sure that the first 4
414 // bytes of the binary file is some combination of the following byte
Martin Pelikand78db152017-09-15 04:22:16 +0000415 // patterns: (observe the code loading them assumes they're little endian)
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000416 //
Martin Pelikand78db152017-09-15 04:22:16 +0000417 // 0x01 0x00 0x00 0x00 - version 1, "naive" format
418 // 0x01 0x00 0x01 0x00 - version 1, "flight data recorder" format
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000419 // 0x02 0x00 0x01 0x00 - version 2, "flight data recorder" format
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000420 //
Martin Pelikand78db152017-09-15 04:22:16 +0000421 // YAML files don't typically have those first four bytes as valid text so we
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000422 // try loading assuming YAML if we don't find these bytes.
423 //
424 // Only if we can't load either the binary or the YAML format will we yield an
425 // error.
Dean Michael Berris5b7548c2018-08-31 17:06:28 +0000426 DataExtractor HeaderExtractor(DE.getData(), DE.isLittleEndian(), 8);
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000427 uint32_t OffsetPtr = 0;
428 uint16_t Version = HeaderExtractor.getU16(&OffsetPtr);
429 uint16_t Type = HeaderExtractor.getU16(&OffsetPtr);
430
Dean Michael Berris4f83c4d2017-02-17 01:47:16 +0000431 enum BinaryFormatType { NAIVE_FORMAT = 0, FLIGHT_DATA_RECORDER_FORMAT = 1 };
432
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000433 Trace T;
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000434 switch (Type) {
435 case NAIVE_FORMAT:
Dean Michael Berris10141262018-07-13 05:38:22 +0000436 if (Version == 1 || Version == 2 || Version == 3) {
Dean Michael Berris5b7548c2018-08-31 17:06:28 +0000437 if (auto E = loadNaiveFormatLog(DE.getData(), DE.isLittleEndian(),
438 T.FileHeader, T.Records))
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000439 return std::move(E);
440 } else {
441 return make_error<StringError>(
442 Twine("Unsupported version for Basic/Naive Mode logging: ") +
443 Twine(Version),
444 std::make_error_code(std::errc::executable_format_error));
445 }
446 break;
447 case FLIGHT_DATA_RECORDER_FORMAT:
Dean Michael Berris59439dd2018-11-07 04:37:42 +0000448 if (Version >= 1 && Version <= 5) {
Dean Michael Berris5b7548c2018-08-31 17:06:28 +0000449 if (auto E = loadFDRLog(DE.getData(), DE.isLittleEndian(), T.FileHeader,
450 T.Records))
Dean Michael Berris6ec72622017-11-21 07:16:57 +0000451 return std::move(E);
452 } else {
453 return make_error<StringError>(
454 Twine("Unsupported version for FDR Mode logging: ") + Twine(Version),
455 std::make_error_code(std::errc::executable_format_error));
456 }
457 break;
458 default:
Dean Michael Berrisf81b0802018-08-24 10:30:37 +0000459 if (auto E = loadYAMLLog(DE.getData(), T.FileHeader, T.Records))
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000460 return std::move(E);
461 }
462
463 if (Sort)
Mandeep Singh Grang28f3d5c2017-11-14 18:11:08 +0000464 std::stable_sort(T.Records.begin(), T.Records.end(),
Dean Michael Berrisa9d477a2018-08-07 04:42:39 +0000465 [&](const XRayRecord &L, const XRayRecord &R) {
466 return L.TSC < R.TSC;
467 });
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000468
469 return std::move(T);
470}