blob: 51000c777de827fafba147f567cf1b6cbebb48dd [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//
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//===----------------------------------------------------------------------===//
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000013#include "llvm/XRay/Trace.h"
14#include "llvm/ADT/STLExtras.h"
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000015#include "llvm/Support/DataExtractor.h"
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000016#include "llvm/Support/Error.h"
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000017#include "llvm/Support/FileSystem.h"
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000018#include "llvm/XRay/YAMLXRayRecord.h"
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000019
20using namespace llvm;
21using namespace llvm::xray;
22using llvm::yaml::Input;
23
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000024using XRayRecordStorage =
25 std::aligned_storage<sizeof(XRayRecord), alignof(XRayRecord)>::type;
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000026
Dean Michael Berrisd6c18652017-01-11 06:39:09 +000027Error NaiveLogLoader(StringRef Data, XRayFileHeader &FileHeader,
28 std::vector<XRayRecord> &Records) {
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000029 // 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}) + "'",
NAKAMURA Takumib09bec22017-01-11 01:06:57 +000092 std::make_error_code(std::errc::executable_format_error));
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +000093 }
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
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000101Error YAMLLogLoader(StringRef Data, XRayFileHeader &FileHeader,
102 std::vector<XRayRecord> &Records) {
Dean Michael Berrisf8f909f2017-01-10 02:38:11 +0000103
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}
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000130
131Expected<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.",
Hans Wennborg84da6612017-01-12 18:33:14 +0000147 std::make_error_code(std::errc::executable_format_error));
Dean Michael Berrisd6c18652017-01-11 06:39:09 +0000148 }
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}