File: | build/source/bolt/lib/Profile/DataAggregator.cpp |
Warning: | line 794, column 5 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
39 | using namespace llvm; | |||
40 | using namespace bolt; | |||
41 | ||||
42 | namespace opts { | |||
43 | ||||
44 | static cl::opt<bool> | |||
45 | BasicAggregation("nl", | |||
46 | cl::desc("aggregate basic samples (without LBR info)"), | |||
47 | cl::cat(AggregatorCategory)); | |||
48 | ||||
49 | static cl::opt<bool> | |||
50 | FilterMemProfile("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 | ||||
56 | static cl::opt<unsigned long long> | |||
57 | FilterPID("pid", | |||
58 | cl::desc("only use samples from process with specified PID"), | |||
59 | cl::init(0), | |||
60 | cl::Optional, | |||
61 | cl::cat(AggregatorCategory)); | |||
62 | ||||
63 | static cl::opt<bool> | |||
64 | IgnoreBuildID("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 | ||||
69 | static 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 | ||||
74 | static cl::opt<unsigned long long> | |||
75 | MaxSamples("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 | ||||
82 | extern cl::opt<opts::ProfileFormatKind> ProfileFormat; | |||
83 | ||||
84 | cl::opt<bool> ReadPreAggregated( | |||
85 | "pa", cl::desc("skip perf and read data from a pre-aggregated file format"), | |||
86 | cl::cat(AggregatorCategory)); | |||
87 | ||||
88 | static cl::opt<bool> | |||
89 | TimeAggregator("time-aggr", | |||
90 | cl::desc("time BOLT aggregator"), | |||
91 | cl::init(false), | |||
92 | cl::ZeroOrMore, | |||
93 | cl::cat(AggregatorCategory)); | |||
94 | ||||
95 | static cl::opt<bool> | |||
96 | UseEventPC("use-event-pc", | |||
97 | cl::desc("use event PC in combination with LBR sampling"), | |||
98 | cl::cat(AggregatorCategory)); | |||
99 | ||||
100 | static 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 | ||||
106 | namespace { | |||
107 | ||||
108 | const char TimerGroupName[] = "aggregator"; | |||
109 | const char TimerGroupDesc[] = "Aggregator"; | |||
110 | ||||
111 | std::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 | ||||
129 | constexpr uint64_t DataAggregator::KernelBaseAddr; | |||
130 | ||||
131 | DataAggregator::~DataAggregator() { deleteTempFiles(); } | |||
132 | ||||
133 | namespace { | |||
134 | void 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 | ||||
141 | void DataAggregator::deleteTempFiles() { | |||
142 | for (std::string &FileName : TempFiles) | |||
143 | deleteTempFile(FileName); | |||
144 | TempFiles.clear(); | |||
145 | } | |||
146 | ||||
147 | void 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 | ||||
157 | void 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 | ||||
195 | void 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 | ||||
212 | void 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 | ||||
273 | void 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 | ||||
325 | bool 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 | ||||
353 | void 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 | ||||
374 | std::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 | ||||
443 | void 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 | ||||
470 | int 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 | ||||
509 | Error 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 | ||||
599 | Error DataAggregator::readProfile(BinaryContext &BC) { | |||
600 | processProfile(BC); | |||
| ||||
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 | ||||
616 | bool DataAggregator::mayHaveProfileData(const BinaryFunction &Function) { | |||
617 | return Function.hasProfileAvailable(); | |||
618 | } | |||
619 | ||||
620 | void DataAggregator::processProfile(BinaryContext &BC) { | |||
621 | if (opts::ReadPreAggregated) | |||
622 | 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 | ||||
647 | BinaryFunction * | |||
648 | DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const { | |||
649 | if (!BC->containsAddress(Address)) | |||
650 | return nullptr; | |||
651 | ||||
652 | return BC->getBinaryFunctionContainingAddress(Address, /*CheckPastEnd=*/false, | |||
653 | /*UseMaxSize=*/true); | |||
654 | } | |||
655 | ||||
656 | StringRef 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 | ||||
682 | bool 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 | ||||
701 | bool 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 | ||||
727 | bool 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 | ||||
773 | bool 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 | ||||
789 | bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second, | |||
790 | uint64_t Count) { | |||
791 | BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(First.To); | |||
792 | BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From); | |||
793 | if (!FromFunc || !ToFunc) { | |||
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) | |||
| ||||
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 | ||||
840 | bool 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 | ||||
926 | std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>> | |||
927 | DataAggregator::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 | ||||
939 | bool 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 | ||||
963 | bool 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 | ||||
974 | ErrorOr<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 | ||||
1025 | bool 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 | ||||
1034 | void 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 | ||||
1047 | bool DataAggregator::checkNewLine() { | |||
1048 | return ParsingBuf[0] == '\n'; | |||
1049 | } | |||
1050 | ||||
1051 | ErrorOr<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 | ||||
1094 | ErrorOr<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 | ||||
1134 | ErrorOr<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 | ||||
1189 | ErrorOr<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 | ||||
1213 | ErrorOr<DataAggregator::AggregatedLBREntry> | |||
1214 | DataAggregator::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 | ||||
1272 | bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const { | |||
1273 | return opts::IgnoreInterruptLBR && | |||
1274 | (LBR.From >= KernelBaseAddr || LBR.To >= KernelBaseAddr); | |||
1275 | } | |||
1276 | ||||
1277 | std::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 | ||||
1377 | std::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 | ||||
1570 | void 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 | ||||
1595 | std::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 | ||||
1617 | void 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 | ||||
1664 | std::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 | ||||
1682 | void 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 | ||||
1723 | std::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 | ||||
1745 | void 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) { | |||
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
| |||
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); | |||
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 | ||||
1806 | std::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 | ||||
1833 | namespace { | |||
1834 | std::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 | ||||
1846 | std::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 | ||||
1895 | ErrorOr<std::pair<StringRef, DataAggregator::MMapInfo>> | |||
1896 | DataAggregator::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 | ||||
1970 | std::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 | ||||
2080 | std::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 | ||||
2131 | std::optional<std::pair<StringRef, StringRef>> | |||
2132 | DataAggregator::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 | ||||
2154 | bool 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 | ||||
2173 | std::optional<StringRef> | |||
2174 | DataAggregator::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 | ||||
2200 | std::error_code | |||
2201 | DataAggregator::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 | ||||
2276 | void DataAggregator::dump() const { DataReader::dump(); } | |||
2277 | ||||
2278 | void 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 | ||||
2284 | void 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 | ||||
2290 | void DataAggregator::dump(const PerfMemSample &Sample) const { | |||
2291 | Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n"; | |||
2292 | } |