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