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