Bug Summary

File:build/source/bolt/lib/Profile/DataAggregator.cpp
Warning:line 794, column 5
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

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