12f09f445SMaksim Panchenko //===- bolt/Profile/DataAggregator.cpp - Perf data aggregator -------------===//
2a34c753fSRafael Auler //
3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a34c753fSRafael Auler //
7a34c753fSRafael Auler //===----------------------------------------------------------------------===//
8a34c753fSRafael Auler //
9a34c753fSRafael Auler // This family of functions reads profile data written by perf record,
10a34c753fSRafael Auler // aggregate it and then write it back to an output file.
11a34c753fSRafael Auler //
12a34c753fSRafael Auler //===----------------------------------------------------------------------===//
13a34c753fSRafael Auler
14a34c753fSRafael Auler #include "bolt/Profile/DataAggregator.h"
15a34c753fSRafael Auler #include "bolt/Core/BinaryContext.h"
16a34c753fSRafael Auler #include "bolt/Core/BinaryFunction.h"
17a34c753fSRafael Auler #include "bolt/Profile/BoltAddressTranslation.h"
18a34c753fSRafael Auler #include "bolt/Profile/Heatmap.h"
19a34c753fSRafael Auler #include "bolt/Utils/CommandLineOpts.h"
20a34c753fSRafael Auler #include "bolt/Utils/Utils.h"
21ae585be1SRafael Auler #include "llvm/ADT/ScopeExit.h"
22a34c753fSRafael Auler #include "llvm/Support/CommandLine.h"
23a34c753fSRafael Auler #include "llvm/Support/Debug.h"
24290e4823Sserge-sans-paille #include "llvm/Support/Errc.h"
25a34c753fSRafael Auler #include "llvm/Support/FileSystem.h"
26a34c753fSRafael Auler #include "llvm/Support/Process.h"
27a34c753fSRafael Auler #include "llvm/Support/Program.h"
28a34c753fSRafael Auler #include "llvm/Support/Regex.h"
29a34c753fSRafael Auler #include "llvm/Support/Timer.h"
30a34c753fSRafael Auler #include "llvm/Support/raw_ostream.h"
31a34c753fSRafael Auler #include <map>
32a34c753fSRafael Auler #include <unordered_map>
33d914486aSAmir Ayupov #include <utility>
34a34c753fSRafael Auler
35a34c753fSRafael Auler #define DEBUG_TYPE "aggregator"
36a34c753fSRafael Auler
37a34c753fSRafael Auler using namespace llvm;
38a34c753fSRafael Auler using namespace bolt;
39a34c753fSRafael Auler
40a34c753fSRafael Auler namespace opts {
41a34c753fSRafael Auler
42a34c753fSRafael Auler static cl::opt<bool>
43a34c753fSRafael Auler BasicAggregation("nl",
44a34c753fSRafael Auler cl::desc("aggregate basic samples (without LBR info)"),
45a34c753fSRafael Auler cl::cat(AggregatorCategory));
46a34c753fSRafael Auler
47a34c753fSRafael Auler static cl::opt<bool>
48a34c753fSRafael Auler FilterMemProfile("filter-mem-profile",
49a34c753fSRafael Auler cl::desc("if processing a memory profile, filter out stack or heap accesses "
50a34c753fSRafael Auler "that won't be useful for BOLT to reduce profile file size"),
51a34c753fSRafael Auler cl::init(true),
52a34c753fSRafael Auler cl::cat(AggregatorCategory));
53a34c753fSRafael Auler
54a34c753fSRafael Auler static cl::opt<unsigned long long>
55a34c753fSRafael Auler FilterPID("pid",
56a34c753fSRafael Auler cl::desc("only use samples from process with specified PID"),
57a34c753fSRafael Auler cl::init(0),
58a34c753fSRafael Auler cl::Optional,
59a34c753fSRafael Auler cl::cat(AggregatorCategory));
60a34c753fSRafael Auler
61a34c753fSRafael Auler static cl::opt<bool>
62a34c753fSRafael Auler IgnoreBuildID("ignore-build-id",
63a34c753fSRafael Auler cl::desc("continue even if build-ids in input binary and perf.data mismatch"),
64a34c753fSRafael Auler cl::init(false),
65a34c753fSRafael Auler cl::cat(AggregatorCategory));
66a34c753fSRafael Auler
67b92436efSFangrui Song static cl::opt<bool> IgnoreInterruptLBR(
68b92436efSFangrui Song "ignore-interrupt-lbr",
69a34c753fSRafael Auler cl::desc("ignore kernel interrupt LBR that happens asynchronously"),
70b92436efSFangrui Song cl::init(true), cl::cat(AggregatorCategory));
71a34c753fSRafael Auler
72a34c753fSRafael Auler static cl::opt<unsigned long long>
73a34c753fSRafael Auler MaxSamples("max-samples",
74a34c753fSRafael Auler cl::init(-1ULL),
75a34c753fSRafael Auler cl::desc("maximum number of samples to read from LBR profile"),
76a34c753fSRafael Auler cl::Optional,
77a34c753fSRafael Auler cl::Hidden,
78a34c753fSRafael Auler cl::cat(AggregatorCategory));
79a34c753fSRafael Auler
80*661577b5SMaksim Panchenko cl::opt<bool> ReadPreAggregated(
81b92436efSFangrui Song "pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
82a34c753fSRafael Auler cl::cat(AggregatorCategory));
83a34c753fSRafael Auler
84a34c753fSRafael Auler static cl::opt<bool>
85a34c753fSRafael Auler TimeAggregator("time-aggr",
86a34c753fSRafael Auler cl::desc("time BOLT aggregator"),
87a34c753fSRafael Auler cl::init(false),
88a34c753fSRafael Auler cl::ZeroOrMore,
89a34c753fSRafael Auler cl::cat(AggregatorCategory));
90a34c753fSRafael Auler
91a34c753fSRafael Auler static cl::opt<bool>
92a34c753fSRafael Auler UseEventPC("use-event-pc",
93a34c753fSRafael Auler cl::desc("use event PC in combination with LBR sampling"),
94a34c753fSRafael Auler cl::cat(AggregatorCategory));
95a34c753fSRafael Auler
96b92436efSFangrui Song static cl::opt<bool> WriteAutoFDOData(
97b92436efSFangrui Song "autofdo", cl::desc("generate autofdo textual data instead of bolt data"),
98a34c753fSRafael Auler cl::cat(AggregatorCategory));
99a34c753fSRafael Auler
10040c2e0faSMaksim Panchenko } // namespace opts
101a34c753fSRafael Auler
102a34c753fSRafael Auler namespace {
103a34c753fSRafael Auler
104a34c753fSRafael Auler const char TimerGroupName[] = "aggregator";
105a34c753fSRafael Auler const char TimerGroupDesc[] = "Aggregator";
106a34c753fSRafael Auler
getTextSections(const BinaryContext * BC)107733dc3e5SRahman Lavaee std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) {
108733dc3e5SRahman Lavaee std::vector<SectionNameAndRange> sections;
109733dc3e5SRahman Lavaee for (BinarySection &Section : BC->sections()) {
110733dc3e5SRahman Lavaee if (!Section.isText())
111733dc3e5SRahman Lavaee continue;
112733dc3e5SRahman Lavaee if (Section.getSize() == 0)
113733dc3e5SRahman Lavaee continue;
114733dc3e5SRahman Lavaee sections.push_back(
115733dc3e5SRahman Lavaee {Section.getName(), Section.getAddress(), Section.getEndAddress()});
116733dc3e5SRahman Lavaee }
117d2c87699SAmir Ayupov llvm::sort(sections,
118733dc3e5SRahman Lavaee [](const SectionNameAndRange &A, const SectionNameAndRange &B) {
119733dc3e5SRahman Lavaee return A.BeginAddress < B.BeginAddress;
120733dc3e5SRahman Lavaee });
121733dc3e5SRahman Lavaee return sections;
122733dc3e5SRahman Lavaee }
123a34c753fSRafael Auler }
124a34c753fSRafael Auler
125a34c753fSRafael Auler constexpr uint64_t DataAggregator::KernelBaseAddr;
126a34c753fSRafael Auler
~DataAggregator()12740c2e0faSMaksim Panchenko DataAggregator::~DataAggregator() { deleteTempFiles(); }
128a34c753fSRafael Auler
129a34c753fSRafael Auler namespace {
deleteTempFile(const std::string & FileName)130a34c753fSRafael Auler void deleteTempFile(const std::string &FileName) {
131def464aaSAmir Ayupov if (std::error_code Errc = sys::fs::remove(FileName.c_str()))
13240c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to delete temporary file " << FileName
13340c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n";
134a34c753fSRafael Auler }
135a34c753fSRafael Auler }
136a34c753fSRafael Auler
deleteTempFiles()137a34c753fSRafael Auler void DataAggregator::deleteTempFiles() {
138def464aaSAmir Ayupov for (std::string &FileName : TempFiles)
139a34c753fSRafael Auler deleteTempFile(FileName);
140a34c753fSRafael Auler TempFiles.clear();
141a34c753fSRafael Auler }
142a34c753fSRafael Auler
findPerfExecutable()143a34c753fSRafael Auler void DataAggregator::findPerfExecutable() {
144a34c753fSRafael Auler Optional<std::string> PerfExecutable =
145a34c753fSRafael Auler sys::Process::FindInEnvPath("PATH", "perf");
146a34c753fSRafael Auler if (!PerfExecutable) {
147a34c753fSRafael Auler outs() << "PERF2BOLT: No perf executable found!\n";
148a34c753fSRafael Auler exit(1);
149a34c753fSRafael Auler }
150a34c753fSRafael Auler PerfPath = *PerfExecutable;
151a34c753fSRafael Auler }
152a34c753fSRafael Auler
start()153a34c753fSRafael Auler void DataAggregator::start() {
15440c2e0faSMaksim Panchenko outs() << "PERF2BOLT: Starting data aggregation job for " << Filename << "\n";
155a34c753fSRafael Auler
156a34c753fSRafael Auler // Don't launch perf for pre-aggregated files
157a34c753fSRafael Auler if (opts::ReadPreAggregated)
158a34c753fSRafael Auler return;
159a34c753fSRafael Auler
160a34c753fSRafael Auler findPerfExecutable();
161a34c753fSRafael Auler
162def464aaSAmir Ayupov if (opts::BasicAggregation)
163a34c753fSRafael Auler launchPerfProcess("events without LBR",
164a34c753fSRafael Auler MainEventsPPI,
165a34c753fSRafael Auler "script -F pid,event,ip",
166a34c753fSRafael Auler /*Wait = */false);
167def464aaSAmir Ayupov else
168a34c753fSRafael Auler launchPerfProcess("branch events",
169a34c753fSRafael Auler MainEventsPPI,
170a34c753fSRafael Auler "script -F pid,ip,brstack",
171a34c753fSRafael Auler /*Wait = */false);
172a34c753fSRafael Auler
173a34c753fSRafael Auler // Note: we launch script for mem events regardless of the option, as the
174a34c753fSRafael Auler // command fails fairly fast if mem events were not collected.
175a34c753fSRafael Auler launchPerfProcess("mem events",
176a34c753fSRafael Auler MemEventsPPI,
177a34c753fSRafael Auler "script -F pid,event,addr,ip",
178a34c753fSRafael Auler /*Wait = */false);
179a34c753fSRafael Auler
180a34c753fSRafael Auler launchPerfProcess("process events",
181a34c753fSRafael Auler MMapEventsPPI,
182a34c753fSRafael Auler "script --show-mmap-events",
183a34c753fSRafael Auler /*Wait = */false);
184a34c753fSRafael Auler
185a34c753fSRafael Auler launchPerfProcess("task events",
186a34c753fSRafael Auler TaskEventsPPI,
187a34c753fSRafael Auler "script --show-task-events",
188a34c753fSRafael Auler /*Wait = */false);
189a34c753fSRafael Auler }
190a34c753fSRafael Auler
abort()191a34c753fSRafael Auler void DataAggregator::abort() {
192a34c753fSRafael Auler if (opts::ReadPreAggregated)
193a34c753fSRafael Auler return;
194a34c753fSRafael Auler
195a34c753fSRafael Auler std::string Error;
196a34c753fSRafael Auler
197a34c753fSRafael Auler // Kill subprocesses in case they are not finished
198a34c753fSRafael Auler sys::Wait(TaskEventsPPI.PI, 1, false, &Error);
199a34c753fSRafael Auler sys::Wait(MMapEventsPPI.PI, 1, false, &Error);
200a34c753fSRafael Auler sys::Wait(MainEventsPPI.PI, 1, false, &Error);
201a34c753fSRafael Auler sys::Wait(MemEventsPPI.PI, 1, false, &Error);
202a34c753fSRafael Auler
203a34c753fSRafael Auler deleteTempFiles();
204a34c753fSRafael Auler
205a34c753fSRafael Auler exit(1);
206a34c753fSRafael Auler }
207a34c753fSRafael Auler
launchPerfProcess(StringRef Name,PerfProcessInfo & PPI,const char * ArgsString,bool Wait)208a34c753fSRafael Auler void DataAggregator::launchPerfProcess(StringRef Name, PerfProcessInfo &PPI,
209a34c753fSRafael Auler const char *ArgsString, bool Wait) {
210a34c753fSRafael Auler SmallVector<StringRef, 4> Argv;
211a34c753fSRafael Auler
212a34c753fSRafael Auler outs() << "PERF2BOLT: spawning perf job to read " << Name << '\n';
213a34c753fSRafael Auler Argv.push_back(PerfPath.data());
214a34c753fSRafael Auler
215a34c753fSRafael Auler char *WritableArgsString = strdup(ArgsString);
216a34c753fSRafael Auler char *Str = WritableArgsString;
217a34c753fSRafael Auler do {
218a34c753fSRafael Auler Argv.push_back(Str);
219a34c753fSRafael Auler while (*Str && *Str != ' ')
220a34c753fSRafael Auler ++Str;
221a34c753fSRafael Auler if (!*Str)
222a34c753fSRafael Auler break;
223a34c753fSRafael Auler *Str++ = 0;
224a34c753fSRafael Auler } while (true);
225a34c753fSRafael Auler
226a34c753fSRafael Auler Argv.push_back("-f");
227a34c753fSRafael Auler Argv.push_back("-i");
228a34c753fSRafael Auler Argv.push_back(Filename.c_str());
229a34c753fSRafael Auler
230a34c753fSRafael Auler if (std::error_code Errc =
231a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "out", PPI.StdoutPath)) {
23240c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StdoutPath
23340c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n";
234a34c753fSRafael Auler exit(1);
235a34c753fSRafael Auler }
236a34c753fSRafael Auler TempFiles.push_back(PPI.StdoutPath.data());
237a34c753fSRafael Auler
238a34c753fSRafael Auler if (std::error_code Errc =
239a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "err", PPI.StderrPath)) {
24040c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StderrPath
24140c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n";
242a34c753fSRafael Auler exit(1);
243a34c753fSRafael Auler }
244a34c753fSRafael Auler TempFiles.push_back(PPI.StderrPath.data());
245a34c753fSRafael Auler
246a34c753fSRafael Auler Optional<StringRef> Redirects[] = {
247a34c753fSRafael Auler llvm::None, // Stdin
248a34c753fSRafael Auler StringRef(PPI.StdoutPath.data()), // Stdout
249a34c753fSRafael Auler StringRef(PPI.StderrPath.data())}; // Stderr
250a34c753fSRafael Auler
251a34c753fSRafael Auler LLVM_DEBUG({
252a34c753fSRafael Auler dbgs() << "Launching perf: ";
253a34c753fSRafael Auler for (StringRef Arg : Argv)
254a34c753fSRafael Auler dbgs() << Arg << " ";
255a34c753fSRafael Auler dbgs() << " 1> " << PPI.StdoutPath.data() << " 2> " << PPI.StderrPath.data()
256a34c753fSRafael Auler << "\n";
257a34c753fSRafael Auler });
258a34c753fSRafael Auler
259def464aaSAmir Ayupov if (Wait)
260a34c753fSRafael Auler PPI.PI.ReturnCode = sys::ExecuteAndWait(PerfPath.data(), Argv,
261a34c753fSRafael Auler /*envp*/ llvm::None, Redirects);
262def464aaSAmir Ayupov else
263a34c753fSRafael Auler PPI.PI = sys::ExecuteNoWait(PerfPath.data(), Argv, /*envp*/ llvm::None,
264a34c753fSRafael Auler Redirects);
265a34c753fSRafael Auler
266a34c753fSRafael Auler free(WritableArgsString);
267a34c753fSRafael Auler }
268a34c753fSRafael Auler
processFileBuildID(StringRef FileBuildID)269a34c753fSRafael Auler void DataAggregator::processFileBuildID(StringRef FileBuildID) {
270a34c753fSRafael Auler PerfProcessInfo BuildIDProcessInfo;
271a34c753fSRafael Auler launchPerfProcess("buildid list",
272a34c753fSRafael Auler BuildIDProcessInfo,
273a34c753fSRafael Auler "buildid-list",
274a34c753fSRafael Auler /*Wait = */true);
275a34c753fSRafael Auler
276a34c753fSRafael Auler if (BuildIDProcessInfo.PI.ReturnCode != 0) {
277a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
278a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StderrPath.data());
279a34c753fSRafael Auler StringRef ErrBuf = (*MB)->getBuffer();
280a34c753fSRafael Auler
281a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << BuildIDProcessInfo.PI.ReturnCode
282a34c753fSRafael Auler << '\n';
283a34c753fSRafael Auler errs() << ErrBuf;
284a34c753fSRafael Auler return;
285a34c753fSRafael Auler }
286a34c753fSRafael Auler
287a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
288a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StdoutPath.data());
289a34c753fSRafael Auler if (std::error_code EC = MB.getError()) {
290a34c753fSRafael Auler errs() << "Cannot open " << BuildIDProcessInfo.StdoutPath.data() << ": "
291a34c753fSRafael Auler << EC.message() << "\n";
292a34c753fSRafael Auler return;
293a34c753fSRafael Auler }
294a34c753fSRafael Auler
295d914486aSAmir Ayupov FileBuf = std::move(*MB);
296a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer();
297a34c753fSRafael Auler
298a34c753fSRafael Auler Optional<StringRef> FileName = getFileNameForBuildID(FileBuildID);
299a34c753fSRafael Auler if (!FileName) {
300*661577b5SMaksim Panchenko if (hasAllBuildIDs()) {
301a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: failed to match build-id from perf output. "
302a34c753fSRafael Auler "This indicates the input binary supplied for data aggregation "
303a34c753fSRafael Auler "is not the same recorded by perf when collecting profiling "
304a34c753fSRafael Auler "data, or there were no samples recorded for the binary. "
305a34c753fSRafael Auler "Use -ignore-build-id option to override.\n";
306def464aaSAmir Ayupov if (!opts::IgnoreBuildID)
307a34c753fSRafael Auler abort();
308*661577b5SMaksim Panchenko } else {
309*661577b5SMaksim Panchenko errs() << "PERF2BOLT-WARNING: build-id will not be checked because perf "
310*661577b5SMaksim Panchenko "data was recorded without it\n";
311*661577b5SMaksim Panchenko return;
312*661577b5SMaksim Panchenko }
313a34c753fSRafael Auler } else if (*FileName != llvm::sys::path::filename(BC->getFilename())) {
314a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: build-id matched a different file name\n";
315a34c753fSRafael Auler BuildIDBinaryName = std::string(*FileName);
316a34c753fSRafael Auler } else {
317a34c753fSRafael Auler outs() << "PERF2BOLT: matched build-id and file name\n";
318a34c753fSRafael Auler }
319a34c753fSRafael Auler
320a34c753fSRafael Auler return;
321a34c753fSRafael Auler }
322a34c753fSRafael Auler
checkPerfDataMagic(StringRef FileName)323a34c753fSRafael Auler bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
324a34c753fSRafael Auler if (opts::ReadPreAggregated)
325a34c753fSRafael Auler return true;
326a34c753fSRafael Auler
327ae585be1SRafael Auler Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
328ae585be1SRafael Auler if (!FD)
329a34c753fSRafael Auler return false;
330a34c753fSRafael Auler
331a34c753fSRafael Auler char Buf[7] = {0, 0, 0, 0, 0, 0, 0};
332a34c753fSRafael Auler
333ae585be1SRafael Auler auto Close = make_scope_exit([&] { sys::fs::closeFile(*FD); });
334ae585be1SRafael Auler Expected<size_t> BytesRead = sys::fs::readNativeFileSlice(
335ae585be1SRafael Auler *FD, makeMutableArrayRef(Buf, sizeof(Buf)), 0);
336ae585be1SRafael Auler if (!BytesRead || *BytesRead != 7)
337a34c753fSRafael Auler return false;
338a34c753fSRafael Auler
339a34c753fSRafael Auler if (strncmp(Buf, "PERFILE", 7) == 0)
340a34c753fSRafael Auler return true;
341a34c753fSRafael Auler return false;
342a34c753fSRafael Auler }
343a34c753fSRafael Auler
parsePreAggregated()344a34c753fSRafael Auler void DataAggregator::parsePreAggregated() {
345a34c753fSRafael Auler std::string Error;
346a34c753fSRafael Auler
347a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
348a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Filename);
349a34c753fSRafael Auler if (std::error_code EC = MB.getError()) {
350a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": "
351a34c753fSRafael Auler << EC.message() << "\n";
352a34c753fSRafael Auler exit(1);
353a34c753fSRafael Auler }
354a34c753fSRafael Auler
355d914486aSAmir Ayupov FileBuf = std::move(*MB);
356a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer();
357a34c753fSRafael Auler Col = 0;
358a34c753fSRafael Auler Line = 1;
359a34c753fSRafael Auler if (parsePreAggregatedLBRSamples()) {
360a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n";
361a34c753fSRafael Auler exit(1);
362a34c753fSRafael Auler }
363a34c753fSRafael Auler }
364a34c753fSRafael Auler
writeAutoFDOData(StringRef OutputFilename)365a34c753fSRafael Auler std::error_code DataAggregator::writeAutoFDOData(StringRef OutputFilename) {
366a34c753fSRafael Auler outs() << "PERF2BOLT: writing data for autofdo tools...\n";
36740c2e0faSMaksim Panchenko NamedRegionTimer T("writeAutoFDO", "Processing branch events", TimerGroupName,
36840c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator);
369a34c753fSRafael Auler
370a34c753fSRafael Auler std::error_code EC;
371a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
372a34c753fSRafael Auler if (EC)
373a34c753fSRafael Auler return EC;
374a34c753fSRafael Auler
375a34c753fSRafael Auler // Format:
376a34c753fSRafael Auler // number of unique traces
377a34c753fSRafael Auler // from_1-to_1:count_1
378a34c753fSRafael Auler // from_2-to_2:count_2
379a34c753fSRafael Auler // ......
380a34c753fSRafael Auler // from_n-to_n:count_n
381a34c753fSRafael Auler // number of unique sample addresses
382a34c753fSRafael Auler // addr_1:count_1
383a34c753fSRafael Auler // addr_2:count_2
384a34c753fSRafael Auler // ......
385a34c753fSRafael Auler // addr_n:count_n
386a34c753fSRafael Auler // number of unique LBR entries
387a34c753fSRafael Auler // src_1->dst_1:count_1
388a34c753fSRafael Auler // src_2->dst_2:count_2
389a34c753fSRafael Auler // ......
390a34c753fSRafael Auler // src_n->dst_n:count_n
391a34c753fSRafael Auler
392a34c753fSRafael Auler const uint64_t FirstAllocAddress = this->BC->FirstAllocAddress;
393a34c753fSRafael Auler
394a34c753fSRafael Auler // AutoFDO addresses are relative to the first allocated loadable program
395a34c753fSRafael Auler // segment
396a34c753fSRafael Auler auto filterAddress = [&FirstAllocAddress](uint64_t Address) -> uint64_t {
397a34c753fSRafael Auler if (Address < FirstAllocAddress)
398a34c753fSRafael Auler return 0;
399a34c753fSRafael Auler return Address - FirstAllocAddress;
400a34c753fSRafael Auler };
401a34c753fSRafael Auler
402a34c753fSRafael Auler OutFile << FallthroughLBRs.size() << "\n";
403a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) {
404a34c753fSRafael Auler const Trace &Trace = AggrLBR.first;
405a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second;
406a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "-"
407a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":"
408a34c753fSRafael Auler << (Info.InternCount + Info.ExternCount) << "\n";
409a34c753fSRafael Auler }
410a34c753fSRafael Auler
411a34c753fSRafael Auler OutFile << BasicSamples.size() << "\n";
412a34c753fSRafael Auler for (const auto &Sample : BasicSamples) {
413a34c753fSRafael Auler uint64_t PC = Sample.first;
414a34c753fSRafael Auler uint64_t HitCount = Sample.second;
415a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(PC)) << ":" << HitCount << "\n";
416a34c753fSRafael Auler }
417a34c753fSRafael Auler
418a34c753fSRafael Auler OutFile << BranchLBRs.size() << "\n";
419a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) {
420a34c753fSRafael Auler const Trace &Trace = AggrLBR.first;
421a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second;
422a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "->"
423a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":"
424a34c753fSRafael Auler << Info.TakenCount << "\n";
425a34c753fSRafael Auler }
426a34c753fSRafael Auler
427a34c753fSRafael Auler outs() << "PERF2BOLT: wrote " << FallthroughLBRs.size() << " unique traces, "
428a34c753fSRafael Auler << BasicSamples.size() << " sample addresses and " << BranchLBRs.size()
429a34c753fSRafael Auler << " unique branches to " << OutputFilename << "\n";
430a34c753fSRafael Auler
431a34c753fSRafael Auler return std::error_code();
432a34c753fSRafael Auler }
433a34c753fSRafael Auler
filterBinaryMMapInfo()434a34c753fSRafael Auler void DataAggregator::filterBinaryMMapInfo() {
435a34c753fSRafael Auler if (opts::FilterPID) {
436a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(opts::FilterPID);
437a34c753fSRafael Auler if (MMapInfoIter != BinaryMMapInfo.end()) {
438a34c753fSRafael Auler MMapInfo MMap = MMapInfoIter->second;
439a34c753fSRafael Auler BinaryMMapInfo.clear();
440a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMap.PID, MMap));
441a34c753fSRafael Auler } else {
442a34c753fSRafael Auler if (errs().has_colors())
443a34c753fSRafael Auler errs().changeColor(raw_ostream::RED);
444a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching PID \""
44540c2e0faSMaksim Panchenko << opts::FilterPID << "\""
44640c2e0faSMaksim Panchenko << " for binary \"" << BC->getFilename() << "\".";
447a34c753fSRafael Auler assert(!BinaryMMapInfo.empty() && "No memory map for matching binary");
448a34c753fSRafael Auler errs() << " Profile for the following process is available:\n";
449def464aaSAmir Ayupov for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo)
450a34c753fSRafael Auler outs() << " " << MMI.second.PID
451a34c753fSRafael Auler << (MMI.second.Forked ? " (forked)\n" : "\n");
452def464aaSAmir Ayupov
453a34c753fSRafael Auler if (errs().has_colors())
454a34c753fSRafael Auler errs().resetColor();
455a34c753fSRafael Auler
456a34c753fSRafael Auler exit(1);
457a34c753fSRafael Auler }
458a34c753fSRafael Auler }
459a34c753fSRafael Auler }
460a34c753fSRafael Auler
preprocessProfile(BinaryContext & BC)461a34c753fSRafael Auler Error DataAggregator::preprocessProfile(BinaryContext &BC) {
462a34c753fSRafael Auler this->BC = &BC;
463a34c753fSRafael Auler
464a34c753fSRafael Auler if (opts::ReadPreAggregated) {
465a34c753fSRafael Auler parsePreAggregated();
466a34c753fSRafael Auler return Error::success();
467a34c753fSRafael Auler }
468a34c753fSRafael Auler
469a34c753fSRafael Auler if (Optional<StringRef> FileBuildID = BC.getFileBuildID()) {
470a34c753fSRafael Auler outs() << "BOLT-INFO: binary build-id is: " << *FileBuildID << "\n";
471a34c753fSRafael Auler processFileBuildID(*FileBuildID);
472a34c753fSRafael Auler } else {
473a34c753fSRafael Auler errs() << "BOLT-WARNING: build-id will not be checked because we could "
474a34c753fSRafael Auler "not read one from input binary\n";
475a34c753fSRafael Auler }
476a34c753fSRafael Auler
477a34c753fSRafael Auler auto prepareToParse = [&](StringRef Name, PerfProcessInfo &Process) {
478a34c753fSRafael Auler std::string Error;
479a34c753fSRafael Auler outs() << "PERF2BOLT: waiting for perf " << Name
480a34c753fSRafael Auler << " collection to finish...\n";
481a34c753fSRafael Auler sys::ProcessInfo PI = sys::Wait(Process.PI, 0, true, &Error);
482a34c753fSRafael Auler
483a34c753fSRafael Auler if (!Error.empty()) {
484a34c753fSRafael Auler errs() << "PERF-ERROR: " << PerfPath << ": " << Error << "\n";
485a34c753fSRafael Auler deleteTempFiles();
486a34c753fSRafael Auler exit(1);
487a34c753fSRafael Auler }
488a34c753fSRafael Auler
489a34c753fSRafael Auler if (PI.ReturnCode != 0) {
490a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorMB =
491a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StderrPath.data());
492a34c753fSRafael Auler StringRef ErrBuf = (*ErrorMB)->getBuffer();
493a34c753fSRafael Auler
494a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << PI.ReturnCode << "\n";
495a34c753fSRafael Auler errs() << ErrBuf;
496a34c753fSRafael Auler deleteTempFiles();
497a34c753fSRafael Auler exit(1);
498a34c753fSRafael Auler }
499a34c753fSRafael Auler
500a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
501a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StdoutPath.data());
502a34c753fSRafael Auler if (std::error_code EC = MB.getError()) {
503a34c753fSRafael Auler errs() << "Cannot open " << Process.StdoutPath.data() << ": "
504a34c753fSRafael Auler << EC.message() << "\n";
505a34c753fSRafael Auler deleteTempFiles();
506a34c753fSRafael Auler exit(1);
507a34c753fSRafael Auler }
508a34c753fSRafael Auler
509d914486aSAmir Ayupov FileBuf = std::move(*MB);
510a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer();
511a34c753fSRafael Auler Col = 0;
512a34c753fSRafael Auler Line = 1;
513a34c753fSRafael Auler };
514a34c753fSRafael Auler
515a34c753fSRafael Auler if (opts::LinuxKernelMode) {
516a34c753fSRafael Auler // Current MMap parsing logic does not work with linux kernel.
517a34c753fSRafael Auler // MMap entries for linux kernel uses PERF_RECORD_MMAP
518a34c753fSRafael Auler // format instead of typical PERF_RECORD_MMAP2 format.
519a34c753fSRafael Auler // Since linux kernel address mapping is absolute (same as
520a34c753fSRafael Auler // in the ELF file), we avoid parsing MMap in linux kernel mode.
521a34c753fSRafael Auler // While generating optimized linux kernel binary, we may need
522a34c753fSRafael Auler // to parse MMap entries.
523a34c753fSRafael Auler
524a34c753fSRafael Auler // In linux kernel mode, we analyze and optimize
525a34c753fSRafael Auler // all linux kernel binary instructions, irrespective
526a34c753fSRafael Auler // of whether they are due to system calls or due to
527a34c753fSRafael Auler // interrupts. Therefore, we cannot ignore interrupt
528a34c753fSRafael Auler // in Linux kernel mode.
529a34c753fSRafael Auler opts::IgnoreInterruptLBR = false;
530a34c753fSRafael Auler } else {
531a34c753fSRafael Auler prepareToParse("mmap events", MMapEventsPPI);
532def464aaSAmir Ayupov if (parseMMapEvents())
533a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse mmap events\n";
534a34c753fSRafael Auler }
535a34c753fSRafael Auler
536a34c753fSRafael Auler prepareToParse("task events", TaskEventsPPI);
537def464aaSAmir Ayupov if (parseTaskEvents())
538a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse task events\n";
539a34c753fSRafael Auler
540a34c753fSRafael Auler filterBinaryMMapInfo();
541a34c753fSRafael Auler prepareToParse("events", MainEventsPPI);
542a34c753fSRafael Auler
543a34c753fSRafael Auler if (opts::HeatmapMode) {
544a34c753fSRafael Auler if (std::error_code EC = printLBRHeatMap()) {
545a34c753fSRafael Auler errs() << "ERROR: failed to print heat map: " << EC.message() << '\n';
546a34c753fSRafael Auler exit(1);
547a34c753fSRafael Auler }
548a34c753fSRafael Auler exit(0);
549a34c753fSRafael Auler }
550a34c753fSRafael Auler
551a34c753fSRafael Auler if ((!opts::BasicAggregation && parseBranchEvents()) ||
552def464aaSAmir Ayupov (opts::BasicAggregation && parseBasicEvents()))
553a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n";
554a34c753fSRafael Auler
555a34c753fSRafael Auler // We can finish early if the goal is just to generate data for autofdo
556a34c753fSRafael Auler if (opts::WriteAutoFDOData) {
557def464aaSAmir Ayupov if (std::error_code EC = writeAutoFDOData(opts::OutputFilename))
558a34c753fSRafael Auler errs() << "Error writing autofdo data to file: " << EC.message() << "\n";
559def464aaSAmir Ayupov
560a34c753fSRafael Auler deleteTempFiles();
561a34c753fSRafael Auler exit(0);
562a34c753fSRafael Auler }
563a34c753fSRafael Auler
564a34c753fSRafael Auler // Special handling for memory events
565a34c753fSRafael Auler std::string Error;
566a34c753fSRafael Auler sys::ProcessInfo PI = sys::Wait(MemEventsPPI.PI, 0, true, &Error);
567a34c753fSRafael Auler if (PI.ReturnCode != 0) {
568a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
569a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(MemEventsPPI.StderrPath.data());
570a34c753fSRafael Auler StringRef ErrBuf = (*MB)->getBuffer();
571a34c753fSRafael Auler
572a34c753fSRafael Auler deleteTempFiles();
573a34c753fSRafael Auler
574a34c753fSRafael Auler Regex NoData("Samples for '.*' event do not have ADDR attribute set. "
575a34c753fSRafael Auler "Cannot print 'addr' field.");
576a34c753fSRafael Auler if (!NoData.match(ErrBuf)) {
577a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << PI.ReturnCode << "\n";
578a34c753fSRafael Auler errs() << ErrBuf;
579a34c753fSRafael Auler exit(1);
580a34c753fSRafael Auler }
581a34c753fSRafael Auler return Error::success();
582a34c753fSRafael Auler }
583a34c753fSRafael Auler
584a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
585a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(MemEventsPPI.StdoutPath.data());
586a34c753fSRafael Auler if (std::error_code EC = MB.getError()) {
587a34c753fSRafael Auler errs() << "Cannot open " << MemEventsPPI.StdoutPath.data() << ": "
588a34c753fSRafael Auler << EC.message() << "\n";
589a34c753fSRafael Auler deleteTempFiles();
590a34c753fSRafael Auler exit(1);
591a34c753fSRafael Auler }
592a34c753fSRafael Auler
593d914486aSAmir Ayupov FileBuf = std::move(*MB);
594a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer();
595a34c753fSRafael Auler Col = 0;
596a34c753fSRafael Auler Line = 1;
597def464aaSAmir Ayupov if (const std::error_code EC = parseMemEvents())
59840c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to parse memory events: " << EC.message()
59940c2e0faSMaksim Panchenko << '\n';
600a34c753fSRafael Auler
601a34c753fSRafael Auler deleteTempFiles();
602a34c753fSRafael Auler
603a34c753fSRafael Auler return Error::success();
604a34c753fSRafael Auler }
605a34c753fSRafael Auler
readProfile(BinaryContext & BC)606a34c753fSRafael Auler Error DataAggregator::readProfile(BinaryContext &BC) {
607a34c753fSRafael Auler processProfile(BC);
608a34c753fSRafael Auler
609a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
610a34c753fSRafael Auler BinaryFunction &Function = BFI.second;
611a34c753fSRafael Auler convertBranchData(Function);
612a34c753fSRafael Auler }
613a34c753fSRafael Auler
614a34c753fSRafael Auler if (opts::AggregateOnly) {
615def464aaSAmir Ayupov if (std::error_code EC = writeAggregatedFile(opts::OutputFilename))
616a34c753fSRafael Auler report_error("cannot create output data file", EC);
617a34c753fSRafael Auler }
618a34c753fSRafael Auler
619a34c753fSRafael Auler return Error::success();
620a34c753fSRafael Auler }
621a34c753fSRafael Auler
mayHaveProfileData(const BinaryFunction & Function)622a34c753fSRafael Auler bool DataAggregator::mayHaveProfileData(const BinaryFunction &Function) {
623a34c753fSRafael Auler return Function.hasProfileAvailable();
624a34c753fSRafael Auler }
625a34c753fSRafael Auler
processProfile(BinaryContext & BC)626a34c753fSRafael Auler void DataAggregator::processProfile(BinaryContext &BC) {
627a34c753fSRafael Auler if (opts::ReadPreAggregated)
628a34c753fSRafael Auler processPreAggregated();
629a34c753fSRafael Auler else if (opts::BasicAggregation)
630a34c753fSRafael Auler processBasicEvents();
631a34c753fSRafael Auler else
632a34c753fSRafael Auler processBranchEvents();
633a34c753fSRafael Auler
634a34c753fSRafael Auler processMemEvents();
635a34c753fSRafael Auler
636a34c753fSRafael Auler // Mark all functions with registered events as having a valid profile.
637a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
638a34c753fSRafael Auler BinaryFunction &BF = BFI.second;
639a34c753fSRafael Auler if (getBranchData(BF)) {
640a34c753fSRafael Auler const auto Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE
641a34c753fSRafael Auler : BinaryFunction::PF_LBR;
642a34c753fSRafael Auler BF.markProfiled(Flags);
643a34c753fSRafael Auler }
644a34c753fSRafael Auler }
645a34c753fSRafael Auler
646a34c753fSRafael Auler // Release intermediate storage.
647a34c753fSRafael Auler clear(BranchLBRs);
648a34c753fSRafael Auler clear(FallthroughLBRs);
649a34c753fSRafael Auler clear(AggregatedLBRs);
650a34c753fSRafael Auler clear(BasicSamples);
651a34c753fSRafael Auler clear(MemSamples);
652a34c753fSRafael Auler }
653a34c753fSRafael Auler
654a34c753fSRafael Auler BinaryFunction *
getBinaryFunctionContainingAddress(uint64_t Address) const655a34c753fSRafael Auler DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const {
656a34c753fSRafael Auler if (!BC->containsAddress(Address))
657a34c753fSRafael Auler return nullptr;
658a34c753fSRafael Auler
659a34c753fSRafael Auler return BC->getBinaryFunctionContainingAddress(Address, /*CheckPastEnd=*/false,
660a34c753fSRafael Auler /*UseMaxSize=*/true);
661a34c753fSRafael Auler }
662a34c753fSRafael Auler
getLocationName(BinaryFunction & Func,uint64_t Count)663a34c753fSRafael Auler StringRef DataAggregator::getLocationName(BinaryFunction &Func,
664a34c753fSRafael Auler uint64_t Count) {
665a34c753fSRafael Auler if (!BAT)
666a34c753fSRafael Auler return Func.getOneName();
667a34c753fSRafael Auler
668a34c753fSRafael Auler const BinaryFunction *OrigFunc = &Func;
669a34c753fSRafael Auler if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress())) {
670a34c753fSRafael Auler NumColdSamples += Count;
671a34c753fSRafael Auler BinaryFunction *HotFunc = getBinaryFunctionContainingAddress(HotAddr);
672a34c753fSRafael Auler if (HotFunc)
673a34c753fSRafael Auler OrigFunc = HotFunc;
674a34c753fSRafael Auler }
675a34c753fSRafael Auler // If it is a local function, prefer the name containing the file name where
676a34c753fSRafael Auler // the local function was declared
677a34c753fSRafael Auler for (StringRef AlternativeName : OrigFunc->getNames()) {
678a34c753fSRafael Auler size_t FileNameIdx = AlternativeName.find('/');
679a34c753fSRafael Auler // Confirm the alternative name has the pattern Symbol/FileName/1 before
680a34c753fSRafael Auler // using it
681a34c753fSRafael Auler if (FileNameIdx == StringRef::npos ||
682a34c753fSRafael Auler AlternativeName.find('/', FileNameIdx + 1) == StringRef::npos)
683a34c753fSRafael Auler continue;
684a34c753fSRafael Auler return AlternativeName;
685a34c753fSRafael Auler }
686a34c753fSRafael Auler return OrigFunc->getOneName();
687a34c753fSRafael Auler }
688a34c753fSRafael Auler
doSample(BinaryFunction & Func,uint64_t Address,uint64_t Count)689a34c753fSRafael Auler bool DataAggregator::doSample(BinaryFunction &Func, uint64_t Address,
690a34c753fSRafael Auler uint64_t Count) {
691a34c753fSRafael Auler auto I = NamesToSamples.find(Func.getOneName());
692a34c753fSRafael Auler if (I == NamesToSamples.end()) {
693a34c753fSRafael Auler bool Success;
694a34c753fSRafael Auler StringRef LocName = getLocationName(Func, Count);
69540c2e0faSMaksim Panchenko std::tie(I, Success) = NamesToSamples.insert(
69640c2e0faSMaksim Panchenko std::make_pair(Func.getOneName(),
697a34c753fSRafael Auler FuncSampleData(LocName, FuncSampleData::ContainerTy())));
698a34c753fSRafael Auler }
699a34c753fSRafael Auler
700a34c753fSRafael Auler Address -= Func.getAddress();
701a34c753fSRafael Auler if (BAT)
702a34c753fSRafael Auler Address = BAT->translate(Func, Address, /*IsBranchSrc=*/false);
703a34c753fSRafael Auler
704a34c753fSRafael Auler I->second.bumpCount(Address, Count);
705a34c753fSRafael Auler return true;
706a34c753fSRafael Auler }
707a34c753fSRafael Auler
doIntraBranch(BinaryFunction & Func,uint64_t From,uint64_t To,uint64_t Count,uint64_t Mispreds)708a34c753fSRafael Auler bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From,
709a34c753fSRafael Auler uint64_t To, uint64_t Count,
710a34c753fSRafael Auler uint64_t Mispreds) {
711a34c753fSRafael Auler FuncBranchData *AggrData = getBranchData(Func);
712a34c753fSRafael Auler if (!AggrData) {
713a34c753fSRafael Auler AggrData = &NamesToBranches[Func.getOneName()];
714a34c753fSRafael Auler AggrData->Name = getLocationName(Func, Count);
715a34c753fSRafael Auler setBranchData(Func, AggrData);
716a34c753fSRafael Auler }
717a34c753fSRafael Auler
718a34c753fSRafael Auler From -= Func.getAddress();
719a34c753fSRafael Auler To -= Func.getAddress();
720a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: bumpBranchCount: " << Func.getPrintName()
721a34c753fSRafael Auler << " @ " << Twine::utohexstr(From) << " -> "
722a34c753fSRafael Auler << Func.getPrintName() << " @ " << Twine::utohexstr(To)
723a34c753fSRafael Auler << '\n');
724a34c753fSRafael Auler if (BAT) {
725a34c753fSRafael Auler From = BAT->translate(Func, From, /*IsBranchSrc=*/true);
726a34c753fSRafael Auler To = BAT->translate(Func, To, /*IsBranchSrc=*/false);
727a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: BAT translation on bumpBranchCount: "
728a34c753fSRafael Auler << Func.getPrintName() << " @ " << Twine::utohexstr(From)
729a34c753fSRafael Auler << " -> " << Func.getPrintName() << " @ "
730a34c753fSRafael Auler << Twine::utohexstr(To) << '\n');
731a34c753fSRafael Auler }
732a34c753fSRafael Auler
733a34c753fSRafael Auler AggrData->bumpBranchCount(From, To, Count, Mispreds);
734a34c753fSRafael Auler return true;
735a34c753fSRafael Auler }
736a34c753fSRafael Auler
doInterBranch(BinaryFunction * FromFunc,BinaryFunction * ToFunc,uint64_t From,uint64_t To,uint64_t Count,uint64_t Mispreds)737a34c753fSRafael Auler bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
738a34c753fSRafael Auler BinaryFunction *ToFunc, uint64_t From,
739a34c753fSRafael Auler uint64_t To, uint64_t Count,
740a34c753fSRafael Auler uint64_t Mispreds) {
741a34c753fSRafael Auler FuncBranchData *FromAggrData = nullptr;
742a34c753fSRafael Auler FuncBranchData *ToAggrData = nullptr;
743a34c753fSRafael Auler StringRef SrcFunc;
744a34c753fSRafael Auler StringRef DstFunc;
745a34c753fSRafael Auler if (FromFunc) {
746a34c753fSRafael Auler SrcFunc = getLocationName(*FromFunc, Count);
747a34c753fSRafael Auler FromAggrData = getBranchData(*FromFunc);
748a34c753fSRafael Auler if (!FromAggrData) {
749a34c753fSRafael Auler FromAggrData = &NamesToBranches[FromFunc->getOneName()];
750a34c753fSRafael Auler FromAggrData->Name = SrcFunc;
751a34c753fSRafael Auler setBranchData(*FromFunc, FromAggrData);
752a34c753fSRafael Auler }
753a34c753fSRafael Auler From -= FromFunc->getAddress();
754a34c753fSRafael Auler if (BAT)
755a34c753fSRafael Auler From = BAT->translate(*FromFunc, From, /*IsBranchSrc=*/true);
756a34c753fSRafael Auler
757a34c753fSRafael Auler recordExit(*FromFunc, From, Mispreds, Count);
758a34c753fSRafael Auler }
759a34c753fSRafael Auler if (ToFunc) {
760a34c753fSRafael Auler DstFunc = getLocationName(*ToFunc, 0);
761a34c753fSRafael Auler ToAggrData = getBranchData(*ToFunc);
762a34c753fSRafael Auler if (!ToAggrData) {
763a34c753fSRafael Auler ToAggrData = &NamesToBranches[ToFunc->getOneName()];
764a34c753fSRafael Auler ToAggrData->Name = DstFunc;
765a34c753fSRafael Auler setBranchData(*ToFunc, ToAggrData);
766a34c753fSRafael Auler }
767a34c753fSRafael Auler To -= ToFunc->getAddress();
768a34c753fSRafael Auler if (BAT)
769a34c753fSRafael Auler To = BAT->translate(*ToFunc, To, /*IsBranchSrc=*/false);
770a34c753fSRafael Auler
771a34c753fSRafael Auler recordEntry(*ToFunc, To, Mispreds, Count);
772a34c753fSRafael Auler }
773a34c753fSRafael Auler
774a34c753fSRafael Auler if (FromAggrData)
775a34c753fSRafael Auler FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To),
776a34c753fSRafael Auler Count, Mispreds);
777a34c753fSRafael Auler if (ToAggrData)
778a34c753fSRafael Auler ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To,
779a34c753fSRafael Auler Count, Mispreds);
780a34c753fSRafael Auler return true;
781a34c753fSRafael Auler }
782a34c753fSRafael Auler
doBranch(uint64_t From,uint64_t To,uint64_t Count,uint64_t Mispreds)783a34c753fSRafael Auler bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
784a34c753fSRafael Auler uint64_t Mispreds) {
785a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(From);
786a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(To);
787a34c753fSRafael Auler if (!FromFunc && !ToFunc)
788a34c753fSRafael Auler return false;
789a34c753fSRafael Auler
790a34c753fSRafael Auler if (FromFunc == ToFunc) {
791a34c753fSRafael Auler recordBranch(*FromFunc, From - FromFunc->getAddress(),
792a34c753fSRafael Auler To - FromFunc->getAddress(), Count, Mispreds);
793a34c753fSRafael Auler return doIntraBranch(*FromFunc, From, To, Count, Mispreds);
794a34c753fSRafael Auler }
795a34c753fSRafael Auler
796a34c753fSRafael Auler return doInterBranch(FromFunc, ToFunc, From, To, Count, Mispreds);
797a34c753fSRafael Auler }
798a34c753fSRafael Auler
doTrace(const LBREntry & First,const LBREntry & Second,uint64_t Count)799a34c753fSRafael Auler bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
800a34c753fSRafael Auler uint64_t Count) {
801a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(First.To);
802a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From);
803a34c753fSRafael Auler if (!FromFunc || !ToFunc) {
804a34c753fSRafael Auler LLVM_DEBUG(
805a34c753fSRafael Auler dbgs() << "Out of range trace starting in " << FromFunc->getPrintName()
806a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress())
807a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ "
808a34c753fSRafael Auler << ToFunc->getPrintName() << " @ "
809a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n');
810a34c753fSRafael Auler NumLongRangeTraces += Count;
811a34c753fSRafael Auler return false;
812a34c753fSRafael Auler }
813a34c753fSRafael Auler if (FromFunc != ToFunc) {
814a34c753fSRafael Auler NumInvalidTraces += Count;
815a34c753fSRafael Auler LLVM_DEBUG(
816a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName()
817a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress())
818a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ "
819a34c753fSRafael Auler << ToFunc->getPrintName() << " @ "
820a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n');
821a34c753fSRafael Auler return false;
822a34c753fSRafael Auler }
823a34c753fSRafael Auler
824a34c753fSRafael Auler Optional<BoltAddressTranslation::FallthroughListTy> FTs =
825a34c753fSRafael Auler BAT ? BAT->getFallthroughsInTrace(*FromFunc, First.To, Second.From)
826a34c753fSRafael Auler : getFallthroughsInTrace(*FromFunc, First, Second, Count);
827a34c753fSRafael Auler if (!FTs) {
828a34c753fSRafael Auler LLVM_DEBUG(
829a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName()
830a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress())
831a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ "
832a34c753fSRafael Auler << ToFunc->getPrintName() << " @ "
833a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n');
834a34c753fSRafael Auler NumInvalidTraces += Count;
835a34c753fSRafael Auler return false;
836a34c753fSRafael Auler }
837a34c753fSRafael Auler
838a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Processing " << FTs->size() << " fallthroughs for "
839a34c753fSRafael Auler << FromFunc->getPrintName() << ":"
840a34c753fSRafael Auler << Twine::utohexstr(First.To) << " to "
841a34c753fSRafael Auler << Twine::utohexstr(Second.From) << ".\n");
842def464aaSAmir Ayupov for (const std::pair<uint64_t, uint64_t> &Pair : *FTs)
843a34c753fSRafael Auler doIntraBranch(*FromFunc, Pair.first + FromFunc->getAddress(),
844a34c753fSRafael Auler Pair.second + FromFunc->getAddress(), Count, false);
845a34c753fSRafael Auler
846a34c753fSRafael Auler return true;
847a34c753fSRafael Auler }
848a34c753fSRafael Auler
recordTrace(BinaryFunction & BF,const LBREntry & FirstLBR,const LBREntry & SecondLBR,uint64_t Count,SmallVector<std::pair<uint64_t,uint64_t>,16> * Branches) const849a34c753fSRafael Auler bool DataAggregator::recordTrace(
850a34c753fSRafael Auler BinaryFunction &BF,
851a34c753fSRafael Auler const LBREntry &FirstLBR,
852a34c753fSRafael Auler const LBREntry &SecondLBR,
853a34c753fSRafael Auler uint64_t Count,
854a34c753fSRafael Auler SmallVector<std::pair<uint64_t, uint64_t>, 16> *Branches) const {
855a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext();
856a34c753fSRafael Auler
857a34c753fSRafael Auler if (!BF.isSimple())
858a34c753fSRafael Auler return false;
859a34c753fSRafael Auler
860a34c753fSRafael Auler assert(BF.hasCFG() && "can only record traces in CFG state");
861a34c753fSRafael Auler
862a34c753fSRafael Auler // Offsets of the trace within this function.
863a34c753fSRafael Auler const uint64_t From = FirstLBR.To - BF.getAddress();
864a34c753fSRafael Auler const uint64_t To = SecondLBR.From - BF.getAddress();
865a34c753fSRafael Auler
866a34c753fSRafael Auler if (From > To)
867a34c753fSRafael Auler return false;
868a34c753fSRafael Auler
869a34c753fSRafael Auler BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From);
870a34c753fSRafael Auler BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To);
871a34c753fSRafael Auler
872a34c753fSRafael Auler if (!FromBB || !ToBB)
873a34c753fSRafael Auler return false;
874a34c753fSRafael Auler
875a34c753fSRafael Auler // Adjust FromBB if the first LBR is a return from the last instruction in
876a34c753fSRafael Auler // the previous block (that instruction should be a call).
877a34c753fSRafael Auler if (From == FromBB->getOffset() && !BF.containsAddress(FirstLBR.From) &&
878a34c753fSRafael Auler !FromBB->isEntryPoint() && !FromBB->isLandingPad()) {
8798477bc67SFabian Parzefall BinaryBasicBlock *PrevBB = BF.getLayout().getBlock(FromBB->getIndex() - 1);
880a34c753fSRafael Auler if (PrevBB->getSuccessor(FromBB->getLabel())) {
881a34c753fSRafael Auler const MCInst *Instr = PrevBB->getLastNonPseudoInstr();
882def464aaSAmir Ayupov if (Instr && BC.MIB->isCall(*Instr))
883a34c753fSRafael Auler FromBB = PrevBB;
884def464aaSAmir Ayupov else
885a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR (no call): " << FirstLBR
886a34c753fSRafael Auler << '\n');
887a34c753fSRafael Auler } else {
888a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR: " << FirstLBR << '\n');
889a34c753fSRafael Auler }
890a34c753fSRafael Auler }
891a34c753fSRafael Auler
892a34c753fSRafael Auler // Fill out information for fall-through edges. The From and To could be
893a34c753fSRafael Auler // within the same basic block, e.g. when two call instructions are in the
894a34c753fSRafael Auler // same block. In this case we skip the processing.
895def464aaSAmir Ayupov if (FromBB == ToBB)
896a34c753fSRafael Auler return true;
897a34c753fSRafael Auler
898a34c753fSRafael Auler // Process blocks in the original layout order.
8998477bc67SFabian Parzefall BinaryBasicBlock *BB = BF.getLayout().getBlock(FromBB->getIndex());
900a34c753fSRafael Auler assert(BB == FromBB && "index mismatch");
901a34c753fSRafael Auler while (BB != ToBB) {
9028477bc67SFabian Parzefall BinaryBasicBlock *NextBB = BF.getLayout().getBlock(BB->getIndex() + 1);
903a34c753fSRafael Auler assert((NextBB && NextBB->getOffset() > BB->getOffset()) && "bad layout");
904a34c753fSRafael Auler
905a34c753fSRafael Auler // Check for bad LBRs.
906a34c753fSRafael Auler if (!BB->getSuccessor(NextBB->getLabel())) {
907a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "no fall-through for the trace:\n"
908a34c753fSRafael Auler << " " << FirstLBR << '\n'
909a34c753fSRafael Auler << " " << SecondLBR << '\n');
910a34c753fSRafael Auler return false;
911a34c753fSRafael Auler }
912a34c753fSRafael Auler
913a34c753fSRafael Auler // Record fall-through jumps
914a34c753fSRafael Auler BinaryBasicBlock::BinaryBranchInfo &BI = BB->getBranchInfo(*NextBB);
915a34c753fSRafael Auler BI.Count += Count;
916a34c753fSRafael Auler
917a34c753fSRafael Auler if (Branches) {
918a34c753fSRafael Auler const MCInst *Instr = BB->getLastNonPseudoInstr();
919a34c753fSRafael Auler uint64_t Offset = 0;
920def464aaSAmir Ayupov if (Instr)
921a9cd49d5SAmir Ayupov Offset = BC.MIB->getOffsetWithDefault(*Instr, 0);
922def464aaSAmir Ayupov else
923a34c753fSRafael Auler Offset = BB->getOffset();
924def464aaSAmir Ayupov
925a34c753fSRafael Auler Branches->emplace_back(Offset, NextBB->getOffset());
926a34c753fSRafael Auler }
927a34c753fSRafael Auler
928a34c753fSRafael Auler BB = NextBB;
929a34c753fSRafael Auler }
930a34c753fSRafael Auler
931a34c753fSRafael Auler return true;
932a34c753fSRafael Auler }
933a34c753fSRafael Auler
934a34c753fSRafael Auler Optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
getFallthroughsInTrace(BinaryFunction & BF,const LBREntry & FirstLBR,const LBREntry & SecondLBR,uint64_t Count) const935a34c753fSRafael Auler DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
936a34c753fSRafael Auler const LBREntry &FirstLBR,
937a34c753fSRafael Auler const LBREntry &SecondLBR,
938a34c753fSRafael Auler uint64_t Count) const {
939a34c753fSRafael Auler SmallVector<std::pair<uint64_t, uint64_t>, 16> Res;
940a34c753fSRafael Auler
941a34c753fSRafael Auler if (!recordTrace(BF, FirstLBR, SecondLBR, Count, &Res))
942a34c753fSRafael Auler return NoneType();
943a34c753fSRafael Auler
944a34c753fSRafael Auler return Res;
945a34c753fSRafael Auler }
946a34c753fSRafael Auler
recordEntry(BinaryFunction & BF,uint64_t To,bool Mispred,uint64_t Count) const947a34c753fSRafael Auler bool DataAggregator::recordEntry(BinaryFunction &BF, uint64_t To, bool Mispred,
948a34c753fSRafael Auler uint64_t Count) const {
949a34c753fSRafael Auler if (To > BF.getSize())
950a34c753fSRafael Auler return false;
951a34c753fSRafael Auler
952a34c753fSRafael Auler if (!BF.hasProfile())
953a34c753fSRafael Auler BF.ExecutionCount = 0;
954a34c753fSRafael Auler
955a34c753fSRafael Auler BinaryBasicBlock *EntryBB = nullptr;
956a34c753fSRafael Auler if (To == 0) {
957a34c753fSRafael Auler BF.ExecutionCount += Count;
958a34c753fSRafael Auler if (!BF.empty())
959a34c753fSRafael Auler EntryBB = &BF.front();
960a34c753fSRafael Auler } else if (BinaryBasicBlock *BB = BF.getBasicBlockAtOffset(To)) {
961a34c753fSRafael Auler if (BB->isEntryPoint())
962a34c753fSRafael Auler EntryBB = BB;
963a34c753fSRafael Auler }
964a34c753fSRafael Auler
965a34c753fSRafael Auler if (EntryBB)
966a34c753fSRafael Auler EntryBB->setExecutionCount(EntryBB->getKnownExecutionCount() + Count);
967a34c753fSRafael Auler
968a34c753fSRafael Auler return true;
969a34c753fSRafael Auler }
970a34c753fSRafael Auler
recordExit(BinaryFunction & BF,uint64_t From,bool Mispred,uint64_t Count) const97140c2e0faSMaksim Panchenko bool DataAggregator::recordExit(BinaryFunction &BF, uint64_t From, bool Mispred,
97240c2e0faSMaksim Panchenko uint64_t Count) const {
973a34c753fSRafael Auler if (!BF.isSimple() || From > BF.getSize())
974a34c753fSRafael Auler return false;
975a34c753fSRafael Auler
976a34c753fSRafael Auler if (!BF.hasProfile())
977a34c753fSRafael Auler BF.ExecutionCount = 0;
978a34c753fSRafael Auler
979a34c753fSRafael Auler return true;
980a34c753fSRafael Auler }
981a34c753fSRafael Auler
parseLBREntry()982a34c753fSRafael Auler ErrorOr<LBREntry> DataAggregator::parseLBREntry() {
983a34c753fSRafael Auler LBREntry Res;
984a34c753fSRafael Auler ErrorOr<StringRef> FromStrRes = parseString('/');
985a34c753fSRafael Auler if (std::error_code EC = FromStrRes.getError())
986a34c753fSRafael Auler return EC;
987a34c753fSRafael Auler StringRef OffsetStr = FromStrRes.get();
988a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.From)) {
989a34c753fSRafael Auler reportError("expected hexadecimal number with From address");
990a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n";
991a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
992a34c753fSRafael Auler }
993a34c753fSRafael Auler
994a34c753fSRafael Auler ErrorOr<StringRef> ToStrRes = parseString('/');
995a34c753fSRafael Auler if (std::error_code EC = ToStrRes.getError())
996a34c753fSRafael Auler return EC;
997a34c753fSRafael Auler OffsetStr = ToStrRes.get();
998a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.To)) {
999a34c753fSRafael Auler reportError("expected hexadecimal number with To address");
1000a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n";
1001a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1002a34c753fSRafael Auler }
1003a34c753fSRafael Auler
1004a34c753fSRafael Auler ErrorOr<StringRef> MispredStrRes = parseString('/');
1005a34c753fSRafael Auler if (std::error_code EC = MispredStrRes.getError())
1006a34c753fSRafael Auler return EC;
1007a34c753fSRafael Auler StringRef MispredStr = MispredStrRes.get();
1008a34c753fSRafael Auler if (MispredStr.size() != 1 ||
1009a34c753fSRafael Auler (MispredStr[0] != 'P' && MispredStr[0] != 'M' && MispredStr[0] != '-')) {
1010a34c753fSRafael Auler reportError("expected single char for mispred bit");
1011a34c753fSRafael Auler Diag << "Found: " << MispredStr << "\n";
1012a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1013a34c753fSRafael Auler }
1014a34c753fSRafael Auler Res.Mispred = MispredStr[0] == 'M';
1015a34c753fSRafael Auler
101640c2e0faSMaksim Panchenko static bool MispredWarning = true;
1017a34c753fSRafael Auler if (MispredStr[0] == '-' && MispredWarning) {
1018a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: misprediction bit is missing in profile\n";
1019a34c753fSRafael Auler MispredWarning = false;
1020a34c753fSRafael Auler }
1021a34c753fSRafael Auler
1022a34c753fSRafael Auler ErrorOr<StringRef> Rest = parseString(FieldSeparator, true);
1023a34c753fSRafael Auler if (std::error_code EC = Rest.getError())
1024a34c753fSRafael Auler return EC;
1025a34c753fSRafael Auler if (Rest.get().size() < 5) {
1026a34c753fSRafael Auler reportError("expected rest of LBR entry");
1027a34c753fSRafael Auler Diag << "Found: " << Rest.get() << "\n";
1028a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1029a34c753fSRafael Auler }
1030a34c753fSRafael Auler return Res;
1031a34c753fSRafael Auler }
1032a34c753fSRafael Auler
checkAndConsumeFS()1033a34c753fSRafael Auler bool DataAggregator::checkAndConsumeFS() {
1034def464aaSAmir Ayupov if (ParsingBuf[0] != FieldSeparator)
1035a34c753fSRafael Auler return false;
1036def464aaSAmir Ayupov
1037a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
1038a34c753fSRafael Auler Col += 1;
1039a34c753fSRafael Auler return true;
1040a34c753fSRafael Auler }
1041a34c753fSRafael Auler
consumeRestOfLine()1042a34c753fSRafael Auler void DataAggregator::consumeRestOfLine() {
1043a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of('\n');
1044a34c753fSRafael Auler if (LineEnd == StringRef::npos) {
1045a34c753fSRafael Auler ParsingBuf = StringRef();
1046a34c753fSRafael Auler Col = 0;
1047a34c753fSRafael Auler Line += 1;
1048a34c753fSRafael Auler return;
1049a34c753fSRafael Auler }
1050a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(LineEnd + 1);
1051a34c753fSRafael Auler Col = 0;
1052a34c753fSRafael Auler Line += 1;
1053a34c753fSRafael Auler }
1054a34c753fSRafael Auler
parseBranchSample()1055a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBranchSample> DataAggregator::parseBranchSample() {
1056a34c753fSRafael Auler PerfBranchSample Res;
1057a34c753fSRafael Auler
105840c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
105940c2e0faSMaksim Panchenko }
1060a34c753fSRafael Auler
1061a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1062a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError())
1063a34c753fSRafael Auler return EC;
1064a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
1065a34c753fSRafael Auler if (!opts::LinuxKernelMode && MMapInfoIter == BinaryMMapInfo.end()) {
1066a34c753fSRafael Auler consumeRestOfLine();
1067a34c753fSRafael Auler return make_error_code(errc::no_such_process);
1068a34c753fSRafael Auler }
1069a34c753fSRafael Auler
107040c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
107140c2e0faSMaksim Panchenko }
1072a34c753fSRafael Auler
1073a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true);
1074a34c753fSRafael Auler if (std::error_code EC = PCRes.getError())
1075a34c753fSRafael Auler return EC;
1076a34c753fSRafael Auler Res.PC = PCRes.get();
1077a34c753fSRafael Auler
1078a34c753fSRafael Auler if (checkAndConsumeNewLine())
1079a34c753fSRafael Auler return Res;
1080a34c753fSRafael Auler
1081a34c753fSRafael Auler while (!checkAndConsumeNewLine()) {
1082a34c753fSRafael Auler checkAndConsumeFS();
1083a34c753fSRafael Auler
1084a34c753fSRafael Auler ErrorOr<LBREntry> LBRRes = parseLBREntry();
1085a34c753fSRafael Auler if (std::error_code EC = LBRRes.getError())
1086a34c753fSRafael Auler return EC;
1087a34c753fSRafael Auler LBREntry LBR = LBRRes.get();
1088a34c753fSRafael Auler if (ignoreKernelInterrupt(LBR))
1089a34c753fSRafael Auler continue;
1090a34c753fSRafael Auler if (!BC->HasFixedLoadAddress)
1091a34c753fSRafael Auler adjustLBR(LBR, MMapInfoIter->second);
1092a34c753fSRafael Auler Res.LBR.push_back(LBR);
1093a34c753fSRafael Auler }
1094a34c753fSRafael Auler
1095a34c753fSRafael Auler return Res;
1096a34c753fSRafael Auler }
1097a34c753fSRafael Auler
parseBasicSample()1098a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBasicSample> DataAggregator::parseBasicSample() {
109940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
110040c2e0faSMaksim Panchenko }
1101a34c753fSRafael Auler
1102a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1103a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError())
1104a34c753fSRafael Auler return EC;
1105a34c753fSRafael Auler
1106a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
1107a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) {
1108a34c753fSRafael Auler consumeRestOfLine();
1109a34c753fSRafael Auler return PerfBasicSample{StringRef(), 0};
1110a34c753fSRafael Auler }
1111a34c753fSRafael Auler
111240c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
111340c2e0faSMaksim Panchenko }
1114a34c753fSRafael Auler
1115a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator);
1116a34c753fSRafael Auler if (std::error_code EC = Event.getError())
1117a34c753fSRafael Auler return EC;
1118a34c753fSRafael Auler
111940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
112040c2e0faSMaksim Panchenko }
1121a34c753fSRafael Auler
1122a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator, true);
1123def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError())
1124a34c753fSRafael Auler return EC;
1125a34c753fSRafael Auler
1126a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1127a34c753fSRafael Auler reportError("expected end of line");
1128a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1129a34c753fSRafael Auler }
1130a34c753fSRafael Auler
1131a34c753fSRafael Auler uint64_t Address = *AddrRes;
1132a34c753fSRafael Auler if (!BC->HasFixedLoadAddress)
1133a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second);
1134a34c753fSRafael Auler
1135a34c753fSRafael Auler return PerfBasicSample{Event.get(), Address};
1136a34c753fSRafael Auler }
1137a34c753fSRafael Auler
parseMemSample()1138a34c753fSRafael Auler ErrorOr<DataAggregator::PerfMemSample> DataAggregator::parseMemSample() {
1139a34c753fSRafael Auler PerfMemSample Res{0, 0};
1140a34c753fSRafael Auler
114140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
114240c2e0faSMaksim Panchenko }
1143a34c753fSRafael Auler
1144a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1145a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError())
1146a34c753fSRafael Auler return EC;
1147a34c753fSRafael Auler
1148a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
1149a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) {
1150a34c753fSRafael Auler consumeRestOfLine();
1151a34c753fSRafael Auler return Res;
1152a34c753fSRafael Auler }
1153a34c753fSRafael Auler
115440c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
115540c2e0faSMaksim Panchenko }
1156a34c753fSRafael Auler
1157a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator);
1158a34c753fSRafael Auler if (std::error_code EC = Event.getError())
1159a34c753fSRafael Auler return EC;
1160a34c753fSRafael Auler if (Event.get().find("mem-loads") == StringRef::npos) {
1161a34c753fSRafael Auler consumeRestOfLine();
1162a34c753fSRafael Auler return Res;
1163a34c753fSRafael Auler }
1164a34c753fSRafael Auler
116540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
116640c2e0faSMaksim Panchenko }
1167a34c753fSRafael Auler
1168a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator);
1169def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError())
1170a34c753fSRafael Auler return EC;
1171a34c753fSRafael Auler
117240c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
117340c2e0faSMaksim Panchenko }
1174a34c753fSRafael Auler
1175a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true);
1176a34c753fSRafael Auler if (std::error_code EC = PCRes.getError()) {
1177a34c753fSRafael Auler consumeRestOfLine();
1178a34c753fSRafael Auler return EC;
1179a34c753fSRafael Auler }
1180a34c753fSRafael Auler
1181a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1182a34c753fSRafael Auler reportError("expected end of line");
1183a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1184a34c753fSRafael Auler }
1185a34c753fSRafael Auler
1186a34c753fSRafael Auler uint64_t Address = *AddrRes;
1187a34c753fSRafael Auler if (!BC->HasFixedLoadAddress)
1188a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second);
1189a34c753fSRafael Auler
1190a34c753fSRafael Auler return PerfMemSample{PCRes.get(), Address};
1191a34c753fSRafael Auler }
1192a34c753fSRafael Auler
parseLocationOrOffset()1193a34c753fSRafael Auler ErrorOr<Location> DataAggregator::parseLocationOrOffset() {
1194a34c753fSRafael Auler auto parseOffset = [this]() -> ErrorOr<Location> {
1195a34c753fSRafael Auler ErrorOr<uint64_t> Res = parseHexField(FieldSeparator);
1196a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1197a34c753fSRafael Auler return EC;
1198a34c753fSRafael Auler return Location(Res.get());
1199a34c753fSRafael Auler };
1200a34c753fSRafael Auler
1201a34c753fSRafael Auler size_t Sep = ParsingBuf.find_first_of(" \n");
1202a34c753fSRafael Auler if (Sep == StringRef::npos)
1203a34c753fSRafael Auler return parseOffset();
1204a34c753fSRafael Auler StringRef LookAhead = ParsingBuf.substr(0, Sep);
1205a34c753fSRafael Auler if (LookAhead.find_first_of(":") == StringRef::npos)
1206a34c753fSRafael Auler return parseOffset();
1207a34c753fSRafael Auler
1208a34c753fSRafael Auler ErrorOr<StringRef> BuildID = parseString(':');
1209a34c753fSRafael Auler if (std::error_code EC = BuildID.getError())
1210a34c753fSRafael Auler return EC;
1211a34c753fSRafael Auler ErrorOr<uint64_t> Offset = parseHexField(FieldSeparator);
1212a34c753fSRafael Auler if (std::error_code EC = Offset.getError())
1213a34c753fSRafael Auler return EC;
1214a34c753fSRafael Auler return Location(true, BuildID.get(), Offset.get());
1215a34c753fSRafael Auler }
1216a34c753fSRafael Auler
1217a34c753fSRafael Auler ErrorOr<DataAggregator::AggregatedLBREntry>
parseAggregatedLBREntry()1218a34c753fSRafael Auler DataAggregator::parseAggregatedLBREntry() {
121940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
122040c2e0faSMaksim Panchenko }
1221a34c753fSRafael Auler
1222a34c753fSRafael Auler ErrorOr<StringRef> TypeOrErr = parseString(FieldSeparator);
1223a34c753fSRafael Auler if (std::error_code EC = TypeOrErr.getError())
1224a34c753fSRafael Auler return EC;
1225a34c753fSRafael Auler auto Type = AggregatedLBREntry::BRANCH;
1226a34c753fSRafael Auler if (TypeOrErr.get() == "B") {
1227a34c753fSRafael Auler Type = AggregatedLBREntry::BRANCH;
1228a34c753fSRafael Auler } else if (TypeOrErr.get() == "F") {
1229a34c753fSRafael Auler Type = AggregatedLBREntry::FT;
1230a34c753fSRafael Auler } else if (TypeOrErr.get() == "f") {
1231a34c753fSRafael Auler Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN;
1232a34c753fSRafael Auler } else {
1233a34c753fSRafael Auler reportError("expected B, F or f");
1234a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1235a34c753fSRafael Auler }
1236a34c753fSRafael Auler
123740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
123840c2e0faSMaksim Panchenko }
1239a34c753fSRafael Auler ErrorOr<Location> From = parseLocationOrOffset();
1240a34c753fSRafael Auler if (std::error_code EC = From.getError())
1241a34c753fSRafael Auler return EC;
1242a34c753fSRafael Auler
124340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
124440c2e0faSMaksim Panchenko }
1245a34c753fSRafael Auler ErrorOr<Location> To = parseLocationOrOffset();
1246a34c753fSRafael Auler if (std::error_code EC = To.getError())
1247a34c753fSRafael Auler return EC;
1248a34c753fSRafael Auler
124940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
125040c2e0faSMaksim Panchenko }
1251a34c753fSRafael Auler ErrorOr<int64_t> Frequency =
1252a34c753fSRafael Auler parseNumberField(FieldSeparator, Type != AggregatedLBREntry::BRANCH);
1253a34c753fSRafael Auler if (std::error_code EC = Frequency.getError())
1254a34c753fSRafael Auler return EC;
1255a34c753fSRafael Auler
1256a34c753fSRafael Auler uint64_t Mispreds = 0;
1257a34c753fSRafael Auler if (Type == AggregatedLBREntry::BRANCH) {
125840c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
125940c2e0faSMaksim Panchenko }
1260a34c753fSRafael Auler ErrorOr<int64_t> MispredsOrErr = parseNumberField(FieldSeparator, true);
1261a34c753fSRafael Auler if (std::error_code EC = MispredsOrErr.getError())
1262a34c753fSRafael Auler return EC;
1263a34c753fSRafael Auler Mispreds = static_cast<uint64_t>(MispredsOrErr.get());
1264a34c753fSRafael Auler }
1265a34c753fSRafael Auler
1266a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1267a34c753fSRafael Auler reportError("expected end of line");
1268a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1269a34c753fSRafael Auler }
1270a34c753fSRafael Auler
1271a34c753fSRafael Auler return AggregatedLBREntry{From.get(), To.get(),
1272a34c753fSRafael Auler static_cast<uint64_t>(Frequency.get()), Mispreds,
1273a34c753fSRafael Auler Type};
1274a34c753fSRafael Auler }
1275a34c753fSRafael Auler
ignoreKernelInterrupt(LBREntry & LBR) const1276a34c753fSRafael Auler bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const {
1277a34c753fSRafael Auler return opts::IgnoreInterruptLBR &&
1278a34c753fSRafael Auler (LBR.From >= KernelBaseAddr || LBR.To >= KernelBaseAddr);
1279a34c753fSRafael Auler }
1280a34c753fSRafael Auler
printLBRHeatMap()1281a34c753fSRafael Auler std::error_code DataAggregator::printLBRHeatMap() {
1282a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n";
1283a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName,
1284a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
1285a34c753fSRafael Auler
1286a34c753fSRafael Auler if (opts::LinuxKernelMode) {
1287a34c753fSRafael Auler opts::HeatmapMaxAddress = 0xffffffffffffffff;
1288a34c753fSRafael Auler opts::HeatmapMinAddress = KernelBaseAddr;
1289a34c753fSRafael Auler }
1290a34c753fSRafael Auler Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress,
1291733dc3e5SRahman Lavaee opts::HeatmapMaxAddress, getTextSections(BC));
1292a34c753fSRafael Auler uint64_t NumTotalSamples = 0;
1293a34c753fSRafael Auler
12940c13d97eSRahman Lavaee if (opts::BasicAggregation) {
1295e59e5801SRahman Lavaee while (hasData()) {
12960c13d97eSRahman Lavaee ErrorOr<PerfBasicSample> SampleRes = parseBasicSample();
12970c13d97eSRahman Lavaee if (std::error_code EC = SampleRes.getError()) {
12980c13d97eSRahman Lavaee if (EC == errc::no_such_process)
12990c13d97eSRahman Lavaee continue;
13000c13d97eSRahman Lavaee return EC;
13010c13d97eSRahman Lavaee }
13020c13d97eSRahman Lavaee PerfBasicSample &Sample = SampleRes.get();
13030c13d97eSRahman Lavaee HM.registerAddress(Sample.PC);
13040c13d97eSRahman Lavaee NumTotalSamples++;
1305e59e5801SRahman Lavaee }
1306e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " basic samples\n";
13070c13d97eSRahman Lavaee } else {
1308e59e5801SRahman Lavaee while (hasData()) {
1309a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample();
1310a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) {
1311a34c753fSRafael Auler if (EC == errc::no_such_process)
1312a34c753fSRafael Auler continue;
1313a34c753fSRafael Auler return EC;
1314a34c753fSRafael Auler }
1315a34c753fSRafael Auler
1316a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get();
1317a34c753fSRafael Auler
1318a34c753fSRafael Auler // LBRs are stored in reverse execution order. NextLBR refers to the next
1319a34c753fSRafael Auler // executed branch record.
1320a34c753fSRafael Auler const LBREntry *NextLBR = nullptr;
1321a34c753fSRafael Auler for (const LBREntry &LBR : Sample.LBR) {
1322a34c753fSRafael Auler if (NextLBR) {
1323a34c753fSRafael Auler // Record fall-through trace.
1324a34c753fSRafael Auler const uint64_t TraceFrom = LBR.To;
1325a34c753fSRafael Auler const uint64_t TraceTo = NextLBR->From;
1326a34c753fSRafael Auler ++FallthroughLBRs[Trace(TraceFrom, TraceTo)].InternCount;
1327a34c753fSRafael Auler }
1328a34c753fSRafael Auler NextLBR = &LBR;
1329a34c753fSRafael Auler }
1330a34c753fSRafael Auler if (!Sample.LBR.empty()) {
1331a34c753fSRafael Auler HM.registerAddress(Sample.LBR.front().To);
1332a34c753fSRafael Auler HM.registerAddress(Sample.LBR.back().From);
1333a34c753fSRafael Auler }
1334a34c753fSRafael Auler NumTotalSamples += Sample.LBR.size();
1335a34c753fSRafael Auler }
1336e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " LBR samples\n";
1337e59e5801SRahman Lavaee outs() << "HEATMAP: " << FallthroughLBRs.size() << " unique traces\n";
13380c13d97eSRahman Lavaee }
1339a34c753fSRafael Auler
1340a34c753fSRafael Auler if (!NumTotalSamples) {
1341e59e5801SRahman Lavaee if (opts::BasicAggregation) {
1342e59e5801SRahman Lavaee errs() << "HEATMAP-ERROR: no basic event samples detected in profile. "
1343e59e5801SRahman Lavaee "Cannot build heatmap.";
1344e59e5801SRahman Lavaee } else {
1345a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no LBR traces detected in profile. "
13460c13d97eSRahman Lavaee "Cannot build heatmap. Use -nl for building heatmap from "
13470c13d97eSRahman Lavaee "basic events.\n";
13480c13d97eSRahman Lavaee }
1349a34c753fSRafael Auler exit(1);
1350a34c753fSRafael Auler }
1351a34c753fSRafael Auler
1352a34c753fSRafael Auler outs() << "HEATMAP: building heat map...\n";
1353a34c753fSRafael Auler
1354a34c753fSRafael Auler for (const auto &LBR : FallthroughLBRs) {
1355a34c753fSRafael Auler const Trace &Trace = LBR.first;
1356a34c753fSRafael Auler const FTInfo &Info = LBR.second;
1357a34c753fSRafael Auler HM.registerAddressRange(Trace.From, Trace.To, Info.InternCount);
1358a34c753fSRafael Auler }
1359a34c753fSRafael Auler
1360a34c753fSRafael Auler if (HM.getNumInvalidRanges())
1361a34c753fSRafael Auler outs() << "HEATMAP: invalid traces: " << HM.getNumInvalidRanges() << '\n';
1362a34c753fSRafael Auler
1363a34c753fSRafael Auler if (!HM.size()) {
1364a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no valid traces registered\n";
1365a34c753fSRafael Auler exit(1);
1366a34c753fSRafael Auler }
1367a34c753fSRafael Auler
13685c2ae5f4SVladislav Khmelevsky HM.print(opts::OutputFilename);
13695c2ae5f4SVladislav Khmelevsky if (opts::OutputFilename == "-")
13705c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename);
1371def464aaSAmir Ayupov else
13725c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename + ".csv");
1373733dc3e5SRahman Lavaee if (opts::OutputFilename == "-")
1374733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename);
1375733dc3e5SRahman Lavaee else
1376733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv");
1377a34c753fSRafael Auler
1378a34c753fSRafael Auler return std::error_code();
1379a34c753fSRafael Auler }
1380a34c753fSRafael Auler
parseBranchEvents()1381a34c753fSRafael Auler std::error_code DataAggregator::parseBranchEvents() {
1382a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n";
1383a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName,
1384a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
1385a34c753fSRafael Auler
1386a34c753fSRafael Auler uint64_t NumTotalSamples = 0;
1387a34c753fSRafael Auler uint64_t NumEntries = 0;
1388a34c753fSRafael Auler uint64_t NumSamples = 0;
1389a34c753fSRafael Auler uint64_t NumSamplesNoLBR = 0;
1390a34c753fSRafael Auler uint64_t NumTraces = 0;
1391a34c753fSRafael Auler bool NeedsSkylakeFix = false;
1392a34c753fSRafael Auler
1393a34c753fSRafael Auler while (hasData() && NumTotalSamples < opts::MaxSamples) {
1394a34c753fSRafael Auler ++NumTotalSamples;
1395a34c753fSRafael Auler
1396a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample();
1397a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) {
1398a34c753fSRafael Auler if (EC == errc::no_such_process)
1399a34c753fSRafael Auler continue;
1400a34c753fSRafael Auler return EC;
1401a34c753fSRafael Auler }
1402a34c753fSRafael Auler ++NumSamples;
1403a34c753fSRafael Auler
1404a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get();
1405a34c753fSRafael Auler if (opts::WriteAutoFDOData)
1406a34c753fSRafael Auler ++BasicSamples[Sample.PC];
1407a34c753fSRafael Auler
1408a34c753fSRafael Auler if (Sample.LBR.empty()) {
1409a34c753fSRafael Auler ++NumSamplesNoLBR;
1410a34c753fSRafael Auler continue;
1411a34c753fSRafael Auler }
1412a34c753fSRafael Auler
1413a34c753fSRafael Auler NumEntries += Sample.LBR.size();
1414a34c753fSRafael Auler if (BAT && Sample.LBR.size() == 32 && !NeedsSkylakeFix) {
1415a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using Intel Skylake bug workaround\n";
1416a34c753fSRafael Auler NeedsSkylakeFix = true;
1417a34c753fSRafael Auler }
1418a34c753fSRafael Auler
1419a34c753fSRafael Auler // LBRs are stored in reverse execution order. NextPC refers to the next
1420a34c753fSRafael Auler // recorded executed PC.
1421a34c753fSRafael Auler uint64_t NextPC = opts::UseEventPC ? Sample.PC : 0;
1422a34c753fSRafael Auler uint32_t NumEntry = 0;
1423a34c753fSRafael Auler for (const LBREntry &LBR : Sample.LBR) {
1424a34c753fSRafael Auler ++NumEntry;
1425a34c753fSRafael Auler // Hardware bug workaround: Intel Skylake (which has 32 LBR entries)
1426a34c753fSRafael Auler // sometimes record entry 32 as an exact copy of entry 31. This will cause
1427a34c753fSRafael Auler // us to likely record an invalid trace and generate a stale function for
1428a34c753fSRafael Auler // BAT mode (non BAT disassembles the function and is able to ignore this
1429a34c753fSRafael Auler // trace at aggregation time). Drop first 2 entries (last two, in
1430a34c753fSRafael Auler // chronological order)
1431a34c753fSRafael Auler if (NeedsSkylakeFix && NumEntry <= 2)
1432a34c753fSRafael Auler continue;
1433a34c753fSRafael Auler if (NextPC) {
1434a34c753fSRafael Auler // Record fall-through trace.
1435a34c753fSRafael Auler const uint64_t TraceFrom = LBR.To;
1436a34c753fSRafael Auler const uint64_t TraceTo = NextPC;
1437a34c753fSRafael Auler const BinaryFunction *TraceBF =
1438a34c753fSRafael Auler getBinaryFunctionContainingAddress(TraceFrom);
1439a34c753fSRafael Auler if (TraceBF && TraceBF->containsAddress(TraceTo)) {
1440a34c753fSRafael Auler FTInfo &Info = FallthroughLBRs[Trace(TraceFrom, TraceTo)];
1441def464aaSAmir Ayupov if (TraceBF->containsAddress(LBR.From))
1442a34c753fSRafael Auler ++Info.InternCount;
1443def464aaSAmir Ayupov else
1444a34c753fSRafael Auler ++Info.ExternCount;
1445a34c753fSRafael Auler } else {
1446a34c753fSRafael Auler if (TraceBF && getBinaryFunctionContainingAddress(TraceTo)) {
1447a34c753fSRafael Auler LLVM_DEBUG(dbgs()
1448a34c753fSRafael Auler << "Invalid trace starting in "
1449a34c753fSRafael Auler << TraceBF->getPrintName() << " @ "
1450a34c753fSRafael Auler << Twine::utohexstr(TraceFrom - TraceBF->getAddress())
1451a34c753fSRafael Auler << " and ending @ " << Twine::utohexstr(TraceTo)
1452a34c753fSRafael Auler << '\n');
1453a34c753fSRafael Auler ++NumInvalidTraces;
1454a34c753fSRafael Auler } else {
1455a34c753fSRafael Auler LLVM_DEBUG(dbgs()
1456a34c753fSRafael Auler << "Out of range trace starting in "
1457a34c753fSRafael Auler << (TraceBF ? TraceBF->getPrintName() : "None") << " @ "
1458a34c753fSRafael Auler << Twine::utohexstr(
1459a34c753fSRafael Auler TraceFrom - (TraceBF ? TraceBF->getAddress() : 0))
1460a34c753fSRafael Auler << " and ending in "
1461a34c753fSRafael Auler << (getBinaryFunctionContainingAddress(TraceTo)
1462a34c753fSRafael Auler ? getBinaryFunctionContainingAddress(TraceTo)
1463a34c753fSRafael Auler ->getPrintName()
1464a34c753fSRafael Auler : "None")
1465a34c753fSRafael Auler << " @ "
1466a34c753fSRafael Auler << Twine::utohexstr(
1467a34c753fSRafael Auler TraceTo -
1468a34c753fSRafael Auler (getBinaryFunctionContainingAddress(TraceTo)
1469a34c753fSRafael Auler ? getBinaryFunctionContainingAddress(TraceTo)
1470a34c753fSRafael Auler ->getAddress()
1471a34c753fSRafael Auler : 0))
1472a34c753fSRafael Auler << '\n');
1473a34c753fSRafael Auler ++NumLongRangeTraces;
1474a34c753fSRafael Auler }
1475a34c753fSRafael Auler }
1476a34c753fSRafael Auler ++NumTraces;
1477a34c753fSRafael Auler }
1478a34c753fSRafael Auler NextPC = LBR.From;
1479a34c753fSRafael Auler
1480a34c753fSRafael Auler uint64_t From = LBR.From;
1481a34c753fSRafael Auler if (!getBinaryFunctionContainingAddress(From))
1482a34c753fSRafael Auler From = 0;
1483a34c753fSRafael Auler uint64_t To = LBR.To;
1484a34c753fSRafael Auler if (!getBinaryFunctionContainingAddress(To))
1485a34c753fSRafael Auler To = 0;
1486a34c753fSRafael Auler if (!From && !To)
1487a34c753fSRafael Auler continue;
1488a34c753fSRafael Auler BranchInfo &Info = BranchLBRs[Trace(From, To)];
1489a34c753fSRafael Auler ++Info.TakenCount;
1490a34c753fSRafael Auler Info.MispredCount += LBR.Mispred;
1491a34c753fSRafael Auler }
1492a34c753fSRafael Auler }
1493a34c753fSRafael Auler
1494a34c753fSRafael Auler for (const auto &LBR : BranchLBRs) {
1495a34c753fSRafael Auler const Trace &Trace = LBR.first;
1496a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Trace.From))
1497a34c753fSRafael Auler BF->setHasProfileAvailable();
1498a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Trace.To))
1499a34c753fSRafael Auler BF->setHasProfileAvailable();
1500a34c753fSRafael Auler }
1501a34c753fSRafael Auler
1502a34c753fSRafael Auler auto printColored = [](raw_ostream &OS, float Percent, float T1, float T2) {
1503a34c753fSRafael Auler OS << " (";
1504a34c753fSRafael Auler if (OS.has_colors()) {
1505def464aaSAmir Ayupov if (Percent > T2)
1506a34c753fSRafael Auler OS.changeColor(raw_ostream::RED);
1507def464aaSAmir Ayupov else if (Percent > T1)
1508a34c753fSRafael Auler OS.changeColor(raw_ostream::YELLOW);
1509def464aaSAmir Ayupov else
1510a34c753fSRafael Auler OS.changeColor(raw_ostream::GREEN);
1511a34c753fSRafael Auler }
1512a34c753fSRafael Auler OS << format("%.1f%%", Percent);
1513a34c753fSRafael Auler if (OS.has_colors())
1514a34c753fSRafael Auler OS.resetColor();
1515a34c753fSRafael Auler OS << ")";
1516a34c753fSRafael Auler };
1517a34c753fSRafael Auler
151840c2e0faSMaksim Panchenko outs() << "PERF2BOLT: read " << NumSamples << " samples and " << NumEntries
151940c2e0faSMaksim Panchenko << " LBR entries\n";
1520a34c753fSRafael Auler if (NumTotalSamples) {
1521a34c753fSRafael Auler if (NumSamples && NumSamplesNoLBR == NumSamples) {
1522a34c753fSRafael Auler // Note: we don't know if perf2bolt is being used to parse memory samples
1523a34c753fSRafael Auler // at this point. In this case, it is OK to parse zero LBRs.
1524a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: all recorded samples for this binary lack "
1525a34c753fSRafael Auler "LBR. Record profile with perf record -j any or run perf2bolt "
1526a34c753fSRafael Auler "in no-LBR mode with -nl (the performance improvement in -nl "
1527a34c753fSRafael Auler "mode may be limited)\n";
1528a34c753fSRafael Auler } else {
1529a34c753fSRafael Auler const uint64_t IgnoredSamples = NumTotalSamples - NumSamples;
1530a34c753fSRafael Auler const float PercentIgnored = 100.0f * IgnoredSamples / NumTotalSamples;
1531a34c753fSRafael Auler outs() << "PERF2BOLT: " << IgnoredSamples << " samples";
1532a34c753fSRafael Auler printColored(outs(), PercentIgnored, 20, 50);
1533a34c753fSRafael Auler outs() << " were ignored\n";
1534def464aaSAmir Ayupov if (PercentIgnored > 50.0f)
1535a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: less than 50% of all recorded samples "
1536a34c753fSRafael Auler "were attributed to the input binary\n";
1537a34c753fSRafael Auler }
1538a34c753fSRafael Auler }
1539a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: "
1540a34c753fSRafael Auler << NumInvalidTraces;
1541a34c753fSRafael Auler float Perc = 0.0f;
1542a34c753fSRafael Auler if (NumTraces > 0) {
1543a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces;
1544a34c753fSRafael Auler printColored(outs(), Perc, 5, 10);
1545a34c753fSRafael Auler }
1546a34c753fSRafael Auler outs() << "\n";
1547def464aaSAmir Ayupov if (Perc > 10.0f)
1548a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1549a34c753fSRafael Auler "binary is probably not the same binary used during profiling "
1550a34c753fSRafael Auler "collection. The generated data may be ineffective for improving "
1551a34c753fSRafael Auler "performance.\n\n";
1552a34c753fSRafael Auler
1553a34c753fSRafael Auler outs() << "PERF2BOLT: out of range traces involving unknown regions: "
1554a34c753fSRafael Auler << NumLongRangeTraces;
1555def464aaSAmir Ayupov if (NumTraces > 0)
1556a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces);
1557a34c753fSRafael Auler outs() << "\n";
1558a34c753fSRafael Auler
1559a34c753fSRafael Auler if (NumColdSamples > 0) {
1560a34c753fSRafael Auler const float ColdSamples = NumColdSamples * 100.0f / NumTotalSamples;
1561a34c753fSRafael Auler outs() << "PERF2BOLT: " << NumColdSamples
1562a34c753fSRafael Auler << format(" (%.1f%%)", ColdSamples)
1563a34c753fSRafael Auler << " samples recorded in cold regions of split functions.\n";
1564def464aaSAmir Ayupov if (ColdSamples > 5.0f)
1565a34c753fSRafael Auler outs()
1566a34c753fSRafael Auler << "WARNING: The BOLT-processed binary where samples were collected "
1567a34c753fSRafael Auler "likely used bad data or your service observed a large shift in "
1568a34c753fSRafael Auler "profile. You may want to audit this.\n";
1569a34c753fSRafael Auler }
1570a34c753fSRafael Auler
1571a34c753fSRafael Auler return std::error_code();
1572a34c753fSRafael Auler }
1573a34c753fSRafael Auler
processBranchEvents()1574a34c753fSRafael Auler void DataAggregator::processBranchEvents() {
1575a34c753fSRafael Auler outs() << "PERF2BOLT: processing branch events...\n";
1576a34c753fSRafael Auler NamedRegionTimer T("processBranch", "Processing branch events",
1577a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1578a34c753fSRafael Auler
1579a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) {
1580a34c753fSRafael Auler const Trace &Loc = AggrLBR.first;
1581a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second;
1582a34c753fSRafael Auler LBREntry First{Loc.From, Loc.From, false};
1583a34c753fSRafael Auler LBREntry Second{Loc.To, Loc.To, false};
1584def464aaSAmir Ayupov if (Info.InternCount)
1585a34c753fSRafael Auler doTrace(First, Second, Info.InternCount);
1586a34c753fSRafael Auler if (Info.ExternCount) {
1587a34c753fSRafael Auler First.From = 0;
1588a34c753fSRafael Auler doTrace(First, Second, Info.ExternCount);
1589a34c753fSRafael Auler }
1590a34c753fSRafael Auler }
1591a34c753fSRafael Auler
1592a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) {
1593a34c753fSRafael Auler const Trace &Loc = AggrLBR.first;
1594a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second;
1595a34c753fSRafael Auler doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount);
1596a34c753fSRafael Auler }
1597a34c753fSRafael Auler }
1598a34c753fSRafael Auler
parseBasicEvents()1599a34c753fSRafael Auler std::error_code DataAggregator::parseBasicEvents() {
1600a34c753fSRafael Auler outs() << "PERF2BOLT: parsing basic events (without LBR)...\n";
1601a34c753fSRafael Auler NamedRegionTimer T("parseBasic", "Parsing basic events", TimerGroupName,
1602a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
1603a34c753fSRafael Auler while (hasData()) {
1604a34c753fSRafael Auler ErrorOr<PerfBasicSample> Sample = parseBasicSample();
1605a34c753fSRafael Auler if (std::error_code EC = Sample.getError())
1606a34c753fSRafael Auler return EC;
1607a34c753fSRafael Auler
1608a34c753fSRafael Auler if (!Sample->PC)
1609a34c753fSRafael Auler continue;
1610a34c753fSRafael Auler
1611a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
1612a34c753fSRafael Auler BF->setHasProfileAvailable();
1613a34c753fSRafael Auler
1614a34c753fSRafael Auler ++BasicSamples[Sample->PC];
1615a34c753fSRafael Auler EventNames.insert(Sample->EventName);
1616a34c753fSRafael Auler }
1617a34c753fSRafael Auler
1618a34c753fSRafael Auler return std::error_code();
1619a34c753fSRafael Auler }
1620a34c753fSRafael Auler
processBasicEvents()1621a34c753fSRafael Auler void DataAggregator::processBasicEvents() {
1622a34c753fSRafael Auler outs() << "PERF2BOLT: processing basic events (without LBR)...\n";
162340c2e0faSMaksim Panchenko NamedRegionTimer T("processBasic", "Processing basic events", TimerGroupName,
162440c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator);
1625a34c753fSRafael Auler uint64_t OutOfRangeSamples = 0;
1626a34c753fSRafael Auler uint64_t NumSamples = 0;
1627a34c753fSRafael Auler for (auto &Sample : BasicSamples) {
1628a34c753fSRafael Auler const uint64_t PC = Sample.first;
1629a34c753fSRafael Auler const uint64_t HitCount = Sample.second;
1630a34c753fSRafael Auler NumSamples += HitCount;
1631a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC);
1632a34c753fSRafael Auler if (!Func) {
1633a34c753fSRafael Auler OutOfRangeSamples += HitCount;
1634a34c753fSRafael Auler continue;
1635a34c753fSRafael Auler }
1636a34c753fSRafael Auler
1637a34c753fSRafael Auler doSample(*Func, PC, HitCount);
1638a34c753fSRafael Auler }
1639a34c753fSRafael Auler outs() << "PERF2BOLT: read " << NumSamples << " samples\n";
1640a34c753fSRafael Auler
1641a34c753fSRafael Auler outs() << "PERF2BOLT: out of range samples recorded in unknown regions: "
1642a34c753fSRafael Auler << OutOfRangeSamples;
1643a34c753fSRafael Auler float Perc = 0.0f;
1644a34c753fSRafael Auler if (NumSamples > 0) {
1645a34c753fSRafael Auler outs() << " (";
1646a34c753fSRafael Auler Perc = OutOfRangeSamples * 100.0f / NumSamples;
1647a34c753fSRafael Auler if (outs().has_colors()) {
1648def464aaSAmir Ayupov if (Perc > 60.0f)
1649a34c753fSRafael Auler outs().changeColor(raw_ostream::RED);
1650def464aaSAmir Ayupov else if (Perc > 40.0f)
1651a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW);
1652def464aaSAmir Ayupov else
1653a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN);
1654a34c753fSRafael Auler }
1655a34c753fSRafael Auler outs() << format("%.1f%%", Perc);
1656a34c753fSRafael Auler if (outs().has_colors())
1657a34c753fSRafael Auler outs().resetColor();
1658a34c753fSRafael Auler outs() << ")";
1659a34c753fSRafael Auler }
1660a34c753fSRafael Auler outs() << "\n";
1661def464aaSAmir Ayupov if (Perc > 80.0f)
1662a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1663a34c753fSRafael Auler "binary is probably not the same binary used during profiling "
1664a34c753fSRafael Auler "collection. The generated data may be ineffective for improving "
1665a34c753fSRafael Auler "performance.\n\n";
1666a34c753fSRafael Auler }
1667a34c753fSRafael Auler
parseMemEvents()1668a34c753fSRafael Auler std::error_code DataAggregator::parseMemEvents() {
1669a34c753fSRafael Auler outs() << "PERF2BOLT: parsing memory events...\n";
1670a34c753fSRafael Auler NamedRegionTimer T("parseMemEvents", "Parsing mem events", TimerGroupName,
1671a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
1672a34c753fSRafael Auler while (hasData()) {
1673a34c753fSRafael Auler ErrorOr<PerfMemSample> Sample = parseMemSample();
1674a34c753fSRafael Auler if (std::error_code EC = Sample.getError())
1675a34c753fSRafael Auler return EC;
1676a34c753fSRafael Auler
1677a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
1678a34c753fSRafael Auler BF->setHasProfileAvailable();
1679a34c753fSRafael Auler
1680a34c753fSRafael Auler MemSamples.emplace_back(std::move(Sample.get()));
1681a34c753fSRafael Auler }
1682a34c753fSRafael Auler
1683a34c753fSRafael Auler return std::error_code();
1684a34c753fSRafael Auler }
1685a34c753fSRafael Auler
processMemEvents()1686a34c753fSRafael Auler void DataAggregator::processMemEvents() {
1687a34c753fSRafael Auler NamedRegionTimer T("ProcessMemEvents", "Processing mem events",
1688a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1689a34c753fSRafael Auler for (const PerfMemSample &Sample : MemSamples) {
1690a34c753fSRafael Auler uint64_t PC = Sample.PC;
1691a34c753fSRafael Auler uint64_t Addr = Sample.Addr;
1692a34c753fSRafael Auler StringRef FuncName;
1693a34c753fSRafael Auler StringRef MemName;
1694a34c753fSRafael Auler
1695a34c753fSRafael Auler // Try to resolve symbol for PC
1696a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC);
1697a34c753fSRafael Auler if (!Func) {
1698a34c753fSRafael Auler LLVM_DEBUG(if (PC != 0) {
1699a34c753fSRafael Auler dbgs() << "Skipped mem event: 0x" << Twine::utohexstr(PC) << " => 0x"
1700a34c753fSRafael Auler << Twine::utohexstr(Addr) << "\n";
1701a34c753fSRafael Auler });
1702a34c753fSRafael Auler continue;
1703a34c753fSRafael Auler }
1704a34c753fSRafael Auler
1705a34c753fSRafael Auler FuncName = Func->getOneName();
1706a34c753fSRafael Auler PC -= Func->getAddress();
1707a34c753fSRafael Auler
1708a34c753fSRafael Auler // Try to resolve symbol for memory load
1709a34c753fSRafael Auler if (BinaryData *BD = BC->getBinaryDataContainingAddress(Addr)) {
1710a34c753fSRafael Auler MemName = BD->getName();
1711a34c753fSRafael Auler Addr -= BD->getAddress();
1712a34c753fSRafael Auler } else if (opts::FilterMemProfile) {
1713a34c753fSRafael Auler // Filter out heap/stack accesses
1714a34c753fSRafael Auler continue;
1715a34c753fSRafael Auler }
1716a34c753fSRafael Auler
1717a34c753fSRafael Auler const Location FuncLoc(!FuncName.empty(), FuncName, PC);
1718a34c753fSRafael Auler const Location AddrLoc(!MemName.empty(), MemName, Addr);
1719a34c753fSRafael Auler
1720a34c753fSRafael Auler FuncMemData *MemData = &NamesToMemEvents[FuncName];
1721a34c753fSRafael Auler setMemData(*Func, MemData);
1722a34c753fSRafael Auler MemData->update(FuncLoc, AddrLoc);
1723a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Mem event: " << FuncLoc << " = " << AddrLoc << "\n");
1724a34c753fSRafael Auler }
1725a34c753fSRafael Auler }
1726a34c753fSRafael Auler
parsePreAggregatedLBRSamples()1727a34c753fSRafael Auler std::error_code DataAggregator::parsePreAggregatedLBRSamples() {
1728a34c753fSRafael Auler outs() << "PERF2BOLT: parsing pre-aggregated profile...\n";
1729a34c753fSRafael Auler NamedRegionTimer T("parseAggregated", "Parsing aggregated branch events",
1730a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1731a34c753fSRafael Auler while (hasData()) {
1732a34c753fSRafael Auler ErrorOr<AggregatedLBREntry> AggrEntry = parseAggregatedLBREntry();
1733a34c753fSRafael Auler if (std::error_code EC = AggrEntry.getError())
1734a34c753fSRafael Auler return EC;
1735a34c753fSRafael Auler
1736a34c753fSRafael Auler if (BinaryFunction *BF =
1737a34c753fSRafael Auler getBinaryFunctionContainingAddress(AggrEntry->From.Offset))
1738a34c753fSRafael Auler BF->setHasProfileAvailable();
1739a34c753fSRafael Auler if (BinaryFunction *BF =
1740a34c753fSRafael Auler getBinaryFunctionContainingAddress(AggrEntry->To.Offset))
1741a34c753fSRafael Auler BF->setHasProfileAvailable();
1742a34c753fSRafael Auler
1743a34c753fSRafael Auler AggregatedLBRs.emplace_back(std::move(AggrEntry.get()));
1744a34c753fSRafael Auler }
1745a34c753fSRafael Auler
1746a34c753fSRafael Auler return std::error_code();
1747a34c753fSRafael Auler }
1748a34c753fSRafael Auler
processPreAggregated()1749a34c753fSRafael Auler void DataAggregator::processPreAggregated() {
1750a34c753fSRafael Auler outs() << "PERF2BOLT: processing pre-aggregated profile...\n";
1751a34c753fSRafael Auler NamedRegionTimer T("processAggregated", "Processing aggregated branch events",
1752a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1753a34c753fSRafael Auler
1754a34c753fSRafael Auler uint64_t NumTraces = 0;
1755a34c753fSRafael Auler for (const AggregatedLBREntry &AggrEntry : AggregatedLBRs) {
1756a34c753fSRafael Auler switch (AggrEntry.EntryType) {
1757a34c753fSRafael Auler case AggregatedLBREntry::BRANCH:
1758a34c753fSRafael Auler doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count,
1759a34c753fSRafael Auler AggrEntry.Mispreds);
1760a34c753fSRafael Auler break;
1761a34c753fSRafael Auler case AggregatedLBREntry::FT:
1762a34c753fSRafael Auler case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: {
1763a34c753fSRafael Auler LBREntry First{AggrEntry.EntryType == AggregatedLBREntry::FT
1764a34c753fSRafael Auler ? AggrEntry.From.Offset
1765a34c753fSRafael Auler : 0,
1766a34c753fSRafael Auler AggrEntry.From.Offset, false};
1767a34c753fSRafael Auler LBREntry Second{AggrEntry.To.Offset, AggrEntry.To.Offset, false};
1768a34c753fSRafael Auler doTrace(First, Second, AggrEntry.Count);
1769a34c753fSRafael Auler NumTraces += AggrEntry.Count;
1770a34c753fSRafael Auler break;
1771a34c753fSRafael Auler }
1772a34c753fSRafael Auler }
1773a34c753fSRafael Auler }
1774a34c753fSRafael Auler
1775a34c753fSRafael Auler outs() << "PERF2BOLT: read " << AggregatedLBRs.size()
1776a34c753fSRafael Auler << " aggregated LBR entries\n";
1777a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: "
1778a34c753fSRafael Auler << NumInvalidTraces;
1779a34c753fSRafael Auler float Perc = 0.0f;
1780a34c753fSRafael Auler if (NumTraces > 0) {
1781a34c753fSRafael Auler outs() << " (";
1782a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces;
1783a34c753fSRafael Auler if (outs().has_colors()) {
1784def464aaSAmir Ayupov if (Perc > 10.0f)
1785a34c753fSRafael Auler outs().changeColor(raw_ostream::RED);
1786def464aaSAmir Ayupov else if (Perc > 5.0f)
1787a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW);
1788def464aaSAmir Ayupov else
1789a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN);
1790a34c753fSRafael Auler }
1791a34c753fSRafael Auler outs() << format("%.1f%%", Perc);
1792a34c753fSRafael Auler if (outs().has_colors())
1793a34c753fSRafael Auler outs().resetColor();
1794a34c753fSRafael Auler outs() << ")";
1795a34c753fSRafael Auler }
1796a34c753fSRafael Auler outs() << "\n";
1797def464aaSAmir Ayupov if (Perc > 10.0f)
1798a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1799a34c753fSRafael Auler "binary is probably not the same binary used during profiling "
1800a34c753fSRafael Auler "collection. The generated data may be ineffective for improving "
1801a34c753fSRafael Auler "performance.\n\n";
1802a34c753fSRafael Auler
1803a34c753fSRafael Auler outs() << "PERF2BOLT: Out of range traces involving unknown regions: "
1804a34c753fSRafael Auler << NumLongRangeTraces;
1805def464aaSAmir Ayupov if (NumTraces > 0)
1806a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces);
1807a34c753fSRafael Auler outs() << "\n";
1808a34c753fSRafael Auler }
1809a34c753fSRafael Auler
parseCommExecEvent()1810ae585be1SRafael Auler Optional<int32_t> DataAggregator::parseCommExecEvent() {
1811a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n");
1812a34c753fSRafael Auler if (LineEnd == StringRef::npos) {
1813a34c753fSRafael Auler reportError("expected rest of line");
1814a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n";
1815a34c753fSRafael Auler return NoneType();
1816a34c753fSRafael Auler }
1817a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd);
1818a34c753fSRafael Auler
1819a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_COMM exec");
1820def464aaSAmir Ayupov if (Pos == StringRef::npos)
1821a34c753fSRafael Auler return NoneType();
1822a34c753fSRafael Auler Line = Line.drop_front(Pos);
1823a34c753fSRafael Auler
1824a34c753fSRafael Auler // Line:
1825a34c753fSRafael Auler // PERF_RECORD_COMM exec: <name>:<pid>/<tid>"
1826a34c753fSRafael Auler StringRef PIDStr = Line.rsplit(':').second.split('/').first;
1827ae585be1SRafael Auler int32_t PID;
1828a34c753fSRafael Auler if (PIDStr.getAsInteger(10, PID)) {
1829a34c753fSRafael Auler reportError("expected PID");
1830a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n";
1831a34c753fSRafael Auler return NoneType();
1832a34c753fSRafael Auler }
1833a34c753fSRafael Auler
1834a34c753fSRafael Auler return PID;
1835a34c753fSRafael Auler }
1836a34c753fSRafael Auler
1837a34c753fSRafael Auler namespace {
parsePerfTime(const StringRef TimeStr)1838a34c753fSRafael Auler Optional<uint64_t> parsePerfTime(const StringRef TimeStr) {
1839a34c753fSRafael Auler const StringRef SecTimeStr = TimeStr.split('.').first;
1840a34c753fSRafael Auler const StringRef USecTimeStr = TimeStr.split('.').second;
1841a34c753fSRafael Auler uint64_t SecTime;
1842a34c753fSRafael Auler uint64_t USecTime;
1843a34c753fSRafael Auler if (SecTimeStr.getAsInteger(10, SecTime) ||
1844def464aaSAmir Ayupov USecTimeStr.getAsInteger(10, USecTime))
1845a34c753fSRafael Auler return NoneType();
1846a34c753fSRafael Auler return SecTime * 1000000ULL + USecTime;
1847a34c753fSRafael Auler }
1848a34c753fSRafael Auler }
1849a34c753fSRafael Auler
parseForkEvent()185040c2e0faSMaksim Panchenko Optional<DataAggregator::ForkInfo> DataAggregator::parseForkEvent() {
185140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
185240c2e0faSMaksim Panchenko }
1853a34c753fSRafael Auler
1854a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n");
1855a34c753fSRafael Auler if (LineEnd == StringRef::npos) {
1856a34c753fSRafael Auler reportError("expected rest of line");
1857a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n";
1858a34c753fSRafael Auler return NoneType();
1859a34c753fSRafael Auler }
1860a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd);
1861a34c753fSRafael Auler
1862a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_FORK");
1863a34c753fSRafael Auler if (Pos == StringRef::npos) {
1864a34c753fSRafael Auler consumeRestOfLine();
1865a34c753fSRafael Auler return NoneType();
1866a34c753fSRafael Auler }
1867a34c753fSRafael Auler
1868a34c753fSRafael Auler ForkInfo FI;
1869a34c753fSRafael Auler
1870a34c753fSRafael Auler const StringRef TimeStr =
1871a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second;
1872a34c753fSRafael Auler if (Optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) {
1873a34c753fSRafael Auler FI.Time = *TimeRes;
1874a34c753fSRafael Auler }
1875a34c753fSRafael Auler
1876a34c753fSRafael Auler Line = Line.drop_front(Pos);
1877a34c753fSRafael Auler
1878a34c753fSRafael Auler // Line:
1879a34c753fSRafael Auler // PERF_RECORD_FORK(<child_pid>:<child_tid>):(<parent_pid>:<parent_tid>)
1880a34c753fSRafael Auler const StringRef ChildPIDStr = Line.split('(').second.split(':').first;
1881a34c753fSRafael Auler if (ChildPIDStr.getAsInteger(10, FI.ChildPID)) {
1882a34c753fSRafael Auler reportError("expected PID");
1883a34c753fSRafael Auler Diag << "Found: " << ChildPIDStr << "in '" << Line << "'\n";
1884a34c753fSRafael Auler return NoneType();
1885a34c753fSRafael Auler }
1886a34c753fSRafael Auler
1887a34c753fSRafael Auler const StringRef ParentPIDStr = Line.rsplit('(').second.split(':').first;
1888a34c753fSRafael Auler if (ParentPIDStr.getAsInteger(10, FI.ParentPID)) {
1889a34c753fSRafael Auler reportError("expected PID");
1890a34c753fSRafael Auler Diag << "Found: " << ParentPIDStr << "in '" << Line << "'\n";
1891a34c753fSRafael Auler return NoneType();
1892a34c753fSRafael Auler }
1893a34c753fSRafael Auler
1894a34c753fSRafael Auler consumeRestOfLine();
1895a34c753fSRafael Auler
1896a34c753fSRafael Auler return FI;
1897a34c753fSRafael Auler }
1898a34c753fSRafael Auler
1899a34c753fSRafael Auler ErrorOr<std::pair<StringRef, DataAggregator::MMapInfo>>
parseMMapEvent()1900a34c753fSRafael Auler DataAggregator::parseMMapEvent() {
190140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
190240c2e0faSMaksim Panchenko }
1903a34c753fSRafael Auler
1904a34c753fSRafael Auler MMapInfo ParsedInfo;
1905a34c753fSRafael Auler
1906a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n");
1907a34c753fSRafael Auler if (LineEnd == StringRef::npos) {
1908a34c753fSRafael Auler reportError("expected rest of line");
1909a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n";
1910a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1911a34c753fSRafael Auler }
1912a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd);
1913a34c753fSRafael Auler
1914a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_MMAP2");
1915a34c753fSRafael Auler if (Pos == StringRef::npos) {
1916a34c753fSRafael Auler consumeRestOfLine();
1917a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo);
1918a34c753fSRafael Auler }
1919a34c753fSRafael Auler
1920a34c753fSRafael Auler // Line:
1921a34c753fSRafael Auler // {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name>
1922a34c753fSRafael Auler
1923a34c753fSRafael Auler const StringRef TimeStr =
1924a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second;
1925def464aaSAmir Ayupov if (Optional<uint64_t> TimeRes = parsePerfTime(TimeStr))
1926a34c753fSRafael Auler ParsedInfo.Time = *TimeRes;
1927a34c753fSRafael Auler
1928a34c753fSRafael Auler Line = Line.drop_front(Pos);
1929a34c753fSRafael Auler
1930a34c753fSRafael Auler // Line:
1931a34c753fSRafael Auler // PERF_RECORD_MMAP2 <pid>/<tid>: [<hexbase>(<hexsize>) .*]: .* <file_name>
1932a34c753fSRafael Auler
1933a34c753fSRafael Auler StringRef FileName = Line.rsplit(FieldSeparator).second;
1934a34c753fSRafael Auler if (FileName.startswith("//") || FileName.startswith("[")) {
1935a34c753fSRafael Auler consumeRestOfLine();
1936a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo);
1937a34c753fSRafael Auler }
1938a34c753fSRafael Auler FileName = sys::path::filename(FileName);
1939a34c753fSRafael Auler
1940a34c753fSRafael Auler const StringRef PIDStr = Line.split(FieldSeparator).second.split('/').first;
1941a34c753fSRafael Auler if (PIDStr.getAsInteger(10, ParsedInfo.PID)) {
1942a34c753fSRafael Auler reportError("expected PID");
1943a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n";
1944a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1945a34c753fSRafael Auler }
1946a34c753fSRafael Auler
1947a34c753fSRafael Auler const StringRef BaseAddressStr = Line.split('[').second.split('(').first;
194877b75ca5SMaksim Panchenko if (BaseAddressStr.getAsInteger(0, ParsedInfo.MMapAddress)) {
1949a34c753fSRafael Auler reportError("expected base address");
1950a34c753fSRafael Auler Diag << "Found: " << BaseAddressStr << "in '" << Line << "'\n";
1951a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1952a34c753fSRafael Auler }
1953a34c753fSRafael Auler
1954a34c753fSRafael Auler const StringRef SizeStr = Line.split('(').second.split(')').first;
1955a34c753fSRafael Auler if (SizeStr.getAsInteger(0, ParsedInfo.Size)) {
1956a34c753fSRafael Auler reportError("expected mmaped size");
1957a34c753fSRafael Auler Diag << "Found: " << SizeStr << "in '" << Line << "'\n";
1958a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1959a34c753fSRafael Auler }
1960a34c753fSRafael Auler
1961a34c753fSRafael Auler const StringRef OffsetStr =
1962a34c753fSRafael Auler Line.split('@').second.ltrim().split(FieldSeparator).first;
1963a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, ParsedInfo.Offset)) {
1964a34c753fSRafael Auler reportError("expected mmaped page-aligned offset");
1965a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "in '" << Line << "'\n";
1966a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1967a34c753fSRafael Auler }
1968a34c753fSRafael Auler
1969a34c753fSRafael Auler consumeRestOfLine();
1970a34c753fSRafael Auler
1971a34c753fSRafael Auler return std::make_pair(FileName, ParsedInfo);
1972a34c753fSRafael Auler }
1973a34c753fSRafael Auler
parseMMapEvents()1974a34c753fSRafael Auler std::error_code DataAggregator::parseMMapEvents() {
1975a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script mmap events output\n";
1976a34c753fSRafael Auler NamedRegionTimer T("parseMMapEvents", "Parsing mmap events", TimerGroupName,
1977a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
1978a34c753fSRafael Auler
1979a34c753fSRafael Auler std::multimap<StringRef, MMapInfo> GlobalMMapInfo;
1980a34c753fSRafael Auler while (hasData()) {
1981a34c753fSRafael Auler ErrorOr<std::pair<StringRef, MMapInfo>> FileMMapInfoRes = parseMMapEvent();
1982a34c753fSRafael Auler if (std::error_code EC = FileMMapInfoRes.getError())
1983a34c753fSRafael Auler return EC;
1984a34c753fSRafael Auler
1985a34c753fSRafael Auler std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get();
1986a34c753fSRafael Auler if (FileMMapInfo.second.PID == -1)
1987a34c753fSRafael Auler continue;
1988a34c753fSRafael Auler
1989a34c753fSRafael Auler // Consider only the first mapping of the file for any given PID
1990a34c753fSRafael Auler bool PIDExists = false;
1991a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(FileMMapInfo.first);
1992a34c753fSRafael Auler for (auto MI = Range.first; MI != Range.second; ++MI) {
1993a34c753fSRafael Auler if (MI->second.PID == FileMMapInfo.second.PID) {
1994a34c753fSRafael Auler PIDExists = true;
1995a34c753fSRafael Auler break;
1996a34c753fSRafael Auler }
1997a34c753fSRafael Auler }
1998a34c753fSRafael Auler if (PIDExists)
1999a34c753fSRafael Auler continue;
2000a34c753fSRafael Auler
2001a34c753fSRafael Auler GlobalMMapInfo.insert(FileMMapInfo);
2002a34c753fSRafael Auler }
2003a34c753fSRafael Auler
2004a34c753fSRafael Auler LLVM_DEBUG({
2005a34c753fSRafael Auler dbgs() << "FileName -> mmap info:\n";
2006def464aaSAmir Ayupov for (const std::pair<const StringRef, MMapInfo> &Pair : GlobalMMapInfo)
2007a34c753fSRafael Auler dbgs() << " " << Pair.first << " : " << Pair.second.PID << " [0x"
200877b75ca5SMaksim Panchenko << Twine::utohexstr(Pair.second.MMapAddress) << ", "
2009a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Size) << " @ "
2010a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Offset) << "]\n";
2011a34c753fSRafael Auler });
2012a34c753fSRafael Auler
2013a34c753fSRafael Auler StringRef NameToUse = llvm::sys::path::filename(BC->getFilename());
2014a34c753fSRafael Auler if (GlobalMMapInfo.count(NameToUse) == 0 && !BuildIDBinaryName.empty()) {
2015a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using \"" << BuildIDBinaryName
2016a34c753fSRafael Auler << "\" for profile matching\n";
2017a34c753fSRafael Auler NameToUse = BuildIDBinaryName;
2018a34c753fSRafael Auler }
2019a34c753fSRafael Auler
2020a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(NameToUse);
2021a34c753fSRafael Auler for (auto I = Range.first; I != Range.second; ++I) {
202277b75ca5SMaksim Panchenko MMapInfo &MMapInfo = I->second;
202377b75ca5SMaksim Panchenko if (BC->HasFixedLoadAddress && MMapInfo.MMapAddress) {
2024a34c753fSRafael Auler // Check that the binary mapping matches one of the segments.
2025a34c753fSRafael Auler bool MatchFound = false;
2026a34c753fSRafael Auler for (auto &KV : BC->SegmentMapInfo) {
2027a34c753fSRafael Auler SegmentInfo &SegInfo = KV.second;
202877b75ca5SMaksim Panchenko // The mapping is page-aligned and hence the MMapAddress could be
2029a34c753fSRafael Auler // different from the segment start address. We cannot know the page
2030a34c753fSRafael Auler // size of the mapping, but we know it should not exceed the segment
2031a34c753fSRafael Auler // alignment value. Hence we are performing an approximate check.
203277b75ca5SMaksim Panchenko if (SegInfo.Address >= MMapInfo.MMapAddress &&
203377b75ca5SMaksim Panchenko SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment) {
2034a34c753fSRafael Auler MatchFound = true;
2035a34c753fSRafael Auler break;
2036a34c753fSRafael Auler }
2037a34c753fSRafael Auler }
2038a34c753fSRafael Auler if (!MatchFound) {
2039a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse
204077b75ca5SMaksim Panchenko << " at 0x" << Twine::utohexstr(MMapInfo.MMapAddress) << '\n';
2041a34c753fSRafael Auler continue;
2042a34c753fSRafael Auler }
2043a34c753fSRafael Auler }
2044a34c753fSRafael Auler
204577b75ca5SMaksim Panchenko // Set base address for shared objects.
204677b75ca5SMaksim Panchenko if (!BC->HasFixedLoadAddress) {
204777b75ca5SMaksim Panchenko Optional<uint64_t> BaseAddress =
204877b75ca5SMaksim Panchenko BC->getBaseAddressForMapping(MMapInfo.MMapAddress, MMapInfo.Offset);
204977b75ca5SMaksim Panchenko if (!BaseAddress) {
205077b75ca5SMaksim Panchenko errs() << "PERF2BOLT-WARNING: unable to find base address of the "
205177b75ca5SMaksim Panchenko "binary when memory mapped at 0x"
205277b75ca5SMaksim Panchenko << Twine::utohexstr(MMapInfo.MMapAddress)
205377b75ca5SMaksim Panchenko << " using file offset 0x" << Twine::utohexstr(MMapInfo.Offset)
205477b75ca5SMaksim Panchenko << ". Ignoring profile data for this mapping\n";
205577b75ca5SMaksim Panchenko continue;
205677b75ca5SMaksim Panchenko } else {
205777b75ca5SMaksim Panchenko MMapInfo.BaseAddress = *BaseAddress;
205877b75ca5SMaksim Panchenko }
205977b75ca5SMaksim Panchenko }
206077b75ca5SMaksim Panchenko
2061a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo));
2062a34c753fSRafael Auler }
2063a34c753fSRafael Auler
2064a34c753fSRafael Auler if (BinaryMMapInfo.empty()) {
2065a34c753fSRafael Auler if (errs().has_colors())
2066a34c753fSRafael Auler errs().changeColor(raw_ostream::RED);
2067a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching binary \""
2068a34c753fSRafael Auler << BC->getFilename() << "\".";
2069a34c753fSRafael Auler if (!GlobalMMapInfo.empty()) {
2070a34c753fSRafael Auler errs() << " Profile for the following binary name(s) is available:\n";
2071a34c753fSRafael Auler for (auto I = GlobalMMapInfo.begin(), IE = GlobalMMapInfo.end(); I != IE;
2072def464aaSAmir Ayupov I = GlobalMMapInfo.upper_bound(I->first))
2073a34c753fSRafael Auler errs() << " " << I->first << '\n';
2074a34c753fSRafael Auler errs() << "Please rename the input binary.\n";
2075a34c753fSRafael Auler } else {
2076a34c753fSRafael Auler errs() << " Failed to extract any binary name from a profile.\n";
2077a34c753fSRafael Auler }
2078a34c753fSRafael Auler if (errs().has_colors())
2079a34c753fSRafael Auler errs().resetColor();
2080a34c753fSRafael Auler
2081a34c753fSRafael Auler exit(1);
2082a34c753fSRafael Auler }
2083a34c753fSRafael Auler
2084a34c753fSRafael Auler return std::error_code();
2085a34c753fSRafael Auler }
2086a34c753fSRafael Auler
parseTaskEvents()2087a34c753fSRafael Auler std::error_code DataAggregator::parseTaskEvents() {
2088a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script task events output\n";
2089a34c753fSRafael Auler NamedRegionTimer T("parseTaskEvents", "Parsing task events", TimerGroupName,
2090a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator);
2091a34c753fSRafael Auler
2092a34c753fSRafael Auler while (hasData()) {
2093ae585be1SRafael Auler if (Optional<int32_t> CommInfo = parseCommExecEvent()) {
2094a34c753fSRafael Auler // Remove forked child that ran execve
2095a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*CommInfo);
2096def464aaSAmir Ayupov if (MMapInfoIter != BinaryMMapInfo.end() && MMapInfoIter->second.Forked)
2097a34c753fSRafael Auler BinaryMMapInfo.erase(MMapInfoIter);
2098a34c753fSRafael Auler consumeRestOfLine();
2099a34c753fSRafael Auler continue;
2100a34c753fSRafael Auler }
2101a34c753fSRafael Auler
2102a34c753fSRafael Auler Optional<ForkInfo> ForkInfo = parseForkEvent();
2103a34c753fSRafael Auler if (!ForkInfo)
2104a34c753fSRafael Auler continue;
2105a34c753fSRafael Auler
2106a34c753fSRafael Auler if (ForkInfo->ParentPID == ForkInfo->ChildPID)
2107a34c753fSRafael Auler continue;
2108a34c753fSRafael Auler
2109a34c753fSRafael Auler if (ForkInfo->Time == 0) {
2110a34c753fSRafael Auler // Process was forked and mmaped before perf ran. In this case the child
2111a34c753fSRafael Auler // should have its own mmap entry unless it was execve'd.
2112a34c753fSRafael Auler continue;
2113a34c753fSRafael Auler }
2114a34c753fSRafael Auler
2115a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(ForkInfo->ParentPID);
2116a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end())
2117a34c753fSRafael Auler continue;
2118a34c753fSRafael Auler
2119a34c753fSRafael Auler MMapInfo MMapInfo = MMapInfoIter->second;
2120a34c753fSRafael Auler MMapInfo.PID = ForkInfo->ChildPID;
2121a34c753fSRafael Auler MMapInfo.Forked = true;
2122a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo));
2123a34c753fSRafael Auler }
2124a34c753fSRafael Auler
2125a34c753fSRafael Auler outs() << "PERF2BOLT: input binary is associated with "
2126a34c753fSRafael Auler << BinaryMMapInfo.size() << " PID(s)\n";
2127a34c753fSRafael Auler
2128a34c753fSRafael Auler LLVM_DEBUG({
2129def464aaSAmir Ayupov for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo)
2130a34c753fSRafael Auler outs() << " " << MMI.second.PID << (MMI.second.Forked ? " (forked)" : "")
213177b75ca5SMaksim Panchenko << ": (0x" << Twine::utohexstr(MMI.second.MMapAddress) << ": 0x"
213240c2e0faSMaksim Panchenko << Twine::utohexstr(MMI.second.Size) << ")\n";
2133a34c753fSRafael Auler });
2134a34c753fSRafael Auler
2135a34c753fSRafael Auler return std::error_code();
2136a34c753fSRafael Auler }
2137a34c753fSRafael Auler
2138a34c753fSRafael Auler Optional<std::pair<StringRef, StringRef>>
parseNameBuildIDPair()2139a34c753fSRafael Auler DataAggregator::parseNameBuildIDPair() {
214040c2e0faSMaksim Panchenko while (checkAndConsumeFS()) {
214140c2e0faSMaksim Panchenko }
2142a34c753fSRafael Auler
2143a34c753fSRafael Auler ErrorOr<StringRef> BuildIDStr = parseString(FieldSeparator, true);
2144a34c753fSRafael Auler if (std::error_code EC = BuildIDStr.getError())
2145a34c753fSRafael Auler return NoneType();
2146a34c753fSRafael Auler
2147*661577b5SMaksim Panchenko // If one of the strings is missing, don't issue a parsing error, but still
2148*661577b5SMaksim Panchenko // do not return a value.
2149*661577b5SMaksim Panchenko if (ParsingBuf[0] == '\n')
2150*661577b5SMaksim Panchenko return NoneType();
2151*661577b5SMaksim Panchenko
2152a34c753fSRafael Auler ErrorOr<StringRef> NameStr = parseString(FieldSeparator, true);
2153a34c753fSRafael Auler if (std::error_code EC = NameStr.getError())
2154a34c753fSRafael Auler return NoneType();
2155a34c753fSRafael Auler
2156a34c753fSRafael Auler consumeRestOfLine();
2157a34c753fSRafael Auler return std::make_pair(NameStr.get(), BuildIDStr.get());
2158a34c753fSRafael Auler }
2159a34c753fSRafael Auler
hasAllBuildIDs()2160*661577b5SMaksim Panchenko bool DataAggregator::hasAllBuildIDs() {
2161*661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf;
2162*661577b5SMaksim Panchenko
2163*661577b5SMaksim Panchenko if (!hasData())
2164*661577b5SMaksim Panchenko return false;
2165*661577b5SMaksim Panchenko
2166*661577b5SMaksim Panchenko bool HasInvalidEntries = false;
2167*661577b5SMaksim Panchenko while (hasData()) {
2168*661577b5SMaksim Panchenko if (!parseNameBuildIDPair()) {
2169*661577b5SMaksim Panchenko HasInvalidEntries = true;
2170*661577b5SMaksim Panchenko break;
2171*661577b5SMaksim Panchenko }
2172*661577b5SMaksim Panchenko }
2173*661577b5SMaksim Panchenko
2174*661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf;
2175*661577b5SMaksim Panchenko
2176*661577b5SMaksim Panchenko return !HasInvalidEntries;
2177*661577b5SMaksim Panchenko }
2178*661577b5SMaksim Panchenko
2179a34c753fSRafael Auler Optional<StringRef>
getFileNameForBuildID(StringRef FileBuildID)2180a34c753fSRafael Auler DataAggregator::getFileNameForBuildID(StringRef FileBuildID) {
2181*661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf;
2182*661577b5SMaksim Panchenko
2183*661577b5SMaksim Panchenko StringRef FileName;
2184a34c753fSRafael Auler while (hasData()) {
2185a34c753fSRafael Auler Optional<std::pair<StringRef, StringRef>> IDPair = parseNameBuildIDPair();
2186*661577b5SMaksim Panchenko if (!IDPair) {
2187*661577b5SMaksim Panchenko consumeRestOfLine();
2188*661577b5SMaksim Panchenko continue;
2189a34c753fSRafael Auler }
2190*661577b5SMaksim Panchenko
2191*661577b5SMaksim Panchenko if (IDPair->second.startswith(FileBuildID)) {
2192*661577b5SMaksim Panchenko FileName = sys::path::filename(IDPair->first);
2193*661577b5SMaksim Panchenko break;
2194*661577b5SMaksim Panchenko }
2195*661577b5SMaksim Panchenko }
2196*661577b5SMaksim Panchenko
2197*661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf;
2198*661577b5SMaksim Panchenko
2199*661577b5SMaksim Panchenko if (!FileName.empty())
2200*661577b5SMaksim Panchenko return FileName;
2201*661577b5SMaksim Panchenko
2202a34c753fSRafael Auler return NoneType();
2203a34c753fSRafael Auler }
2204a34c753fSRafael Auler
2205a34c753fSRafael Auler std::error_code
writeAggregatedFile(StringRef OutputFilename) const2206a34c753fSRafael Auler DataAggregator::writeAggregatedFile(StringRef OutputFilename) const {
2207a34c753fSRafael Auler std::error_code EC;
2208a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
2209a34c753fSRafael Auler if (EC)
2210a34c753fSRafael Auler return EC;
2211a34c753fSRafael Auler
2212a34c753fSRafael Auler bool WriteMemLocs = false;
2213a34c753fSRafael Auler
2214a34c753fSRafael Auler auto writeLocation = [&OutFile, &WriteMemLocs](const Location &Loc) {
2215a34c753fSRafael Auler if (WriteMemLocs)
2216a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "4 " : "3 ");
2217a34c753fSRafael Auler else
2218a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "1 " : "0 ");
2219a34c753fSRafael Auler OutFile << (Loc.Name.empty() ? "[unknown]" : getEscapedName(Loc.Name))
2220a34c753fSRafael Auler << " " << Twine::utohexstr(Loc.Offset) << FieldSeparator;
2221a34c753fSRafael Auler };
2222a34c753fSRafael Auler
2223a34c753fSRafael Auler uint64_t BranchValues = 0;
2224a34c753fSRafael Auler uint64_t MemValues = 0;
2225a34c753fSRafael Auler
2226a34c753fSRafael Auler if (BAT)
2227a34c753fSRafael Auler OutFile << "boltedcollection\n";
2228a34c753fSRafael Auler if (opts::BasicAggregation) {
2229a34c753fSRafael Auler OutFile << "no_lbr";
2230def464aaSAmir Ayupov for (const StringMapEntry<NoneType> &Entry : EventNames)
2231a34c753fSRafael Auler OutFile << " " << Entry.getKey();
2232a34c753fSRafael Auler OutFile << "\n";
2233a34c753fSRafael Auler
2234a34c753fSRafael Auler for (const StringMapEntry<FuncSampleData> &Func : NamesToSamples) {
2235a34c753fSRafael Auler for (const SampleInfo &SI : Func.getValue().Data) {
2236a34c753fSRafael Auler writeLocation(SI.Loc);
2237a34c753fSRafael Auler OutFile << SI.Hits << "\n";
2238a34c753fSRafael Auler ++BranchValues;
2239a34c753fSRafael Auler }
2240a34c753fSRafael Auler }
2241a34c753fSRafael Auler } else {
2242a34c753fSRafael Auler for (const StringMapEntry<FuncBranchData> &Func : NamesToBranches) {
2243a34c753fSRafael Auler for (const llvm::bolt::BranchInfo &BI : Func.getValue().Data) {
2244a34c753fSRafael Auler writeLocation(BI.From);
2245a34c753fSRafael Auler writeLocation(BI.To);
2246a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n";
2247a34c753fSRafael Auler ++BranchValues;
2248a34c753fSRafael Auler }
2249a34c753fSRafael Auler for (const llvm::bolt::BranchInfo &BI : Func.getValue().EntryData) {
2250a34c753fSRafael Auler // Do not output if source is a known symbol, since this was already
2251a34c753fSRafael Auler // accounted for in the source function
2252a34c753fSRafael Auler if (BI.From.IsSymbol)
2253a34c753fSRafael Auler continue;
2254a34c753fSRafael Auler writeLocation(BI.From);
2255a34c753fSRafael Auler writeLocation(BI.To);
2256a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n";
2257a34c753fSRafael Auler ++BranchValues;
2258a34c753fSRafael Auler }
2259a34c753fSRafael Auler }
2260a34c753fSRafael Auler
2261a34c753fSRafael Auler WriteMemLocs = true;
2262a34c753fSRafael Auler for (const StringMapEntry<FuncMemData> &Func : NamesToMemEvents) {
2263a34c753fSRafael Auler for (const MemInfo &MemEvent : Func.getValue().Data) {
2264a34c753fSRafael Auler writeLocation(MemEvent.Offset);
2265a34c753fSRafael Auler writeLocation(MemEvent.Addr);
2266a34c753fSRafael Auler OutFile << MemEvent.Count << "\n";
2267a34c753fSRafael Auler ++MemValues;
2268a34c753fSRafael Auler }
2269a34c753fSRafael Auler }
2270a34c753fSRafael Auler }
2271a34c753fSRafael Auler
227240c2e0faSMaksim Panchenko outs() << "PERF2BOLT: wrote " << BranchValues << " objects and " << MemValues
227340c2e0faSMaksim Panchenko << " memory objects to " << OutputFilename << "\n";
2274a34c753fSRafael Auler
2275a34c753fSRafael Auler return std::error_code();
2276a34c753fSRafael Auler }
2277a34c753fSRafael Auler
dump() const227840c2e0faSMaksim Panchenko void DataAggregator::dump() const { DataReader::dump(); }
2279a34c753fSRafael Auler
dump(const LBREntry & LBR) const2280a34c753fSRafael Auler void DataAggregator::dump(const LBREntry &LBR) const {
2281a34c753fSRafael Auler Diag << "From: " << Twine::utohexstr(LBR.From)
2282a34c753fSRafael Auler << " To: " << Twine::utohexstr(LBR.To) << " Mispred? " << LBR.Mispred
2283a34c753fSRafael Auler << "\n";
2284a34c753fSRafael Auler }
2285a34c753fSRafael Auler
dump(const PerfBranchSample & Sample) const2286a34c753fSRafael Auler void DataAggregator::dump(const PerfBranchSample &Sample) const {
2287a34c753fSRafael Auler Diag << "Sample LBR entries: " << Sample.LBR.size() << "\n";
2288def464aaSAmir Ayupov for (const LBREntry &LBR : Sample.LBR)
2289a34c753fSRafael Auler dump(LBR);
2290a34c753fSRafael Auler }
2291a34c753fSRafael Auler
dump(const PerfMemSample & Sample) const2292a34c753fSRafael Auler void DataAggregator::dump(const PerfMemSample &Sample) const {
2293a34c753fSRafael Auler Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n";
2294a34c753fSRafael Auler }
2295