xref: /llvm-project-15.0.7/llvm/lib/XRay/Trace.cpp (revision d6c18657)
1*d6c18657SDean Michael Berris //===- Trace.cpp - XRay Trace Loading implementation. ---------------------===//
2*d6c18657SDean Michael Berris //
3*d6c18657SDean Michael Berris //                     The LLVM Compiler Infrastructure
4*d6c18657SDean Michael Berris //
5*d6c18657SDean Michael Berris // This file is distributed under the University of Illinois Open Source
6*d6c18657SDean Michael Berris // License. See LICENSE.TXT for details.
7*d6c18657SDean Michael Berris //
8*d6c18657SDean Michael Berris //===----------------------------------------------------------------------===//
9*d6c18657SDean Michael Berris //
10*d6c18657SDean Michael Berris // XRay log reader implementation.
11*d6c18657SDean Michael Berris //
12*d6c18657SDean Michael Berris //===----------------------------------------------------------------------===//
13*d6c18657SDean Michael Berris #include "llvm/XRay/Trace.h"
14*d6c18657SDean Michael Berris #include "llvm/ADT/STLExtras.h"
15*d6c18657SDean Michael Berris #include "llvm/Support/DataExtractor.h"
16*d6c18657SDean Michael Berris #include "llvm/Support/Error.h"
17*d6c18657SDean Michael Berris #include "llvm/Support/FileSystem.h"
18*d6c18657SDean Michael Berris #include "llvm/XRay/YAMLXRayRecord.h"
19*d6c18657SDean Michael Berris 
20*d6c18657SDean Michael Berris using namespace llvm;
21*d6c18657SDean Michael Berris using namespace llvm::xray;
22*d6c18657SDean Michael Berris using llvm::yaml::Input;
23*d6c18657SDean Michael Berris 
24*d6c18657SDean Michael Berris using XRayRecordStorage =
25*d6c18657SDean Michael Berris     std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type;
26*d6c18657SDean Michael Berris 
27*d6c18657SDean Michael Berris Error NaiveLogLoader(StringRef Data, XRayFileHeader &FileHeader,
28*d6c18657SDean Michael Berris                      std::vector<XRayRecord> &Records) {
29*d6c18657SDean Michael Berris   // FIXME: Maybe deduce whether the data is little or big-endian using some
30*d6c18657SDean Michael Berris   // magic bytes in the beginning of the file?
31*d6c18657SDean Michael Berris 
32*d6c18657SDean Michael Berris   // First 32 bytes of the file will always be the header. We assume a certain
33*d6c18657SDean Michael Berris   // format here:
34*d6c18657SDean Michael Berris   //
35*d6c18657SDean Michael Berris   //   (2)   uint16 : version
36*d6c18657SDean Michael Berris   //   (2)   uint16 : type
37*d6c18657SDean Michael Berris   //   (4)   uint32 : bitfield
38*d6c18657SDean Michael Berris   //   (8)   uint64 : cycle frequency
39*d6c18657SDean Michael Berris   //   (16)  -      : padding
40*d6c18657SDean Michael Berris   //
41*d6c18657SDean Michael Berris   if (Data.size() < 32)
42*d6c18657SDean Michael Berris     return make_error<StringError>(
43*d6c18657SDean Michael Berris         "Not enough bytes for an XRay log.",
44*d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
45*d6c18657SDean Michael Berris 
46*d6c18657SDean Michael Berris   if (Data.size() - 32 == 0 || Data.size() % 32 != 0)
47*d6c18657SDean Michael Berris     return make_error<StringError>(
48*d6c18657SDean Michael Berris         "Invalid-sized XRay data.",
49*d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
50*d6c18657SDean Michael Berris 
51*d6c18657SDean Michael Berris   DataExtractor HeaderExtractor(Data, true, 8);
52*d6c18657SDean Michael Berris   uint32_t OffsetPtr = 0;
53*d6c18657SDean Michael Berris   FileHeader.Version = HeaderExtractor.getU16(&OffsetPtr);
54*d6c18657SDean Michael Berris   FileHeader.Type = HeaderExtractor.getU16(&OffsetPtr);
55*d6c18657SDean Michael Berris   uint32_t Bitfield = HeaderExtractor.getU32(&OffsetPtr);
56*d6c18657SDean Michael Berris   FileHeader.ConstantTSC = Bitfield & 1uL;
57*d6c18657SDean Michael Berris   FileHeader.NonstopTSC = Bitfield & 1uL << 1;
58*d6c18657SDean Michael Berris   FileHeader.CycleFrequency = HeaderExtractor.getU64(&OffsetPtr);
59*d6c18657SDean Michael Berris 
60*d6c18657SDean Michael Berris   if (FileHeader.Version != 1)
61*d6c18657SDean Michael Berris     return make_error<StringError>(
62*d6c18657SDean Michael Berris         Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
63*d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
64*d6c18657SDean Michael Berris 
65*d6c18657SDean Michael Berris   // Each record after the header will be 32 bytes, in the following format:
66*d6c18657SDean Michael Berris   //
67*d6c18657SDean Michael Berris   //   (2)   uint16 : record type
68*d6c18657SDean Michael Berris   //   (1)   uint8  : cpu id
69*d6c18657SDean Michael Berris   //   (1)   uint8  : type
70*d6c18657SDean Michael Berris   //   (4)   sint32 : function id
71*d6c18657SDean Michael Berris   //   (8)   uint64 : tsc
72*d6c18657SDean Michael Berris   //   (4)   uint32 : thread id
73*d6c18657SDean Michael Berris   //   (12)  -      : padding
74*d6c18657SDean Michael Berris   for (auto S = Data.drop_front(32); !S.empty(); S = S.drop_front(32)) {
75*d6c18657SDean Michael Berris     DataExtractor RecordExtractor(S, true, 8);
76*d6c18657SDean Michael Berris     uint32_t OffsetPtr = 0;
77*d6c18657SDean Michael Berris     Records.emplace_back();
78*d6c18657SDean Michael Berris     auto &Record = Records.back();
79*d6c18657SDean Michael Berris     Record.RecordType = RecordExtractor.getU16(&OffsetPtr);
80*d6c18657SDean Michael Berris     Record.CPU = RecordExtractor.getU8(&OffsetPtr);
81*d6c18657SDean Michael Berris     auto Type = RecordExtractor.getU8(&OffsetPtr);
82*d6c18657SDean Michael Berris     switch (Type) {
83*d6c18657SDean Michael Berris     case 0:
84*d6c18657SDean Michael Berris       Record.Type = RecordTypes::ENTER;
85*d6c18657SDean Michael Berris       break;
86*d6c18657SDean Michael Berris     case 1:
87*d6c18657SDean Michael Berris       Record.Type = RecordTypes::EXIT;
88*d6c18657SDean Michael Berris       break;
89*d6c18657SDean Michael Berris     default:
90*d6c18657SDean Michael Berris       return make_error<StringError>(
91*d6c18657SDean Michael Berris           Twine("Unknown record type '") + Twine(int{Type}) + "'",
92*d6c18657SDean Michael Berris           std::make_error_code(std::errc::executable_format_error));
93*d6c18657SDean Michael Berris     }
94*d6c18657SDean Michael Berris     Record.FuncId = RecordExtractor.getSigned(&OffsetPtr, sizeof(int32_t));
95*d6c18657SDean Michael Berris     Record.TSC = RecordExtractor.getU64(&OffsetPtr);
96*d6c18657SDean Michael Berris     Record.TId = RecordExtractor.getU32(&OffsetPtr);
97*d6c18657SDean Michael Berris   }
98*d6c18657SDean Michael Berris   return Error::success();
99*d6c18657SDean Michael Berris }
100*d6c18657SDean Michael Berris 
101*d6c18657SDean Michael Berris Error YAMLLogLoader(StringRef Data, XRayFileHeader &FileHeader,
102*d6c18657SDean Michael Berris                     std::vector<XRayRecord> &Records) {
103*d6c18657SDean Michael Berris 
104*d6c18657SDean Michael Berris   // Load the documents from the MappedFile.
105*d6c18657SDean Michael Berris   YAMLXRayTrace Trace;
106*d6c18657SDean Michael Berris   Input In(Data);
107*d6c18657SDean Michael Berris   In >> Trace;
108*d6c18657SDean Michael Berris   if (In.error())
109*d6c18657SDean Michael Berris     return make_error<StringError>("Failed loading YAML Data.", In.error());
110*d6c18657SDean Michael Berris 
111*d6c18657SDean Michael Berris   FileHeader.Version = Trace.Header.Version;
112*d6c18657SDean Michael Berris   FileHeader.Type = Trace.Header.Type;
113*d6c18657SDean Michael Berris   FileHeader.ConstantTSC = Trace.Header.ConstantTSC;
114*d6c18657SDean Michael Berris   FileHeader.NonstopTSC = Trace.Header.NonstopTSC;
115*d6c18657SDean Michael Berris   FileHeader.CycleFrequency = Trace.Header.CycleFrequency;
116*d6c18657SDean Michael Berris 
117*d6c18657SDean Michael Berris   if (FileHeader.Version != 1)
118*d6c18657SDean Michael Berris     return make_error<StringError>(
119*d6c18657SDean Michael Berris         Twine("Unsupported XRay file version: ") + Twine(FileHeader.Version),
120*d6c18657SDean Michael Berris         std::make_error_code(std::errc::invalid_argument));
121*d6c18657SDean Michael Berris 
122*d6c18657SDean Michael Berris   Records.clear();
123*d6c18657SDean Michael Berris   std::transform(Trace.Records.begin(), Trace.Records.end(),
124*d6c18657SDean Michael Berris                  std::back_inserter(Records), [&](const YAMLXRayRecord &R) {
125*d6c18657SDean Michael Berris                    return XRayRecord{R.RecordType, R.CPU, R.Type,
126*d6c18657SDean Michael Berris                                      R.FuncId,     R.TSC, R.TId};
127*d6c18657SDean Michael Berris                  });
128*d6c18657SDean Michael Berris   return Error::success();
129*d6c18657SDean Michael Berris }
130*d6c18657SDean Michael Berris 
131*d6c18657SDean Michael Berris Expected<Trace> llvm::xray::loadTraceFile(StringRef Filename, bool Sort) {
132*d6c18657SDean Michael Berris   int Fd;
133*d6c18657SDean Michael Berris   if (auto EC = sys::fs::openFileForRead(Filename, Fd)) {
134*d6c18657SDean Michael Berris     return make_error<StringError>(
135*d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
136*d6c18657SDean Michael Berris   }
137*d6c18657SDean Michael Berris 
138*d6c18657SDean Michael Berris   // Attempt to get the filesize.
139*d6c18657SDean Michael Berris   uint64_t FileSize;
140*d6c18657SDean Michael Berris   if (auto EC = sys::fs::file_size(Filename, FileSize)) {
141*d6c18657SDean Michael Berris     return make_error<StringError>(
142*d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
143*d6c18657SDean Michael Berris   }
144*d6c18657SDean Michael Berris   if (FileSize < 4) {
145*d6c18657SDean Michael Berris     return make_error<StringError>(
146*d6c18657SDean Michael Berris         Twine("File '") + Filename + "' too small for XRay.",
147*d6c18657SDean Michael Berris         std::make_error_code(std::errc::protocol_error));
148*d6c18657SDean Michael Berris   }
149*d6c18657SDean Michael Berris 
150*d6c18657SDean Michael Berris   // Attempt to mmap the file.
151*d6c18657SDean Michael Berris   std::error_code EC;
152*d6c18657SDean Michael Berris   sys::fs::mapped_file_region MappedFile(
153*d6c18657SDean Michael Berris       Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC);
154*d6c18657SDean Michael Berris   if (EC) {
155*d6c18657SDean Michael Berris     return make_error<StringError>(
156*d6c18657SDean Michael Berris         Twine("Cannot read log from '") + Filename + "'", EC);
157*d6c18657SDean Michael Berris   }
158*d6c18657SDean Michael Berris 
159*d6c18657SDean Michael Berris   // Attempt to detect the file type using file magic. We have a slight bias
160*d6c18657SDean Michael Berris   // towards the binary format, and we do this by making sure that the first 4
161*d6c18657SDean Michael Berris   // bytes of the binary file is some combination of the following byte
162*d6c18657SDean Michael Berris   // patterns:
163*d6c18657SDean Michael Berris   //
164*d6c18657SDean Michael Berris   //   0x0001 0x0000 - version 1, "naive" format
165*d6c18657SDean Michael Berris   //   0x0001 0x0001 - version 1, "flight data recorder" format
166*d6c18657SDean Michael Berris   //
167*d6c18657SDean Michael Berris   // YAML files dont' typically have those first four bytes as valid text so we
168*d6c18657SDean Michael Berris   // try loading assuming YAML if we don't find these bytes.
169*d6c18657SDean Michael Berris   //
170*d6c18657SDean Michael Berris   // Only if we can't load either the binary or the YAML format will we yield an
171*d6c18657SDean Michael Berris   // error.
172*d6c18657SDean Michael Berris   StringRef Magic(MappedFile.data(), 4);
173*d6c18657SDean Michael Berris   DataExtractor HeaderExtractor(Magic, true, 8);
174*d6c18657SDean Michael Berris   uint32_t OffsetPtr = 0;
175*d6c18657SDean Michael Berris   uint16_t Version = HeaderExtractor.getU16(&OffsetPtr);
176*d6c18657SDean Michael Berris   uint16_t Type = HeaderExtractor.getU16(&OffsetPtr);
177*d6c18657SDean Michael Berris 
178*d6c18657SDean Michael Berris   Trace T;
179*d6c18657SDean Michael Berris   if (Version == 1 && (Type == 0 || Type == 1)) {
180*d6c18657SDean Michael Berris     if (auto E = NaiveLogLoader(StringRef(MappedFile.data(), MappedFile.size()),
181*d6c18657SDean Michael Berris                                 T.FileHeader, T.Records))
182*d6c18657SDean Michael Berris       return std::move(E);
183*d6c18657SDean Michael Berris   } else {
184*d6c18657SDean Michael Berris     if (auto E = YAMLLogLoader(StringRef(MappedFile.data(), MappedFile.size()),
185*d6c18657SDean Michael Berris                                T.FileHeader, T.Records))
186*d6c18657SDean Michael Berris       return std::move(E);
187*d6c18657SDean Michael Berris   }
188*d6c18657SDean Michael Berris 
189*d6c18657SDean Michael Berris   if (Sort)
190*d6c18657SDean Michael Berris     std::sort(T.Records.begin(), T.Records.end(),
191*d6c18657SDean Michael Berris               [&](const XRayRecord &L, const XRayRecord &R) {
192*d6c18657SDean Michael Berris                 return L.TSC < R.TSC;
193*d6c18657SDean Michael Berris               });
194*d6c18657SDean Michael Berris 
195*d6c18657SDean Michael Berris   return std::move(T);
196*d6c18657SDean Michael Berris }
197