xref: /llvm-project/bolt/lib/Profile/DataAggregator.cpp (revision e6c9cd9c060c1fa8343398b9556a5a6c0f35d515)
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"
175fb59e74SAmir Ayupov #include "bolt/Passes/BinaryPasses.h"
18a34c753fSRafael Auler #include "bolt/Profile/BoltAddressTranslation.h"
19a34c753fSRafael Auler #include "bolt/Profile/Heatmap.h"
2062806811SAmir Ayupov #include "bolt/Profile/YAMLProfileWriter.h"
21a34c753fSRafael Auler #include "bolt/Utils/CommandLineOpts.h"
22a34c753fSRafael Auler #include "bolt/Utils/Utils.h"
23f119a248SAmir Ayupov #include "llvm/ADT/STLExtras.h"
24ae585be1SRafael Auler #include "llvm/ADT/ScopeExit.h"
25a34c753fSRafael Auler #include "llvm/Support/CommandLine.h"
26b5af667bSAmir Ayupov #include "llvm/Support/Compiler.h"
27a34c753fSRafael Auler #include "llvm/Support/Debug.h"
28290e4823Sserge-sans-paille #include "llvm/Support/Errc.h"
29a34c753fSRafael Auler #include "llvm/Support/FileSystem.h"
30a34c753fSRafael Auler #include "llvm/Support/Process.h"
31a34c753fSRafael Auler #include "llvm/Support/Program.h"
32a34c753fSRafael Auler #include "llvm/Support/Regex.h"
33a34c753fSRafael Auler #include "llvm/Support/Timer.h"
34a34c753fSRafael Auler #include "llvm/Support/raw_ostream.h"
35a34c753fSRafael Auler #include <map>
363c255f67SKrzysztof Parzyszek #include <optional>
37a34c753fSRafael Auler #include <unordered_map>
38d914486aSAmir Ayupov #include <utility>
39a34c753fSRafael Auler 
40a34c753fSRafael Auler #define DEBUG_TYPE "aggregator"
41a34c753fSRafael Auler 
42a34c753fSRafael Auler using namespace llvm;
43a34c753fSRafael Auler using namespace bolt;
44a34c753fSRafael Auler 
45a34c753fSRafael Auler namespace opts {
46a34c753fSRafael Auler 
47a34c753fSRafael Auler static cl::opt<bool>
48a34c753fSRafael Auler     BasicAggregation("nl",
49a34c753fSRafael Auler                      cl::desc("aggregate basic samples (without LBR info)"),
50a34c753fSRafael Auler                      cl::cat(AggregatorCategory));
51a34c753fSRafael Auler 
5222bea0c5SJonathan Davies static cl::opt<std::string>
5322bea0c5SJonathan Davies     ITraceAggregation("itrace",
5422bea0c5SJonathan Davies                       cl::desc("Generate LBR info with perf itrace argument"),
5522bea0c5SJonathan Davies                       cl::cat(AggregatorCategory));
5622bea0c5SJonathan Davies 
57a34c753fSRafael Auler static cl::opt<bool>
58a34c753fSRafael Auler FilterMemProfile("filter-mem-profile",
59a34c753fSRafael Auler   cl::desc("if processing a memory profile, filter out stack or heap accesses "
60a34c753fSRafael Auler            "that won't be useful for BOLT to reduce profile file size"),
61a34c753fSRafael Auler   cl::init(true),
62a34c753fSRafael Auler   cl::cat(AggregatorCategory));
63a34c753fSRafael Auler 
64a34c753fSRafael Auler static cl::opt<unsigned long long>
65a34c753fSRafael Auler FilterPID("pid",
66a34c753fSRafael Auler   cl::desc("only use samples from process with specified PID"),
67a34c753fSRafael Auler   cl::init(0),
68a34c753fSRafael Auler   cl::Optional,
69a34c753fSRafael Auler   cl::cat(AggregatorCategory));
70a34c753fSRafael Auler 
71a34c753fSRafael Auler static cl::opt<bool>
72a34c753fSRafael Auler IgnoreBuildID("ignore-build-id",
73a34c753fSRafael Auler   cl::desc("continue even if build-ids in input binary and perf.data mismatch"),
74a34c753fSRafael Auler   cl::init(false),
75a34c753fSRafael Auler   cl::cat(AggregatorCategory));
76a34c753fSRafael Auler 
77b92436efSFangrui Song static cl::opt<bool> IgnoreInterruptLBR(
78b92436efSFangrui Song     "ignore-interrupt-lbr",
79a34c753fSRafael Auler     cl::desc("ignore kernel interrupt LBR that happens asynchronously"),
80b92436efSFangrui Song     cl::init(true), cl::cat(AggregatorCategory));
81a34c753fSRafael Auler 
82a34c753fSRafael Auler static cl::opt<unsigned long long>
83a34c753fSRafael Auler MaxSamples("max-samples",
84a34c753fSRafael Auler   cl::init(-1ULL),
85a34c753fSRafael Auler   cl::desc("maximum number of samples to read from LBR profile"),
86a34c753fSRafael Auler   cl::Optional,
87a34c753fSRafael Auler   cl::Hidden,
88a34c753fSRafael Auler   cl::cat(AggregatorCategory));
89a34c753fSRafael Auler 
9039336fc0SAmir Ayupov extern cl::opt<opts::ProfileFormatKind> ProfileFormat;
91c820bd3eSAmir Ayupov extern cl::opt<bool> ProfileWritePseudoProbes;
9262806811SAmir Ayupov extern cl::opt<std::string> SaveProfile;
9339336fc0SAmir Ayupov 
94661577b5SMaksim Panchenko cl::opt<bool> ReadPreAggregated(
95b92436efSFangrui Song     "pa", cl::desc("skip perf and read data from a pre-aggregated file format"),
96a34c753fSRafael Auler     cl::cat(AggregatorCategory));
97a34c753fSRafael Auler 
9851003076SPaschalis Mpeis cl::opt<std::string>
9951003076SPaschalis Mpeis     ReadPerfEvents("perf-script-events",
10051003076SPaschalis Mpeis                    cl::desc("skip perf event collection by supplying a "
10151003076SPaschalis Mpeis                             "perf-script output in a textual format"),
10251003076SPaschalis Mpeis                    cl::ReallyHidden, cl::init(""), cl::cat(AggregatorCategory));
10351003076SPaschalis Mpeis 
104a34c753fSRafael Auler static cl::opt<bool>
105a34c753fSRafael Auler TimeAggregator("time-aggr",
106a34c753fSRafael Auler   cl::desc("time BOLT aggregator"),
107a34c753fSRafael Auler   cl::init(false),
108a34c753fSRafael Auler   cl::ZeroOrMore,
109a34c753fSRafael Auler   cl::cat(AggregatorCategory));
110a34c753fSRafael Auler 
11140c2e0faSMaksim Panchenko } // namespace opts
112a34c753fSRafael Auler 
113a34c753fSRafael Auler namespace {
114a34c753fSRafael Auler 
115a34c753fSRafael Auler const char TimerGroupName[] = "aggregator";
116a34c753fSRafael Auler const char TimerGroupDesc[] = "Aggregator";
117a34c753fSRafael Auler 
118733dc3e5SRahman Lavaee std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) {
119733dc3e5SRahman Lavaee   std::vector<SectionNameAndRange> sections;
120733dc3e5SRahman Lavaee   for (BinarySection &Section : BC->sections()) {
121733dc3e5SRahman Lavaee     if (!Section.isText())
122733dc3e5SRahman Lavaee       continue;
123733dc3e5SRahman Lavaee     if (Section.getSize() == 0)
124733dc3e5SRahman Lavaee       continue;
125733dc3e5SRahman Lavaee     sections.push_back(
126733dc3e5SRahman Lavaee         {Section.getName(), Section.getAddress(), Section.getEndAddress()});
127733dc3e5SRahman Lavaee   }
128d2c87699SAmir Ayupov   llvm::sort(sections,
129733dc3e5SRahman Lavaee              [](const SectionNameAndRange &A, const SectionNameAndRange &B) {
130733dc3e5SRahman Lavaee                return A.BeginAddress < B.BeginAddress;
131733dc3e5SRahman Lavaee              });
132733dc3e5SRahman Lavaee   return sections;
133733dc3e5SRahman Lavaee }
134a34c753fSRafael Auler }
135a34c753fSRafael Auler 
136a34c753fSRafael Auler constexpr uint64_t DataAggregator::KernelBaseAddr;
137a34c753fSRafael Auler 
13840c2e0faSMaksim Panchenko DataAggregator::~DataAggregator() { deleteTempFiles(); }
139a34c753fSRafael Auler 
140a34c753fSRafael Auler namespace {
141a34c753fSRafael Auler void deleteTempFile(const std::string &FileName) {
142def464aaSAmir Ayupov   if (std::error_code Errc = sys::fs::remove(FileName.c_str()))
14340c2e0faSMaksim Panchenko     errs() << "PERF2BOLT: failed to delete temporary file " << FileName
14440c2e0faSMaksim Panchenko            << " with error " << Errc.message() << "\n";
145a34c753fSRafael Auler }
146a34c753fSRafael Auler }
147a34c753fSRafael Auler 
148a34c753fSRafael Auler void DataAggregator::deleteTempFiles() {
149def464aaSAmir Ayupov   for (std::string &FileName : TempFiles)
150a34c753fSRafael Auler     deleteTempFile(FileName);
151a34c753fSRafael Auler   TempFiles.clear();
152a34c753fSRafael Auler }
153a34c753fSRafael Auler 
154a34c753fSRafael Auler void DataAggregator::findPerfExecutable() {
1553c255f67SKrzysztof Parzyszek   std::optional<std::string> PerfExecutable =
156a34c753fSRafael Auler       sys::Process::FindInEnvPath("PATH", "perf");
157a34c753fSRafael Auler   if (!PerfExecutable) {
158a34c753fSRafael Auler     outs() << "PERF2BOLT: No perf executable found!\n";
159a34c753fSRafael Auler     exit(1);
160a34c753fSRafael Auler   }
161a34c753fSRafael Auler   PerfPath = *PerfExecutable;
162a34c753fSRafael Auler }
163a34c753fSRafael Auler 
164a34c753fSRafael Auler void DataAggregator::start() {
16540c2e0faSMaksim Panchenko   outs() << "PERF2BOLT: Starting data aggregation job for " << Filename << "\n";
166a34c753fSRafael Auler 
16751003076SPaschalis Mpeis   // Don't launch perf for pre-aggregated files or when perf input is specified
16851003076SPaschalis Mpeis   // by the user.
16951003076SPaschalis Mpeis   if (opts::ReadPreAggregated || !opts::ReadPerfEvents.empty())
170a34c753fSRafael Auler     return;
171a34c753fSRafael Auler 
172a34c753fSRafael Auler   findPerfExecutable();
173a34c753fSRafael Auler 
17422bea0c5SJonathan Davies   if (opts::BasicAggregation) {
175a34c753fSRafael Auler     launchPerfProcess("events without LBR",
176a34c753fSRafael Auler                       MainEventsPPI,
177a34c753fSRafael Auler                       "script -F pid,event,ip",
178a34c753fSRafael Auler                       /*Wait = */false);
17922bea0c5SJonathan Davies   } else if (!opts::ITraceAggregation.empty()) {
18022bea0c5SJonathan Davies     std::string ItracePerfScriptArgs = llvm::formatv(
181*e6c9cd9cSAmir Ayupov         "script -F pid,brstack --itrace={0}", opts::ITraceAggregation);
18222bea0c5SJonathan Davies     launchPerfProcess("branch events with itrace", MainEventsPPI,
18322bea0c5SJonathan Davies                       ItracePerfScriptArgs.c_str(),
18422bea0c5SJonathan Davies                       /*Wait = */ false);
18522bea0c5SJonathan Davies   } else {
186*e6c9cd9cSAmir Ayupov     launchPerfProcess("branch events", MainEventsPPI, "script -F pid,brstack",
187a34c753fSRafael Auler                       /*Wait = */ false);
18822bea0c5SJonathan Davies   }
189a34c753fSRafael Auler 
190a34c753fSRafael Auler   // Note: we launch script for mem events regardless of the option, as the
191a34c753fSRafael Auler   //       command fails fairly fast if mem events were not collected.
192a34c753fSRafael Auler   launchPerfProcess("mem events",
193a34c753fSRafael Auler                     MemEventsPPI,
194a34c753fSRafael Auler                     "script -F pid,event,addr,ip",
195a34c753fSRafael Auler                     /*Wait = */false);
196a34c753fSRafael Auler 
1975db75d74SJonathan Davies   launchPerfProcess("process events", MMapEventsPPI,
1985db75d74SJonathan Davies                     "script --show-mmap-events --no-itrace",
199a34c753fSRafael Auler                     /*Wait = */ false);
200a34c753fSRafael Auler 
2015db75d74SJonathan Davies   launchPerfProcess("task events", TaskEventsPPI,
2025db75d74SJonathan Davies                     "script --show-task-events --no-itrace",
203a34c753fSRafael Auler                     /*Wait = */ false);
204a34c753fSRafael Auler }
205a34c753fSRafael Auler 
206a34c753fSRafael Auler void DataAggregator::abort() {
207a34c753fSRafael Auler   if (opts::ReadPreAggregated)
208a34c753fSRafael Auler     return;
209a34c753fSRafael Auler 
210a34c753fSRafael Auler   std::string Error;
211a34c753fSRafael Auler 
212a34c753fSRafael Auler   // Kill subprocesses in case they are not finished
2136be2db6cSMatt Arsenault   sys::Wait(TaskEventsPPI.PI, 1, &Error);
2146be2db6cSMatt Arsenault   sys::Wait(MMapEventsPPI.PI, 1, &Error);
2156be2db6cSMatt Arsenault   sys::Wait(MainEventsPPI.PI, 1, &Error);
2166be2db6cSMatt Arsenault   sys::Wait(MemEventsPPI.PI, 1, &Error);
217a34c753fSRafael Auler 
218a34c753fSRafael Auler   deleteTempFiles();
219a34c753fSRafael Auler 
220a34c753fSRafael Auler   exit(1);
221a34c753fSRafael Auler }
222a34c753fSRafael Auler 
223a34c753fSRafael Auler void DataAggregator::launchPerfProcess(StringRef Name, PerfProcessInfo &PPI,
224a34c753fSRafael Auler                                        const char *ArgsString, bool Wait) {
225a34c753fSRafael Auler   SmallVector<StringRef, 4> Argv;
226a34c753fSRafael Auler 
227a34c753fSRafael Auler   outs() << "PERF2BOLT: spawning perf job to read " << Name << '\n';
228a34c753fSRafael Auler   Argv.push_back(PerfPath.data());
229a34c753fSRafael Auler 
2305acac7dbSAmir Ayupov   StringRef(ArgsString).split(Argv, ' ');
231a34c753fSRafael Auler   Argv.push_back("-f");
232a34c753fSRafael Auler   Argv.push_back("-i");
233a34c753fSRafael Auler   Argv.push_back(Filename.c_str());
234a34c753fSRafael Auler 
235a34c753fSRafael Auler   if (std::error_code Errc =
236a34c753fSRafael Auler           sys::fs::createTemporaryFile("perf.script", "out", PPI.StdoutPath)) {
23740c2e0faSMaksim Panchenko     errs() << "PERF2BOLT: failed to create temporary file " << PPI.StdoutPath
23840c2e0faSMaksim Panchenko            << " with error " << Errc.message() << "\n";
239a34c753fSRafael Auler     exit(1);
240a34c753fSRafael Auler   }
241a34c753fSRafael Auler   TempFiles.push_back(PPI.StdoutPath.data());
242a34c753fSRafael Auler 
243a34c753fSRafael Auler   if (std::error_code Errc =
244a34c753fSRafael Auler           sys::fs::createTemporaryFile("perf.script", "err", PPI.StderrPath)) {
24540c2e0faSMaksim Panchenko     errs() << "PERF2BOLT: failed to create temporary file " << PPI.StderrPath
24640c2e0faSMaksim Panchenko            << " with error " << Errc.message() << "\n";
247a34c753fSRafael Auler     exit(1);
248a34c753fSRafael Auler   }
249a34c753fSRafael Auler   TempFiles.push_back(PPI.StderrPath.data());
250a34c753fSRafael Auler 
2511028b165SKazu Hirata   std::optional<StringRef> Redirects[] = {
252e324a80fSKazu Hirata       std::nullopt,                      // Stdin
253a34c753fSRafael Auler       StringRef(PPI.StdoutPath.data()),  // Stdout
254a34c753fSRafael Auler       StringRef(PPI.StderrPath.data())}; // Stderr
255a34c753fSRafael Auler 
256a34c753fSRafael Auler   LLVM_DEBUG({
257a34c753fSRafael Auler     dbgs() << "Launching perf: ";
258a34c753fSRafael Auler     for (StringRef Arg : Argv)
259a34c753fSRafael Auler       dbgs() << Arg << " ";
260a34c753fSRafael Auler     dbgs() << " 1> " << PPI.StdoutPath.data() << " 2> " << PPI.StderrPath.data()
261a34c753fSRafael Auler            << "\n";
262a34c753fSRafael Auler   });
263a34c753fSRafael Auler 
264def464aaSAmir Ayupov   if (Wait)
265a34c753fSRafael Auler     PPI.PI.ReturnCode = sys::ExecuteAndWait(PerfPath.data(), Argv,
266e324a80fSKazu Hirata                                             /*envp*/ std::nullopt, Redirects);
267def464aaSAmir Ayupov   else
268e324a80fSKazu Hirata     PPI.PI = sys::ExecuteNoWait(PerfPath.data(), Argv, /*envp*/ std::nullopt,
269a34c753fSRafael Auler                                 Redirects);
270a34c753fSRafael Auler }
271a34c753fSRafael Auler 
272a34c753fSRafael Auler void DataAggregator::processFileBuildID(StringRef FileBuildID) {
273a34c753fSRafael Auler   PerfProcessInfo BuildIDProcessInfo;
274a34c753fSRafael Auler   launchPerfProcess("buildid list",
275a34c753fSRafael Auler                     BuildIDProcessInfo,
276a34c753fSRafael Auler                     "buildid-list",
277a34c753fSRafael Auler                     /*Wait = */true);
278a34c753fSRafael Auler 
279a34c753fSRafael Auler   if (BuildIDProcessInfo.PI.ReturnCode != 0) {
280a34c753fSRafael Auler     ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
281a34c753fSRafael Auler         MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StderrPath.data());
282a34c753fSRafael Auler     StringRef ErrBuf = (*MB)->getBuffer();
283a34c753fSRafael Auler 
284a34c753fSRafael Auler     errs() << "PERF-ERROR: return code " << BuildIDProcessInfo.PI.ReturnCode
285a34c753fSRafael Auler            << '\n';
286a34c753fSRafael Auler     errs() << ErrBuf;
287a34c753fSRafael Auler     return;
288a34c753fSRafael Auler   }
289a34c753fSRafael Auler 
290a34c753fSRafael Auler   ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
291a34c753fSRafael Auler       MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StdoutPath.data());
292a34c753fSRafael Auler   if (std::error_code EC = MB.getError()) {
293a34c753fSRafael Auler     errs() << "Cannot open " << BuildIDProcessInfo.StdoutPath.data() << ": "
294a34c753fSRafael Auler            << EC.message() << "\n";
295a34c753fSRafael Auler     return;
296a34c753fSRafael Auler   }
297a34c753fSRafael Auler 
298d914486aSAmir Ayupov   FileBuf = std::move(*MB);
299a34c753fSRafael Auler   ParsingBuf = FileBuf->getBuffer();
300a34c753fSRafael Auler 
301835a9c28SAmir Ayupov   std::optional<StringRef> FileName = getFileNameForBuildID(FileBuildID);
302a34c753fSRafael Auler   if (!FileName) {
303661577b5SMaksim Panchenko     if (hasAllBuildIDs()) {
304a34c753fSRafael Auler       errs() << "PERF2BOLT-ERROR: failed to match build-id from perf output. "
305a34c753fSRafael Auler                 "This indicates the input binary supplied for data aggregation "
306a34c753fSRafael Auler                 "is not the same recorded by perf when collecting profiling "
307a34c753fSRafael Auler                 "data, or there were no samples recorded for the binary. "
308a34c753fSRafael Auler                 "Use -ignore-build-id option to override.\n";
309def464aaSAmir Ayupov       if (!opts::IgnoreBuildID)
310a34c753fSRafael Auler         abort();
311661577b5SMaksim Panchenko     } else {
312661577b5SMaksim Panchenko       errs() << "PERF2BOLT-WARNING: build-id will not be checked because perf "
313661577b5SMaksim Panchenko                 "data was recorded without it\n";
314661577b5SMaksim Panchenko       return;
315661577b5SMaksim Panchenko     }
316a34c753fSRafael Auler   } else if (*FileName != llvm::sys::path::filename(BC->getFilename())) {
317a34c753fSRafael Auler     errs() << "PERF2BOLT-WARNING: build-id matched a different file name\n";
318a34c753fSRafael Auler     BuildIDBinaryName = std::string(*FileName);
319a34c753fSRafael Auler   } else {
320a34c753fSRafael Auler     outs() << "PERF2BOLT: matched build-id and file name\n";
321a34c753fSRafael Auler   }
322a34c753fSRafael Auler }
323a34c753fSRafael Auler 
324a34c753fSRafael Auler bool DataAggregator::checkPerfDataMagic(StringRef FileName) {
325a34c753fSRafael Auler   if (opts::ReadPreAggregated)
326a34c753fSRafael Auler     return true;
327a34c753fSRafael Auler 
328ae585be1SRafael Auler   Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName);
3290f915826SMaksim Panchenko   if (!FD) {
3300f915826SMaksim Panchenko     consumeError(FD.takeError());
331a34c753fSRafael Auler     return false;
3320f915826SMaksim Panchenko   }
333a34c753fSRafael Auler 
334a34c753fSRafael Auler   char Buf[7] = {0, 0, 0, 0, 0, 0, 0};
335a34c753fSRafael Auler 
336ae585be1SRafael Auler   auto Close = make_scope_exit([&] { sys::fs::closeFile(*FD); });
337ae585be1SRafael Auler   Expected<size_t> BytesRead = sys::fs::readNativeFileSlice(
338a288d7f9SJoe Loser       *FD, MutableArrayRef(Buf, sizeof(Buf)), 0);
3390f915826SMaksim Panchenko   if (!BytesRead) {
3400f915826SMaksim Panchenko     consumeError(BytesRead.takeError());
3410f915826SMaksim Panchenko     return false;
3420f915826SMaksim Panchenko   }
3430f915826SMaksim Panchenko 
3440f915826SMaksim Panchenko   if (*BytesRead != 7)
345a34c753fSRafael Auler     return false;
346a34c753fSRafael Auler 
347a34c753fSRafael Auler   if (strncmp(Buf, "PERFILE", 7) == 0)
348a34c753fSRafael Auler     return true;
349a34c753fSRafael Auler   return false;
350a34c753fSRafael Auler }
351a34c753fSRafael Auler 
352a34c753fSRafael Auler void DataAggregator::parsePreAggregated() {
353a34c753fSRafael Auler   std::string Error;
354a34c753fSRafael Auler 
355a34c753fSRafael Auler   ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
356a34c753fSRafael Auler       MemoryBuffer::getFileOrSTDIN(Filename);
357a34c753fSRafael Auler   if (std::error_code EC = MB.getError()) {
358a34c753fSRafael Auler     errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": "
359a34c753fSRafael Auler            << EC.message() << "\n";
360a34c753fSRafael Auler     exit(1);
361a34c753fSRafael Auler   }
362a34c753fSRafael Auler 
363d914486aSAmir Ayupov   FileBuf = std::move(*MB);
364a34c753fSRafael Auler   ParsingBuf = FileBuf->getBuffer();
365a34c753fSRafael Auler   Col = 0;
366a34c753fSRafael Auler   Line = 1;
367a34c753fSRafael Auler   if (parsePreAggregatedLBRSamples()) {
368a34c753fSRafael Auler     errs() << "PERF2BOLT: failed to parse samples\n";
369a34c753fSRafael Auler     exit(1);
370a34c753fSRafael Auler   }
371a34c753fSRafael Auler }
372a34c753fSRafael Auler 
373a34c753fSRafael Auler void DataAggregator::filterBinaryMMapInfo() {
374a34c753fSRafael Auler   if (opts::FilterPID) {
375a34c753fSRafael Auler     auto MMapInfoIter = BinaryMMapInfo.find(opts::FilterPID);
376a34c753fSRafael Auler     if (MMapInfoIter != BinaryMMapInfo.end()) {
377a34c753fSRafael Auler       MMapInfo MMap = MMapInfoIter->second;
378a34c753fSRafael Auler       BinaryMMapInfo.clear();
379a34c753fSRafael Auler       BinaryMMapInfo.insert(std::make_pair(MMap.PID, MMap));
380a34c753fSRafael Auler     } else {
381a34c753fSRafael Auler       if (errs().has_colors())
382a34c753fSRafael Auler         errs().changeColor(raw_ostream::RED);
383a34c753fSRafael Auler       errs() << "PERF2BOLT-ERROR: could not find a profile matching PID \""
38440c2e0faSMaksim Panchenko              << opts::FilterPID << "\""
38540c2e0faSMaksim Panchenko              << " for binary \"" << BC->getFilename() << "\".";
386a34c753fSRafael Auler       assert(!BinaryMMapInfo.empty() && "No memory map for matching binary");
387a34c753fSRafael Auler       errs() << " Profile for the following process is available:\n";
388def464aaSAmir Ayupov       for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo)
389a34c753fSRafael Auler         outs() << "  " << MMI.second.PID
390a34c753fSRafael Auler                << (MMI.second.Forked ? " (forked)\n" : "\n");
391def464aaSAmir Ayupov 
392a34c753fSRafael Auler       if (errs().has_colors())
393a34c753fSRafael Auler         errs().resetColor();
394a34c753fSRafael Auler 
395a34c753fSRafael Auler       exit(1);
396a34c753fSRafael Auler     }
397a34c753fSRafael Auler   }
398a34c753fSRafael Auler }
399a34c753fSRafael Auler 
400c7af4f38SAmir Ayupov int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process,
401c7af4f38SAmir Ayupov                                    PerfProcessErrorCallbackTy Callback) {
40251003076SPaschalis Mpeis   if (!opts::ReadPerfEvents.empty()) {
40351003076SPaschalis Mpeis     outs() << "PERF2BOLT: using pre-processed perf events for '" << Name
40451003076SPaschalis Mpeis            << "' (perf-script-events)\n";
40551003076SPaschalis Mpeis     ParsingBuf = opts::ReadPerfEvents;
40651003076SPaschalis Mpeis     return 0;
40751003076SPaschalis Mpeis   }
40851003076SPaschalis Mpeis 
409a34c753fSRafael Auler   std::string Error;
410a34c753fSRafael Auler   outs() << "PERF2BOLT: waiting for perf " << Name
411a34c753fSRafael Auler          << " collection to finish...\n";
412765f3cafSMatt Arsenault   sys::ProcessInfo PI = sys::Wait(Process.PI, std::nullopt, &Error);
413a34c753fSRafael Auler 
414a34c753fSRafael Auler   if (!Error.empty()) {
415a34c753fSRafael Auler     errs() << "PERF-ERROR: " << PerfPath << ": " << Error << "\n";
416a34c753fSRafael Auler     deleteTempFiles();
417a34c753fSRafael Auler     exit(1);
418a34c753fSRafael Auler   }
419a34c753fSRafael Auler 
420a34c753fSRafael Auler   if (PI.ReturnCode != 0) {
421a34c753fSRafael Auler     ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorMB =
422a34c753fSRafael Auler         MemoryBuffer::getFileOrSTDIN(Process.StderrPath.data());
423a34c753fSRafael Auler     StringRef ErrBuf = (*ErrorMB)->getBuffer();
424a34c753fSRafael Auler 
425a34c753fSRafael Auler     deleteTempFiles();
426c7af4f38SAmir Ayupov     Callback(PI.ReturnCode, ErrBuf);
427c7af4f38SAmir Ayupov     return PI.ReturnCode;
428a34c753fSRafael Auler   }
429a34c753fSRafael Auler 
430a34c753fSRafael Auler   ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
431a34c753fSRafael Auler       MemoryBuffer::getFileOrSTDIN(Process.StdoutPath.data());
432a34c753fSRafael Auler   if (std::error_code EC = MB.getError()) {
433a34c753fSRafael Auler     errs() << "Cannot open " << Process.StdoutPath.data() << ": "
434a34c753fSRafael Auler            << EC.message() << "\n";
435a34c753fSRafael Auler     deleteTempFiles();
436a34c753fSRafael Auler     exit(1);
437a34c753fSRafael Auler   }
438a34c753fSRafael Auler 
439d914486aSAmir Ayupov   FileBuf = std::move(*MB);
440a34c753fSRafael Auler   ParsingBuf = FileBuf->getBuffer();
441a34c753fSRafael Auler   Col = 0;
442a34c753fSRafael Auler   Line = 1;
443c7af4f38SAmir Ayupov   return PI.ReturnCode;
444c7af4f38SAmir Ayupov }
445c7af4f38SAmir Ayupov 
446c7af4f38SAmir Ayupov Error DataAggregator::preprocessProfile(BinaryContext &BC) {
447c7af4f38SAmir Ayupov   this->BC = &BC;
448c7af4f38SAmir Ayupov 
449c7af4f38SAmir Ayupov   if (opts::ReadPreAggregated) {
450c7af4f38SAmir Ayupov     parsePreAggregated();
451c7af4f38SAmir Ayupov     return Error::success();
452c7af4f38SAmir Ayupov   }
453c7af4f38SAmir Ayupov 
454c7af4f38SAmir Ayupov   if (std::optional<StringRef> FileBuildID = BC.getFileBuildID()) {
455c7af4f38SAmir Ayupov     outs() << "BOLT-INFO: binary build-id is:     " << *FileBuildID << "\n";
456c7af4f38SAmir Ayupov     processFileBuildID(*FileBuildID);
457c7af4f38SAmir Ayupov   } else {
458c7af4f38SAmir Ayupov     errs() << "BOLT-WARNING: build-id will not be checked because we could "
459c7af4f38SAmir Ayupov               "not read one from input binary\n";
460c7af4f38SAmir Ayupov   }
461c7af4f38SAmir Ayupov 
462c7af4f38SAmir Ayupov   auto ErrorCallback = [](int ReturnCode, StringRef ErrBuf) {
463c7af4f38SAmir Ayupov     errs() << "PERF-ERROR: return code " << ReturnCode << "\n" << ErrBuf;
464c7af4f38SAmir Ayupov     exit(1);
465c7af4f38SAmir Ayupov   };
466c7af4f38SAmir Ayupov 
467c7af4f38SAmir Ayupov   auto MemEventsErrorCallback = [&](int ReturnCode, StringRef ErrBuf) {
468c7af4f38SAmir Ayupov     Regex NoData("Samples for '.*' event do not have ADDR attribute set. "
469c7af4f38SAmir Ayupov                  "Cannot print 'addr' field.");
470c7af4f38SAmir Ayupov     if (!NoData.match(ErrBuf))
471c7af4f38SAmir Ayupov       ErrorCallback(ReturnCode, ErrBuf);
472a34c753fSRafael Auler   };
473a34c753fSRafael Auler 
4742abcbbd9SMaksim Panchenko   if (BC.IsLinuxKernel) {
475a34c753fSRafael Auler     // Current MMap parsing logic does not work with linux kernel.
476a34c753fSRafael Auler     // MMap entries for linux kernel uses PERF_RECORD_MMAP
477a34c753fSRafael Auler     // format instead of typical PERF_RECORD_MMAP2 format.
478a34c753fSRafael Auler     // Since linux kernel address mapping is absolute (same as
479a34c753fSRafael Auler     // in the ELF file), we avoid parsing MMap in linux kernel mode.
480a34c753fSRafael Auler     // While generating optimized linux kernel binary, we may need
481a34c753fSRafael Auler     // to parse MMap entries.
482a34c753fSRafael Auler 
483a34c753fSRafael Auler     // In linux kernel mode, we analyze and optimize
484a34c753fSRafael Auler     // all linux kernel binary instructions, irrespective
485a34c753fSRafael Auler     // of whether they are due to system calls or due to
486a34c753fSRafael Auler     // interrupts. Therefore, we cannot ignore interrupt
487a34c753fSRafael Auler     // in Linux kernel mode.
488a34c753fSRafael Auler     opts::IgnoreInterruptLBR = false;
489a34c753fSRafael Auler   } else {
490c7af4f38SAmir Ayupov     prepareToParse("mmap events", MMapEventsPPI, ErrorCallback);
491def464aaSAmir Ayupov     if (parseMMapEvents())
492a34c753fSRafael Auler       errs() << "PERF2BOLT: failed to parse mmap events\n";
493a34c753fSRafael Auler   }
494a34c753fSRafael Auler 
495c7af4f38SAmir Ayupov   prepareToParse("task events", TaskEventsPPI, ErrorCallback);
496def464aaSAmir Ayupov   if (parseTaskEvents())
497a34c753fSRafael Auler     errs() << "PERF2BOLT: failed to parse task events\n";
498a34c753fSRafael Auler 
499a34c753fSRafael Auler   filterBinaryMMapInfo();
500c7af4f38SAmir Ayupov   prepareToParse("events", MainEventsPPI, ErrorCallback);
501a34c753fSRafael Auler 
502a34c753fSRafael Auler   if (opts::HeatmapMode) {
503a34c753fSRafael Auler     if (std::error_code EC = printLBRHeatMap()) {
504a34c753fSRafael Auler       errs() << "ERROR: failed to print heat map: " << EC.message() << '\n';
505a34c753fSRafael Auler       exit(1);
506a34c753fSRafael Auler     }
507a34c753fSRafael Auler     exit(0);
508a34c753fSRafael Auler   }
509a34c753fSRafael Auler 
510a34c753fSRafael Auler   if ((!opts::BasicAggregation && parseBranchEvents()) ||
511def464aaSAmir Ayupov       (opts::BasicAggregation && parseBasicEvents()))
512a34c753fSRafael Auler     errs() << "PERF2BOLT: failed to parse samples\n";
513a34c753fSRafael Auler 
514a34c753fSRafael Auler   // Special handling for memory events
515c7af4f38SAmir Ayupov   if (prepareToParse("mem events", MemEventsPPI, MemEventsErrorCallback))
516a34c753fSRafael Auler     return Error::success();
517a34c753fSRafael Auler 
518def464aaSAmir Ayupov   if (const std::error_code EC = parseMemEvents())
51940c2e0faSMaksim Panchenko     errs() << "PERF2BOLT: failed to parse memory events: " << EC.message()
52040c2e0faSMaksim Panchenko            << '\n';
521a34c753fSRafael Auler 
522a34c753fSRafael Auler   deleteTempFiles();
523a34c753fSRafael Auler 
524a34c753fSRafael Auler   return Error::success();
525a34c753fSRafael Auler }
526a34c753fSRafael Auler 
527a34c753fSRafael Auler Error DataAggregator::readProfile(BinaryContext &BC) {
528a34c753fSRafael Auler   processProfile(BC);
529a34c753fSRafael Auler 
530a34c753fSRafael Auler   for (auto &BFI : BC.getBinaryFunctions()) {
531a34c753fSRafael Auler     BinaryFunction &Function = BFI.second;
532a34c753fSRafael Auler     convertBranchData(Function);
533a34c753fSRafael Auler   }
534a34c753fSRafael Auler 
53562806811SAmir Ayupov   if (opts::AggregateOnly) {
53662806811SAmir Ayupov     if (opts::ProfileFormat == opts::ProfileFormatKind::PF_Fdata)
537def464aaSAmir Ayupov       if (std::error_code EC = writeAggregatedFile(opts::OutputFilename))
538a34c753fSRafael Auler         report_error("cannot create output data file", EC);
53962806811SAmir Ayupov 
54062806811SAmir Ayupov     // BAT YAML is handled by DataAggregator since normal YAML output requires
54162806811SAmir Ayupov     // CFG which is not available in BAT mode.
54262806811SAmir Ayupov     if (usesBAT()) {
54362806811SAmir Ayupov       if (opts::ProfileFormat == opts::ProfileFormatKind::PF_YAML)
54462806811SAmir Ayupov         if (std::error_code EC = writeBATYAML(BC, opts::OutputFilename))
54562806811SAmir Ayupov           report_error("cannot create output data file", EC);
54662806811SAmir Ayupov       if (!opts::SaveProfile.empty())
54762806811SAmir Ayupov         if (std::error_code EC = writeBATYAML(BC, opts::SaveProfile))
54862806811SAmir Ayupov           report_error("cannot create output data file", EC);
54962806811SAmir Ayupov     }
550a34c753fSRafael Auler   }
551a34c753fSRafael Auler 
552a34c753fSRafael Auler   return Error::success();
553a34c753fSRafael Auler }
554a34c753fSRafael Auler 
555a34c753fSRafael Auler bool DataAggregator::mayHaveProfileData(const BinaryFunction &Function) {
556a34c753fSRafael Auler   return Function.hasProfileAvailable();
557a34c753fSRafael Auler }
558a34c753fSRafael Auler 
559a34c753fSRafael Auler void DataAggregator::processProfile(BinaryContext &BC) {
560a34c753fSRafael Auler   if (opts::ReadPreAggregated)
561a34c753fSRafael Auler     processPreAggregated();
562a34c753fSRafael Auler   else if (opts::BasicAggregation)
563a34c753fSRafael Auler     processBasicEvents();
564a34c753fSRafael Auler   else
565a34c753fSRafael Auler     processBranchEvents();
566a34c753fSRafael Auler 
567a34c753fSRafael Auler   processMemEvents();
568a34c753fSRafael Auler 
569a34c753fSRafael Auler   // Mark all functions with registered events as having a valid profile.
570a34c753fSRafael Auler   const auto Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE
571a34c753fSRafael Auler                                             : BinaryFunction::PF_LBR;
5724a7966eaSAmir Ayupov   for (auto &BFI : BC.getBinaryFunctions()) {
5734a7966eaSAmir Ayupov     BinaryFunction &BF = BFI.second;
57408916cefSAmir Ayupov     FuncBranchData *FBD = getBranchData(BF);
57508916cefSAmir Ayupov     if (FBD || getFuncSampleData(BF.getNames())) {
576a34c753fSRafael Auler       BF.markProfiled(Flags);
57708916cefSAmir Ayupov       if (FBD)
57808916cefSAmir Ayupov         BF.RawBranchCount = FBD->getNumExecutedBranches();
57908916cefSAmir Ayupov     }
580a34c753fSRafael Auler   }
581a34c753fSRafael Auler 
582224e4cc5SAmir Ayupov   for (auto &FuncBranches : NamesToBranches)
583224e4cc5SAmir Ayupov     llvm::stable_sort(FuncBranches.second.Data);
584224e4cc5SAmir Ayupov 
585224e4cc5SAmir Ayupov   for (auto &MemEvents : NamesToMemEvents)
586224e4cc5SAmir Ayupov     llvm::stable_sort(MemEvents.second.Data);
587224e4cc5SAmir Ayupov 
588a34c753fSRafael Auler   // Release intermediate storage.
589a34c753fSRafael Auler   clear(BranchLBRs);
590a34c753fSRafael Auler   clear(FallthroughLBRs);
591a34c753fSRafael Auler   clear(AggregatedLBRs);
592a34c753fSRafael Auler   clear(BasicSamples);
593a34c753fSRafael Auler   clear(MemSamples);
594a34c753fSRafael Auler }
595a34c753fSRafael Auler 
596a34c753fSRafael Auler BinaryFunction *
597a34c753fSRafael Auler DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const {
598a34c753fSRafael Auler   if (!BC->containsAddress(Address))
599a34c753fSRafael Auler     return nullptr;
600a34c753fSRafael Auler 
601a34c753fSRafael Auler   return BC->getBinaryFunctionContainingAddress(Address, /*CheckPastEnd=*/false,
602a34c753fSRafael Auler                                                 /*UseMaxSize=*/true);
603a34c753fSRafael Auler }
604a34c753fSRafael Auler 
60588409926SAmir Ayupov BinaryFunction *
60688409926SAmir Ayupov DataAggregator::getBATParentFunction(const BinaryFunction &Func) const {
60788409926SAmir Ayupov   if (BAT)
60888409926SAmir Ayupov     if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress()))
60988409926SAmir Ayupov       return getBinaryFunctionContainingAddress(HotAddr);
61088409926SAmir Ayupov   return nullptr;
61188409926SAmir Ayupov }
61288409926SAmir Ayupov 
61397025bd9SAmir Ayupov StringRef DataAggregator::getLocationName(const BinaryFunction &Func,
61497025bd9SAmir Ayupov                                           bool BAT) {
615a34c753fSRafael Auler   if (!BAT)
616a34c753fSRafael Auler     return Func.getOneName();
617a34c753fSRafael Auler 
618a34c753fSRafael Auler   const BinaryFunction *OrigFunc = &Func;
619a34c753fSRafael Auler   // If it is a local function, prefer the name containing the file name where
620a34c753fSRafael Auler   // the local function was declared
621a34c753fSRafael Auler   for (StringRef AlternativeName : OrigFunc->getNames()) {
622a34c753fSRafael Auler     size_t FileNameIdx = AlternativeName.find('/');
623a34c753fSRafael Auler     // Confirm the alternative name has the pattern Symbol/FileName/1 before
624a34c753fSRafael Auler     // using it
625a34c753fSRafael Auler     if (FileNameIdx == StringRef::npos ||
626a34c753fSRafael Auler         AlternativeName.find('/', FileNameIdx + 1) == StringRef::npos)
627a34c753fSRafael Auler       continue;
628a34c753fSRafael Auler     return AlternativeName;
629a34c753fSRafael Auler   }
630a34c753fSRafael Auler   return OrigFunc->getOneName();
631a34c753fSRafael Auler }
632a34c753fSRafael Auler 
63388409926SAmir Ayupov bool DataAggregator::doSample(BinaryFunction &OrigFunc, uint64_t Address,
634a34c753fSRafael Auler                               uint64_t Count) {
63588409926SAmir Ayupov   BinaryFunction *ParentFunc = getBATParentFunction(OrigFunc);
63688409926SAmir Ayupov   BinaryFunction &Func = ParentFunc ? *ParentFunc : OrigFunc;
63788409926SAmir Ayupov   if (ParentFunc)
63888409926SAmir Ayupov     NumColdSamples += Count;
63988409926SAmir Ayupov 
640a34c753fSRafael Auler   auto I = NamesToSamples.find(Func.getOneName());
641a34c753fSRafael Auler   if (I == NamesToSamples.end()) {
642a34c753fSRafael Auler     bool Success;
64397025bd9SAmir Ayupov     StringRef LocName = getLocationName(Func, BAT);
64440c2e0faSMaksim Panchenko     std::tie(I, Success) = NamesToSamples.insert(
64540c2e0faSMaksim Panchenko         std::make_pair(Func.getOneName(),
646a34c753fSRafael Auler                        FuncSampleData(LocName, FuncSampleData::ContainerTy())));
647a34c753fSRafael Auler   }
648a34c753fSRafael Auler 
649a34c753fSRafael Auler   Address -= Func.getAddress();
650a34c753fSRafael Auler   if (BAT)
651fc0ced73SRafael Auler     Address = BAT->translate(Func.getAddress(), Address, /*IsBranchSrc=*/false);
652a34c753fSRafael Auler 
653a34c753fSRafael Auler   I->second.bumpCount(Address, Count);
654a34c753fSRafael Auler   return true;
655a34c753fSRafael Auler }
656a34c753fSRafael Auler 
657a34c753fSRafael Auler bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From,
658a34c753fSRafael Auler                                    uint64_t To, uint64_t Count,
659a34c753fSRafael Auler                                    uint64_t Mispreds) {
660a34c753fSRafael Auler   FuncBranchData *AggrData = getBranchData(Func);
661a34c753fSRafael Auler   if (!AggrData) {
662a34c753fSRafael Auler     AggrData = &NamesToBranches[Func.getOneName()];
66397025bd9SAmir Ayupov     AggrData->Name = getLocationName(Func, BAT);
664a34c753fSRafael Auler     setBranchData(Func, AggrData);
665a34c753fSRafael Auler   }
666a34c753fSRafael Auler 
6674a7966eaSAmir Ayupov   LLVM_DEBUG(dbgs() << "BOLT-DEBUG: bumpBranchCount: "
6684a7966eaSAmir Ayupov                     << formatv("{0} @ {1:x} -> {0} @ {2:x}\n", Func, From, To));
669a34c753fSRafael Auler   AggrData->bumpBranchCount(From, To, Count, Mispreds);
670a34c753fSRafael Auler   return true;
671a34c753fSRafael Auler }
672a34c753fSRafael Auler 
673a34c753fSRafael Auler bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
674a34c753fSRafael Auler                                    BinaryFunction *ToFunc, uint64_t From,
675a34c753fSRafael Auler                                    uint64_t To, uint64_t Count,
676a34c753fSRafael Auler                                    uint64_t Mispreds) {
677a34c753fSRafael Auler   FuncBranchData *FromAggrData = nullptr;
678a34c753fSRafael Auler   FuncBranchData *ToAggrData = nullptr;
679a34c753fSRafael Auler   StringRef SrcFunc;
680a34c753fSRafael Auler   StringRef DstFunc;
681a34c753fSRafael Auler   if (FromFunc) {
68297025bd9SAmir Ayupov     SrcFunc = getLocationName(*FromFunc, BAT);
683a34c753fSRafael Auler     FromAggrData = getBranchData(*FromFunc);
684a34c753fSRafael Auler     if (!FromAggrData) {
685a34c753fSRafael Auler       FromAggrData = &NamesToBranches[FromFunc->getOneName()];
686a34c753fSRafael Auler       FromAggrData->Name = SrcFunc;
687a34c753fSRafael Auler       setBranchData(*FromFunc, FromAggrData);
688a34c753fSRafael Auler     }
689a34c753fSRafael Auler 
690a34c753fSRafael Auler     recordExit(*FromFunc, From, Mispreds, Count);
691a34c753fSRafael Auler   }
692a34c753fSRafael Auler   if (ToFunc) {
69397025bd9SAmir Ayupov     DstFunc = getLocationName(*ToFunc, BAT);
694a34c753fSRafael Auler     ToAggrData = getBranchData(*ToFunc);
695a34c753fSRafael Auler     if (!ToAggrData) {
696a34c753fSRafael Auler       ToAggrData = &NamesToBranches[ToFunc->getOneName()];
697a34c753fSRafael Auler       ToAggrData->Name = DstFunc;
698a34c753fSRafael Auler       setBranchData(*ToFunc, ToAggrData);
699a34c753fSRafael Auler     }
700a34c753fSRafael Auler 
701a34c753fSRafael Auler     recordEntry(*ToFunc, To, Mispreds, Count);
702a34c753fSRafael Auler   }
703a34c753fSRafael Auler 
704a34c753fSRafael Auler   if (FromAggrData)
705a34c753fSRafael Auler     FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To),
706a34c753fSRafael Auler                                 Count, Mispreds);
707a34c753fSRafael Auler   if (ToAggrData)
708a34c753fSRafael Auler     ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To,
709a34c753fSRafael Auler                                Count, Mispreds);
710a34c753fSRafael Auler   return true;
711a34c753fSRafael Auler }
712a34c753fSRafael Auler 
713a34c753fSRafael Auler bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count,
71474e6478fSAmir Ayupov                               uint64_t Mispreds, bool IsPreagg) {
71574e6478fSAmir Ayupov   // Returns whether \p Offset in \p Func contains a return instruction.
71674e6478fSAmir Ayupov   auto checkReturn = [&](const BinaryFunction &Func, const uint64_t Offset) {
71774e6478fSAmir Ayupov     auto isReturn = [&](auto MI) { return MI && BC->MIB->isReturn(*MI); };
71874e6478fSAmir Ayupov     return Func.hasInstructions()
71974e6478fSAmir Ayupov                ? isReturn(Func.getInstructionAtOffset(Offset))
72074e6478fSAmir Ayupov                : isReturn(Func.disassembleInstructionAtOffset(Offset));
721db29f20fSAmir Ayupov   };
72274e6478fSAmir Ayupov 
72374e6478fSAmir Ayupov   // Returns whether \p Offset in \p Func may be a call continuation excluding
72474e6478fSAmir Ayupov   // entry points and landing pads.
72574e6478fSAmir Ayupov   auto checkCallCont = [&](const BinaryFunction &Func, const uint64_t Offset) {
72674e6478fSAmir Ayupov     // No call continuation at a function start.
72774e6478fSAmir Ayupov     if (!Offset)
72874e6478fSAmir Ayupov       return false;
72974e6478fSAmir Ayupov 
73074e6478fSAmir Ayupov     // FIXME: support BAT case where the function might be in empty state
73174e6478fSAmir Ayupov     // (split fragments declared non-simple).
73274e6478fSAmir Ayupov     if (!Func.hasCFG())
73374e6478fSAmir Ayupov       return false;
73474e6478fSAmir Ayupov 
73574e6478fSAmir Ayupov     // The offset should not be an entry point or a landing pad.
73674e6478fSAmir Ayupov     const BinaryBasicBlock *ContBB = Func.getBasicBlockAtOffset(Offset);
73774e6478fSAmir Ayupov     return ContBB && !ContBB->isEntryPoint() && !ContBB->isLandingPad();
73874e6478fSAmir Ayupov   };
73974e6478fSAmir Ayupov 
74074e6478fSAmir Ayupov   // Mutates \p Addr to an offset into the containing function, performing BAT
74174e6478fSAmir Ayupov   // offset translation and parent lookup.
74274e6478fSAmir Ayupov   //
74374e6478fSAmir Ayupov   // Returns the containing function (or BAT parent) and whether the address
74474e6478fSAmir Ayupov   // corresponds to a return (if \p IsFrom) or a call continuation (otherwise).
74574e6478fSAmir Ayupov   auto handleAddress = [&](uint64_t &Addr, bool IsFrom) {
74674e6478fSAmir Ayupov     BinaryFunction *Func = getBinaryFunctionContainingAddress(Addr);
74774e6478fSAmir Ayupov     if (!Func)
74874e6478fSAmir Ayupov       return std::pair{Func, false};
74974e6478fSAmir Ayupov 
75074e6478fSAmir Ayupov     Addr -= Func->getAddress();
75174e6478fSAmir Ayupov 
75274e6478fSAmir Ayupov     bool IsRetOrCallCont =
75374e6478fSAmir Ayupov         IsFrom ? checkReturn(*Func, Addr) : checkCallCont(*Func, Addr);
75488409926SAmir Ayupov 
75588409926SAmir Ayupov     if (BAT)
75688409926SAmir Ayupov       Addr = BAT->translate(Func->getAddress(), Addr, IsFrom);
75788409926SAmir Ayupov 
75874e6478fSAmir Ayupov     BinaryFunction *ParentFunc = getBATParentFunction(*Func);
75974e6478fSAmir Ayupov     if (!ParentFunc)
76074e6478fSAmir Ayupov       return std::pair{Func, IsRetOrCallCont};
76174e6478fSAmir Ayupov 
76288409926SAmir Ayupov     if (IsFrom)
76388409926SAmir Ayupov       NumColdSamples += Count;
76488409926SAmir Ayupov 
76574e6478fSAmir Ayupov     return std::pair{ParentFunc, IsRetOrCallCont};
76688409926SAmir Ayupov   };
76788409926SAmir Ayupov 
76874e6478fSAmir Ayupov   uint64_t ToOrig = To;
76974e6478fSAmir Ayupov   auto [FromFunc, IsReturn] = handleAddress(From, /*IsFrom*/ true);
77074e6478fSAmir Ayupov   auto [ToFunc, IsCallCont] = handleAddress(To, /*IsFrom*/ false);
77174e6478fSAmir Ayupov   if (!FromFunc && !ToFunc)
77274e6478fSAmir Ayupov     return false;
77374e6478fSAmir Ayupov 
77474e6478fSAmir Ayupov   // Record call to continuation trace.
77574e6478fSAmir Ayupov   if (IsPreagg && FromFunc != ToFunc && (IsReturn || IsCallCont)) {
77674e6478fSAmir Ayupov     LBREntry First{ToOrig - 1, ToOrig - 1, false};
77774e6478fSAmir Ayupov     LBREntry Second{ToOrig, ToOrig, false};
77874e6478fSAmir Ayupov     return doTrace(First, Second, Count);
77974e6478fSAmir Ayupov   }
780db29f20fSAmir Ayupov   // Ignore returns.
781db29f20fSAmir Ayupov   if (IsReturn)
782db29f20fSAmir Ayupov     return true;
783a34c753fSRafael Auler 
784c061f755SAmir Ayupov   // Treat recursive control transfers as inter-branches.
78588409926SAmir Ayupov   if (FromFunc == ToFunc && To != 0) {
78688409926SAmir Ayupov     recordBranch(*FromFunc, From, To, Count, Mispreds);
787a34c753fSRafael Auler     return doIntraBranch(*FromFunc, From, To, Count, Mispreds);
788a34c753fSRafael Auler   }
789a34c753fSRafael Auler 
790a34c753fSRafael Auler   return doInterBranch(FromFunc, ToFunc, From, To, Count, Mispreds);
791a34c753fSRafael Auler }
792a34c753fSRafael Auler 
793a34c753fSRafael Auler bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second,
794a34c753fSRafael Auler                              uint64_t Count) {
795a34c753fSRafael Auler   BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(First.To);
796a34c753fSRafael Auler   BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From);
797a34c753fSRafael Auler   if (!FromFunc || !ToFunc) {
798713b2853SAmir Ayupov     LLVM_DEBUG({
79974e6478fSAmir Ayupov       dbgs() << "Out of range trace starting in ";
80074e6478fSAmir Ayupov       if (FromFunc)
80174e6478fSAmir Ayupov         dbgs() << formatv("{0} @ {1:x}", *FromFunc,
80274e6478fSAmir Ayupov                           First.To - FromFunc->getAddress());
80374e6478fSAmir Ayupov       else
80474e6478fSAmir Ayupov         dbgs() << Twine::utohexstr(First.To);
80574e6478fSAmir Ayupov       dbgs() << " and ending in ";
80674e6478fSAmir Ayupov       if (ToFunc)
80774e6478fSAmir Ayupov         dbgs() << formatv("{0} @ {1:x}", *ToFunc,
80874e6478fSAmir Ayupov                           Second.From - ToFunc->getAddress());
80974e6478fSAmir Ayupov       else
81074e6478fSAmir Ayupov         dbgs() << Twine::utohexstr(Second.From);
81174e6478fSAmir Ayupov       dbgs() << '\n';
812713b2853SAmir Ayupov     });
813a34c753fSRafael Auler     NumLongRangeTraces += Count;
814a34c753fSRafael Auler     return false;
815a34c753fSRafael Auler   }
816a34c753fSRafael Auler   if (FromFunc != ToFunc) {
817a34c753fSRafael Auler     NumInvalidTraces += Count;
818713b2853SAmir Ayupov     LLVM_DEBUG({
819a34c753fSRafael Auler       dbgs() << "Invalid trace starting in " << FromFunc->getPrintName()
820713b2853SAmir Ayupov              << formatv(" @ {0:x}", First.To - FromFunc->getAddress())
821713b2853SAmir Ayupov              << " and ending in " << ToFunc->getPrintName()
822713b2853SAmir Ayupov              << formatv(" @ {0:x}\n", Second.From - ToFunc->getAddress());
823713b2853SAmir Ayupov     });
824a34c753fSRafael Auler     return false;
825a34c753fSRafael Auler   }
826a34c753fSRafael Auler 
8276ee5ff95SAmir Ayupov   // Set ParentFunc to BAT parent function or FromFunc itself.
8286ee5ff95SAmir Ayupov   BinaryFunction *ParentFunc = getBATParentFunction(*FromFunc);
8296ee5ff95SAmir Ayupov   if (!ParentFunc)
8306ee5ff95SAmir Ayupov     ParentFunc = FromFunc;
8316ee5ff95SAmir Ayupov   ParentFunc->SampleCountInBytes += Count * (Second.From - First.To);
8326ee5ff95SAmir Ayupov 
8333d573fdbSAmir Ayupov   std::optional<BoltAddressTranslation::FallthroughListTy> FTs =
834fc0ced73SRafael Auler       BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To,
835fc0ced73SRafael Auler                                         Second.From)
836a34c753fSRafael Auler           : getFallthroughsInTrace(*FromFunc, First, Second, Count);
837a34c753fSRafael Auler   if (!FTs) {
838a34c753fSRafael Auler     LLVM_DEBUG(
839a34c753fSRafael Auler         dbgs() << "Invalid trace starting in " << FromFunc->getPrintName()
840a34c753fSRafael Auler                << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress())
841a34c753fSRafael Auler                << " and ending in " << ToFunc->getPrintName() << " @ "
842a34c753fSRafael Auler                << ToFunc->getPrintName() << " @ "
843a34c753fSRafael Auler                << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n');
844a34c753fSRafael Auler     NumInvalidTraces += Count;
845a34c753fSRafael Auler     return false;
846a34c753fSRafael Auler   }
847a34c753fSRafael Auler 
848a34c753fSRafael Auler   LLVM_DEBUG(dbgs() << "Processing " << FTs->size() << " fallthroughs for "
849a34c753fSRafael Auler                     << FromFunc->getPrintName() << ":"
850a34c753fSRafael Auler                     << Twine::utohexstr(First.To) << " to "
851a34c753fSRafael Auler                     << Twine::utohexstr(Second.From) << ".\n");
85288409926SAmir Ayupov   for (auto [From, To] : *FTs) {
85388409926SAmir Ayupov     if (BAT) {
85488409926SAmir Ayupov       From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true);
85588409926SAmir Ayupov       To = BAT->translate(FromFunc->getAddress(), To, /*IsBranchSrc=*/false);
85688409926SAmir Ayupov     }
8576ee5ff95SAmir Ayupov     doIntraBranch(*ParentFunc, From, To, Count, false);
85888409926SAmir Ayupov   }
859a34c753fSRafael Auler 
860a34c753fSRafael Auler   return true;
861a34c753fSRafael Auler }
862a34c753fSRafael Auler 
863f2d71305SAmir Ayupov std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>>
864f2d71305SAmir Ayupov DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
865f2d71305SAmir Ayupov                                        const LBREntry &FirstLBR,
866f2d71305SAmir Ayupov                                        const LBREntry &SecondLBR,
867f2d71305SAmir Ayupov                                        uint64_t Count) const {
868f2d71305SAmir Ayupov   SmallVector<std::pair<uint64_t, uint64_t>, 16> Branches;
869f2d71305SAmir Ayupov 
870a34c753fSRafael Auler   BinaryContext &BC = BF.getBinaryContext();
871a34c753fSRafael Auler 
872a34c753fSRafael Auler   if (!BF.isSimple())
873f2d71305SAmir Ayupov     return std::nullopt;
874a34c753fSRafael Auler 
875a34c753fSRafael Auler   assert(BF.hasCFG() && "can only record traces in CFG state");
876a34c753fSRafael Auler 
877a34c753fSRafael Auler   // Offsets of the trace within this function.
878a34c753fSRafael Auler   const uint64_t From = FirstLBR.To - BF.getAddress();
879a34c753fSRafael Auler   const uint64_t To = SecondLBR.From - BF.getAddress();
880a34c753fSRafael Auler 
881a34c753fSRafael Auler   if (From > To)
882f2d71305SAmir Ayupov     return std::nullopt;
883a34c753fSRafael Auler 
884d5c03defSFabian Parzefall   const BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From);
885d5c03defSFabian Parzefall   const BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To);
886a34c753fSRafael Auler 
887a34c753fSRafael Auler   if (!FromBB || !ToBB)
888f2d71305SAmir Ayupov     return std::nullopt;
889a34c753fSRafael Auler 
890a34c753fSRafael Auler   // Adjust FromBB if the first LBR is a return from the last instruction in
891a34c753fSRafael Auler   // the previous block (that instruction should be a call).
892a34c753fSRafael Auler   if (From == FromBB->getOffset() && !BF.containsAddress(FirstLBR.From) &&
893a34c753fSRafael Auler       !FromBB->isEntryPoint() && !FromBB->isLandingPad()) {
894d5c03defSFabian Parzefall     const BinaryBasicBlock *PrevBB =
895d5c03defSFabian Parzefall         BF.getLayout().getBlock(FromBB->getIndex() - 1);
896a34c753fSRafael Auler     if (PrevBB->getSuccessor(FromBB->getLabel())) {
897a34c753fSRafael Auler       const MCInst *Instr = PrevBB->getLastNonPseudoInstr();
898def464aaSAmir Ayupov       if (Instr && BC.MIB->isCall(*Instr))
899a34c753fSRafael Auler         FromBB = PrevBB;
900def464aaSAmir Ayupov       else
901a34c753fSRafael Auler         LLVM_DEBUG(dbgs() << "invalid incoming LBR (no call): " << FirstLBR
902a34c753fSRafael Auler                           << '\n');
903a34c753fSRafael Auler     } else {
904a34c753fSRafael Auler       LLVM_DEBUG(dbgs() << "invalid incoming LBR: " << FirstLBR << '\n');
905a34c753fSRafael Auler     }
906a34c753fSRafael Auler   }
907a34c753fSRafael Auler 
908a34c753fSRafael Auler   // Fill out information for fall-through edges. The From and To could be
909a34c753fSRafael Auler   // within the same basic block, e.g. when two call instructions are in the
910a34c753fSRafael Auler   // same block. In this case we skip the processing.
911def464aaSAmir Ayupov   if (FromBB == ToBB)
912f2d71305SAmir Ayupov     return Branches;
913a34c753fSRafael Auler 
914a34c753fSRafael Auler   // Process blocks in the original layout order.
9158477bc67SFabian Parzefall   BinaryBasicBlock *BB = BF.getLayout().getBlock(FromBB->getIndex());
916a34c753fSRafael Auler   assert(BB == FromBB && "index mismatch");
917a34c753fSRafael Auler   while (BB != ToBB) {
9188477bc67SFabian Parzefall     BinaryBasicBlock *NextBB = BF.getLayout().getBlock(BB->getIndex() + 1);
919a34c753fSRafael Auler     assert((NextBB && NextBB->getOffset() > BB->getOffset()) && "bad layout");
920a34c753fSRafael Auler 
921a34c753fSRafael Auler     // Check for bad LBRs.
922a34c753fSRafael Auler     if (!BB->getSuccessor(NextBB->getLabel())) {
923a34c753fSRafael Auler       LLVM_DEBUG(dbgs() << "no fall-through for the trace:\n"
924a34c753fSRafael Auler                         << "  " << FirstLBR << '\n'
925a34c753fSRafael Auler                         << "  " << SecondLBR << '\n');
926f2d71305SAmir Ayupov       return std::nullopt;
927a34c753fSRafael Auler     }
928a34c753fSRafael Auler 
929a34c753fSRafael Auler     const MCInst *Instr = BB->getLastNonPseudoInstr();
930a34c753fSRafael Auler     uint64_t Offset = 0;
931def464aaSAmir Ayupov     if (Instr)
932a9cd49d5SAmir Ayupov       Offset = BC.MIB->getOffsetWithDefault(*Instr, 0);
933def464aaSAmir Ayupov     else
934a34c753fSRafael Auler       Offset = BB->getOffset();
935def464aaSAmir Ayupov 
936bce889c8SAmir Ayupov     Branches.emplace_back(Offset, NextBB->getOffset());
937a34c753fSRafael Auler 
938a34c753fSRafael Auler     BB = NextBB;
939a34c753fSRafael Auler   }
940a34c753fSRafael Auler 
941bce889c8SAmir Ayupov   // Record fall-through jumps
942bce889c8SAmir Ayupov   for (const auto &[FromOffset, ToOffset] : Branches) {
943bce889c8SAmir Ayupov     BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(FromOffset);
944bce889c8SAmir Ayupov     BinaryBasicBlock *ToBB = BF.getBasicBlockAtOffset(ToOffset);
945bce889c8SAmir Ayupov     assert(FromBB && ToBB);
946bce889c8SAmir Ayupov     BinaryBasicBlock::BinaryBranchInfo &BI = FromBB->getBranchInfo(*ToBB);
947bce889c8SAmir Ayupov     BI.Count += Count;
948bce889c8SAmir Ayupov   }
949bce889c8SAmir Ayupov 
950f2d71305SAmir Ayupov   return Branches;
951a34c753fSRafael Auler }
952a34c753fSRafael Auler 
953a34c753fSRafael Auler bool DataAggregator::recordEntry(BinaryFunction &BF, uint64_t To, bool Mispred,
954a34c753fSRafael Auler                                  uint64_t Count) const {
955a34c753fSRafael Auler   if (To > BF.getSize())
956a34c753fSRafael Auler     return false;
957a34c753fSRafael Auler 
958a34c753fSRafael Auler   if (!BF.hasProfile())
959a34c753fSRafael Auler     BF.ExecutionCount = 0;
960a34c753fSRafael Auler 
961a34c753fSRafael Auler   BinaryBasicBlock *EntryBB = nullptr;
962a34c753fSRafael Auler   if (To == 0) {
963a34c753fSRafael Auler     BF.ExecutionCount += Count;
964a34c753fSRafael Auler     if (!BF.empty())
965a34c753fSRafael Auler       EntryBB = &BF.front();
966a34c753fSRafael Auler   } else if (BinaryBasicBlock *BB = BF.getBasicBlockAtOffset(To)) {
967a34c753fSRafael Auler     if (BB->isEntryPoint())
968a34c753fSRafael Auler       EntryBB = BB;
969a34c753fSRafael Auler   }
970a34c753fSRafael Auler 
971a34c753fSRafael Auler   if (EntryBB)
972a34c753fSRafael Auler     EntryBB->setExecutionCount(EntryBB->getKnownExecutionCount() + Count);
973a34c753fSRafael Auler 
974a34c753fSRafael Auler   return true;
975a34c753fSRafael Auler }
976a34c753fSRafael Auler 
97740c2e0faSMaksim Panchenko bool DataAggregator::recordExit(BinaryFunction &BF, uint64_t From, bool Mispred,
97840c2e0faSMaksim Panchenko                                 uint64_t Count) const {
979a34c753fSRafael Auler   if (!BF.isSimple() || From > BF.getSize())
980a34c753fSRafael Auler     return false;
981a34c753fSRafael Auler 
982a34c753fSRafael Auler   if (!BF.hasProfile())
983a34c753fSRafael Auler     BF.ExecutionCount = 0;
984a34c753fSRafael Auler 
985a34c753fSRafael Auler   return true;
986a34c753fSRafael Auler }
987a34c753fSRafael Auler 
988a34c753fSRafael Auler ErrorOr<LBREntry> DataAggregator::parseLBREntry() {
989a34c753fSRafael Auler   LBREntry Res;
990a34c753fSRafael Auler   ErrorOr<StringRef> FromStrRes = parseString('/');
991a34c753fSRafael Auler   if (std::error_code EC = FromStrRes.getError())
992a34c753fSRafael Auler     return EC;
993a34c753fSRafael Auler   StringRef OffsetStr = FromStrRes.get();
994a34c753fSRafael Auler   if (OffsetStr.getAsInteger(0, Res.From)) {
995a34c753fSRafael Auler     reportError("expected hexadecimal number with From address");
996a34c753fSRafael Auler     Diag << "Found: " << OffsetStr << "\n";
997a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
998a34c753fSRafael Auler   }
999a34c753fSRafael Auler 
1000a34c753fSRafael Auler   ErrorOr<StringRef> ToStrRes = parseString('/');
1001a34c753fSRafael Auler   if (std::error_code EC = ToStrRes.getError())
1002a34c753fSRafael Auler     return EC;
1003a34c753fSRafael Auler   OffsetStr = ToStrRes.get();
1004a34c753fSRafael Auler   if (OffsetStr.getAsInteger(0, Res.To)) {
1005a34c753fSRafael Auler     reportError("expected hexadecimal number with To address");
1006a34c753fSRafael Auler     Diag << "Found: " << OffsetStr << "\n";
1007a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1008a34c753fSRafael Auler   }
1009a34c753fSRafael Auler 
1010a34c753fSRafael Auler   ErrorOr<StringRef> MispredStrRes = parseString('/');
1011a34c753fSRafael Auler   if (std::error_code EC = MispredStrRes.getError())
1012a34c753fSRafael Auler     return EC;
1013a34c753fSRafael Auler   StringRef MispredStr = MispredStrRes.get();
1014a34c753fSRafael Auler   if (MispredStr.size() != 1 ||
1015a34c753fSRafael Auler       (MispredStr[0] != 'P' && MispredStr[0] != 'M' && MispredStr[0] != '-')) {
1016a34c753fSRafael Auler     reportError("expected single char for mispred bit");
1017a34c753fSRafael Auler     Diag << "Found: " << MispredStr << "\n";
1018a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1019a34c753fSRafael Auler   }
1020a34c753fSRafael Auler   Res.Mispred = MispredStr[0] == 'M';
1021a34c753fSRafael Auler 
102240c2e0faSMaksim Panchenko   static bool MispredWarning = true;
1023a34c753fSRafael Auler   if (MispredStr[0] == '-' && MispredWarning) {
1024a34c753fSRafael Auler     errs() << "PERF2BOLT-WARNING: misprediction bit is missing in profile\n";
1025a34c753fSRafael Auler     MispredWarning = false;
1026a34c753fSRafael Auler   }
1027a34c753fSRafael Auler 
1028a34c753fSRafael Auler   ErrorOr<StringRef> Rest = parseString(FieldSeparator, true);
1029a34c753fSRafael Auler   if (std::error_code EC = Rest.getError())
1030a34c753fSRafael Auler     return EC;
1031a34c753fSRafael Auler   if (Rest.get().size() < 5) {
1032a34c753fSRafael Auler     reportError("expected rest of LBR entry");
1033a34c753fSRafael Auler     Diag << "Found: " << Rest.get() << "\n";
1034a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1035a34c753fSRafael Auler   }
1036a34c753fSRafael Auler   return Res;
1037a34c753fSRafael Auler }
1038a34c753fSRafael Auler 
1039a34c753fSRafael Auler bool DataAggregator::checkAndConsumeFS() {
1040def464aaSAmir Ayupov   if (ParsingBuf[0] != FieldSeparator)
1041a34c753fSRafael Auler     return false;
1042def464aaSAmir Ayupov 
1043a34c753fSRafael Auler   ParsingBuf = ParsingBuf.drop_front(1);
1044a34c753fSRafael Auler   Col += 1;
1045a34c753fSRafael Auler   return true;
1046a34c753fSRafael Auler }
1047a34c753fSRafael Auler 
1048a34c753fSRafael Auler void DataAggregator::consumeRestOfLine() {
1049a34c753fSRafael Auler   size_t LineEnd = ParsingBuf.find_first_of('\n');
1050a34c753fSRafael Auler   if (LineEnd == StringRef::npos) {
1051a34c753fSRafael Auler     ParsingBuf = StringRef();
1052a34c753fSRafael Auler     Col = 0;
1053a34c753fSRafael Auler     Line += 1;
1054a34c753fSRafael Auler     return;
1055a34c753fSRafael Auler   }
1056a34c753fSRafael Auler   ParsingBuf = ParsingBuf.drop_front(LineEnd + 1);
1057a34c753fSRafael Auler   Col = 0;
1058a34c753fSRafael Auler   Line += 1;
1059a34c753fSRafael Auler }
1060a34c753fSRafael Auler 
1061ba9cc653SRafael Auler bool DataAggregator::checkNewLine() {
1062ba9cc653SRafael Auler   return ParsingBuf[0] == '\n';
1063ba9cc653SRafael Auler }
1064ba9cc653SRafael Auler 
1065a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBranchSample> DataAggregator::parseBranchSample() {
1066a34c753fSRafael Auler   PerfBranchSample Res;
1067a34c753fSRafael Auler 
106840c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
106940c2e0faSMaksim Panchenko   }
1070a34c753fSRafael Auler 
1071a34c753fSRafael Auler   ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1072a34c753fSRafael Auler   if (std::error_code EC = PIDRes.getError())
1073a34c753fSRafael Auler     return EC;
1074a34c753fSRafael Auler   auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
10752abcbbd9SMaksim Panchenko   if (!BC->IsLinuxKernel && MMapInfoIter == BinaryMMapInfo.end()) {
1076a34c753fSRafael Auler     consumeRestOfLine();
1077a34c753fSRafael Auler     return make_error_code(errc::no_such_process);
1078a34c753fSRafael Auler   }
1079a34c753fSRafael Auler 
1080a34c753fSRafael Auler   if (checkAndConsumeNewLine())
1081a34c753fSRafael Auler     return Res;
1082a34c753fSRafael Auler 
1083a34c753fSRafael Auler   while (!checkAndConsumeNewLine()) {
1084a34c753fSRafael Auler     checkAndConsumeFS();
1085a34c753fSRafael Auler 
1086a34c753fSRafael Auler     ErrorOr<LBREntry> LBRRes = parseLBREntry();
1087a34c753fSRafael Auler     if (std::error_code EC = LBRRes.getError())
1088a34c753fSRafael Auler       return EC;
1089a34c753fSRafael Auler     LBREntry LBR = LBRRes.get();
1090a34c753fSRafael Auler     if (ignoreKernelInterrupt(LBR))
1091a34c753fSRafael Auler       continue;
1092a34c753fSRafael Auler     if (!BC->HasFixedLoadAddress)
1093a34c753fSRafael Auler       adjustLBR(LBR, MMapInfoIter->second);
1094a34c753fSRafael Auler     Res.LBR.push_back(LBR);
1095a34c753fSRafael Auler   }
1096a34c753fSRafael Auler 
1097a34c753fSRafael Auler   return Res;
1098a34c753fSRafael Auler }
1099a34c753fSRafael Auler 
1100a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBasicSample> DataAggregator::parseBasicSample() {
110140c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
110240c2e0faSMaksim Panchenko   }
1103a34c753fSRafael Auler 
1104a34c753fSRafael Auler   ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1105a34c753fSRafael Auler   if (std::error_code EC = PIDRes.getError())
1106a34c753fSRafael Auler     return EC;
1107a34c753fSRafael Auler 
1108a34c753fSRafael Auler   auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
1109a34c753fSRafael Auler   if (MMapInfoIter == BinaryMMapInfo.end()) {
1110a34c753fSRafael Auler     consumeRestOfLine();
1111a34c753fSRafael Auler     return PerfBasicSample{StringRef(), 0};
1112a34c753fSRafael Auler   }
1113a34c753fSRafael Auler 
111440c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
111540c2e0faSMaksim Panchenko   }
1116a34c753fSRafael Auler 
1117a34c753fSRafael Auler   ErrorOr<StringRef> Event = parseString(FieldSeparator);
1118a34c753fSRafael Auler   if (std::error_code EC = Event.getError())
1119a34c753fSRafael Auler     return EC;
1120a34c753fSRafael Auler 
112140c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
112240c2e0faSMaksim Panchenko   }
1123a34c753fSRafael Auler 
1124a34c753fSRafael Auler   ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator, true);
1125def464aaSAmir Ayupov   if (std::error_code EC = AddrRes.getError())
1126a34c753fSRafael Auler     return EC;
1127a34c753fSRafael Auler 
1128a34c753fSRafael Auler   if (!checkAndConsumeNewLine()) {
1129a34c753fSRafael Auler     reportError("expected end of line");
1130a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1131a34c753fSRafael Auler   }
1132a34c753fSRafael Auler 
1133a34c753fSRafael Auler   uint64_t Address = *AddrRes;
1134a34c753fSRafael Auler   if (!BC->HasFixedLoadAddress)
1135a34c753fSRafael Auler     adjustAddress(Address, MMapInfoIter->second);
1136a34c753fSRafael Auler 
1137a34c753fSRafael Auler   return PerfBasicSample{Event.get(), Address};
1138a34c753fSRafael Auler }
1139a34c753fSRafael Auler 
1140a34c753fSRafael Auler ErrorOr<DataAggregator::PerfMemSample> DataAggregator::parseMemSample() {
1141a34c753fSRafael Auler   PerfMemSample Res{0, 0};
1142a34c753fSRafael Auler 
114340c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
114440c2e0faSMaksim Panchenko   }
1145a34c753fSRafael Auler 
1146a34c753fSRafael Auler   ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true);
1147a34c753fSRafael Auler   if (std::error_code EC = PIDRes.getError())
1148a34c753fSRafael Auler     return EC;
1149a34c753fSRafael Auler 
1150a34c753fSRafael Auler   auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes);
1151a34c753fSRafael Auler   if (MMapInfoIter == BinaryMMapInfo.end()) {
1152a34c753fSRafael Auler     consumeRestOfLine();
1153a34c753fSRafael Auler     return Res;
1154a34c753fSRafael Auler   }
1155a34c753fSRafael Auler 
115640c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
115740c2e0faSMaksim Panchenko   }
1158a34c753fSRafael Auler 
1159a34c753fSRafael Auler   ErrorOr<StringRef> Event = parseString(FieldSeparator);
1160a34c753fSRafael Auler   if (std::error_code EC = Event.getError())
1161a34c753fSRafael Auler     return EC;
116220f0f15aSKazu Hirata   if (!Event.get().contains("mem-loads")) {
1163a34c753fSRafael Auler     consumeRestOfLine();
1164a34c753fSRafael Auler     return Res;
1165a34c753fSRafael Auler   }
1166a34c753fSRafael Auler 
116740c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
116840c2e0faSMaksim Panchenko   }
1169a34c753fSRafael Auler 
1170a34c753fSRafael Auler   ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator);
1171def464aaSAmir Ayupov   if (std::error_code EC = AddrRes.getError())
1172a34c753fSRafael Auler     return EC;
1173a34c753fSRafael Auler 
117440c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
117540c2e0faSMaksim Panchenko   }
1176a34c753fSRafael Auler 
1177a34c753fSRafael Auler   ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true);
1178a34c753fSRafael Auler   if (std::error_code EC = PCRes.getError()) {
1179a34c753fSRafael Auler     consumeRestOfLine();
1180a34c753fSRafael Auler     return EC;
1181a34c753fSRafael Auler   }
1182a34c753fSRafael Auler 
1183a34c753fSRafael Auler   if (!checkAndConsumeNewLine()) {
1184a34c753fSRafael Auler     reportError("expected end of line");
1185a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1186a34c753fSRafael Auler   }
1187a34c753fSRafael Auler 
1188a34c753fSRafael Auler   uint64_t Address = *AddrRes;
1189a34c753fSRafael Auler   if (!BC->HasFixedLoadAddress)
1190a34c753fSRafael Auler     adjustAddress(Address, MMapInfoIter->second);
1191a34c753fSRafael Auler 
1192a34c753fSRafael Auler   return PerfMemSample{PCRes.get(), Address};
1193a34c753fSRafael Auler }
1194a34c753fSRafael Auler 
1195a34c753fSRafael Auler ErrorOr<Location> DataAggregator::parseLocationOrOffset() {
1196a34c753fSRafael Auler   auto parseOffset = [this]() -> ErrorOr<Location> {
1197a34c753fSRafael Auler     ErrorOr<uint64_t> Res = parseHexField(FieldSeparator);
1198a34c753fSRafael Auler     if (std::error_code EC = Res.getError())
1199a34c753fSRafael Auler       return EC;
1200a34c753fSRafael Auler     return Location(Res.get());
1201a34c753fSRafael Auler   };
1202a34c753fSRafael Auler 
1203a34c753fSRafael Auler   size_t Sep = ParsingBuf.find_first_of(" \n");
1204a34c753fSRafael Auler   if (Sep == StringRef::npos)
1205a34c753fSRafael Auler     return parseOffset();
1206a34c753fSRafael Auler   StringRef LookAhead = ParsingBuf.substr(0, Sep);
12071486653dSKazu Hirata   if (!LookAhead.contains(':'))
1208a34c753fSRafael Auler     return parseOffset();
1209a34c753fSRafael Auler 
1210a34c753fSRafael Auler   ErrorOr<StringRef> BuildID = parseString(':');
1211a34c753fSRafael Auler   if (std::error_code EC = BuildID.getError())
1212a34c753fSRafael Auler     return EC;
1213a34c753fSRafael Auler   ErrorOr<uint64_t> Offset = parseHexField(FieldSeparator);
1214a34c753fSRafael Auler   if (std::error_code EC = Offset.getError())
1215a34c753fSRafael Auler     return EC;
1216a34c753fSRafael Auler   return Location(true, BuildID.get(), Offset.get());
1217a34c753fSRafael Auler }
1218a34c753fSRafael Auler 
1219a34c753fSRafael Auler ErrorOr<DataAggregator::AggregatedLBREntry>
1220a34c753fSRafael Auler DataAggregator::parseAggregatedLBREntry() {
122140c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
122240c2e0faSMaksim Panchenko   }
1223a34c753fSRafael Auler 
1224a34c753fSRafael Auler   ErrorOr<StringRef> TypeOrErr = parseString(FieldSeparator);
1225a34c753fSRafael Auler   if (std::error_code EC = TypeOrErr.getError())
1226a34c753fSRafael Auler     return EC;
1227a34c753fSRafael Auler   auto Type = AggregatedLBREntry::BRANCH;
1228a34c753fSRafael Auler   if (TypeOrErr.get() == "B") {
1229a34c753fSRafael Auler     Type = AggregatedLBREntry::BRANCH;
1230a34c753fSRafael Auler   } else if (TypeOrErr.get() == "F") {
1231a34c753fSRafael Auler     Type = AggregatedLBREntry::FT;
1232a34c753fSRafael Auler   } else if (TypeOrErr.get() == "f") {
1233a34c753fSRafael Auler     Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN;
1234a34c753fSRafael Auler   } else {
1235a34c753fSRafael Auler     reportError("expected B, F or f");
1236a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1237a34c753fSRafael Auler   }
1238a34c753fSRafael Auler 
123940c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
124040c2e0faSMaksim Panchenko   }
1241a34c753fSRafael Auler   ErrorOr<Location> From = parseLocationOrOffset();
1242a34c753fSRafael Auler   if (std::error_code EC = From.getError())
1243a34c753fSRafael Auler     return EC;
1244a34c753fSRafael Auler 
124540c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
124640c2e0faSMaksim Panchenko   }
1247a34c753fSRafael Auler   ErrorOr<Location> To = parseLocationOrOffset();
1248a34c753fSRafael Auler   if (std::error_code EC = To.getError())
1249a34c753fSRafael Auler     return EC;
1250a34c753fSRafael Auler 
125140c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
125240c2e0faSMaksim Panchenko   }
1253a34c753fSRafael Auler   ErrorOr<int64_t> Frequency =
1254a34c753fSRafael Auler       parseNumberField(FieldSeparator, Type != AggregatedLBREntry::BRANCH);
1255a34c753fSRafael Auler   if (std::error_code EC = Frequency.getError())
1256a34c753fSRafael Auler     return EC;
1257a34c753fSRafael Auler 
1258a34c753fSRafael Auler   uint64_t Mispreds = 0;
1259a34c753fSRafael Auler   if (Type == AggregatedLBREntry::BRANCH) {
126040c2e0faSMaksim Panchenko     while (checkAndConsumeFS()) {
126140c2e0faSMaksim Panchenko     }
1262a34c753fSRafael Auler     ErrorOr<int64_t> MispredsOrErr = parseNumberField(FieldSeparator, true);
1263a34c753fSRafael Auler     if (std::error_code EC = MispredsOrErr.getError())
1264a34c753fSRafael Auler       return EC;
1265a34c753fSRafael Auler     Mispreds = static_cast<uint64_t>(MispredsOrErr.get());
1266a34c753fSRafael Auler   }
1267a34c753fSRafael Auler 
1268a34c753fSRafael Auler   if (!checkAndConsumeNewLine()) {
1269a34c753fSRafael Auler     reportError("expected end of line");
1270a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1271a34c753fSRafael Auler   }
1272a34c753fSRafael Auler 
1273a34c753fSRafael Auler   return AggregatedLBREntry{From.get(), To.get(),
1274a34c753fSRafael Auler                             static_cast<uint64_t>(Frequency.get()), Mispreds,
1275a34c753fSRafael Auler                             Type};
1276a34c753fSRafael Auler }
1277a34c753fSRafael Auler 
1278a34c753fSRafael Auler bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const {
1279a34c753fSRafael Auler   return opts::IgnoreInterruptLBR &&
1280a34c753fSRafael Auler          (LBR.From >= KernelBaseAddr || LBR.To >= KernelBaseAddr);
1281a34c753fSRafael Auler }
1282a34c753fSRafael Auler 
1283a34c753fSRafael Auler std::error_code DataAggregator::printLBRHeatMap() {
1284a34c753fSRafael Auler   outs() << "PERF2BOLT: parse branch events...\n";
1285a34c753fSRafael Auler   NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName,
1286a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
1287a34c753fSRafael Auler 
12882abcbbd9SMaksim Panchenko   if (BC->IsLinuxKernel) {
1289a34c753fSRafael Auler     opts::HeatmapMaxAddress = 0xffffffffffffffff;
1290a34c753fSRafael Auler     opts::HeatmapMinAddress = KernelBaseAddr;
1291a34c753fSRafael Auler   }
1292a34c753fSRafael Auler   Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress,
1293733dc3e5SRahman Lavaee              opts::HeatmapMaxAddress, getTextSections(BC));
1294a34c753fSRafael Auler   uint64_t NumTotalSamples = 0;
1295a34c753fSRafael Auler 
12960c13d97eSRahman Lavaee   if (opts::BasicAggregation) {
1297e59e5801SRahman Lavaee     while (hasData()) {
12980c13d97eSRahman Lavaee       ErrorOr<PerfBasicSample> SampleRes = parseBasicSample();
12990c13d97eSRahman Lavaee       if (std::error_code EC = SampleRes.getError()) {
13000c13d97eSRahman Lavaee         if (EC == errc::no_such_process)
13010c13d97eSRahman Lavaee           continue;
13020c13d97eSRahman Lavaee         return EC;
13030c13d97eSRahman Lavaee       }
13040c13d97eSRahman Lavaee       PerfBasicSample &Sample = SampleRes.get();
13050c13d97eSRahman Lavaee       HM.registerAddress(Sample.PC);
13060c13d97eSRahman Lavaee       NumTotalSamples++;
1307e59e5801SRahman Lavaee     }
1308e59e5801SRahman Lavaee     outs() << "HEATMAP: read " << NumTotalSamples << " basic samples\n";
13090c13d97eSRahman Lavaee   } else {
1310e59e5801SRahman Lavaee     while (hasData()) {
1311a34c753fSRafael Auler       ErrorOr<PerfBranchSample> SampleRes = parseBranchSample();
1312a34c753fSRafael Auler       if (std::error_code EC = SampleRes.getError()) {
1313a34c753fSRafael Auler         if (EC == errc::no_such_process)
1314a34c753fSRafael Auler           continue;
1315a34c753fSRafael Auler         return EC;
1316a34c753fSRafael Auler       }
1317a34c753fSRafael Auler 
1318a34c753fSRafael Auler       PerfBranchSample &Sample = SampleRes.get();
1319a34c753fSRafael Auler 
1320a34c753fSRafael Auler       // LBRs are stored in reverse execution order. NextLBR refers to the next
1321a34c753fSRafael Auler       // executed branch record.
1322a34c753fSRafael Auler       const LBREntry *NextLBR = nullptr;
1323a34c753fSRafael Auler       for (const LBREntry &LBR : Sample.LBR) {
1324a34c753fSRafael Auler         if (NextLBR) {
1325a34c753fSRafael Auler           // Record fall-through trace.
1326a34c753fSRafael Auler           const uint64_t TraceFrom = LBR.To;
1327a34c753fSRafael Auler           const uint64_t TraceTo = NextLBR->From;
1328a34c753fSRafael Auler           ++FallthroughLBRs[Trace(TraceFrom, TraceTo)].InternCount;
1329a34c753fSRafael Auler         }
1330a34c753fSRafael Auler         NextLBR = &LBR;
1331a34c753fSRafael Auler       }
1332a34c753fSRafael Auler       if (!Sample.LBR.empty()) {
1333a34c753fSRafael Auler         HM.registerAddress(Sample.LBR.front().To);
1334a34c753fSRafael Auler         HM.registerAddress(Sample.LBR.back().From);
1335a34c753fSRafael Auler       }
1336a34c753fSRafael Auler       NumTotalSamples += Sample.LBR.size();
1337a34c753fSRafael Auler     }
1338e59e5801SRahman Lavaee     outs() << "HEATMAP: read " << NumTotalSamples << " LBR samples\n";
1339e59e5801SRahman Lavaee     outs() << "HEATMAP: " << FallthroughLBRs.size() << " unique traces\n";
13400c13d97eSRahman Lavaee   }
1341a34c753fSRafael Auler 
1342a34c753fSRafael Auler   if (!NumTotalSamples) {
1343e59e5801SRahman Lavaee     if (opts::BasicAggregation) {
1344e59e5801SRahman Lavaee       errs() << "HEATMAP-ERROR: no basic event samples detected in profile. "
1345e59e5801SRahman Lavaee                 "Cannot build heatmap.";
1346e59e5801SRahman Lavaee     } else {
1347a34c753fSRafael Auler       errs() << "HEATMAP-ERROR: no LBR traces detected in profile. "
13480c13d97eSRahman Lavaee                 "Cannot build heatmap. Use -nl for building heatmap from "
13490c13d97eSRahman Lavaee                 "basic events.\n";
13500c13d97eSRahman Lavaee     }
1351a34c753fSRafael Auler     exit(1);
1352a34c753fSRafael Auler   }
1353a34c753fSRafael Auler 
1354a34c753fSRafael Auler   outs() << "HEATMAP: building heat map...\n";
1355a34c753fSRafael Auler 
1356a34c753fSRafael Auler   for (const auto &LBR : FallthroughLBRs) {
1357a34c753fSRafael Auler     const Trace &Trace = LBR.first;
1358a34c753fSRafael Auler     const FTInfo &Info = LBR.second;
1359a34c753fSRafael Auler     HM.registerAddressRange(Trace.From, Trace.To, Info.InternCount);
1360a34c753fSRafael Auler   }
1361a34c753fSRafael Auler 
1362a34c753fSRafael Auler   if (HM.getNumInvalidRanges())
1363a34c753fSRafael Auler     outs() << "HEATMAP: invalid traces: " << HM.getNumInvalidRanges() << '\n';
1364a34c753fSRafael Auler 
1365a34c753fSRafael Auler   if (!HM.size()) {
1366a34c753fSRafael Auler     errs() << "HEATMAP-ERROR: no valid traces registered\n";
1367a34c753fSRafael Auler     exit(1);
1368a34c753fSRafael Auler   }
1369a34c753fSRafael Auler 
13705c2ae5f4SVladislav Khmelevsky   HM.print(opts::OutputFilename);
13715c2ae5f4SVladislav Khmelevsky   if (opts::OutputFilename == "-")
13725c2ae5f4SVladislav Khmelevsky     HM.printCDF(opts::OutputFilename);
1373def464aaSAmir Ayupov   else
13745c2ae5f4SVladislav Khmelevsky     HM.printCDF(opts::OutputFilename + ".csv");
1375733dc3e5SRahman Lavaee   if (opts::OutputFilename == "-")
1376733dc3e5SRahman Lavaee     HM.printSectionHotness(opts::OutputFilename);
1377733dc3e5SRahman Lavaee   else
1378733dc3e5SRahman Lavaee     HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv");
1379a34c753fSRafael Auler 
1380a34c753fSRafael Auler   return std::error_code();
1381a34c753fSRafael Auler }
1382a34c753fSRafael Auler 
1383860543d9SAmir Ayupov uint64_t DataAggregator::parseLBRSample(const PerfBranchSample &Sample,
1384860543d9SAmir Ayupov                                         bool NeedsSkylakeFix) {
1385860543d9SAmir Ayupov   uint64_t NumTraces{0};
1386*e6c9cd9cSAmir Ayupov   // LBRs are stored in reverse execution order. NextLBR refers to the next
1387*e6c9cd9cSAmir Ayupov   // executed branch record.
1388*e6c9cd9cSAmir Ayupov   const LBREntry *NextLBR = nullptr;
1389860543d9SAmir Ayupov   uint32_t NumEntry = 0;
1390860543d9SAmir Ayupov   for (const LBREntry &LBR : Sample.LBR) {
1391860543d9SAmir Ayupov     ++NumEntry;
1392860543d9SAmir Ayupov     // Hardware bug workaround: Intel Skylake (which has 32 LBR entries)
1393860543d9SAmir Ayupov     // sometimes record entry 32 as an exact copy of entry 31. This will cause
1394860543d9SAmir Ayupov     // us to likely record an invalid trace and generate a stale function for
1395860543d9SAmir Ayupov     // BAT mode (non BAT disassembles the function and is able to ignore this
1396860543d9SAmir Ayupov     // trace at aggregation time). Drop first 2 entries (last two, in
1397860543d9SAmir Ayupov     // chronological order)
1398860543d9SAmir Ayupov     if (NeedsSkylakeFix && NumEntry <= 2)
1399860543d9SAmir Ayupov       continue;
1400*e6c9cd9cSAmir Ayupov     if (NextLBR) {
1401860543d9SAmir Ayupov       // Record fall-through trace.
1402860543d9SAmir Ayupov       const uint64_t TraceFrom = LBR.To;
1403*e6c9cd9cSAmir Ayupov       const uint64_t TraceTo = NextLBR->From;
1404860543d9SAmir Ayupov       const BinaryFunction *TraceBF =
1405860543d9SAmir Ayupov           getBinaryFunctionContainingAddress(TraceFrom);
1406860543d9SAmir Ayupov       if (TraceBF && TraceBF->containsAddress(TraceTo)) {
1407860543d9SAmir Ayupov         FTInfo &Info = FallthroughLBRs[Trace(TraceFrom, TraceTo)];
1408860543d9SAmir Ayupov         if (TraceBF->containsAddress(LBR.From))
1409860543d9SAmir Ayupov           ++Info.InternCount;
1410860543d9SAmir Ayupov         else
1411860543d9SAmir Ayupov           ++Info.ExternCount;
1412860543d9SAmir Ayupov       } else {
1413860543d9SAmir Ayupov         const BinaryFunction *ToFunc =
1414860543d9SAmir Ayupov             getBinaryFunctionContainingAddress(TraceTo);
1415860543d9SAmir Ayupov         if (TraceBF && ToFunc) {
1416860543d9SAmir Ayupov           LLVM_DEBUG({
1417860543d9SAmir Ayupov             dbgs() << "Invalid trace starting in " << TraceBF->getPrintName()
1418860543d9SAmir Ayupov                    << formatv(" @ {0:x}", TraceFrom - TraceBF->getAddress())
1419860543d9SAmir Ayupov                    << formatv(" and ending @ {0:x}\n", TraceTo);
1420860543d9SAmir Ayupov           });
1421860543d9SAmir Ayupov           ++NumInvalidTraces;
1422860543d9SAmir Ayupov         } else {
1423860543d9SAmir Ayupov           LLVM_DEBUG({
1424860543d9SAmir Ayupov             dbgs() << "Out of range trace starting in "
1425860543d9SAmir Ayupov                    << (TraceBF ? TraceBF->getPrintName() : "None")
1426860543d9SAmir Ayupov                    << formatv(" @ {0:x}",
1427860543d9SAmir Ayupov                               TraceFrom - (TraceBF ? TraceBF->getAddress() : 0))
1428860543d9SAmir Ayupov                    << " and ending in "
1429860543d9SAmir Ayupov                    << (ToFunc ? ToFunc->getPrintName() : "None")
1430860543d9SAmir Ayupov                    << formatv(" @ {0:x}\n",
1431860543d9SAmir Ayupov                               TraceTo - (ToFunc ? ToFunc->getAddress() : 0));
1432860543d9SAmir Ayupov           });
1433860543d9SAmir Ayupov           ++NumLongRangeTraces;
1434860543d9SAmir Ayupov         }
1435860543d9SAmir Ayupov       }
1436860543d9SAmir Ayupov       ++NumTraces;
1437860543d9SAmir Ayupov     }
1438*e6c9cd9cSAmir Ayupov     NextLBR = &LBR;
1439860543d9SAmir Ayupov 
1440860543d9SAmir Ayupov     uint64_t From = getBinaryFunctionContainingAddress(LBR.From) ? LBR.From : 0;
1441860543d9SAmir Ayupov     uint64_t To = getBinaryFunctionContainingAddress(LBR.To) ? LBR.To : 0;
1442860543d9SAmir Ayupov     if (!From && !To)
1443860543d9SAmir Ayupov       continue;
14449f15aa00SAmir Ayupov     TakenBranchInfo &Info = BranchLBRs[Trace(From, To)];
1445860543d9SAmir Ayupov     ++Info.TakenCount;
1446860543d9SAmir Ayupov     Info.MispredCount += LBR.Mispred;
1447860543d9SAmir Ayupov   }
1448860543d9SAmir Ayupov   return NumTraces;
1449860543d9SAmir Ayupov }
1450860543d9SAmir Ayupov 
1451a34c753fSRafael Auler std::error_code DataAggregator::parseBranchEvents() {
1452a34c753fSRafael Auler   outs() << "PERF2BOLT: parse branch events...\n";
1453a34c753fSRafael Auler   NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName,
1454a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
1455a34c753fSRafael Auler 
1456a34c753fSRafael Auler   uint64_t NumTotalSamples = 0;
1457a34c753fSRafael Auler   uint64_t NumEntries = 0;
1458a34c753fSRafael Auler   uint64_t NumSamples = 0;
1459a34c753fSRafael Auler   uint64_t NumSamplesNoLBR = 0;
1460a34c753fSRafael Auler   uint64_t NumTraces = 0;
1461a34c753fSRafael Auler   bool NeedsSkylakeFix = false;
1462a34c753fSRafael Auler 
1463a34c753fSRafael Auler   while (hasData() && NumTotalSamples < opts::MaxSamples) {
1464a34c753fSRafael Auler     ++NumTotalSamples;
1465a34c753fSRafael Auler 
1466a34c753fSRafael Auler     ErrorOr<PerfBranchSample> SampleRes = parseBranchSample();
1467a34c753fSRafael Auler     if (std::error_code EC = SampleRes.getError()) {
1468a34c753fSRafael Auler       if (EC == errc::no_such_process)
1469a34c753fSRafael Auler         continue;
1470a34c753fSRafael Auler       return EC;
1471a34c753fSRafael Auler     }
1472a34c753fSRafael Auler     ++NumSamples;
1473a34c753fSRafael Auler 
1474a34c753fSRafael Auler     PerfBranchSample &Sample = SampleRes.get();
1475a34c753fSRafael Auler 
1476a34c753fSRafael Auler     if (Sample.LBR.empty()) {
1477a34c753fSRafael Auler       ++NumSamplesNoLBR;
1478a34c753fSRafael Auler       continue;
1479a34c753fSRafael Auler     }
1480a34c753fSRafael Auler 
1481a34c753fSRafael Auler     NumEntries += Sample.LBR.size();
1482a34c753fSRafael Auler     if (BAT && Sample.LBR.size() == 32 && !NeedsSkylakeFix) {
1483a34c753fSRafael Auler       errs() << "PERF2BOLT-WARNING: using Intel Skylake bug workaround\n";
1484a34c753fSRafael Auler       NeedsSkylakeFix = true;
1485a34c753fSRafael Auler     }
1486a34c753fSRafael Auler 
1487860543d9SAmir Ayupov     NumTraces += parseLBRSample(Sample, NeedsSkylakeFix);
1488a34c753fSRafael Auler   }
1489a34c753fSRafael Auler 
1490d796f36fSAmir Ayupov   for (const Trace &Trace : llvm::make_first_range(BranchLBRs))
1491d796f36fSAmir Ayupov     for (const uint64_t Addr : {Trace.From, Trace.To})
1492d796f36fSAmir Ayupov       if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr))
1493a34c753fSRafael Auler         BF->setHasProfileAvailable();
1494a34c753fSRafael Auler 
1495a34c753fSRafael Auler   auto printColored = [](raw_ostream &OS, float Percent, float T1, float T2) {
1496a34c753fSRafael Auler     OS << " (";
1497a34c753fSRafael Auler     if (OS.has_colors()) {
1498def464aaSAmir Ayupov       if (Percent > T2)
1499a34c753fSRafael Auler         OS.changeColor(raw_ostream::RED);
1500def464aaSAmir Ayupov       else if (Percent > T1)
1501a34c753fSRafael Auler         OS.changeColor(raw_ostream::YELLOW);
1502def464aaSAmir Ayupov       else
1503a34c753fSRafael Auler         OS.changeColor(raw_ostream::GREEN);
1504a34c753fSRafael Auler     }
1505a34c753fSRafael Auler     OS << format("%.1f%%", Percent);
1506a34c753fSRafael Auler     if (OS.has_colors())
1507a34c753fSRafael Auler       OS.resetColor();
1508a34c753fSRafael Auler     OS << ")";
1509a34c753fSRafael Auler   };
1510a34c753fSRafael Auler 
151140c2e0faSMaksim Panchenko   outs() << "PERF2BOLT: read " << NumSamples << " samples and " << NumEntries
151240c2e0faSMaksim Panchenko          << " LBR entries\n";
1513a34c753fSRafael Auler   if (NumTotalSamples) {
1514a34c753fSRafael Auler     if (NumSamples && NumSamplesNoLBR == NumSamples) {
1515a34c753fSRafael Auler       // Note: we don't know if perf2bolt is being used to parse memory samples
1516a34c753fSRafael Auler       // at this point. In this case, it is OK to parse zero LBRs.
1517a34c753fSRafael Auler       errs() << "PERF2BOLT-WARNING: all recorded samples for this binary lack "
1518a34c753fSRafael Auler                 "LBR. Record profile with perf record -j any or run perf2bolt "
1519a34c753fSRafael Auler                 "in no-LBR mode with -nl (the performance improvement in -nl "
1520a34c753fSRafael Auler                 "mode may be limited)\n";
1521a34c753fSRafael Auler     } else {
1522a34c753fSRafael Auler       const uint64_t IgnoredSamples = NumTotalSamples - NumSamples;
1523a34c753fSRafael Auler       const float PercentIgnored = 100.0f * IgnoredSamples / NumTotalSamples;
1524a34c753fSRafael Auler       outs() << "PERF2BOLT: " << IgnoredSamples << " samples";
1525a34c753fSRafael Auler       printColored(outs(), PercentIgnored, 20, 50);
1526a34c753fSRafael Auler       outs() << " were ignored\n";
1527def464aaSAmir Ayupov       if (PercentIgnored > 50.0f)
1528a34c753fSRafael Auler         errs() << "PERF2BOLT-WARNING: less than 50% of all recorded samples "
1529a34c753fSRafael Auler                   "were attributed to the input binary\n";
1530a34c753fSRafael Auler     }
1531a34c753fSRafael Auler   }
1532a34c753fSRafael Auler   outs() << "PERF2BOLT: traces mismatching disassembled function contents: "
1533a34c753fSRafael Auler          << NumInvalidTraces;
1534a34c753fSRafael Auler   float Perc = 0.0f;
1535a34c753fSRafael Auler   if (NumTraces > 0) {
1536a34c753fSRafael Auler     Perc = NumInvalidTraces * 100.0f / NumTraces;
1537a34c753fSRafael Auler     printColored(outs(), Perc, 5, 10);
1538a34c753fSRafael Auler   }
1539a34c753fSRafael Auler   outs() << "\n";
1540def464aaSAmir Ayupov   if (Perc > 10.0f)
1541a34c753fSRafael Auler     outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1542a34c753fSRafael Auler               "binary is probably not the same binary used during profiling "
1543a34c753fSRafael Auler               "collection. The generated data may be ineffective for improving "
1544a34c753fSRafael Auler               "performance.\n\n";
1545a34c753fSRafael Auler 
1546a34c753fSRafael Auler   outs() << "PERF2BOLT: out of range traces involving unknown regions: "
1547a34c753fSRafael Auler          << NumLongRangeTraces;
1548def464aaSAmir Ayupov   if (NumTraces > 0)
1549a34c753fSRafael Auler     outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces);
1550a34c753fSRafael Auler   outs() << "\n";
1551a34c753fSRafael Auler 
1552a34c753fSRafael Auler   if (NumColdSamples > 0) {
1553a34c753fSRafael Auler     const float ColdSamples = NumColdSamples * 100.0f / NumTotalSamples;
1554a34c753fSRafael Auler     outs() << "PERF2BOLT: " << NumColdSamples
1555a34c753fSRafael Auler            << format(" (%.1f%%)", ColdSamples)
1556a34c753fSRafael Auler            << " samples recorded in cold regions of split functions.\n";
1557def464aaSAmir Ayupov     if (ColdSamples > 5.0f)
1558a34c753fSRafael Auler       outs()
1559a34c753fSRafael Auler           << "WARNING: The BOLT-processed binary where samples were collected "
1560a34c753fSRafael Auler              "likely used bad data or your service observed a large shift in "
1561a34c753fSRafael Auler              "profile. You may want to audit this.\n";
1562a34c753fSRafael Auler   }
1563a34c753fSRafael Auler 
1564a34c753fSRafael Auler   return std::error_code();
1565a34c753fSRafael Auler }
1566a34c753fSRafael Auler 
1567a34c753fSRafael Auler void DataAggregator::processBranchEvents() {
1568a34c753fSRafael Auler   outs() << "PERF2BOLT: processing branch events...\n";
1569a34c753fSRafael Auler   NamedRegionTimer T("processBranch", "Processing branch events",
1570a34c753fSRafael Auler                      TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1571a34c753fSRafael Auler 
1572a34c753fSRafael Auler   for (const auto &AggrLBR : FallthroughLBRs) {
1573a34c753fSRafael Auler     const Trace &Loc = AggrLBR.first;
1574a34c753fSRafael Auler     const FTInfo &Info = AggrLBR.second;
1575a34c753fSRafael Auler     LBREntry First{Loc.From, Loc.From, false};
1576a34c753fSRafael Auler     LBREntry Second{Loc.To, Loc.To, false};
1577def464aaSAmir Ayupov     if (Info.InternCount)
1578a34c753fSRafael Auler       doTrace(First, Second, Info.InternCount);
1579a34c753fSRafael Auler     if (Info.ExternCount) {
1580a34c753fSRafael Auler       First.From = 0;
1581a34c753fSRafael Auler       doTrace(First, Second, Info.ExternCount);
1582a34c753fSRafael Auler     }
1583a34c753fSRafael Auler   }
1584a34c753fSRafael Auler 
1585a34c753fSRafael Auler   for (const auto &AggrLBR : BranchLBRs) {
1586a34c753fSRafael Auler     const Trace &Loc = AggrLBR.first;
15879f15aa00SAmir Ayupov     const TakenBranchInfo &Info = AggrLBR.second;
158874e6478fSAmir Ayupov     doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount,
158974e6478fSAmir Ayupov              /*IsPreagg*/ false);
1590a34c753fSRafael Auler   }
1591a34c753fSRafael Auler }
1592a34c753fSRafael Auler 
1593a34c753fSRafael Auler std::error_code DataAggregator::parseBasicEvents() {
1594a34c753fSRafael Auler   outs() << "PERF2BOLT: parsing basic events (without LBR)...\n";
1595a34c753fSRafael Auler   NamedRegionTimer T("parseBasic", "Parsing basic events", TimerGroupName,
1596a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
1597a34c753fSRafael Auler   while (hasData()) {
1598a34c753fSRafael Auler     ErrorOr<PerfBasicSample> Sample = parseBasicSample();
1599a34c753fSRafael Auler     if (std::error_code EC = Sample.getError())
1600a34c753fSRafael Auler       return EC;
1601a34c753fSRafael Auler 
1602a34c753fSRafael Auler     if (!Sample->PC)
1603a34c753fSRafael Auler       continue;
1604a34c753fSRafael Auler 
1605a34c753fSRafael Auler     if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
1606a34c753fSRafael Auler       BF->setHasProfileAvailable();
1607a34c753fSRafael Auler 
1608a34c753fSRafael Auler     ++BasicSamples[Sample->PC];
1609a34c753fSRafael Auler     EventNames.insert(Sample->EventName);
1610a34c753fSRafael Auler   }
1611a34c753fSRafael Auler 
1612a34c753fSRafael Auler   return std::error_code();
1613a34c753fSRafael Auler }
1614a34c753fSRafael Auler 
1615a34c753fSRafael Auler void DataAggregator::processBasicEvents() {
1616a34c753fSRafael Auler   outs() << "PERF2BOLT: processing basic events (without LBR)...\n";
161740c2e0faSMaksim Panchenko   NamedRegionTimer T("processBasic", "Processing basic events", TimerGroupName,
161840c2e0faSMaksim Panchenko                      TimerGroupDesc, opts::TimeAggregator);
1619a34c753fSRafael Auler   uint64_t OutOfRangeSamples = 0;
1620a34c753fSRafael Auler   uint64_t NumSamples = 0;
1621a34c753fSRafael Auler   for (auto &Sample : BasicSamples) {
1622a34c753fSRafael Auler     const uint64_t PC = Sample.first;
1623a34c753fSRafael Auler     const uint64_t HitCount = Sample.second;
1624a34c753fSRafael Auler     NumSamples += HitCount;
1625a34c753fSRafael Auler     BinaryFunction *Func = getBinaryFunctionContainingAddress(PC);
1626a34c753fSRafael Auler     if (!Func) {
1627a34c753fSRafael Auler       OutOfRangeSamples += HitCount;
1628a34c753fSRafael Auler       continue;
1629a34c753fSRafael Auler     }
1630a34c753fSRafael Auler 
1631a34c753fSRafael Auler     doSample(*Func, PC, HitCount);
1632a34c753fSRafael Auler   }
1633a34c753fSRafael Auler   outs() << "PERF2BOLT: read " << NumSamples << " samples\n";
1634a34c753fSRafael Auler 
1635a34c753fSRafael Auler   outs() << "PERF2BOLT: out of range samples recorded in unknown regions: "
1636a34c753fSRafael Auler          << OutOfRangeSamples;
1637a34c753fSRafael Auler   float Perc = 0.0f;
1638a34c753fSRafael Auler   if (NumSamples > 0) {
1639a34c753fSRafael Auler     outs() << " (";
1640a34c753fSRafael Auler     Perc = OutOfRangeSamples * 100.0f / NumSamples;
1641a34c753fSRafael Auler     if (outs().has_colors()) {
1642def464aaSAmir Ayupov       if (Perc > 60.0f)
1643a34c753fSRafael Auler         outs().changeColor(raw_ostream::RED);
1644def464aaSAmir Ayupov       else if (Perc > 40.0f)
1645a34c753fSRafael Auler         outs().changeColor(raw_ostream::YELLOW);
1646def464aaSAmir Ayupov       else
1647a34c753fSRafael Auler         outs().changeColor(raw_ostream::GREEN);
1648a34c753fSRafael Auler     }
1649a34c753fSRafael Auler     outs() << format("%.1f%%", Perc);
1650a34c753fSRafael Auler     if (outs().has_colors())
1651a34c753fSRafael Auler       outs().resetColor();
1652a34c753fSRafael Auler     outs() << ")";
1653a34c753fSRafael Auler   }
1654a34c753fSRafael Auler   outs() << "\n";
1655def464aaSAmir Ayupov   if (Perc > 80.0f)
1656a34c753fSRafael Auler     outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1657a34c753fSRafael Auler               "binary is probably not the same binary used during profiling "
1658a34c753fSRafael Auler               "collection. The generated data may be ineffective for improving "
1659a34c753fSRafael Auler               "performance.\n\n";
1660a34c753fSRafael Auler }
1661a34c753fSRafael Auler 
1662a34c753fSRafael Auler std::error_code DataAggregator::parseMemEvents() {
1663a34c753fSRafael Auler   outs() << "PERF2BOLT: parsing memory events...\n";
1664a34c753fSRafael Auler   NamedRegionTimer T("parseMemEvents", "Parsing mem events", TimerGroupName,
1665a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
1666a34c753fSRafael Auler   while (hasData()) {
1667a34c753fSRafael Auler     ErrorOr<PerfMemSample> Sample = parseMemSample();
1668a34c753fSRafael Auler     if (std::error_code EC = Sample.getError())
1669a34c753fSRafael Auler       return EC;
1670a34c753fSRafael Auler 
1671a34c753fSRafael Auler     if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC))
1672a34c753fSRafael Auler       BF->setHasProfileAvailable();
1673a34c753fSRafael Auler 
1674a34c753fSRafael Auler     MemSamples.emplace_back(std::move(Sample.get()));
1675a34c753fSRafael Auler   }
1676a34c753fSRafael Auler 
1677a34c753fSRafael Auler   return std::error_code();
1678a34c753fSRafael Auler }
1679a34c753fSRafael Auler 
1680a34c753fSRafael Auler void DataAggregator::processMemEvents() {
1681a34c753fSRafael Auler   NamedRegionTimer T("ProcessMemEvents", "Processing mem events",
1682a34c753fSRafael Auler                      TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1683a34c753fSRafael Auler   for (const PerfMemSample &Sample : MemSamples) {
1684a34c753fSRafael Auler     uint64_t PC = Sample.PC;
1685a34c753fSRafael Auler     uint64_t Addr = Sample.Addr;
1686a34c753fSRafael Auler     StringRef FuncName;
1687a34c753fSRafael Auler     StringRef MemName;
1688a34c753fSRafael Auler 
1689a34c753fSRafael Auler     // Try to resolve symbol for PC
1690a34c753fSRafael Auler     BinaryFunction *Func = getBinaryFunctionContainingAddress(PC);
1691a34c753fSRafael Auler     if (!Func) {
1692a34c753fSRafael Auler       LLVM_DEBUG(if (PC != 0) {
16934a7966eaSAmir Ayupov         dbgs() << formatv("Skipped mem event: {0:x} => {1:x}\n", PC, Addr);
1694a34c753fSRafael Auler       });
1695a34c753fSRafael Auler       continue;
1696a34c753fSRafael Auler     }
1697a34c753fSRafael Auler 
1698a34c753fSRafael Auler     FuncName = Func->getOneName();
1699a34c753fSRafael Auler     PC -= Func->getAddress();
1700a34c753fSRafael Auler 
1701a34c753fSRafael Auler     // Try to resolve symbol for memory load
1702a34c753fSRafael Auler     if (BinaryData *BD = BC->getBinaryDataContainingAddress(Addr)) {
1703a34c753fSRafael Auler       MemName = BD->getName();
1704a34c753fSRafael Auler       Addr -= BD->getAddress();
1705a34c753fSRafael Auler     } else if (opts::FilterMemProfile) {
1706a34c753fSRafael Auler       // Filter out heap/stack accesses
1707a34c753fSRafael Auler       continue;
1708a34c753fSRafael Auler     }
1709a34c753fSRafael Auler 
1710a34c753fSRafael Auler     const Location FuncLoc(!FuncName.empty(), FuncName, PC);
1711a34c753fSRafael Auler     const Location AddrLoc(!MemName.empty(), MemName, Addr);
1712a34c753fSRafael Auler 
1713a34c753fSRafael Auler     FuncMemData *MemData = &NamesToMemEvents[FuncName];
171473b89e3fSMaksim Panchenko     MemData->Name = FuncName;
1715a34c753fSRafael Auler     setMemData(*Func, MemData);
1716a34c753fSRafael Auler     MemData->update(FuncLoc, AddrLoc);
1717a34c753fSRafael Auler     LLVM_DEBUG(dbgs() << "Mem event: " << FuncLoc << " = " << AddrLoc << "\n");
1718a34c753fSRafael Auler   }
1719a34c753fSRafael Auler }
1720a34c753fSRafael Auler 
1721a34c753fSRafael Auler std::error_code DataAggregator::parsePreAggregatedLBRSamples() {
1722a34c753fSRafael Auler   outs() << "PERF2BOLT: parsing pre-aggregated profile...\n";
1723a34c753fSRafael Auler   NamedRegionTimer T("parseAggregated", "Parsing aggregated branch events",
1724a34c753fSRafael Auler                      TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1725a34c753fSRafael Auler   while (hasData()) {
1726a34c753fSRafael Auler     ErrorOr<AggregatedLBREntry> AggrEntry = parseAggregatedLBREntry();
1727a34c753fSRafael Auler     if (std::error_code EC = AggrEntry.getError())
1728a34c753fSRafael Auler       return EC;
1729a34c753fSRafael Auler 
1730d796f36fSAmir Ayupov     for (const uint64_t Addr : {AggrEntry->From.Offset, AggrEntry->To.Offset})
1731d796f36fSAmir Ayupov       if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr))
1732a34c753fSRafael Auler         BF->setHasProfileAvailable();
1733a34c753fSRafael Auler 
1734a34c753fSRafael Auler     AggregatedLBRs.emplace_back(std::move(AggrEntry.get()));
1735a34c753fSRafael Auler   }
1736a34c753fSRafael Auler 
1737a34c753fSRafael Auler   return std::error_code();
1738a34c753fSRafael Auler }
1739a34c753fSRafael Auler 
1740a34c753fSRafael Auler void DataAggregator::processPreAggregated() {
1741a34c753fSRafael Auler   outs() << "PERF2BOLT: processing pre-aggregated profile...\n";
1742a34c753fSRafael Auler   NamedRegionTimer T("processAggregated", "Processing aggregated branch events",
1743a34c753fSRafael Auler                      TimerGroupName, TimerGroupDesc, opts::TimeAggregator);
1744a34c753fSRafael Auler 
1745a34c753fSRafael Auler   uint64_t NumTraces = 0;
1746a34c753fSRafael Auler   for (const AggregatedLBREntry &AggrEntry : AggregatedLBRs) {
1747a34c753fSRafael Auler     switch (AggrEntry.EntryType) {
1748a34c753fSRafael Auler     case AggregatedLBREntry::BRANCH:
1749a34c753fSRafael Auler       doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count,
175074e6478fSAmir Ayupov                AggrEntry.Mispreds, /*IsPreagg*/ true);
1751a34c753fSRafael Auler       break;
1752a34c753fSRafael Auler     case AggregatedLBREntry::FT:
1753a34c753fSRafael Auler     case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: {
1754a34c753fSRafael Auler       LBREntry First{AggrEntry.EntryType == AggregatedLBREntry::FT
1755a34c753fSRafael Auler                          ? AggrEntry.From.Offset
1756a34c753fSRafael Auler                          : 0,
1757a34c753fSRafael Auler                      AggrEntry.From.Offset, false};
1758a34c753fSRafael Auler       LBREntry Second{AggrEntry.To.Offset, AggrEntry.To.Offset, false};
1759a34c753fSRafael Auler       doTrace(First, Second, AggrEntry.Count);
1760a34c753fSRafael Auler       NumTraces += AggrEntry.Count;
1761a34c753fSRafael Auler       break;
1762a34c753fSRafael Auler     }
1763a34c753fSRafael Auler     }
1764a34c753fSRafael Auler   }
1765a34c753fSRafael Auler 
1766a34c753fSRafael Auler   outs() << "PERF2BOLT: read " << AggregatedLBRs.size()
1767a34c753fSRafael Auler          << " aggregated LBR entries\n";
1768a34c753fSRafael Auler   outs() << "PERF2BOLT: traces mismatching disassembled function contents: "
1769a34c753fSRafael Auler          << NumInvalidTraces;
1770a34c753fSRafael Auler   float Perc = 0.0f;
1771a34c753fSRafael Auler   if (NumTraces > 0) {
1772a34c753fSRafael Auler     outs() << " (";
1773a34c753fSRafael Auler     Perc = NumInvalidTraces * 100.0f / NumTraces;
1774a34c753fSRafael Auler     if (outs().has_colors()) {
1775def464aaSAmir Ayupov       if (Perc > 10.0f)
1776a34c753fSRafael Auler         outs().changeColor(raw_ostream::RED);
1777def464aaSAmir Ayupov       else if (Perc > 5.0f)
1778a34c753fSRafael Auler         outs().changeColor(raw_ostream::YELLOW);
1779def464aaSAmir Ayupov       else
1780a34c753fSRafael Auler         outs().changeColor(raw_ostream::GREEN);
1781a34c753fSRafael Auler     }
1782a34c753fSRafael Auler     outs() << format("%.1f%%", Perc);
1783a34c753fSRafael Auler     if (outs().has_colors())
1784a34c753fSRafael Auler       outs().resetColor();
1785a34c753fSRafael Auler     outs() << ")";
1786a34c753fSRafael Auler   }
1787a34c753fSRafael Auler   outs() << "\n";
1788def464aaSAmir Ayupov   if (Perc > 10.0f)
1789a34c753fSRafael Auler     outs() << "\n !! WARNING !! This high mismatch ratio indicates the input "
1790a34c753fSRafael Auler               "binary is probably not the same binary used during profiling "
1791a34c753fSRafael Auler               "collection. The generated data may be ineffective for improving "
1792a34c753fSRafael Auler               "performance.\n\n";
1793a34c753fSRafael Auler 
1794a34c753fSRafael Auler   outs() << "PERF2BOLT: Out of range traces involving unknown regions: "
1795a34c753fSRafael Auler          << NumLongRangeTraces;
1796def464aaSAmir Ayupov   if (NumTraces > 0)
1797a34c753fSRafael Auler     outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces);
1798a34c753fSRafael Auler   outs() << "\n";
1799a34c753fSRafael Auler }
1800a34c753fSRafael Auler 
1801835a9c28SAmir Ayupov std::optional<int32_t> DataAggregator::parseCommExecEvent() {
1802a34c753fSRafael Auler   size_t LineEnd = ParsingBuf.find_first_of("\n");
1803a34c753fSRafael Auler   if (LineEnd == StringRef::npos) {
1804a34c753fSRafael Auler     reportError("expected rest of line");
1805a34c753fSRafael Auler     Diag << "Found: " << ParsingBuf << "\n";
1806e324a80fSKazu Hirata     return std::nullopt;
1807a34c753fSRafael Auler   }
1808a34c753fSRafael Auler   StringRef Line = ParsingBuf.substr(0, LineEnd);
1809a34c753fSRafael Auler 
1810a34c753fSRafael Auler   size_t Pos = Line.find("PERF_RECORD_COMM exec");
1811def464aaSAmir Ayupov   if (Pos == StringRef::npos)
1812e324a80fSKazu Hirata     return std::nullopt;
1813a34c753fSRafael Auler   Line = Line.drop_front(Pos);
1814a34c753fSRafael Auler 
1815a34c753fSRafael Auler   // Line:
1816a34c753fSRafael Auler   //  PERF_RECORD_COMM exec: <name>:<pid>/<tid>"
1817a34c753fSRafael Auler   StringRef PIDStr = Line.rsplit(':').second.split('/').first;
1818ae585be1SRafael Auler   int32_t PID;
1819a34c753fSRafael Auler   if (PIDStr.getAsInteger(10, PID)) {
1820a34c753fSRafael Auler     reportError("expected PID");
1821a34c753fSRafael Auler     Diag << "Found: " << PIDStr << "in '" << Line << "'\n";
1822e324a80fSKazu Hirata     return std::nullopt;
1823a34c753fSRafael Auler   }
1824a34c753fSRafael Auler 
1825a34c753fSRafael Auler   return PID;
1826a34c753fSRafael Auler }
1827a34c753fSRafael Auler 
1828a34c753fSRafael Auler namespace {
1829835a9c28SAmir Ayupov std::optional<uint64_t> parsePerfTime(const StringRef TimeStr) {
1830a34c753fSRafael Auler   const StringRef SecTimeStr = TimeStr.split('.').first;
1831a34c753fSRafael Auler   const StringRef USecTimeStr = TimeStr.split('.').second;
1832a34c753fSRafael Auler   uint64_t SecTime;
1833a34c753fSRafael Auler   uint64_t USecTime;
1834a34c753fSRafael Auler   if (SecTimeStr.getAsInteger(10, SecTime) ||
1835def464aaSAmir Ayupov       USecTimeStr.getAsInteger(10, USecTime))
1836e324a80fSKazu Hirata     return std::nullopt;
1837a34c753fSRafael Auler   return SecTime * 1000000ULL + USecTime;
1838a34c753fSRafael Auler }
1839a34c753fSRafael Auler }
1840a34c753fSRafael Auler 
1841835a9c28SAmir Ayupov std::optional<DataAggregator::ForkInfo> DataAggregator::parseForkEvent() {
184240c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
184340c2e0faSMaksim Panchenko   }
1844a34c753fSRafael Auler 
1845a34c753fSRafael Auler   size_t LineEnd = ParsingBuf.find_first_of("\n");
1846a34c753fSRafael Auler   if (LineEnd == StringRef::npos) {
1847a34c753fSRafael Auler     reportError("expected rest of line");
1848a34c753fSRafael Auler     Diag << "Found: " << ParsingBuf << "\n";
1849e324a80fSKazu Hirata     return std::nullopt;
1850a34c753fSRafael Auler   }
1851a34c753fSRafael Auler   StringRef Line = ParsingBuf.substr(0, LineEnd);
1852a34c753fSRafael Auler 
1853a34c753fSRafael Auler   size_t Pos = Line.find("PERF_RECORD_FORK");
1854a34c753fSRafael Auler   if (Pos == StringRef::npos) {
1855a34c753fSRafael Auler     consumeRestOfLine();
1856e324a80fSKazu Hirata     return std::nullopt;
1857a34c753fSRafael Auler   }
1858a34c753fSRafael Auler 
1859a34c753fSRafael Auler   ForkInfo FI;
1860a34c753fSRafael Auler 
1861a34c753fSRafael Auler   const StringRef TimeStr =
1862a34c753fSRafael Auler       Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second;
1863835a9c28SAmir Ayupov   if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) {
1864a34c753fSRafael Auler     FI.Time = *TimeRes;
1865a34c753fSRafael Auler   }
1866a34c753fSRafael Auler 
1867a34c753fSRafael Auler   Line = Line.drop_front(Pos);
1868a34c753fSRafael Auler 
1869a34c753fSRafael Auler   // Line:
1870a34c753fSRafael Auler   //  PERF_RECORD_FORK(<child_pid>:<child_tid>):(<parent_pid>:<parent_tid>)
1871a34c753fSRafael Auler   const StringRef ChildPIDStr = Line.split('(').second.split(':').first;
1872a34c753fSRafael Auler   if (ChildPIDStr.getAsInteger(10, FI.ChildPID)) {
1873a34c753fSRafael Auler     reportError("expected PID");
1874a34c753fSRafael Auler     Diag << "Found: " << ChildPIDStr << "in '" << Line << "'\n";
1875e324a80fSKazu Hirata     return std::nullopt;
1876a34c753fSRafael Auler   }
1877a34c753fSRafael Auler 
1878a34c753fSRafael Auler   const StringRef ParentPIDStr = Line.rsplit('(').second.split(':').first;
1879a34c753fSRafael Auler   if (ParentPIDStr.getAsInteger(10, FI.ParentPID)) {
1880a34c753fSRafael Auler     reportError("expected PID");
1881a34c753fSRafael Auler     Diag << "Found: " << ParentPIDStr << "in '" << Line << "'\n";
1882e324a80fSKazu Hirata     return std::nullopt;
1883a34c753fSRafael Auler   }
1884a34c753fSRafael Auler 
1885a34c753fSRafael Auler   consumeRestOfLine();
1886a34c753fSRafael Auler 
1887a34c753fSRafael Auler   return FI;
1888a34c753fSRafael Auler }
1889a34c753fSRafael Auler 
1890a34c753fSRafael Auler ErrorOr<std::pair<StringRef, DataAggregator::MMapInfo>>
1891a34c753fSRafael Auler DataAggregator::parseMMapEvent() {
189240c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
189340c2e0faSMaksim Panchenko   }
1894a34c753fSRafael Auler 
1895a34c753fSRafael Auler   MMapInfo ParsedInfo;
1896a34c753fSRafael Auler 
1897a34c753fSRafael Auler   size_t LineEnd = ParsingBuf.find_first_of("\n");
1898a34c753fSRafael Auler   if (LineEnd == StringRef::npos) {
1899a34c753fSRafael Auler     reportError("expected rest of line");
1900a34c753fSRafael Auler     Diag << "Found: " << ParsingBuf << "\n";
1901a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1902a34c753fSRafael Auler   }
1903a34c753fSRafael Auler   StringRef Line = ParsingBuf.substr(0, LineEnd);
1904a34c753fSRafael Auler 
1905a34c753fSRafael Auler   size_t Pos = Line.find("PERF_RECORD_MMAP2");
1906a34c753fSRafael Auler   if (Pos == StringRef::npos) {
1907a34c753fSRafael Auler     consumeRestOfLine();
1908a34c753fSRafael Auler     return std::make_pair(StringRef(), ParsedInfo);
1909a34c753fSRafael Auler   }
1910a34c753fSRafael Auler 
1911a34c753fSRafael Auler   // Line:
1912a34c753fSRafael Auler   //   {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name>
1913a34c753fSRafael Auler 
1914a34c753fSRafael Auler   const StringRef TimeStr =
1915a34c753fSRafael Auler       Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second;
1916835a9c28SAmir Ayupov   if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr))
1917a34c753fSRafael Auler     ParsedInfo.Time = *TimeRes;
1918a34c753fSRafael Auler 
1919a34c753fSRafael Auler   Line = Line.drop_front(Pos);
1920a34c753fSRafael Auler 
1921a34c753fSRafael Auler   // Line:
1922a34c753fSRafael Auler   //   PERF_RECORD_MMAP2 <pid>/<tid>: [<hexbase>(<hexsize>) .*]: .* <file_name>
1923a34c753fSRafael Auler 
1924a34c753fSRafael Auler   StringRef FileName = Line.rsplit(FieldSeparator).second;
1925ad8fd5b1SKazu Hirata   if (FileName.starts_with("//") || FileName.starts_with("[")) {
1926a34c753fSRafael Auler     consumeRestOfLine();
1927a34c753fSRafael Auler     return std::make_pair(StringRef(), ParsedInfo);
1928a34c753fSRafael Auler   }
1929a34c753fSRafael Auler   FileName = sys::path::filename(FileName);
1930a34c753fSRafael Auler 
1931a34c753fSRafael Auler   const StringRef PIDStr = Line.split(FieldSeparator).second.split('/').first;
1932a34c753fSRafael Auler   if (PIDStr.getAsInteger(10, ParsedInfo.PID)) {
1933a34c753fSRafael Auler     reportError("expected PID");
1934a34c753fSRafael Auler     Diag << "Found: " << PIDStr << "in '" << Line << "'\n";
1935a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1936a34c753fSRafael Auler   }
1937a34c753fSRafael Auler 
1938a34c753fSRafael Auler   const StringRef BaseAddressStr = Line.split('[').second.split('(').first;
193977b75ca5SMaksim Panchenko   if (BaseAddressStr.getAsInteger(0, ParsedInfo.MMapAddress)) {
1940a34c753fSRafael Auler     reportError("expected base address");
1941a34c753fSRafael Auler     Diag << "Found: " << BaseAddressStr << "in '" << Line << "'\n";
1942a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1943a34c753fSRafael Auler   }
1944a34c753fSRafael Auler 
1945a34c753fSRafael Auler   const StringRef SizeStr = Line.split('(').second.split(')').first;
1946a34c753fSRafael Auler   if (SizeStr.getAsInteger(0, ParsedInfo.Size)) {
1947a34c753fSRafael Auler     reportError("expected mmaped size");
1948a34c753fSRafael Auler     Diag << "Found: " << SizeStr << "in '" << Line << "'\n";
1949a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1950a34c753fSRafael Auler   }
1951a34c753fSRafael Auler 
1952a34c753fSRafael Auler   const StringRef OffsetStr =
1953a34c753fSRafael Auler       Line.split('@').second.ltrim().split(FieldSeparator).first;
1954a34c753fSRafael Auler   if (OffsetStr.getAsInteger(0, ParsedInfo.Offset)) {
1955a34c753fSRafael Auler     reportError("expected mmaped page-aligned offset");
1956a34c753fSRafael Auler     Diag << "Found: " << OffsetStr << "in '" << Line << "'\n";
1957a34c753fSRafael Auler     return make_error_code(llvm::errc::io_error);
1958a34c753fSRafael Auler   }
1959a34c753fSRafael Auler 
1960a34c753fSRafael Auler   consumeRestOfLine();
1961a34c753fSRafael Auler 
1962a34c753fSRafael Auler   return std::make_pair(FileName, ParsedInfo);
1963a34c753fSRafael Auler }
1964a34c753fSRafael Auler 
1965a34c753fSRafael Auler std::error_code DataAggregator::parseMMapEvents() {
1966a34c753fSRafael Auler   outs() << "PERF2BOLT: parsing perf-script mmap events output\n";
1967a34c753fSRafael Auler   NamedRegionTimer T("parseMMapEvents", "Parsing mmap events", TimerGroupName,
1968a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
1969a34c753fSRafael Auler 
1970a34c753fSRafael Auler   std::multimap<StringRef, MMapInfo> GlobalMMapInfo;
1971a34c753fSRafael Auler   while (hasData()) {
1972a34c753fSRafael Auler     ErrorOr<std::pair<StringRef, MMapInfo>> FileMMapInfoRes = parseMMapEvent();
1973a34c753fSRafael Auler     if (std::error_code EC = FileMMapInfoRes.getError())
1974a34c753fSRafael Auler       return EC;
1975a34c753fSRafael Auler 
1976a34c753fSRafael Auler     std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get();
1977a34c753fSRafael Auler     if (FileMMapInfo.second.PID == -1)
1978a34c753fSRafael Auler       continue;
1979f841ca0cSKazu Hirata     if (FileMMapInfo.first == "(deleted)")
1980a478a091SAmir Ayupov       continue;
1981a34c753fSRafael Auler 
1982a34c753fSRafael Auler     GlobalMMapInfo.insert(FileMMapInfo);
1983a34c753fSRafael Auler   }
1984a34c753fSRafael Auler 
1985a34c753fSRafael Auler   LLVM_DEBUG({
1986ffef4fe0SAmir Ayupov     dbgs() << "FileName -> mmap info:\n"
1987ffef4fe0SAmir Ayupov            << "  Filename : PID [MMapAddr, Size, Offset]\n";
1988ffef4fe0SAmir Ayupov     for (const auto &[Name, MMap] : GlobalMMapInfo)
1989ffef4fe0SAmir Ayupov       dbgs() << formatv("  {0} : {1} [{2:x}, {3:x} @ {4:x}]\n", Name, MMap.PID,
1990ffef4fe0SAmir Ayupov                         MMap.MMapAddress, MMap.Size, MMap.Offset);
1991a34c753fSRafael Auler   });
1992a34c753fSRafael Auler 
1993a34c753fSRafael Auler   StringRef NameToUse = llvm::sys::path::filename(BC->getFilename());
1994a34c753fSRafael Auler   if (GlobalMMapInfo.count(NameToUse) == 0 && !BuildIDBinaryName.empty()) {
1995a34c753fSRafael Auler     errs() << "PERF2BOLT-WARNING: using \"" << BuildIDBinaryName
1996a34c753fSRafael Auler            << "\" for profile matching\n";
1997a34c753fSRafael Auler     NameToUse = BuildIDBinaryName;
1998a34c753fSRafael Auler   }
1999a34c753fSRafael Auler 
2000a34c753fSRafael Auler   auto Range = GlobalMMapInfo.equal_range(NameToUse);
200117f3cbe3SAmir Ayupov   for (MMapInfo &MMapInfo : llvm::make_second_range(make_range(Range))) {
200277b75ca5SMaksim Panchenko     if (BC->HasFixedLoadAddress && MMapInfo.MMapAddress) {
2003a34c753fSRafael Auler       // Check that the binary mapping matches one of the segments.
2004f119a248SAmir Ayupov       bool MatchFound = llvm::any_of(
2005f119a248SAmir Ayupov           llvm::make_second_range(BC->SegmentMapInfo),
2006f119a248SAmir Ayupov           [&](SegmentInfo &SegInfo) {
200777b75ca5SMaksim Panchenko             // The mapping is page-aligned and hence the MMapAddress could be
2008a34c753fSRafael Auler             // different from the segment start address. We cannot know the page
2009a34c753fSRafael Auler             // size of the mapping, but we know it should not exceed the segment
2010a34c753fSRafael Auler             // alignment value. Hence we are performing an approximate check.
2011f119a248SAmir Ayupov             return SegInfo.Address >= MMapInfo.MMapAddress &&
20126d216fb7SKristof Beyls                    SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment &&
20136d216fb7SKristof Beyls                    SegInfo.IsExecutable;
2014f119a248SAmir Ayupov           });
2015a34c753fSRafael Auler       if (!MatchFound) {
2016a34c753fSRafael Auler         errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse
201777b75ca5SMaksim Panchenko                << " at 0x" << Twine::utohexstr(MMapInfo.MMapAddress) << '\n';
2018a34c753fSRafael Auler         continue;
2019a34c753fSRafael Auler       }
2020a34c753fSRafael Auler     }
2021a34c753fSRafael Auler 
202277b75ca5SMaksim Panchenko     // Set base address for shared objects.
202377b75ca5SMaksim Panchenko     if (!BC->HasFixedLoadAddress) {
2024e8f5743eSAmir Ayupov       std::optional<uint64_t> BaseAddress =
202577b75ca5SMaksim Panchenko           BC->getBaseAddressForMapping(MMapInfo.MMapAddress, MMapInfo.Offset);
202677b75ca5SMaksim Panchenko       if (!BaseAddress) {
202777b75ca5SMaksim Panchenko         errs() << "PERF2BOLT-WARNING: unable to find base address of the "
202877b75ca5SMaksim Panchenko                   "binary when memory mapped at 0x"
202977b75ca5SMaksim Panchenko                << Twine::utohexstr(MMapInfo.MMapAddress)
203077b75ca5SMaksim Panchenko                << " using file offset 0x" << Twine::utohexstr(MMapInfo.Offset)
203177b75ca5SMaksim Panchenko                << ". Ignoring profile data for this mapping\n";
203277b75ca5SMaksim Panchenko         continue;
203351003076SPaschalis Mpeis       }
203477b75ca5SMaksim Panchenko       MMapInfo.BaseAddress = *BaseAddress;
203577b75ca5SMaksim Panchenko     }
203677b75ca5SMaksim Panchenko 
203751003076SPaschalis Mpeis     // Try to add MMapInfo to the map and update its size. Large binaries may
203851003076SPaschalis Mpeis     // span to multiple text segments, so the mapping is inserted only on the
203951003076SPaschalis Mpeis     // first occurrence.
204051003076SPaschalis Mpeis     if (!BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)).second)
204151003076SPaschalis Mpeis       assert(MMapInfo.BaseAddress == BinaryMMapInfo[MMapInfo.PID].BaseAddress &&
204251003076SPaschalis Mpeis              "Base address on multiple segment mappings should match");
204351003076SPaschalis Mpeis 
204451003076SPaschalis Mpeis     // Update mapping size.
204551003076SPaschalis Mpeis     const uint64_t EndAddress = MMapInfo.MMapAddress + MMapInfo.Size;
204651003076SPaschalis Mpeis     const uint64_t Size = EndAddress - BinaryMMapInfo[MMapInfo.PID].BaseAddress;
204751003076SPaschalis Mpeis     if (Size > BinaryMMapInfo[MMapInfo.PID].Size)
204851003076SPaschalis Mpeis       BinaryMMapInfo[MMapInfo.PID].Size = Size;
2049a34c753fSRafael Auler   }
2050a34c753fSRafael Auler 
2051a34c753fSRafael Auler   if (BinaryMMapInfo.empty()) {
2052a34c753fSRafael Auler     if (errs().has_colors())
2053a34c753fSRafael Auler       errs().changeColor(raw_ostream::RED);
2054a34c753fSRafael Auler     errs() << "PERF2BOLT-ERROR: could not find a profile matching binary \""
2055a34c753fSRafael Auler            << BC->getFilename() << "\".";
2056a34c753fSRafael Auler     if (!GlobalMMapInfo.empty()) {
2057a34c753fSRafael Auler       errs() << " Profile for the following binary name(s) is available:\n";
2058a34c753fSRafael Auler       for (auto I = GlobalMMapInfo.begin(), IE = GlobalMMapInfo.end(); I != IE;
2059def464aaSAmir Ayupov            I = GlobalMMapInfo.upper_bound(I->first))
2060a34c753fSRafael Auler         errs() << "  " << I->first << '\n';
2061a34c753fSRafael Auler       errs() << "Please rename the input binary.\n";
2062a34c753fSRafael Auler     } else {
2063a34c753fSRafael Auler       errs() << " Failed to extract any binary name from a profile.\n";
2064a34c753fSRafael Auler     }
2065a34c753fSRafael Auler     if (errs().has_colors())
2066a34c753fSRafael Auler       errs().resetColor();
2067a34c753fSRafael Auler 
2068a34c753fSRafael Auler     exit(1);
2069a34c753fSRafael Auler   }
2070a34c753fSRafael Auler 
2071a34c753fSRafael Auler   return std::error_code();
2072a34c753fSRafael Auler }
2073a34c753fSRafael Auler 
2074a34c753fSRafael Auler std::error_code DataAggregator::parseTaskEvents() {
2075a34c753fSRafael Auler   outs() << "PERF2BOLT: parsing perf-script task events output\n";
2076a34c753fSRafael Auler   NamedRegionTimer T("parseTaskEvents", "Parsing task events", TimerGroupName,
2077a34c753fSRafael Auler                      TimerGroupDesc, opts::TimeAggregator);
2078a34c753fSRafael Auler 
2079a34c753fSRafael Auler   while (hasData()) {
2080835a9c28SAmir Ayupov     if (std::optional<int32_t> CommInfo = parseCommExecEvent()) {
2081a34c753fSRafael Auler       // Remove forked child that ran execve
2082a34c753fSRafael Auler       auto MMapInfoIter = BinaryMMapInfo.find(*CommInfo);
2083def464aaSAmir Ayupov       if (MMapInfoIter != BinaryMMapInfo.end() && MMapInfoIter->second.Forked)
2084a34c753fSRafael Auler         BinaryMMapInfo.erase(MMapInfoIter);
2085a34c753fSRafael Auler       consumeRestOfLine();
2086a34c753fSRafael Auler       continue;
2087a34c753fSRafael Auler     }
2088a34c753fSRafael Auler 
2089835a9c28SAmir Ayupov     std::optional<ForkInfo> ForkInfo = parseForkEvent();
2090a34c753fSRafael Auler     if (!ForkInfo)
2091a34c753fSRafael Auler       continue;
2092a34c753fSRafael Auler 
2093a34c753fSRafael Auler     if (ForkInfo->ParentPID == ForkInfo->ChildPID)
2094a34c753fSRafael Auler       continue;
2095a34c753fSRafael Auler 
2096a34c753fSRafael Auler     if (ForkInfo->Time == 0) {
2097a34c753fSRafael Auler       // Process was forked and mmaped before perf ran. In this case the child
2098a34c753fSRafael Auler       // should have its own mmap entry unless it was execve'd.
2099a34c753fSRafael Auler       continue;
2100a34c753fSRafael Auler     }
2101a34c753fSRafael Auler 
2102a34c753fSRafael Auler     auto MMapInfoIter = BinaryMMapInfo.find(ForkInfo->ParentPID);
2103a34c753fSRafael Auler     if (MMapInfoIter == BinaryMMapInfo.end())
2104a34c753fSRafael Auler       continue;
2105a34c753fSRafael Auler 
2106a34c753fSRafael Auler     MMapInfo MMapInfo = MMapInfoIter->second;
2107a34c753fSRafael Auler     MMapInfo.PID = ForkInfo->ChildPID;
2108a34c753fSRafael Auler     MMapInfo.Forked = true;
2109a34c753fSRafael Auler     BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo));
2110a34c753fSRafael Auler   }
2111a34c753fSRafael Auler 
2112a34c753fSRafael Auler   outs() << "PERF2BOLT: input binary is associated with "
2113a34c753fSRafael Auler          << BinaryMMapInfo.size() << " PID(s)\n";
2114a34c753fSRafael Auler 
2115a34c753fSRafael Auler   LLVM_DEBUG({
21164a7966eaSAmir Ayupov     for (const MMapInfo &MMI : llvm::make_second_range(BinaryMMapInfo))
21174a7966eaSAmir Ayupov       outs() << formatv("  {0}{1}: ({2:x}: {3:x})\n", MMI.PID,
21184a7966eaSAmir Ayupov                         (MMI.Forked ? " (forked)" : ""), MMI.MMapAddress,
21194a7966eaSAmir Ayupov                         MMI.Size);
2120a34c753fSRafael Auler   });
2121a34c753fSRafael Auler 
2122a34c753fSRafael Auler   return std::error_code();
2123a34c753fSRafael Auler }
2124a34c753fSRafael Auler 
2125835a9c28SAmir Ayupov std::optional<std::pair<StringRef, StringRef>>
2126a34c753fSRafael Auler DataAggregator::parseNameBuildIDPair() {
212740c2e0faSMaksim Panchenko   while (checkAndConsumeFS()) {
212840c2e0faSMaksim Panchenko   }
2129a34c753fSRafael Auler 
2130a34c753fSRafael Auler   ErrorOr<StringRef> BuildIDStr = parseString(FieldSeparator, true);
2131a34c753fSRafael Auler   if (std::error_code EC = BuildIDStr.getError())
2132e324a80fSKazu Hirata     return std::nullopt;
2133a34c753fSRafael Auler 
2134661577b5SMaksim Panchenko   // If one of the strings is missing, don't issue a parsing error, but still
2135661577b5SMaksim Panchenko   // do not return a value.
2136e549ac07SRafael Auler   consumeAllRemainingFS();
2137ba9cc653SRafael Auler   if (checkNewLine())
2138e324a80fSKazu Hirata     return std::nullopt;
2139661577b5SMaksim Panchenko 
2140a34c753fSRafael Auler   ErrorOr<StringRef> NameStr = parseString(FieldSeparator, true);
2141a34c753fSRafael Auler   if (std::error_code EC = NameStr.getError())
2142e324a80fSKazu Hirata     return std::nullopt;
2143a34c753fSRafael Auler 
2144a34c753fSRafael Auler   consumeRestOfLine();
2145a34c753fSRafael Auler   return std::make_pair(NameStr.get(), BuildIDStr.get());
2146a34c753fSRafael Auler }
2147a34c753fSRafael Auler 
2148661577b5SMaksim Panchenko bool DataAggregator::hasAllBuildIDs() {
2149661577b5SMaksim Panchenko   const StringRef SavedParsingBuf = ParsingBuf;
2150661577b5SMaksim Panchenko 
2151661577b5SMaksim Panchenko   if (!hasData())
2152661577b5SMaksim Panchenko     return false;
2153661577b5SMaksim Panchenko 
2154661577b5SMaksim Panchenko   bool HasInvalidEntries = false;
2155661577b5SMaksim Panchenko   while (hasData()) {
2156661577b5SMaksim Panchenko     if (!parseNameBuildIDPair()) {
2157661577b5SMaksim Panchenko       HasInvalidEntries = true;
2158661577b5SMaksim Panchenko       break;
2159661577b5SMaksim Panchenko     }
2160661577b5SMaksim Panchenko   }
2161661577b5SMaksim Panchenko 
2162661577b5SMaksim Panchenko   ParsingBuf = SavedParsingBuf;
2163661577b5SMaksim Panchenko 
2164661577b5SMaksim Panchenko   return !HasInvalidEntries;
2165661577b5SMaksim Panchenko }
2166661577b5SMaksim Panchenko 
2167835a9c28SAmir Ayupov std::optional<StringRef>
2168a34c753fSRafael Auler DataAggregator::getFileNameForBuildID(StringRef FileBuildID) {
2169661577b5SMaksim Panchenko   const StringRef SavedParsingBuf = ParsingBuf;
2170661577b5SMaksim Panchenko 
2171661577b5SMaksim Panchenko   StringRef FileName;
2172a34c753fSRafael Auler   while (hasData()) {
2173835a9c28SAmir Ayupov     std::optional<std::pair<StringRef, StringRef>> IDPair =
2174835a9c28SAmir Ayupov         parseNameBuildIDPair();
2175661577b5SMaksim Panchenko     if (!IDPair) {
2176661577b5SMaksim Panchenko       consumeRestOfLine();
2177661577b5SMaksim Panchenko       continue;
2178a34c753fSRafael Auler     }
2179661577b5SMaksim Panchenko 
2180ad8fd5b1SKazu Hirata     if (IDPair->second.starts_with(FileBuildID)) {
2181661577b5SMaksim Panchenko       FileName = sys::path::filename(IDPair->first);
2182661577b5SMaksim Panchenko       break;
2183661577b5SMaksim Panchenko     }
2184661577b5SMaksim Panchenko   }
2185661577b5SMaksim Panchenko 
2186661577b5SMaksim Panchenko   ParsingBuf = SavedParsingBuf;
2187661577b5SMaksim Panchenko 
2188661577b5SMaksim Panchenko   if (!FileName.empty())
2189661577b5SMaksim Panchenko     return FileName;
2190661577b5SMaksim Panchenko 
2191e324a80fSKazu Hirata   return std::nullopt;
2192a34c753fSRafael Auler }
2193a34c753fSRafael Auler 
2194a34c753fSRafael Auler std::error_code
2195a34c753fSRafael Auler DataAggregator::writeAggregatedFile(StringRef OutputFilename) const {
2196a34c753fSRafael Auler   std::error_code EC;
2197a34c753fSRafael Auler   raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
2198a34c753fSRafael Auler   if (EC)
2199a34c753fSRafael Auler     return EC;
2200a34c753fSRafael Auler 
2201a34c753fSRafael Auler   bool WriteMemLocs = false;
2202a34c753fSRafael Auler 
2203a34c753fSRafael Auler   auto writeLocation = [&OutFile, &WriteMemLocs](const Location &Loc) {
2204a34c753fSRafael Auler     if (WriteMemLocs)
2205a34c753fSRafael Auler       OutFile << (Loc.IsSymbol ? "4 " : "3 ");
2206a34c753fSRafael Auler     else
2207a34c753fSRafael Auler       OutFile << (Loc.IsSymbol ? "1 " : "0 ");
2208a34c753fSRafael Auler     OutFile << (Loc.Name.empty() ? "[unknown]" : getEscapedName(Loc.Name))
2209a34c753fSRafael Auler             << " " << Twine::utohexstr(Loc.Offset) << FieldSeparator;
2210a34c753fSRafael Auler   };
2211a34c753fSRafael Auler 
2212a34c753fSRafael Auler   uint64_t BranchValues = 0;
2213a34c753fSRafael Auler   uint64_t MemValues = 0;
2214a34c753fSRafael Auler 
2215a34c753fSRafael Auler   if (BAT)
2216a34c753fSRafael Auler     OutFile << "boltedcollection\n";
2217a34c753fSRafael Auler   if (opts::BasicAggregation) {
2218a34c753fSRafael Auler     OutFile << "no_lbr";
221934bcadc3SKazu Hirata     for (const StringMapEntry<std::nullopt_t> &Entry : EventNames)
2220a34c753fSRafael Auler       OutFile << " " << Entry.getKey();
2221a34c753fSRafael Auler     OutFile << "\n";
2222a34c753fSRafael Auler 
222373b89e3fSMaksim Panchenko     for (const auto &KV : NamesToSamples) {
222473b89e3fSMaksim Panchenko       const FuncSampleData &FSD = KV.second;
222573b89e3fSMaksim Panchenko       for (const SampleInfo &SI : FSD.Data) {
2226a34c753fSRafael Auler         writeLocation(SI.Loc);
2227a34c753fSRafael Auler         OutFile << SI.Hits << "\n";
2228a34c753fSRafael Auler         ++BranchValues;
2229a34c753fSRafael Auler       }
2230a34c753fSRafael Auler     }
2231a34c753fSRafael Auler   } else {
223273b89e3fSMaksim Panchenko     for (const auto &KV : NamesToBranches) {
223373b89e3fSMaksim Panchenko       const FuncBranchData &FBD = KV.second;
22349f15aa00SAmir Ayupov       for (const BranchInfo &BI : FBD.Data) {
2235a34c753fSRafael Auler         writeLocation(BI.From);
2236a34c753fSRafael Auler         writeLocation(BI.To);
2237a34c753fSRafael Auler         OutFile << BI.Mispreds << " " << BI.Branches << "\n";
2238a34c753fSRafael Auler         ++BranchValues;
2239a34c753fSRafael Auler       }
22409f15aa00SAmir Ayupov       for (const BranchInfo &BI : FBD.EntryData) {
2241a34c753fSRafael Auler         // Do not output if source is a known symbol, since this was already
2242a34c753fSRafael Auler         // accounted for in the source function
2243a34c753fSRafael Auler         if (BI.From.IsSymbol)
2244a34c753fSRafael Auler           continue;
2245a34c753fSRafael Auler         writeLocation(BI.From);
2246a34c753fSRafael Auler         writeLocation(BI.To);
2247a34c753fSRafael Auler         OutFile << BI.Mispreds << " " << BI.Branches << "\n";
2248a34c753fSRafael Auler         ++BranchValues;
2249a34c753fSRafael Auler       }
2250a34c753fSRafael Auler     }
2251a34c753fSRafael Auler 
2252a34c753fSRafael Auler     WriteMemLocs = true;
225373b89e3fSMaksim Panchenko     for (const auto &KV : NamesToMemEvents) {
225473b89e3fSMaksim Panchenko       const FuncMemData &FMD = KV.second;
225573b89e3fSMaksim Panchenko       for (const MemInfo &MemEvent : FMD.Data) {
2256a34c753fSRafael Auler         writeLocation(MemEvent.Offset);
2257a34c753fSRafael Auler         writeLocation(MemEvent.Addr);
2258a34c753fSRafael Auler         OutFile << MemEvent.Count << "\n";
2259a34c753fSRafael Auler         ++MemValues;
2260a34c753fSRafael Auler       }
2261a34c753fSRafael Auler     }
2262a34c753fSRafael Auler   }
2263a34c753fSRafael Auler 
226440c2e0faSMaksim Panchenko   outs() << "PERF2BOLT: wrote " << BranchValues << " objects and " << MemValues
226540c2e0faSMaksim Panchenko          << " memory objects to " << OutputFilename << "\n";
2266a34c753fSRafael Auler 
2267a34c753fSRafael Auler   return std::error_code();
2268a34c753fSRafael Auler }
2269a34c753fSRafael Auler 
227062806811SAmir Ayupov std::error_code DataAggregator::writeBATYAML(BinaryContext &BC,
227162806811SAmir Ayupov                                              StringRef OutputFilename) const {
227262806811SAmir Ayupov   std::error_code EC;
227362806811SAmir Ayupov   raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None);
227462806811SAmir Ayupov   if (EC)
227562806811SAmir Ayupov     return EC;
227662806811SAmir Ayupov 
227762806811SAmir Ayupov   yaml::bolt::BinaryProfile BP;
227862806811SAmir Ayupov 
22794d19676dSAmir Ayupov   const MCPseudoProbeDecoder *PseudoProbeDecoder =
2280c820bd3eSAmir Ayupov       opts::ProfileWritePseudoProbes ? BC.getPseudoProbeDecoder() : nullptr;
22819b007a19SAmir Ayupov 
228262806811SAmir Ayupov   // Fill out the header info.
228362806811SAmir Ayupov   BP.Header.Version = 1;
228462806811SAmir Ayupov   BP.Header.FileName = std::string(BC.getFilename());
228562806811SAmir Ayupov   std::optional<StringRef> BuildID = BC.getFileBuildID();
228662806811SAmir Ayupov   BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>";
228762806811SAmir Ayupov   BP.Header.Origin = std::string(getReaderName());
228862806811SAmir Ayupov   // Only the input binary layout order is supported.
228962806811SAmir Ayupov   BP.Header.IsDFSOrder = false;
229062806811SAmir Ayupov   // FIXME: Need to match hash function used to produce BAT hashes.
229162806811SAmir Ayupov   BP.Header.HashFunction = HashFunction::Default;
229262806811SAmir Ayupov 
229362806811SAmir Ayupov   ListSeparator LS(",");
229462806811SAmir Ayupov   raw_string_ostream EventNamesOS(BP.Header.EventNames);
229562806811SAmir Ayupov   for (const StringMapEntry<std::nullopt_t> &EventEntry : EventNames)
229662806811SAmir Ayupov     EventNamesOS << LS << EventEntry.first().str();
229762806811SAmir Ayupov 
229862806811SAmir Ayupov   BP.Header.Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE
229962806811SAmir Ayupov                                            : BinaryFunction::PF_LBR;
230062806811SAmir Ayupov 
2301c00c62c1SAmir Ayupov   // Add probe inline tree nodes.
2302c00c62c1SAmir Ayupov   YAMLProfileWriter::InlineTreeDesc InlineTree;
2303c00c62c1SAmir Ayupov   if (PseudoProbeDecoder)
2304c00c62c1SAmir Ayupov     std::tie(BP.PseudoProbeDesc, InlineTree) =
2305c00c62c1SAmir Ayupov         YAMLProfileWriter::convertPseudoProbeDesc(*PseudoProbeDecoder);
2306c00c62c1SAmir Ayupov 
230762806811SAmir Ayupov   if (!opts::BasicAggregation) {
230862806811SAmir Ayupov     // Convert profile for functions not covered by BAT
230962806811SAmir Ayupov     for (auto &BFI : BC.getBinaryFunctions()) {
231062806811SAmir Ayupov       BinaryFunction &Function = BFI.second;
231162806811SAmir Ayupov       if (!Function.hasProfile())
231262806811SAmir Ayupov         continue;
231362806811SAmir Ayupov       if (BAT->isBATFunction(Function.getAddress()))
231462806811SAmir Ayupov         continue;
2315c00c62c1SAmir Ayupov       BP.Functions.emplace_back(YAMLProfileWriter::convert(
2316c00c62c1SAmir Ayupov           Function, /*UseDFS=*/false, InlineTree, BAT));
231762806811SAmir Ayupov     }
2318d7d2f7caSAmir Ayupov 
2319d7d2f7caSAmir Ayupov     for (const auto &KV : NamesToBranches) {
2320d7d2f7caSAmir Ayupov       const StringRef FuncName = KV.first;
2321d7d2f7caSAmir Ayupov       const FuncBranchData &Branches = KV.second;
2322d7d2f7caSAmir Ayupov       yaml::bolt::BinaryFunctionProfile YamlBF;
2323d7d2f7caSAmir Ayupov       BinaryData *BD = BC.getBinaryDataByName(FuncName);
2324d7d2f7caSAmir Ayupov       assert(BD);
2325d7d2f7caSAmir Ayupov       uint64_t FuncAddress = BD->getAddress();
2326d7d2f7caSAmir Ayupov       if (!BAT->isBATFunction(FuncAddress))
2327d7d2f7caSAmir Ayupov         continue;
2328d7d2f7caSAmir Ayupov       BinaryFunction *BF = BC.getBinaryFunctionAtAddress(FuncAddress);
2329d7d2f7caSAmir Ayupov       assert(BF);
233097025bd9SAmir Ayupov       YamlBF.Name = getLocationName(*BF, BAT);
2331d7d2f7caSAmir Ayupov       YamlBF.Id = BF->getFunctionNumber();
2332d7d2f7caSAmir Ayupov       YamlBF.Hash = BAT->getBFHash(FuncAddress);
2333d7d2f7caSAmir Ayupov       YamlBF.ExecCount = BF->getKnownExecutionCount();
2334d7d2f7caSAmir Ayupov       YamlBF.NumBasicBlocks = BAT->getNumBasicBlocks(FuncAddress);
2335d7d2f7caSAmir Ayupov       const BoltAddressTranslation::BBHashMapTy &BlockMap =
2336d7d2f7caSAmir Ayupov           BAT->getBBHashMap(FuncAddress);
23373997f0ebSAmir Ayupov       YamlBF.Blocks.resize(YamlBF.NumBasicBlocks);
2338d7d2f7caSAmir Ayupov 
2339d1d9545eSAmir Ayupov       for (auto &&[Entry, YamlBB] : llvm::zip(BlockMap, YamlBF.Blocks)) {
2340d1d9545eSAmir Ayupov         const auto &Block = Entry.second;
2341d1d9545eSAmir Ayupov         YamlBB.Hash = Block.Hash;
2342d1d9545eSAmir Ayupov         YamlBB.Index = Block.Index;
2343d1d9545eSAmir Ayupov       }
23443997f0ebSAmir Ayupov 
2345b5af667bSAmir Ayupov       // Lookup containing basic block offset and index
2346b5af667bSAmir Ayupov       auto getBlock = [&BlockMap](uint32_t Offset) {
2347b5af667bSAmir Ayupov         auto BlockIt = BlockMap.upper_bound(Offset);
2348b5af667bSAmir Ayupov         if (LLVM_UNLIKELY(BlockIt == BlockMap.begin())) {
2349b5af667bSAmir Ayupov           errs() << "BOLT-ERROR: invalid BAT section\n";
2350b5af667bSAmir Ayupov           exit(1);
2351b5af667bSAmir Ayupov         }
2352b5af667bSAmir Ayupov         --BlockIt;
2353465bfd41SAmir Ayupov         return std::pair(BlockIt->first, BlockIt->second.Index);
2354b5af667bSAmir Ayupov       };
2355b5af667bSAmir Ayupov 
23569f15aa00SAmir Ayupov       for (const BranchInfo &BI : Branches.Data) {
23574ecf2cafSAmir Ayupov         using namespace yaml::bolt;
23584ecf2cafSAmir Ayupov         const auto &[BlockOffset, BlockIndex] = getBlock(BI.From.Offset);
23594ecf2cafSAmir Ayupov         BinaryBasicBlockProfile &YamlBB = YamlBF.Blocks[BlockIndex];
23604ecf2cafSAmir Ayupov         if (BI.To.IsSymbol && BI.To.Name == BI.From.Name && BI.To.Offset != 0) {
23614ecf2cafSAmir Ayupov           // Internal branch
23624ecf2cafSAmir Ayupov           const unsigned SuccIndex = getBlock(BI.To.Offset).second;
23634ecf2cafSAmir Ayupov           auto &SI = YamlBB.Successors.emplace_back(SuccessorInfo{SuccIndex});
23644ecf2cafSAmir Ayupov           SI.Count = BI.Branches;
23654ecf2cafSAmir Ayupov           SI.Mispreds = BI.Mispreds;
23664ecf2cafSAmir Ayupov         } else {
23674ecf2cafSAmir Ayupov           // Call
23684ecf2cafSAmir Ayupov           const uint32_t Offset = BI.From.Offset - BlockOffset;
23694ecf2cafSAmir Ayupov           auto &CSI = YamlBB.CallSites.emplace_back(CallSiteInfo{Offset});
23704ecf2cafSAmir Ayupov           CSI.Count = BI.Branches;
23714ecf2cafSAmir Ayupov           CSI.Mispreds = BI.Mispreds;
23724ecf2cafSAmir Ayupov           if (const BinaryData *BD = BC.getBinaryDataByName(BI.To.Name))
23734ecf2cafSAmir Ayupov             YAMLProfileWriter::setCSIDestination(BC, CSI, BD->getSymbol(), BAT,
23744ecf2cafSAmir Ayupov                                                  BI.To.Offset);
2375d7d2f7caSAmir Ayupov         }
23763997f0ebSAmir Ayupov       }
23774f127667SAmir Ayupov       // Set entry counts, similar to DataReader::readProfile.
23789f15aa00SAmir Ayupov       for (const BranchInfo &BI : Branches.EntryData) {
23794f127667SAmir Ayupov         if (!BlockMap.isInputBlock(BI.To.Offset)) {
23804f127667SAmir Ayupov           if (opts::Verbosity >= 1)
23814f127667SAmir Ayupov             errs() << "BOLT-WARNING: Unexpected EntryData in " << FuncName
23824f127667SAmir Ayupov                    << " at 0x" << Twine::utohexstr(BI.To.Offset) << '\n';
23834f127667SAmir Ayupov           continue;
23844f127667SAmir Ayupov         }
23854f127667SAmir Ayupov         const unsigned BlockIndex = BlockMap.getBBIndex(BI.To.Offset);
23864f127667SAmir Ayupov         YamlBF.Blocks[BlockIndex].ExecCount += BI.Branches;
23874f127667SAmir Ayupov       }
23889b007a19SAmir Ayupov       if (PseudoProbeDecoder) {
2389c00c62c1SAmir Ayupov         DenseMap<const MCDecodedPseudoProbeInlineTree *, uint32_t>
2390c00c62c1SAmir Ayupov             InlineTreeNodeId;
2391c00c62c1SAmir Ayupov         if (BF->getGUID()) {
2392c00c62c1SAmir Ayupov           std::tie(YamlBF.InlineTree, InlineTreeNodeId) =
2393c00c62c1SAmir Ayupov               YAMLProfileWriter::convertBFInlineTree(*PseudoProbeDecoder,
2394c00c62c1SAmir Ayupov                                                      InlineTree, BF->getGUID());
23959b007a19SAmir Ayupov         }
2396c905db67SAmir Ayupov         // Fetch probes belonging to all fragments
2397c905db67SAmir Ayupov         const AddressProbesMap &ProbeMap =
2398c905db67SAmir Ayupov             PseudoProbeDecoder->getAddress2ProbesMap();
2399c905db67SAmir Ayupov         BinaryFunction::FragmentsSetTy Fragments(BF->Fragments);
2400c905db67SAmir Ayupov         Fragments.insert(BF);
2401c00c62c1SAmir Ayupov         DenseMap<
2402c00c62c1SAmir Ayupov             uint32_t,
2403c00c62c1SAmir Ayupov             std::vector<std::reference_wrapper<const MCDecodedPseudoProbe>>>
2404c00c62c1SAmir Ayupov             BlockProbes;
2405c905db67SAmir Ayupov         for (const BinaryFunction *F : Fragments) {
2406c905db67SAmir Ayupov           const uint64_t FuncAddr = F->getAddress();
2407ee09f7d1SAmir Ayupov           for (const MCDecodedPseudoProbe &Probe :
2408ee09f7d1SAmir Ayupov                ProbeMap.find(FuncAddr, FuncAddr + F->getSize())) {
2409ee09f7d1SAmir Ayupov             const uint32_t OutputAddress = Probe.getAddress();
2410c905db67SAmir Ayupov             const uint32_t InputOffset = BAT->translate(
2411c905db67SAmir Ayupov                 FuncAddr, OutputAddress - FuncAddr, /*IsBranchSrc=*/true);
2412c905db67SAmir Ayupov             const unsigned BlockIndex = getBlock(InputOffset).second;
2413c00c62c1SAmir Ayupov             BlockProbes[BlockIndex].emplace_back(Probe);
2414c905db67SAmir Ayupov           }
2415c905db67SAmir Ayupov         }
2416c00c62c1SAmir Ayupov 
2417c00c62c1SAmir Ayupov         for (auto &[Block, Probes] : BlockProbes) {
2418c00c62c1SAmir Ayupov           YamlBF.Blocks[Block].PseudoProbes =
2419c00c62c1SAmir Ayupov               YAMLProfileWriter::writeBlockProbes(Probes, InlineTreeNodeId);
2420c00c62c1SAmir Ayupov         }
24219b007a19SAmir Ayupov       }
2422ccc7a072SAmir Ayupov       // Skip printing if there's no profile data
2423ccc7a072SAmir Ayupov       llvm::erase_if(
2424ccc7a072SAmir Ayupov           YamlBF.Blocks, [](const yaml::bolt::BinaryBasicBlockProfile &YamlBB) {
2425ccc7a072SAmir Ayupov             auto HasCount = [](const auto &SI) { return SI.Count; };
2426ccc7a072SAmir Ayupov             bool HasAnyCount = YamlBB.ExecCount ||
2427ccc7a072SAmir Ayupov                                llvm::any_of(YamlBB.Successors, HasCount) ||
2428ccc7a072SAmir Ayupov                                llvm::any_of(YamlBB.CallSites, HasCount);
2429ccc7a072SAmir Ayupov             return !HasAnyCount;
24303997f0ebSAmir Ayupov           });
2431d7d2f7caSAmir Ayupov       BP.Functions.emplace_back(YamlBF);
2432d7d2f7caSAmir Ayupov     }
243362806811SAmir Ayupov   }
243462806811SAmir Ayupov 
243562806811SAmir Ayupov   // Write the profile.
243662806811SAmir Ayupov   yaml::Output Out(OutFile, nullptr, 0);
243762806811SAmir Ayupov   Out << BP;
243862806811SAmir Ayupov   return std::error_code();
243962806811SAmir Ayupov }
244062806811SAmir Ayupov 
244140c2e0faSMaksim Panchenko void DataAggregator::dump() const { DataReader::dump(); }
2442a34c753fSRafael Auler 
2443a34c753fSRafael Auler void DataAggregator::dump(const LBREntry &LBR) const {
2444a34c753fSRafael Auler   Diag << "From: " << Twine::utohexstr(LBR.From)
2445a34c753fSRafael Auler        << " To: " << Twine::utohexstr(LBR.To) << " Mispred? " << LBR.Mispred
2446a34c753fSRafael Auler        << "\n";
2447a34c753fSRafael Auler }
2448a34c753fSRafael Auler 
2449a34c753fSRafael Auler void DataAggregator::dump(const PerfBranchSample &Sample) const {
2450a34c753fSRafael Auler   Diag << "Sample LBR entries: " << Sample.LBR.size() << "\n";
2451def464aaSAmir Ayupov   for (const LBREntry &LBR : Sample.LBR)
2452a34c753fSRafael Auler     dump(LBR);
2453a34c753fSRafael Auler }
2454a34c753fSRafael Auler 
2455a34c753fSRafael Auler void DataAggregator::dump(const PerfMemSample &Sample) const {
2456a34c753fSRafael Auler   Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n";
2457a34c753fSRafael Auler }
2458