12f09f445SMaksim Panchenko //===- bolt/Profile/DataAggregator.cpp - Perf data aggregator -------------===// 2a34c753fSRafael Auler // 3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information. 5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a34c753fSRafael Auler // 7a34c753fSRafael Auler //===----------------------------------------------------------------------===// 8a34c753fSRafael Auler // 9a34c753fSRafael Auler // This family of functions reads profile data written by perf record, 10a34c753fSRafael Auler // aggregate it and then write it back to an output file. 11a34c753fSRafael Auler // 12a34c753fSRafael Auler //===----------------------------------------------------------------------===// 13a34c753fSRafael Auler 14a34c753fSRafael Auler #include "bolt/Profile/DataAggregator.h" 15a34c753fSRafael Auler #include "bolt/Core/BinaryContext.h" 16a34c753fSRafael Auler #include "bolt/Core/BinaryFunction.h" 17a34c753fSRafael Auler #include "bolt/Profile/BoltAddressTranslation.h" 18a34c753fSRafael Auler #include "bolt/Profile/Heatmap.h" 19a34c753fSRafael Auler #include "bolt/Utils/CommandLineOpts.h" 20a34c753fSRafael Auler #include "bolt/Utils/Utils.h" 21f119a248SAmir Ayupov #include "llvm/ADT/STLExtras.h" 22ae585be1SRafael Auler #include "llvm/ADT/ScopeExit.h" 23a34c753fSRafael Auler #include "llvm/Support/CommandLine.h" 24a34c753fSRafael Auler #include "llvm/Support/Debug.h" 25290e4823Sserge-sans-paille #include "llvm/Support/Errc.h" 26a34c753fSRafael Auler #include "llvm/Support/FileSystem.h" 27a34c753fSRafael Auler #include "llvm/Support/Process.h" 28a34c753fSRafael Auler #include "llvm/Support/Program.h" 29a34c753fSRafael Auler #include "llvm/Support/Regex.h" 30a34c753fSRafael Auler #include "llvm/Support/Timer.h" 31a34c753fSRafael Auler #include "llvm/Support/raw_ostream.h" 32a34c753fSRafael Auler #include <map> 333c255f67SKrzysztof Parzyszek #include <optional> 34a34c753fSRafael Auler #include <unordered_map> 35d914486aSAmir Ayupov #include <utility> 36a34c753fSRafael Auler 37a34c753fSRafael Auler #define DEBUG_TYPE "aggregator" 38a34c753fSRafael Auler 39a34c753fSRafael Auler using namespace llvm; 40a34c753fSRafael Auler using namespace bolt; 41a34c753fSRafael Auler 42a34c753fSRafael Auler namespace opts { 43a34c753fSRafael Auler 44a34c753fSRafael Auler static cl::opt<bool> 45a34c753fSRafael Auler BasicAggregation("nl", 46a34c753fSRafael Auler cl::desc("aggregate basic samples (without LBR info)"), 47a34c753fSRafael Auler cl::cat(AggregatorCategory)); 48a34c753fSRafael Auler 49a34c753fSRafael Auler static cl::opt<bool> 50a34c753fSRafael Auler FilterMemProfile("filter-mem-profile", 51a34c753fSRafael Auler cl::desc("if processing a memory profile, filter out stack or heap accesses " 52a34c753fSRafael Auler "that won't be useful for BOLT to reduce profile file size"), 53a34c753fSRafael Auler cl::init(true), 54a34c753fSRafael Auler cl::cat(AggregatorCategory)); 55a34c753fSRafael Auler 56a34c753fSRafael Auler static cl::opt<unsigned long long> 57a34c753fSRafael Auler FilterPID("pid", 58a34c753fSRafael Auler cl::desc("only use samples from process with specified PID"), 59a34c753fSRafael Auler cl::init(0), 60a34c753fSRafael Auler cl::Optional, 61a34c753fSRafael Auler cl::cat(AggregatorCategory)); 62a34c753fSRafael Auler 63a34c753fSRafael Auler static cl::opt<bool> 64a34c753fSRafael Auler IgnoreBuildID("ignore-build-id", 65a34c753fSRafael Auler cl::desc("continue even if build-ids in input binary and perf.data mismatch"), 66a34c753fSRafael Auler cl::init(false), 67a34c753fSRafael Auler cl::cat(AggregatorCategory)); 68a34c753fSRafael Auler 69b92436efSFangrui Song static cl::opt<bool> IgnoreInterruptLBR( 70b92436efSFangrui Song "ignore-interrupt-lbr", 71a34c753fSRafael Auler cl::desc("ignore kernel interrupt LBR that happens asynchronously"), 72b92436efSFangrui Song cl::init(true), cl::cat(AggregatorCategory)); 73a34c753fSRafael Auler 74a34c753fSRafael Auler static cl::opt<unsigned long long> 75a34c753fSRafael Auler MaxSamples("max-samples", 76a34c753fSRafael Auler cl::init(-1ULL), 77a34c753fSRafael Auler cl::desc("maximum number of samples to read from LBR profile"), 78a34c753fSRafael Auler cl::Optional, 79a34c753fSRafael Auler cl::Hidden, 80a34c753fSRafael Auler cl::cat(AggregatorCategory)); 81a34c753fSRafael Auler 8239336fc0SAmir Ayupov extern cl::opt<opts::ProfileFormatKind> ProfileFormat; 8339336fc0SAmir Ayupov 84661577b5SMaksim Panchenko cl::opt<bool> ReadPreAggregated( 85b92436efSFangrui Song "pa", cl::desc("skip perf and read data from a pre-aggregated file format"), 86a34c753fSRafael Auler cl::cat(AggregatorCategory)); 87a34c753fSRafael Auler 88a34c753fSRafael Auler static cl::opt<bool> 89a34c753fSRafael Auler TimeAggregator("time-aggr", 90a34c753fSRafael Auler cl::desc("time BOLT aggregator"), 91a34c753fSRafael Auler cl::init(false), 92a34c753fSRafael Auler cl::ZeroOrMore, 93a34c753fSRafael Auler cl::cat(AggregatorCategory)); 94a34c753fSRafael Auler 95a34c753fSRafael Auler static cl::opt<bool> 96a34c753fSRafael Auler UseEventPC("use-event-pc", 97a34c753fSRafael Auler cl::desc("use event PC in combination with LBR sampling"), 98a34c753fSRafael Auler cl::cat(AggregatorCategory)); 99a34c753fSRafael Auler 100b92436efSFangrui Song static cl::opt<bool> WriteAutoFDOData( 101b92436efSFangrui Song "autofdo", cl::desc("generate autofdo textual data instead of bolt data"), 102a34c753fSRafael Auler cl::cat(AggregatorCategory)); 103a34c753fSRafael Auler 10440c2e0faSMaksim Panchenko } // namespace opts 105a34c753fSRafael Auler 106a34c753fSRafael Auler namespace { 107a34c753fSRafael Auler 108a34c753fSRafael Auler const char TimerGroupName[] = "aggregator"; 109a34c753fSRafael Auler const char TimerGroupDesc[] = "Aggregator"; 110a34c753fSRafael Auler 111733dc3e5SRahman Lavaee std::vector<SectionNameAndRange> getTextSections(const BinaryContext *BC) { 112733dc3e5SRahman Lavaee std::vector<SectionNameAndRange> sections; 113733dc3e5SRahman Lavaee for (BinarySection &Section : BC->sections()) { 114733dc3e5SRahman Lavaee if (!Section.isText()) 115733dc3e5SRahman Lavaee continue; 116733dc3e5SRahman Lavaee if (Section.getSize() == 0) 117733dc3e5SRahman Lavaee continue; 118733dc3e5SRahman Lavaee sections.push_back( 119733dc3e5SRahman Lavaee {Section.getName(), Section.getAddress(), Section.getEndAddress()}); 120733dc3e5SRahman Lavaee } 121d2c87699SAmir Ayupov llvm::sort(sections, 122733dc3e5SRahman Lavaee [](const SectionNameAndRange &A, const SectionNameAndRange &B) { 123733dc3e5SRahman Lavaee return A.BeginAddress < B.BeginAddress; 124733dc3e5SRahman Lavaee }); 125733dc3e5SRahman Lavaee return sections; 126733dc3e5SRahman Lavaee } 127a34c753fSRafael Auler } 128a34c753fSRafael Auler 129a34c753fSRafael Auler constexpr uint64_t DataAggregator::KernelBaseAddr; 130a34c753fSRafael Auler 13140c2e0faSMaksim Panchenko DataAggregator::~DataAggregator() { deleteTempFiles(); } 132a34c753fSRafael Auler 133a34c753fSRafael Auler namespace { 134a34c753fSRafael Auler void deleteTempFile(const std::string &FileName) { 135def464aaSAmir Ayupov if (std::error_code Errc = sys::fs::remove(FileName.c_str())) 13640c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to delete temporary file " << FileName 13740c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n"; 138a34c753fSRafael Auler } 139a34c753fSRafael Auler } 140a34c753fSRafael Auler 141a34c753fSRafael Auler void DataAggregator::deleteTempFiles() { 142def464aaSAmir Ayupov for (std::string &FileName : TempFiles) 143a34c753fSRafael Auler deleteTempFile(FileName); 144a34c753fSRafael Auler TempFiles.clear(); 145a34c753fSRafael Auler } 146a34c753fSRafael Auler 147a34c753fSRafael Auler void DataAggregator::findPerfExecutable() { 1483c255f67SKrzysztof Parzyszek std::optional<std::string> PerfExecutable = 149a34c753fSRafael Auler sys::Process::FindInEnvPath("PATH", "perf"); 150a34c753fSRafael Auler if (!PerfExecutable) { 151a34c753fSRafael Auler outs() << "PERF2BOLT: No perf executable found!\n"; 152a34c753fSRafael Auler exit(1); 153a34c753fSRafael Auler } 154a34c753fSRafael Auler PerfPath = *PerfExecutable; 155a34c753fSRafael Auler } 156a34c753fSRafael Auler 157a34c753fSRafael Auler void DataAggregator::start() { 15840c2e0faSMaksim Panchenko outs() << "PERF2BOLT: Starting data aggregation job for " << Filename << "\n"; 159a34c753fSRafael Auler 160a34c753fSRafael Auler // Don't launch perf for pre-aggregated files 161a34c753fSRafael Auler if (opts::ReadPreAggregated) 162a34c753fSRafael Auler return; 163a34c753fSRafael Auler 164a34c753fSRafael Auler findPerfExecutable(); 165a34c753fSRafael Auler 166def464aaSAmir Ayupov if (opts::BasicAggregation) 167a34c753fSRafael Auler launchPerfProcess("events without LBR", 168a34c753fSRafael Auler MainEventsPPI, 169a34c753fSRafael Auler "script -F pid,event,ip", 170a34c753fSRafael Auler /*Wait = */false); 171def464aaSAmir Ayupov else 172a34c753fSRafael Auler launchPerfProcess("branch events", 173a34c753fSRafael Auler MainEventsPPI, 174a34c753fSRafael Auler "script -F pid,ip,brstack", 175a34c753fSRafael Auler /*Wait = */false); 176a34c753fSRafael Auler 177a34c753fSRafael Auler // Note: we launch script for mem events regardless of the option, as the 178a34c753fSRafael Auler // command fails fairly fast if mem events were not collected. 179a34c753fSRafael Auler launchPerfProcess("mem events", 180a34c753fSRafael Auler MemEventsPPI, 181a34c753fSRafael Auler "script -F pid,event,addr,ip", 182a34c753fSRafael Auler /*Wait = */false); 183a34c753fSRafael Auler 184a34c753fSRafael Auler launchPerfProcess("process events", 185a34c753fSRafael Auler MMapEventsPPI, 186a34c753fSRafael Auler "script --show-mmap-events", 187a34c753fSRafael Auler /*Wait = */false); 188a34c753fSRafael Auler 189a34c753fSRafael Auler launchPerfProcess("task events", 190a34c753fSRafael Auler TaskEventsPPI, 191a34c753fSRafael Auler "script --show-task-events", 192a34c753fSRafael Auler /*Wait = */false); 193a34c753fSRafael Auler } 194a34c753fSRafael Auler 195a34c753fSRafael Auler void DataAggregator::abort() { 196a34c753fSRafael Auler if (opts::ReadPreAggregated) 197a34c753fSRafael Auler return; 198a34c753fSRafael Auler 199a34c753fSRafael Auler std::string Error; 200a34c753fSRafael Auler 201a34c753fSRafael Auler // Kill subprocesses in case they are not finished 2026be2db6cSMatt Arsenault sys::Wait(TaskEventsPPI.PI, 1, &Error); 2036be2db6cSMatt Arsenault sys::Wait(MMapEventsPPI.PI, 1, &Error); 2046be2db6cSMatt Arsenault sys::Wait(MainEventsPPI.PI, 1, &Error); 2056be2db6cSMatt Arsenault sys::Wait(MemEventsPPI.PI, 1, &Error); 206a34c753fSRafael Auler 207a34c753fSRafael Auler deleteTempFiles(); 208a34c753fSRafael Auler 209a34c753fSRafael Auler exit(1); 210a34c753fSRafael Auler } 211a34c753fSRafael Auler 212a34c753fSRafael Auler void DataAggregator::launchPerfProcess(StringRef Name, PerfProcessInfo &PPI, 213a34c753fSRafael Auler const char *ArgsString, bool Wait) { 214a34c753fSRafael Auler SmallVector<StringRef, 4> Argv; 215a34c753fSRafael Auler 216a34c753fSRafael Auler outs() << "PERF2BOLT: spawning perf job to read " << Name << '\n'; 217a34c753fSRafael Auler Argv.push_back(PerfPath.data()); 218a34c753fSRafael Auler 2195acac7dbSAmir Ayupov StringRef(ArgsString).split(Argv, ' '); 220a34c753fSRafael Auler Argv.push_back("-f"); 221a34c753fSRafael Auler Argv.push_back("-i"); 222a34c753fSRafael Auler Argv.push_back(Filename.c_str()); 223a34c753fSRafael Auler 224a34c753fSRafael Auler if (std::error_code Errc = 225a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "out", PPI.StdoutPath)) { 22640c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StdoutPath 22740c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n"; 228a34c753fSRafael Auler exit(1); 229a34c753fSRafael Auler } 230a34c753fSRafael Auler TempFiles.push_back(PPI.StdoutPath.data()); 231a34c753fSRafael Auler 232a34c753fSRafael Auler if (std::error_code Errc = 233a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "err", PPI.StderrPath)) { 23440c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StderrPath 23540c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n"; 236a34c753fSRafael Auler exit(1); 237a34c753fSRafael Auler } 238a34c753fSRafael Auler TempFiles.push_back(PPI.StderrPath.data()); 239a34c753fSRafael Auler 2401028b165SKazu Hirata std::optional<StringRef> Redirects[] = { 241e324a80fSKazu Hirata std::nullopt, // Stdin 242a34c753fSRafael Auler StringRef(PPI.StdoutPath.data()), // Stdout 243a34c753fSRafael Auler StringRef(PPI.StderrPath.data())}; // Stderr 244a34c753fSRafael Auler 245a34c753fSRafael Auler LLVM_DEBUG({ 246a34c753fSRafael Auler dbgs() << "Launching perf: "; 247a34c753fSRafael Auler for (StringRef Arg : Argv) 248a34c753fSRafael Auler dbgs() << Arg << " "; 249a34c753fSRafael Auler dbgs() << " 1> " << PPI.StdoutPath.data() << " 2> " << PPI.StderrPath.data() 250a34c753fSRafael Auler << "\n"; 251a34c753fSRafael Auler }); 252a34c753fSRafael Auler 253def464aaSAmir Ayupov if (Wait) 254a34c753fSRafael Auler PPI.PI.ReturnCode = sys::ExecuteAndWait(PerfPath.data(), Argv, 255e324a80fSKazu Hirata /*envp*/ std::nullopt, Redirects); 256def464aaSAmir Ayupov else 257e324a80fSKazu Hirata PPI.PI = sys::ExecuteNoWait(PerfPath.data(), Argv, /*envp*/ std::nullopt, 258a34c753fSRafael Auler Redirects); 259a34c753fSRafael Auler } 260a34c753fSRafael Auler 261a34c753fSRafael Auler void DataAggregator::processFileBuildID(StringRef FileBuildID) { 262a34c753fSRafael Auler PerfProcessInfo BuildIDProcessInfo; 263a34c753fSRafael Auler launchPerfProcess("buildid list", 264a34c753fSRafael Auler BuildIDProcessInfo, 265a34c753fSRafael Auler "buildid-list", 266a34c753fSRafael Auler /*Wait = */true); 267a34c753fSRafael Auler 268a34c753fSRafael Auler if (BuildIDProcessInfo.PI.ReturnCode != 0) { 269a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 270a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StderrPath.data()); 271a34c753fSRafael Auler StringRef ErrBuf = (*MB)->getBuffer(); 272a34c753fSRafael Auler 273a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << BuildIDProcessInfo.PI.ReturnCode 274a34c753fSRafael Auler << '\n'; 275a34c753fSRafael Auler errs() << ErrBuf; 276a34c753fSRafael Auler return; 277a34c753fSRafael Auler } 278a34c753fSRafael Auler 279a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 280a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StdoutPath.data()); 281a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 282a34c753fSRafael Auler errs() << "Cannot open " << BuildIDProcessInfo.StdoutPath.data() << ": " 283a34c753fSRafael Auler << EC.message() << "\n"; 284a34c753fSRafael Auler return; 285a34c753fSRafael Auler } 286a34c753fSRafael Auler 287d914486aSAmir Ayupov FileBuf = std::move(*MB); 288a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 289a34c753fSRafael Auler 290835a9c28SAmir Ayupov std::optional<StringRef> FileName = getFileNameForBuildID(FileBuildID); 291a34c753fSRafael Auler if (!FileName) { 292661577b5SMaksim Panchenko if (hasAllBuildIDs()) { 293a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: failed to match build-id from perf output. " 294a34c753fSRafael Auler "This indicates the input binary supplied for data aggregation " 295a34c753fSRafael Auler "is not the same recorded by perf when collecting profiling " 296a34c753fSRafael Auler "data, or there were no samples recorded for the binary. " 297a34c753fSRafael Auler "Use -ignore-build-id option to override.\n"; 298def464aaSAmir Ayupov if (!opts::IgnoreBuildID) 299a34c753fSRafael Auler abort(); 300661577b5SMaksim Panchenko } else { 301661577b5SMaksim Panchenko errs() << "PERF2BOLT-WARNING: build-id will not be checked because perf " 302661577b5SMaksim Panchenko "data was recorded without it\n"; 303661577b5SMaksim Panchenko return; 304661577b5SMaksim Panchenko } 305a34c753fSRafael Auler } else if (*FileName != llvm::sys::path::filename(BC->getFilename())) { 306a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: build-id matched a different file name\n"; 307a34c753fSRafael Auler BuildIDBinaryName = std::string(*FileName); 308a34c753fSRafael Auler } else { 309a34c753fSRafael Auler outs() << "PERF2BOLT: matched build-id and file name\n"; 310a34c753fSRafael Auler } 311a34c753fSRafael Auler } 312a34c753fSRafael Auler 313a34c753fSRafael Auler bool DataAggregator::checkPerfDataMagic(StringRef FileName) { 314a34c753fSRafael Auler if (opts::ReadPreAggregated) 315a34c753fSRafael Auler return true; 316a34c753fSRafael Auler 317ae585be1SRafael Auler Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName); 3180f915826SMaksim Panchenko if (!FD) { 3190f915826SMaksim Panchenko consumeError(FD.takeError()); 320a34c753fSRafael Auler return false; 3210f915826SMaksim Panchenko } 322a34c753fSRafael Auler 323a34c753fSRafael Auler char Buf[7] = {0, 0, 0, 0, 0, 0, 0}; 324a34c753fSRafael Auler 325ae585be1SRafael Auler auto Close = make_scope_exit([&] { sys::fs::closeFile(*FD); }); 326ae585be1SRafael Auler Expected<size_t> BytesRead = sys::fs::readNativeFileSlice( 327a288d7f9SJoe Loser *FD, MutableArrayRef(Buf, sizeof(Buf)), 0); 3280f915826SMaksim Panchenko if (!BytesRead) { 3290f915826SMaksim Panchenko consumeError(BytesRead.takeError()); 3300f915826SMaksim Panchenko return false; 3310f915826SMaksim Panchenko } 3320f915826SMaksim Panchenko 3330f915826SMaksim Panchenko if (*BytesRead != 7) 334a34c753fSRafael Auler return false; 335a34c753fSRafael Auler 336a34c753fSRafael Auler if (strncmp(Buf, "PERFILE", 7) == 0) 337a34c753fSRafael Auler return true; 338a34c753fSRafael Auler return false; 339a34c753fSRafael Auler } 340a34c753fSRafael Auler 341a34c753fSRafael Auler void DataAggregator::parsePreAggregated() { 342a34c753fSRafael Auler std::string Error; 343a34c753fSRafael Auler 344a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 345a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Filename); 346a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 347a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": " 348a34c753fSRafael Auler << EC.message() << "\n"; 349a34c753fSRafael Auler exit(1); 350a34c753fSRafael Auler } 351a34c753fSRafael Auler 352d914486aSAmir Ayupov FileBuf = std::move(*MB); 353a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 354a34c753fSRafael Auler Col = 0; 355a34c753fSRafael Auler Line = 1; 356a34c753fSRafael Auler if (parsePreAggregatedLBRSamples()) { 357a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n"; 358a34c753fSRafael Auler exit(1); 359a34c753fSRafael Auler } 360a34c753fSRafael Auler } 361a34c753fSRafael Auler 362a34c753fSRafael Auler std::error_code DataAggregator::writeAutoFDOData(StringRef OutputFilename) { 363a34c753fSRafael Auler outs() << "PERF2BOLT: writing data for autofdo tools...\n"; 36440c2e0faSMaksim Panchenko NamedRegionTimer T("writeAutoFDO", "Processing branch events", TimerGroupName, 36540c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator); 366a34c753fSRafael Auler 367a34c753fSRafael Auler std::error_code EC; 368a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None); 369a34c753fSRafael Auler if (EC) 370a34c753fSRafael Auler return EC; 371a34c753fSRafael Auler 372a34c753fSRafael Auler // Format: 373a34c753fSRafael Auler // number of unique traces 374a34c753fSRafael Auler // from_1-to_1:count_1 375a34c753fSRafael Auler // from_2-to_2:count_2 376a34c753fSRafael Auler // ...... 377a34c753fSRafael Auler // from_n-to_n:count_n 378a34c753fSRafael Auler // number of unique sample addresses 379a34c753fSRafael Auler // addr_1:count_1 380a34c753fSRafael Auler // addr_2:count_2 381a34c753fSRafael Auler // ...... 382a34c753fSRafael Auler // addr_n:count_n 383a34c753fSRafael Auler // number of unique LBR entries 384a34c753fSRafael Auler // src_1->dst_1:count_1 385a34c753fSRafael Auler // src_2->dst_2:count_2 386a34c753fSRafael Auler // ...... 387a34c753fSRafael Auler // src_n->dst_n:count_n 388a34c753fSRafael Auler 389a34c753fSRafael Auler const uint64_t FirstAllocAddress = this->BC->FirstAllocAddress; 390a34c753fSRafael Auler 391a34c753fSRafael Auler // AutoFDO addresses are relative to the first allocated loadable program 392a34c753fSRafael Auler // segment 393a34c753fSRafael Auler auto filterAddress = [&FirstAllocAddress](uint64_t Address) -> uint64_t { 394a34c753fSRafael Auler if (Address < FirstAllocAddress) 395a34c753fSRafael Auler return 0; 396a34c753fSRafael Auler return Address - FirstAllocAddress; 397a34c753fSRafael Auler }; 398a34c753fSRafael Auler 399a34c753fSRafael Auler OutFile << FallthroughLBRs.size() << "\n"; 400a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) { 401a34c753fSRafael Auler const Trace &Trace = AggrLBR.first; 402a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second; 403a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "-" 404a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":" 405a34c753fSRafael Auler << (Info.InternCount + Info.ExternCount) << "\n"; 406a34c753fSRafael Auler } 407a34c753fSRafael Auler 408a34c753fSRafael Auler OutFile << BasicSamples.size() << "\n"; 409a34c753fSRafael Auler for (const auto &Sample : BasicSamples) { 410a34c753fSRafael Auler uint64_t PC = Sample.first; 411a34c753fSRafael Auler uint64_t HitCount = Sample.second; 412a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(PC)) << ":" << HitCount << "\n"; 413a34c753fSRafael Auler } 414a34c753fSRafael Auler 415a34c753fSRafael Auler OutFile << BranchLBRs.size() << "\n"; 416a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) { 417a34c753fSRafael Auler const Trace &Trace = AggrLBR.first; 418a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second; 419a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "->" 420a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":" 421a34c753fSRafael Auler << Info.TakenCount << "\n"; 422a34c753fSRafael Auler } 423a34c753fSRafael Auler 424a34c753fSRafael Auler outs() << "PERF2BOLT: wrote " << FallthroughLBRs.size() << " unique traces, " 425a34c753fSRafael Auler << BasicSamples.size() << " sample addresses and " << BranchLBRs.size() 426a34c753fSRafael Auler << " unique branches to " << OutputFilename << "\n"; 427a34c753fSRafael Auler 428a34c753fSRafael Auler return std::error_code(); 429a34c753fSRafael Auler } 430a34c753fSRafael Auler 431a34c753fSRafael Auler void DataAggregator::filterBinaryMMapInfo() { 432a34c753fSRafael Auler if (opts::FilterPID) { 433a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(opts::FilterPID); 434a34c753fSRafael Auler if (MMapInfoIter != BinaryMMapInfo.end()) { 435a34c753fSRafael Auler MMapInfo MMap = MMapInfoIter->second; 436a34c753fSRafael Auler BinaryMMapInfo.clear(); 437a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMap.PID, MMap)); 438a34c753fSRafael Auler } else { 439a34c753fSRafael Auler if (errs().has_colors()) 440a34c753fSRafael Auler errs().changeColor(raw_ostream::RED); 441a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching PID \"" 44240c2e0faSMaksim Panchenko << opts::FilterPID << "\"" 44340c2e0faSMaksim Panchenko << " for binary \"" << BC->getFilename() << "\"."; 444a34c753fSRafael Auler assert(!BinaryMMapInfo.empty() && "No memory map for matching binary"); 445a34c753fSRafael Auler errs() << " Profile for the following process is available:\n"; 446def464aaSAmir Ayupov for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo) 447a34c753fSRafael Auler outs() << " " << MMI.second.PID 448a34c753fSRafael Auler << (MMI.second.Forked ? " (forked)\n" : "\n"); 449def464aaSAmir Ayupov 450a34c753fSRafael Auler if (errs().has_colors()) 451a34c753fSRafael Auler errs().resetColor(); 452a34c753fSRafael Auler 453a34c753fSRafael Auler exit(1); 454a34c753fSRafael Auler } 455a34c753fSRafael Auler } 456a34c753fSRafael Auler } 457a34c753fSRafael Auler 458c7af4f38SAmir Ayupov int DataAggregator::prepareToParse(StringRef Name, PerfProcessInfo &Process, 459c7af4f38SAmir Ayupov PerfProcessErrorCallbackTy Callback) { 460a34c753fSRafael Auler std::string Error; 461a34c753fSRafael Auler outs() << "PERF2BOLT: waiting for perf " << Name 462a34c753fSRafael Auler << " collection to finish...\n"; 463765f3cafSMatt Arsenault sys::ProcessInfo PI = sys::Wait(Process.PI, std::nullopt, &Error); 464a34c753fSRafael Auler 465a34c753fSRafael Auler if (!Error.empty()) { 466a34c753fSRafael Auler errs() << "PERF-ERROR: " << PerfPath << ": " << Error << "\n"; 467a34c753fSRafael Auler deleteTempFiles(); 468a34c753fSRafael Auler exit(1); 469a34c753fSRafael Auler } 470a34c753fSRafael Auler 471a34c753fSRafael Auler if (PI.ReturnCode != 0) { 472a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorMB = 473a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StderrPath.data()); 474a34c753fSRafael Auler StringRef ErrBuf = (*ErrorMB)->getBuffer(); 475a34c753fSRafael Auler 476a34c753fSRafael Auler deleteTempFiles(); 477c7af4f38SAmir Ayupov Callback(PI.ReturnCode, ErrBuf); 478c7af4f38SAmir Ayupov return PI.ReturnCode; 479a34c753fSRafael Auler } 480a34c753fSRafael Auler 481a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 482a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StdoutPath.data()); 483a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 484a34c753fSRafael Auler errs() << "Cannot open " << Process.StdoutPath.data() << ": " 485a34c753fSRafael Auler << EC.message() << "\n"; 486a34c753fSRafael Auler deleteTempFiles(); 487a34c753fSRafael Auler exit(1); 488a34c753fSRafael Auler } 489a34c753fSRafael Auler 490d914486aSAmir Ayupov FileBuf = std::move(*MB); 491a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 492a34c753fSRafael Auler Col = 0; 493a34c753fSRafael Auler Line = 1; 494c7af4f38SAmir Ayupov return PI.ReturnCode; 495c7af4f38SAmir Ayupov } 496c7af4f38SAmir Ayupov 497c7af4f38SAmir Ayupov Error DataAggregator::preprocessProfile(BinaryContext &BC) { 498c7af4f38SAmir Ayupov this->BC = &BC; 499c7af4f38SAmir Ayupov 500c7af4f38SAmir Ayupov if (opts::ReadPreAggregated) { 501c7af4f38SAmir Ayupov parsePreAggregated(); 502c7af4f38SAmir Ayupov return Error::success(); 503c7af4f38SAmir Ayupov } 504c7af4f38SAmir Ayupov 505c7af4f38SAmir Ayupov if (std::optional<StringRef> FileBuildID = BC.getFileBuildID()) { 506c7af4f38SAmir Ayupov outs() << "BOLT-INFO: binary build-id is: " << *FileBuildID << "\n"; 507c7af4f38SAmir Ayupov processFileBuildID(*FileBuildID); 508c7af4f38SAmir Ayupov } else { 509c7af4f38SAmir Ayupov errs() << "BOLT-WARNING: build-id will not be checked because we could " 510c7af4f38SAmir Ayupov "not read one from input binary\n"; 511c7af4f38SAmir Ayupov } 512c7af4f38SAmir Ayupov 513c7af4f38SAmir Ayupov auto ErrorCallback = [](int ReturnCode, StringRef ErrBuf) { 514c7af4f38SAmir Ayupov errs() << "PERF-ERROR: return code " << ReturnCode << "\n" << ErrBuf; 515c7af4f38SAmir Ayupov exit(1); 516c7af4f38SAmir Ayupov }; 517c7af4f38SAmir Ayupov 518c7af4f38SAmir Ayupov auto MemEventsErrorCallback = [&](int ReturnCode, StringRef ErrBuf) { 519c7af4f38SAmir Ayupov Regex NoData("Samples for '.*' event do not have ADDR attribute set. " 520c7af4f38SAmir Ayupov "Cannot print 'addr' field."); 521c7af4f38SAmir Ayupov if (!NoData.match(ErrBuf)) 522c7af4f38SAmir Ayupov ErrorCallback(ReturnCode, ErrBuf); 523a34c753fSRafael Auler }; 524a34c753fSRafael Auler 525a34c753fSRafael Auler if (opts::LinuxKernelMode) { 526a34c753fSRafael Auler // Current MMap parsing logic does not work with linux kernel. 527a34c753fSRafael Auler // MMap entries for linux kernel uses PERF_RECORD_MMAP 528a34c753fSRafael Auler // format instead of typical PERF_RECORD_MMAP2 format. 529a34c753fSRafael Auler // Since linux kernel address mapping is absolute (same as 530a34c753fSRafael Auler // in the ELF file), we avoid parsing MMap in linux kernel mode. 531a34c753fSRafael Auler // While generating optimized linux kernel binary, we may need 532a34c753fSRafael Auler // to parse MMap entries. 533a34c753fSRafael Auler 534a34c753fSRafael Auler // In linux kernel mode, we analyze and optimize 535a34c753fSRafael Auler // all linux kernel binary instructions, irrespective 536a34c753fSRafael Auler // of whether they are due to system calls or due to 537a34c753fSRafael Auler // interrupts. Therefore, we cannot ignore interrupt 538a34c753fSRafael Auler // in Linux kernel mode. 539a34c753fSRafael Auler opts::IgnoreInterruptLBR = false; 540a34c753fSRafael Auler } else { 541c7af4f38SAmir Ayupov prepareToParse("mmap events", MMapEventsPPI, ErrorCallback); 542def464aaSAmir Ayupov if (parseMMapEvents()) 543a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse mmap events\n"; 544a34c753fSRafael Auler } 545a34c753fSRafael Auler 546c7af4f38SAmir Ayupov prepareToParse("task events", TaskEventsPPI, ErrorCallback); 547def464aaSAmir Ayupov if (parseTaskEvents()) 548a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse task events\n"; 549a34c753fSRafael Auler 550a34c753fSRafael Auler filterBinaryMMapInfo(); 551c7af4f38SAmir Ayupov prepareToParse("events", MainEventsPPI, ErrorCallback); 552a34c753fSRafael Auler 553a34c753fSRafael Auler if (opts::HeatmapMode) { 554a34c753fSRafael Auler if (std::error_code EC = printLBRHeatMap()) { 555a34c753fSRafael Auler errs() << "ERROR: failed to print heat map: " << EC.message() << '\n'; 556a34c753fSRafael Auler exit(1); 557a34c753fSRafael Auler } 558a34c753fSRafael Auler exit(0); 559a34c753fSRafael Auler } 560a34c753fSRafael Auler 561a34c753fSRafael Auler if ((!opts::BasicAggregation && parseBranchEvents()) || 562def464aaSAmir Ayupov (opts::BasicAggregation && parseBasicEvents())) 563a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n"; 564a34c753fSRafael Auler 565a34c753fSRafael Auler // We can finish early if the goal is just to generate data for autofdo 566a34c753fSRafael Auler if (opts::WriteAutoFDOData) { 567def464aaSAmir Ayupov if (std::error_code EC = writeAutoFDOData(opts::OutputFilename)) 568a34c753fSRafael Auler errs() << "Error writing autofdo data to file: " << EC.message() << "\n"; 569def464aaSAmir Ayupov 570a34c753fSRafael Auler deleteTempFiles(); 571a34c753fSRafael Auler exit(0); 572a34c753fSRafael Auler } 573a34c753fSRafael Auler 574a34c753fSRafael Auler // Special handling for memory events 575c7af4f38SAmir Ayupov if (prepareToParse("mem events", MemEventsPPI, MemEventsErrorCallback)) 576a34c753fSRafael Auler return Error::success(); 577a34c753fSRafael Auler 578def464aaSAmir Ayupov if (const std::error_code EC = parseMemEvents()) 57940c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to parse memory events: " << EC.message() 58040c2e0faSMaksim Panchenko << '\n'; 581a34c753fSRafael Auler 582a34c753fSRafael Auler deleteTempFiles(); 583a34c753fSRafael Auler 584a34c753fSRafael Auler return Error::success(); 585a34c753fSRafael Auler } 586a34c753fSRafael Auler 587a34c753fSRafael Auler Error DataAggregator::readProfile(BinaryContext &BC) { 588a34c753fSRafael Auler processProfile(BC); 589a34c753fSRafael Auler 590a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) { 591a34c753fSRafael Auler BinaryFunction &Function = BFI.second; 592a34c753fSRafael Auler convertBranchData(Function); 593a34c753fSRafael Auler } 594a34c753fSRafael Auler 59539336fc0SAmir Ayupov if (opts::AggregateOnly && 59639336fc0SAmir Ayupov opts::ProfileFormat == opts::ProfileFormatKind::PF_Fdata) { 597def464aaSAmir Ayupov if (std::error_code EC = writeAggregatedFile(opts::OutputFilename)) 598a34c753fSRafael Auler report_error("cannot create output data file", EC); 599a34c753fSRafael Auler } 600a34c753fSRafael Auler 601a34c753fSRafael Auler return Error::success(); 602a34c753fSRafael Auler } 603a34c753fSRafael Auler 604a34c753fSRafael Auler bool DataAggregator::mayHaveProfileData(const BinaryFunction &Function) { 605a34c753fSRafael Auler return Function.hasProfileAvailable(); 606a34c753fSRafael Auler } 607a34c753fSRafael Auler 608a34c753fSRafael Auler void DataAggregator::processProfile(BinaryContext &BC) { 609a34c753fSRafael Auler if (opts::ReadPreAggregated) 610a34c753fSRafael Auler processPreAggregated(); 611a34c753fSRafael Auler else if (opts::BasicAggregation) 612a34c753fSRafael Auler processBasicEvents(); 613a34c753fSRafael Auler else 614a34c753fSRafael Auler processBranchEvents(); 615a34c753fSRafael Auler 616a34c753fSRafael Auler processMemEvents(); 617a34c753fSRafael Auler 618a34c753fSRafael Auler // Mark all functions with registered events as having a valid profile. 619a34c753fSRafael Auler const auto Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE 620a34c753fSRafael Auler : BinaryFunction::PF_LBR; 6214a7966eaSAmir Ayupov for (auto &BFI : BC.getBinaryFunctions()) { 6224a7966eaSAmir Ayupov BinaryFunction &BF = BFI.second; 6234a7966eaSAmir Ayupov if (getBranchData(BF) || getFuncSampleData(BF.getNames())) 624a34c753fSRafael Auler BF.markProfiled(Flags); 625a34c753fSRafael Auler } 626a34c753fSRafael Auler 627224e4cc5SAmir Ayupov for (auto &FuncBranches : NamesToBranches) 628224e4cc5SAmir Ayupov llvm::stable_sort(FuncBranches.second.Data); 629224e4cc5SAmir Ayupov 630224e4cc5SAmir Ayupov for (auto &MemEvents : NamesToMemEvents) 631224e4cc5SAmir Ayupov llvm::stable_sort(MemEvents.second.Data); 632224e4cc5SAmir Ayupov 633a34c753fSRafael Auler // Release intermediate storage. 634a34c753fSRafael Auler clear(BranchLBRs); 635a34c753fSRafael Auler clear(FallthroughLBRs); 636a34c753fSRafael Auler clear(AggregatedLBRs); 637a34c753fSRafael Auler clear(BasicSamples); 638a34c753fSRafael Auler clear(MemSamples); 639a34c753fSRafael Auler } 640a34c753fSRafael Auler 641a34c753fSRafael Auler BinaryFunction * 642a34c753fSRafael Auler DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const { 643a34c753fSRafael Auler if (!BC->containsAddress(Address)) 644a34c753fSRafael Auler return nullptr; 645a34c753fSRafael Auler 646a34c753fSRafael Auler return BC->getBinaryFunctionContainingAddress(Address, /*CheckPastEnd=*/false, 647a34c753fSRafael Auler /*UseMaxSize=*/true); 648a34c753fSRafael Auler } 649a34c753fSRafael Auler 650a34c753fSRafael Auler StringRef DataAggregator::getLocationName(BinaryFunction &Func, 651a34c753fSRafael Auler uint64_t Count) { 652a34c753fSRafael Auler if (!BAT) 653a34c753fSRafael Auler return Func.getOneName(); 654a34c753fSRafael Auler 655a34c753fSRafael Auler const BinaryFunction *OrigFunc = &Func; 656a34c753fSRafael Auler if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress())) { 657a34c753fSRafael Auler NumColdSamples += Count; 658a34c753fSRafael Auler BinaryFunction *HotFunc = getBinaryFunctionContainingAddress(HotAddr); 659a34c753fSRafael Auler if (HotFunc) 660a34c753fSRafael Auler OrigFunc = HotFunc; 661a34c753fSRafael Auler } 662a34c753fSRafael Auler // If it is a local function, prefer the name containing the file name where 663a34c753fSRafael Auler // the local function was declared 664a34c753fSRafael Auler for (StringRef AlternativeName : OrigFunc->getNames()) { 665a34c753fSRafael Auler size_t FileNameIdx = AlternativeName.find('/'); 666a34c753fSRafael Auler // Confirm the alternative name has the pattern Symbol/FileName/1 before 667a34c753fSRafael Auler // using it 668a34c753fSRafael Auler if (FileNameIdx == StringRef::npos || 669a34c753fSRafael Auler AlternativeName.find('/', FileNameIdx + 1) == StringRef::npos) 670a34c753fSRafael Auler continue; 671a34c753fSRafael Auler return AlternativeName; 672a34c753fSRafael Auler } 673a34c753fSRafael Auler return OrigFunc->getOneName(); 674a34c753fSRafael Auler } 675a34c753fSRafael Auler 676a34c753fSRafael Auler bool DataAggregator::doSample(BinaryFunction &Func, uint64_t Address, 677a34c753fSRafael Auler uint64_t Count) { 678a34c753fSRafael Auler auto I = NamesToSamples.find(Func.getOneName()); 679a34c753fSRafael Auler if (I == NamesToSamples.end()) { 680a34c753fSRafael Auler bool Success; 681a34c753fSRafael Auler StringRef LocName = getLocationName(Func, Count); 68240c2e0faSMaksim Panchenko std::tie(I, Success) = NamesToSamples.insert( 68340c2e0faSMaksim Panchenko std::make_pair(Func.getOneName(), 684a34c753fSRafael Auler FuncSampleData(LocName, FuncSampleData::ContainerTy()))); 685a34c753fSRafael Auler } 686a34c753fSRafael Auler 687a34c753fSRafael Auler Address -= Func.getAddress(); 688a34c753fSRafael Auler if (BAT) 689fc0ced73SRafael Auler Address = BAT->translate(Func.getAddress(), Address, /*IsBranchSrc=*/false); 690a34c753fSRafael Auler 691a34c753fSRafael Auler I->second.bumpCount(Address, Count); 692a34c753fSRafael Auler return true; 693a34c753fSRafael Auler } 694a34c753fSRafael Auler 695a34c753fSRafael Auler bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From, 696a34c753fSRafael Auler uint64_t To, uint64_t Count, 697a34c753fSRafael Auler uint64_t Mispreds) { 698a34c753fSRafael Auler FuncBranchData *AggrData = getBranchData(Func); 699a34c753fSRafael Auler if (!AggrData) { 700a34c753fSRafael Auler AggrData = &NamesToBranches[Func.getOneName()]; 701a34c753fSRafael Auler AggrData->Name = getLocationName(Func, Count); 702a34c753fSRafael Auler setBranchData(Func, AggrData); 703a34c753fSRafael Auler } 704a34c753fSRafael Auler 705a34c753fSRafael Auler From -= Func.getAddress(); 706a34c753fSRafael Auler To -= Func.getAddress(); 7074a7966eaSAmir Ayupov LLVM_DEBUG(dbgs() << "BOLT-DEBUG: bumpBranchCount: " 7084a7966eaSAmir Ayupov << formatv("{0} @ {1:x} -> {0} @ {2:x}\n", Func, From, To)); 709a34c753fSRafael Auler if (BAT) { 710fc0ced73SRafael Auler From = BAT->translate(Func.getAddress(), From, /*IsBranchSrc=*/true); 711fc0ced73SRafael Auler To = BAT->translate(Func.getAddress(), To, /*IsBranchSrc=*/false); 7124a7966eaSAmir Ayupov LLVM_DEBUG( 7134a7966eaSAmir Ayupov dbgs() << "BOLT-DEBUG: BAT translation on bumpBranchCount: " 7144a7966eaSAmir Ayupov << formatv("{0} @ {1:x} -> {0} @ {2:x}\n", Func, From, To)); 715a34c753fSRafael Auler } 716a34c753fSRafael Auler 717a34c753fSRafael Auler AggrData->bumpBranchCount(From, To, Count, Mispreds); 718a34c753fSRafael Auler return true; 719a34c753fSRafael Auler } 720a34c753fSRafael Auler 721a34c753fSRafael Auler bool DataAggregator::doInterBranch(BinaryFunction *FromFunc, 722a34c753fSRafael Auler BinaryFunction *ToFunc, uint64_t From, 723a34c753fSRafael Auler uint64_t To, uint64_t Count, 724a34c753fSRafael Auler uint64_t Mispreds) { 725a34c753fSRafael Auler FuncBranchData *FromAggrData = nullptr; 726a34c753fSRafael Auler FuncBranchData *ToAggrData = nullptr; 727a34c753fSRafael Auler StringRef SrcFunc; 728a34c753fSRafael Auler StringRef DstFunc; 729a34c753fSRafael Auler if (FromFunc) { 730a34c753fSRafael Auler SrcFunc = getLocationName(*FromFunc, Count); 731a34c753fSRafael Auler FromAggrData = getBranchData(*FromFunc); 732a34c753fSRafael Auler if (!FromAggrData) { 733a34c753fSRafael Auler FromAggrData = &NamesToBranches[FromFunc->getOneName()]; 734a34c753fSRafael Auler FromAggrData->Name = SrcFunc; 735a34c753fSRafael Auler setBranchData(*FromFunc, FromAggrData); 736a34c753fSRafael Auler } 737a34c753fSRafael Auler From -= FromFunc->getAddress(); 738a34c753fSRafael Auler if (BAT) 739fc0ced73SRafael Auler From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true); 740a34c753fSRafael Auler 741a34c753fSRafael Auler recordExit(*FromFunc, From, Mispreds, Count); 742a34c753fSRafael Auler } 743a34c753fSRafael Auler if (ToFunc) { 744a34c753fSRafael Auler DstFunc = getLocationName(*ToFunc, 0); 745a34c753fSRafael Auler ToAggrData = getBranchData(*ToFunc); 746a34c753fSRafael Auler if (!ToAggrData) { 747a34c753fSRafael Auler ToAggrData = &NamesToBranches[ToFunc->getOneName()]; 748a34c753fSRafael Auler ToAggrData->Name = DstFunc; 749a34c753fSRafael Auler setBranchData(*ToFunc, ToAggrData); 750a34c753fSRafael Auler } 751a34c753fSRafael Auler To -= ToFunc->getAddress(); 752a34c753fSRafael Auler if (BAT) 753fc0ced73SRafael Auler To = BAT->translate(ToFunc->getAddress(), To, /*IsBranchSrc=*/false); 754a34c753fSRafael Auler 755a34c753fSRafael Auler recordEntry(*ToFunc, To, Mispreds, Count); 756a34c753fSRafael Auler } 757a34c753fSRafael Auler 758a34c753fSRafael Auler if (FromAggrData) 759a34c753fSRafael Auler FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To), 760a34c753fSRafael Auler Count, Mispreds); 761a34c753fSRafael Auler if (ToAggrData) 762a34c753fSRafael Auler ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To, 763a34c753fSRafael Auler Count, Mispreds); 764a34c753fSRafael Auler return true; 765a34c753fSRafael Auler } 766a34c753fSRafael Auler 767a34c753fSRafael Auler bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count, 768a34c753fSRafael Auler uint64_t Mispreds) { 769a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(From); 770a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(To); 771a34c753fSRafael Auler if (!FromFunc && !ToFunc) 772a34c753fSRafael Auler return false; 773a34c753fSRafael Auler 774c061f755SAmir Ayupov // Treat recursive control transfers as inter-branches. 775c061f755SAmir Ayupov if (FromFunc == ToFunc && (To != ToFunc->getAddress())) { 776a34c753fSRafael Auler recordBranch(*FromFunc, From - FromFunc->getAddress(), 777a34c753fSRafael Auler To - FromFunc->getAddress(), Count, Mispreds); 778a34c753fSRafael Auler return doIntraBranch(*FromFunc, From, To, Count, Mispreds); 779a34c753fSRafael Auler } 780a34c753fSRafael Auler 781a34c753fSRafael Auler return doInterBranch(FromFunc, ToFunc, From, To, Count, Mispreds); 782a34c753fSRafael Auler } 783a34c753fSRafael Auler 784a34c753fSRafael Auler bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second, 785a34c753fSRafael Auler uint64_t Count) { 786a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(First.To); 787a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From); 788a34c753fSRafael Auler if (!FromFunc || !ToFunc) { 789713b2853SAmir Ayupov LLVM_DEBUG({ 790a34c753fSRafael Auler dbgs() << "Out of range trace starting in " << FromFunc->getPrintName() 791713b2853SAmir Ayupov << formatv(" @ {0:x}", First.To - FromFunc->getAddress()) 792713b2853SAmir Ayupov << " and ending in " << ToFunc->getPrintName() 793713b2853SAmir Ayupov << formatv(" @ {0:x}\n", Second.From - ToFunc->getAddress()); 794713b2853SAmir Ayupov }); 795a34c753fSRafael Auler NumLongRangeTraces += Count; 796a34c753fSRafael Auler return false; 797a34c753fSRafael Auler } 798a34c753fSRafael Auler if (FromFunc != ToFunc) { 799a34c753fSRafael Auler NumInvalidTraces += Count; 800713b2853SAmir Ayupov LLVM_DEBUG({ 801a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName() 802713b2853SAmir Ayupov << formatv(" @ {0:x}", First.To - FromFunc->getAddress()) 803713b2853SAmir Ayupov << " and ending in " << ToFunc->getPrintName() 804713b2853SAmir Ayupov << formatv(" @ {0:x}\n", Second.From - ToFunc->getAddress()); 805713b2853SAmir Ayupov }); 806a34c753fSRafael Auler return false; 807a34c753fSRafael Auler } 808a34c753fSRafael Auler 8093d573fdbSAmir Ayupov std::optional<BoltAddressTranslation::FallthroughListTy> FTs = 810fc0ced73SRafael Auler BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To, 811fc0ced73SRafael Auler Second.From) 812a34c753fSRafael Auler : getFallthroughsInTrace(*FromFunc, First, Second, Count); 813a34c753fSRafael Auler if (!FTs) { 814a34c753fSRafael Auler LLVM_DEBUG( 815a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName() 816a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress()) 817a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ " 818a34c753fSRafael Auler << ToFunc->getPrintName() << " @ " 819a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n'); 820a34c753fSRafael Auler NumInvalidTraces += Count; 821a34c753fSRafael Auler return false; 822a34c753fSRafael Auler } 823a34c753fSRafael Auler 824a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Processing " << FTs->size() << " fallthroughs for " 825a34c753fSRafael Auler << FromFunc->getPrintName() << ":" 826a34c753fSRafael Auler << Twine::utohexstr(First.To) << " to " 827a34c753fSRafael Auler << Twine::utohexstr(Second.From) << ".\n"); 828def464aaSAmir Ayupov for (const std::pair<uint64_t, uint64_t> &Pair : *FTs) 829a34c753fSRafael Auler doIntraBranch(*FromFunc, Pair.first + FromFunc->getAddress(), 830a34c753fSRafael Auler Pair.second + FromFunc->getAddress(), Count, false); 831a34c753fSRafael Auler 832a34c753fSRafael Auler return true; 833a34c753fSRafael Auler } 834a34c753fSRafael Auler 835a34c753fSRafael Auler bool DataAggregator::recordTrace( 836bce889c8SAmir Ayupov BinaryFunction &BF, const LBREntry &FirstLBR, const LBREntry &SecondLBR, 837a34c753fSRafael Auler uint64_t Count, 838bce889c8SAmir Ayupov SmallVector<std::pair<uint64_t, uint64_t>, 16> &Branches) const { 839a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext(); 840a34c753fSRafael Auler 841a34c753fSRafael Auler if (!BF.isSimple()) 842a34c753fSRafael Auler return false; 843a34c753fSRafael Auler 844a34c753fSRafael Auler assert(BF.hasCFG() && "can only record traces in CFG state"); 845a34c753fSRafael Auler 846a34c753fSRafael Auler // Offsets of the trace within this function. 847a34c753fSRafael Auler const uint64_t From = FirstLBR.To - BF.getAddress(); 848a34c753fSRafael Auler const uint64_t To = SecondLBR.From - BF.getAddress(); 849a34c753fSRafael Auler 850a34c753fSRafael Auler if (From > To) 851a34c753fSRafael Auler return false; 852a34c753fSRafael Auler 853d5c03defSFabian Parzefall const BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From); 854d5c03defSFabian Parzefall const BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To); 855a34c753fSRafael Auler 856a34c753fSRafael Auler if (!FromBB || !ToBB) 857a34c753fSRafael Auler return false; 858a34c753fSRafael Auler 859a34c753fSRafael Auler // Adjust FromBB if the first LBR is a return from the last instruction in 860a34c753fSRafael Auler // the previous block (that instruction should be a call). 861a34c753fSRafael Auler if (From == FromBB->getOffset() && !BF.containsAddress(FirstLBR.From) && 862a34c753fSRafael Auler !FromBB->isEntryPoint() && !FromBB->isLandingPad()) { 863d5c03defSFabian Parzefall const BinaryBasicBlock *PrevBB = 864d5c03defSFabian Parzefall BF.getLayout().getBlock(FromBB->getIndex() - 1); 865a34c753fSRafael Auler if (PrevBB->getSuccessor(FromBB->getLabel())) { 866a34c753fSRafael Auler const MCInst *Instr = PrevBB->getLastNonPseudoInstr(); 867def464aaSAmir Ayupov if (Instr && BC.MIB->isCall(*Instr)) 868a34c753fSRafael Auler FromBB = PrevBB; 869def464aaSAmir Ayupov else 870a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR (no call): " << FirstLBR 871a34c753fSRafael Auler << '\n'); 872a34c753fSRafael Auler } else { 873a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR: " << FirstLBR << '\n'); 874a34c753fSRafael Auler } 875a34c753fSRafael Auler } 876a34c753fSRafael Auler 877a34c753fSRafael Auler // Fill out information for fall-through edges. The From and To could be 878a34c753fSRafael Auler // within the same basic block, e.g. when two call instructions are in the 879a34c753fSRafael Auler // same block. In this case we skip the processing. 880def464aaSAmir Ayupov if (FromBB == ToBB) 881a34c753fSRafael Auler return true; 882a34c753fSRafael Auler 883a34c753fSRafael Auler // Process blocks in the original layout order. 8848477bc67SFabian Parzefall BinaryBasicBlock *BB = BF.getLayout().getBlock(FromBB->getIndex()); 885a34c753fSRafael Auler assert(BB == FromBB && "index mismatch"); 886a34c753fSRafael Auler while (BB != ToBB) { 8878477bc67SFabian Parzefall BinaryBasicBlock *NextBB = BF.getLayout().getBlock(BB->getIndex() + 1); 888a34c753fSRafael Auler assert((NextBB && NextBB->getOffset() > BB->getOffset()) && "bad layout"); 889a34c753fSRafael Auler 890a34c753fSRafael Auler // Check for bad LBRs. 891a34c753fSRafael Auler if (!BB->getSuccessor(NextBB->getLabel())) { 892a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "no fall-through for the trace:\n" 893a34c753fSRafael Auler << " " << FirstLBR << '\n' 894a34c753fSRafael Auler << " " << SecondLBR << '\n'); 895a34c753fSRafael Auler return false; 896a34c753fSRafael Auler } 897a34c753fSRafael Auler 898a34c753fSRafael Auler const MCInst *Instr = BB->getLastNonPseudoInstr(); 899a34c753fSRafael Auler uint64_t Offset = 0; 900def464aaSAmir Ayupov if (Instr) 901a9cd49d5SAmir Ayupov Offset = BC.MIB->getOffsetWithDefault(*Instr, 0); 902def464aaSAmir Ayupov else 903a34c753fSRafael Auler Offset = BB->getOffset(); 904def464aaSAmir Ayupov 905bce889c8SAmir Ayupov Branches.emplace_back(Offset, NextBB->getOffset()); 906a34c753fSRafael Auler 907a34c753fSRafael Auler BB = NextBB; 908a34c753fSRafael Auler } 909a34c753fSRafael Auler 910bce889c8SAmir Ayupov // Record fall-through jumps 911bce889c8SAmir Ayupov for (const auto &[FromOffset, ToOffset] : Branches) { 912bce889c8SAmir Ayupov BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(FromOffset); 913bce889c8SAmir Ayupov BinaryBasicBlock *ToBB = BF.getBasicBlockAtOffset(ToOffset); 914bce889c8SAmir Ayupov assert(FromBB && ToBB); 915bce889c8SAmir Ayupov BinaryBasicBlock::BinaryBranchInfo &BI = FromBB->getBranchInfo(*ToBB); 916bce889c8SAmir Ayupov BI.Count += Count; 917bce889c8SAmir Ayupov } 918bce889c8SAmir Ayupov 919a34c753fSRafael Auler return true; 920a34c753fSRafael Auler } 921a34c753fSRafael Auler 9223d573fdbSAmir Ayupov std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>> 923a34c753fSRafael Auler DataAggregator::getFallthroughsInTrace(BinaryFunction &BF, 924a34c753fSRafael Auler const LBREntry &FirstLBR, 925a34c753fSRafael Auler const LBREntry &SecondLBR, 926a34c753fSRafael Auler uint64_t Count) const { 927a34c753fSRafael Auler SmallVector<std::pair<uint64_t, uint64_t>, 16> Res; 928a34c753fSRafael Auler 929bce889c8SAmir Ayupov if (!recordTrace(BF, FirstLBR, SecondLBR, Count, Res)) 930e324a80fSKazu Hirata return std::nullopt; 931a34c753fSRafael Auler 932a34c753fSRafael Auler return Res; 933a34c753fSRafael Auler } 934a34c753fSRafael Auler 935a34c753fSRafael Auler bool DataAggregator::recordEntry(BinaryFunction &BF, uint64_t To, bool Mispred, 936a34c753fSRafael Auler uint64_t Count) const { 937a34c753fSRafael Auler if (To > BF.getSize()) 938a34c753fSRafael Auler return false; 939a34c753fSRafael Auler 940a34c753fSRafael Auler if (!BF.hasProfile()) 941a34c753fSRafael Auler BF.ExecutionCount = 0; 942a34c753fSRafael Auler 943a34c753fSRafael Auler BinaryBasicBlock *EntryBB = nullptr; 944a34c753fSRafael Auler if (To == 0) { 945a34c753fSRafael Auler BF.ExecutionCount += Count; 946a34c753fSRafael Auler if (!BF.empty()) 947a34c753fSRafael Auler EntryBB = &BF.front(); 948a34c753fSRafael Auler } else if (BinaryBasicBlock *BB = BF.getBasicBlockAtOffset(To)) { 949a34c753fSRafael Auler if (BB->isEntryPoint()) 950a34c753fSRafael Auler EntryBB = BB; 951a34c753fSRafael Auler } 952a34c753fSRafael Auler 953a34c753fSRafael Auler if (EntryBB) 954a34c753fSRafael Auler EntryBB->setExecutionCount(EntryBB->getKnownExecutionCount() + Count); 955a34c753fSRafael Auler 956a34c753fSRafael Auler return true; 957a34c753fSRafael Auler } 958a34c753fSRafael Auler 95940c2e0faSMaksim Panchenko bool DataAggregator::recordExit(BinaryFunction &BF, uint64_t From, bool Mispred, 96040c2e0faSMaksim Panchenko uint64_t Count) const { 961a34c753fSRafael Auler if (!BF.isSimple() || From > BF.getSize()) 962a34c753fSRafael Auler return false; 963a34c753fSRafael Auler 964a34c753fSRafael Auler if (!BF.hasProfile()) 965a34c753fSRafael Auler BF.ExecutionCount = 0; 966a34c753fSRafael Auler 967a34c753fSRafael Auler return true; 968a34c753fSRafael Auler } 969a34c753fSRafael Auler 970a34c753fSRafael Auler ErrorOr<LBREntry> DataAggregator::parseLBREntry() { 971a34c753fSRafael Auler LBREntry Res; 972a34c753fSRafael Auler ErrorOr<StringRef> FromStrRes = parseString('/'); 973a34c753fSRafael Auler if (std::error_code EC = FromStrRes.getError()) 974a34c753fSRafael Auler return EC; 975a34c753fSRafael Auler StringRef OffsetStr = FromStrRes.get(); 976a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.From)) { 977a34c753fSRafael Auler reportError("expected hexadecimal number with From address"); 978a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n"; 979a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 980a34c753fSRafael Auler } 981a34c753fSRafael Auler 982a34c753fSRafael Auler ErrorOr<StringRef> ToStrRes = parseString('/'); 983a34c753fSRafael Auler if (std::error_code EC = ToStrRes.getError()) 984a34c753fSRafael Auler return EC; 985a34c753fSRafael Auler OffsetStr = ToStrRes.get(); 986a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.To)) { 987a34c753fSRafael Auler reportError("expected hexadecimal number with To address"); 988a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n"; 989a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 990a34c753fSRafael Auler } 991a34c753fSRafael Auler 992a34c753fSRafael Auler ErrorOr<StringRef> MispredStrRes = parseString('/'); 993a34c753fSRafael Auler if (std::error_code EC = MispredStrRes.getError()) 994a34c753fSRafael Auler return EC; 995a34c753fSRafael Auler StringRef MispredStr = MispredStrRes.get(); 996a34c753fSRafael Auler if (MispredStr.size() != 1 || 997a34c753fSRafael Auler (MispredStr[0] != 'P' && MispredStr[0] != 'M' && MispredStr[0] != '-')) { 998a34c753fSRafael Auler reportError("expected single char for mispred bit"); 999a34c753fSRafael Auler Diag << "Found: " << MispredStr << "\n"; 1000a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1001a34c753fSRafael Auler } 1002a34c753fSRafael Auler Res.Mispred = MispredStr[0] == 'M'; 1003a34c753fSRafael Auler 100440c2e0faSMaksim Panchenko static bool MispredWarning = true; 1005a34c753fSRafael Auler if (MispredStr[0] == '-' && MispredWarning) { 1006a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: misprediction bit is missing in profile\n"; 1007a34c753fSRafael Auler MispredWarning = false; 1008a34c753fSRafael Auler } 1009a34c753fSRafael Auler 1010a34c753fSRafael Auler ErrorOr<StringRef> Rest = parseString(FieldSeparator, true); 1011a34c753fSRafael Auler if (std::error_code EC = Rest.getError()) 1012a34c753fSRafael Auler return EC; 1013a34c753fSRafael Auler if (Rest.get().size() < 5) { 1014a34c753fSRafael Auler reportError("expected rest of LBR entry"); 1015a34c753fSRafael Auler Diag << "Found: " << Rest.get() << "\n"; 1016a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1017a34c753fSRafael Auler } 1018a34c753fSRafael Auler return Res; 1019a34c753fSRafael Auler } 1020a34c753fSRafael Auler 1021a34c753fSRafael Auler bool DataAggregator::checkAndConsumeFS() { 1022def464aaSAmir Ayupov if (ParsingBuf[0] != FieldSeparator) 1023a34c753fSRafael Auler return false; 1024def464aaSAmir Ayupov 1025a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1); 1026a34c753fSRafael Auler Col += 1; 1027a34c753fSRafael Auler return true; 1028a34c753fSRafael Auler } 1029a34c753fSRafael Auler 1030a34c753fSRafael Auler void DataAggregator::consumeRestOfLine() { 1031a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of('\n'); 1032a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1033a34c753fSRafael Auler ParsingBuf = StringRef(); 1034a34c753fSRafael Auler Col = 0; 1035a34c753fSRafael Auler Line += 1; 1036a34c753fSRafael Auler return; 1037a34c753fSRafael Auler } 1038a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(LineEnd + 1); 1039a34c753fSRafael Auler Col = 0; 1040a34c753fSRafael Auler Line += 1; 1041a34c753fSRafael Auler } 1042a34c753fSRafael Auler 1043ba9cc653SRafael Auler bool DataAggregator::checkNewLine() { 1044ba9cc653SRafael Auler return ParsingBuf[0] == '\n'; 1045ba9cc653SRafael Auler } 1046ba9cc653SRafael Auler 1047a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBranchSample> DataAggregator::parseBranchSample() { 1048a34c753fSRafael Auler PerfBranchSample Res; 1049a34c753fSRafael Auler 105040c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 105140c2e0faSMaksim Panchenko } 1052a34c753fSRafael Auler 1053a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1054a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1055a34c753fSRafael Auler return EC; 1056a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1057a34c753fSRafael Auler if (!opts::LinuxKernelMode && MMapInfoIter == BinaryMMapInfo.end()) { 1058a34c753fSRafael Auler consumeRestOfLine(); 1059a34c753fSRafael Auler return make_error_code(errc::no_such_process); 1060a34c753fSRafael Auler } 1061a34c753fSRafael Auler 106240c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 106340c2e0faSMaksim Panchenko } 1064a34c753fSRafael Auler 1065a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true); 1066a34c753fSRafael Auler if (std::error_code EC = PCRes.getError()) 1067a34c753fSRafael Auler return EC; 1068a34c753fSRafael Auler Res.PC = PCRes.get(); 1069a34c753fSRafael Auler 1070a34c753fSRafael Auler if (checkAndConsumeNewLine()) 1071a34c753fSRafael Auler return Res; 1072a34c753fSRafael Auler 1073a34c753fSRafael Auler while (!checkAndConsumeNewLine()) { 1074a34c753fSRafael Auler checkAndConsumeFS(); 1075a34c753fSRafael Auler 1076a34c753fSRafael Auler ErrorOr<LBREntry> LBRRes = parseLBREntry(); 1077a34c753fSRafael Auler if (std::error_code EC = LBRRes.getError()) 1078a34c753fSRafael Auler return EC; 1079a34c753fSRafael Auler LBREntry LBR = LBRRes.get(); 1080a34c753fSRafael Auler if (ignoreKernelInterrupt(LBR)) 1081a34c753fSRafael Auler continue; 1082a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1083a34c753fSRafael Auler adjustLBR(LBR, MMapInfoIter->second); 1084a34c753fSRafael Auler Res.LBR.push_back(LBR); 1085a34c753fSRafael Auler } 1086a34c753fSRafael Auler 1087a34c753fSRafael Auler return Res; 1088a34c753fSRafael Auler } 1089a34c753fSRafael Auler 1090a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBasicSample> DataAggregator::parseBasicSample() { 109140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 109240c2e0faSMaksim Panchenko } 1093a34c753fSRafael Auler 1094a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1095a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1096a34c753fSRafael Auler return EC; 1097a34c753fSRafael Auler 1098a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1099a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) { 1100a34c753fSRafael Auler consumeRestOfLine(); 1101a34c753fSRafael Auler return PerfBasicSample{StringRef(), 0}; 1102a34c753fSRafael Auler } 1103a34c753fSRafael Auler 110440c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 110540c2e0faSMaksim Panchenko } 1106a34c753fSRafael Auler 1107a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator); 1108a34c753fSRafael Auler if (std::error_code EC = Event.getError()) 1109a34c753fSRafael Auler return EC; 1110a34c753fSRafael Auler 111140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 111240c2e0faSMaksim Panchenko } 1113a34c753fSRafael Auler 1114a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator, true); 1115def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError()) 1116a34c753fSRafael Auler return EC; 1117a34c753fSRafael Auler 1118a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1119a34c753fSRafael Auler reportError("expected end of line"); 1120a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1121a34c753fSRafael Auler } 1122a34c753fSRafael Auler 1123a34c753fSRafael Auler uint64_t Address = *AddrRes; 1124a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1125a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second); 1126a34c753fSRafael Auler 1127a34c753fSRafael Auler return PerfBasicSample{Event.get(), Address}; 1128a34c753fSRafael Auler } 1129a34c753fSRafael Auler 1130a34c753fSRafael Auler ErrorOr<DataAggregator::PerfMemSample> DataAggregator::parseMemSample() { 1131a34c753fSRafael Auler PerfMemSample Res{0, 0}; 1132a34c753fSRafael Auler 113340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 113440c2e0faSMaksim Panchenko } 1135a34c753fSRafael Auler 1136a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1137a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1138a34c753fSRafael Auler return EC; 1139a34c753fSRafael Auler 1140a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1141a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) { 1142a34c753fSRafael Auler consumeRestOfLine(); 1143a34c753fSRafael Auler return Res; 1144a34c753fSRafael Auler } 1145a34c753fSRafael Auler 114640c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 114740c2e0faSMaksim Panchenko } 1148a34c753fSRafael Auler 1149a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator); 1150a34c753fSRafael Auler if (std::error_code EC = Event.getError()) 1151a34c753fSRafael Auler return EC; 115220f0f15aSKazu Hirata if (!Event.get().contains("mem-loads")) { 1153a34c753fSRafael Auler consumeRestOfLine(); 1154a34c753fSRafael Auler return Res; 1155a34c753fSRafael Auler } 1156a34c753fSRafael Auler 115740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 115840c2e0faSMaksim Panchenko } 1159a34c753fSRafael Auler 1160a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator); 1161def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError()) 1162a34c753fSRafael Auler return EC; 1163a34c753fSRafael Auler 116440c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 116540c2e0faSMaksim Panchenko } 1166a34c753fSRafael Auler 1167a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true); 1168a34c753fSRafael Auler if (std::error_code EC = PCRes.getError()) { 1169a34c753fSRafael Auler consumeRestOfLine(); 1170a34c753fSRafael Auler return EC; 1171a34c753fSRafael Auler } 1172a34c753fSRafael Auler 1173a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1174a34c753fSRafael Auler reportError("expected end of line"); 1175a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1176a34c753fSRafael Auler } 1177a34c753fSRafael Auler 1178a34c753fSRafael Auler uint64_t Address = *AddrRes; 1179a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1180a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second); 1181a34c753fSRafael Auler 1182a34c753fSRafael Auler return PerfMemSample{PCRes.get(), Address}; 1183a34c753fSRafael Auler } 1184a34c753fSRafael Auler 1185a34c753fSRafael Auler ErrorOr<Location> DataAggregator::parseLocationOrOffset() { 1186a34c753fSRafael Auler auto parseOffset = [this]() -> ErrorOr<Location> { 1187a34c753fSRafael Auler ErrorOr<uint64_t> Res = parseHexField(FieldSeparator); 1188a34c753fSRafael Auler if (std::error_code EC = Res.getError()) 1189a34c753fSRafael Auler return EC; 1190a34c753fSRafael Auler return Location(Res.get()); 1191a34c753fSRafael Auler }; 1192a34c753fSRafael Auler 1193a34c753fSRafael Auler size_t Sep = ParsingBuf.find_first_of(" \n"); 1194a34c753fSRafael Auler if (Sep == StringRef::npos) 1195a34c753fSRafael Auler return parseOffset(); 1196a34c753fSRafael Auler StringRef LookAhead = ParsingBuf.substr(0, Sep); 1197a34c753fSRafael Auler if (LookAhead.find_first_of(":") == StringRef::npos) 1198a34c753fSRafael Auler return parseOffset(); 1199a34c753fSRafael Auler 1200a34c753fSRafael Auler ErrorOr<StringRef> BuildID = parseString(':'); 1201a34c753fSRafael Auler if (std::error_code EC = BuildID.getError()) 1202a34c753fSRafael Auler return EC; 1203a34c753fSRafael Auler ErrorOr<uint64_t> Offset = parseHexField(FieldSeparator); 1204a34c753fSRafael Auler if (std::error_code EC = Offset.getError()) 1205a34c753fSRafael Auler return EC; 1206a34c753fSRafael Auler return Location(true, BuildID.get(), Offset.get()); 1207a34c753fSRafael Auler } 1208a34c753fSRafael Auler 1209a34c753fSRafael Auler ErrorOr<DataAggregator::AggregatedLBREntry> 1210a34c753fSRafael Auler DataAggregator::parseAggregatedLBREntry() { 121140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 121240c2e0faSMaksim Panchenko } 1213a34c753fSRafael Auler 1214a34c753fSRafael Auler ErrorOr<StringRef> TypeOrErr = parseString(FieldSeparator); 1215a34c753fSRafael Auler if (std::error_code EC = TypeOrErr.getError()) 1216a34c753fSRafael Auler return EC; 1217a34c753fSRafael Auler auto Type = AggregatedLBREntry::BRANCH; 1218a34c753fSRafael Auler if (TypeOrErr.get() == "B") { 1219a34c753fSRafael Auler Type = AggregatedLBREntry::BRANCH; 1220a34c753fSRafael Auler } else if (TypeOrErr.get() == "F") { 1221a34c753fSRafael Auler Type = AggregatedLBREntry::FT; 1222a34c753fSRafael Auler } else if (TypeOrErr.get() == "f") { 1223a34c753fSRafael Auler Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN; 1224a34c753fSRafael Auler } else { 1225a34c753fSRafael Auler reportError("expected B, F or f"); 1226a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1227a34c753fSRafael Auler } 1228a34c753fSRafael Auler 122940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 123040c2e0faSMaksim Panchenko } 1231a34c753fSRafael Auler ErrorOr<Location> From = parseLocationOrOffset(); 1232a34c753fSRafael Auler if (std::error_code EC = From.getError()) 1233a34c753fSRafael Auler return EC; 1234a34c753fSRafael Auler 123540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 123640c2e0faSMaksim Panchenko } 1237a34c753fSRafael Auler ErrorOr<Location> To = parseLocationOrOffset(); 1238a34c753fSRafael Auler if (std::error_code EC = To.getError()) 1239a34c753fSRafael Auler return EC; 1240a34c753fSRafael Auler 124140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 124240c2e0faSMaksim Panchenko } 1243a34c753fSRafael Auler ErrorOr<int64_t> Frequency = 1244a34c753fSRafael Auler parseNumberField(FieldSeparator, Type != AggregatedLBREntry::BRANCH); 1245a34c753fSRafael Auler if (std::error_code EC = Frequency.getError()) 1246a34c753fSRafael Auler return EC; 1247a34c753fSRafael Auler 1248a34c753fSRafael Auler uint64_t Mispreds = 0; 1249a34c753fSRafael Auler if (Type == AggregatedLBREntry::BRANCH) { 125040c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 125140c2e0faSMaksim Panchenko } 1252a34c753fSRafael Auler ErrorOr<int64_t> MispredsOrErr = parseNumberField(FieldSeparator, true); 1253a34c753fSRafael Auler if (std::error_code EC = MispredsOrErr.getError()) 1254a34c753fSRafael Auler return EC; 1255a34c753fSRafael Auler Mispreds = static_cast<uint64_t>(MispredsOrErr.get()); 1256a34c753fSRafael Auler } 1257a34c753fSRafael Auler 1258a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1259a34c753fSRafael Auler reportError("expected end of line"); 1260a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1261a34c753fSRafael Auler } 1262a34c753fSRafael Auler 1263a34c753fSRafael Auler return AggregatedLBREntry{From.get(), To.get(), 1264a34c753fSRafael Auler static_cast<uint64_t>(Frequency.get()), Mispreds, 1265a34c753fSRafael Auler Type}; 1266a34c753fSRafael Auler } 1267a34c753fSRafael Auler 1268a34c753fSRafael Auler bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const { 1269a34c753fSRafael Auler return opts::IgnoreInterruptLBR && 1270a34c753fSRafael Auler (LBR.From >= KernelBaseAddr || LBR.To >= KernelBaseAddr); 1271a34c753fSRafael Auler } 1272a34c753fSRafael Auler 1273a34c753fSRafael Auler std::error_code DataAggregator::printLBRHeatMap() { 1274a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n"; 1275a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName, 1276a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1277a34c753fSRafael Auler 1278a34c753fSRafael Auler if (opts::LinuxKernelMode) { 1279a34c753fSRafael Auler opts::HeatmapMaxAddress = 0xffffffffffffffff; 1280a34c753fSRafael Auler opts::HeatmapMinAddress = KernelBaseAddr; 1281a34c753fSRafael Auler } 1282a34c753fSRafael Auler Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress, 1283733dc3e5SRahman Lavaee opts::HeatmapMaxAddress, getTextSections(BC)); 1284a34c753fSRafael Auler uint64_t NumTotalSamples = 0; 1285a34c753fSRafael Auler 12860c13d97eSRahman Lavaee if (opts::BasicAggregation) { 1287e59e5801SRahman Lavaee while (hasData()) { 12880c13d97eSRahman Lavaee ErrorOr<PerfBasicSample> SampleRes = parseBasicSample(); 12890c13d97eSRahman Lavaee if (std::error_code EC = SampleRes.getError()) { 12900c13d97eSRahman Lavaee if (EC == errc::no_such_process) 12910c13d97eSRahman Lavaee continue; 12920c13d97eSRahman Lavaee return EC; 12930c13d97eSRahman Lavaee } 12940c13d97eSRahman Lavaee PerfBasicSample &Sample = SampleRes.get(); 12950c13d97eSRahman Lavaee HM.registerAddress(Sample.PC); 12960c13d97eSRahman Lavaee NumTotalSamples++; 1297e59e5801SRahman Lavaee } 1298e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " basic samples\n"; 12990c13d97eSRahman Lavaee } else { 1300e59e5801SRahman Lavaee while (hasData()) { 1301a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample(); 1302a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) { 1303a34c753fSRafael Auler if (EC == errc::no_such_process) 1304a34c753fSRafael Auler continue; 1305a34c753fSRafael Auler return EC; 1306a34c753fSRafael Auler } 1307a34c753fSRafael Auler 1308a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get(); 1309a34c753fSRafael Auler 1310a34c753fSRafael Auler // LBRs are stored in reverse execution order. NextLBR refers to the next 1311a34c753fSRafael Auler // executed branch record. 1312a34c753fSRafael Auler const LBREntry *NextLBR = nullptr; 1313a34c753fSRafael Auler for (const LBREntry &LBR : Sample.LBR) { 1314a34c753fSRafael Auler if (NextLBR) { 1315a34c753fSRafael Auler // Record fall-through trace. 1316a34c753fSRafael Auler const uint64_t TraceFrom = LBR.To; 1317a34c753fSRafael Auler const uint64_t TraceTo = NextLBR->From; 1318a34c753fSRafael Auler ++FallthroughLBRs[Trace(TraceFrom, TraceTo)].InternCount; 1319a34c753fSRafael Auler } 1320a34c753fSRafael Auler NextLBR = &LBR; 1321a34c753fSRafael Auler } 1322a34c753fSRafael Auler if (!Sample.LBR.empty()) { 1323a34c753fSRafael Auler HM.registerAddress(Sample.LBR.front().To); 1324a34c753fSRafael Auler HM.registerAddress(Sample.LBR.back().From); 1325a34c753fSRafael Auler } 1326a34c753fSRafael Auler NumTotalSamples += Sample.LBR.size(); 1327a34c753fSRafael Auler } 1328e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " LBR samples\n"; 1329e59e5801SRahman Lavaee outs() << "HEATMAP: " << FallthroughLBRs.size() << " unique traces\n"; 13300c13d97eSRahman Lavaee } 1331a34c753fSRafael Auler 1332a34c753fSRafael Auler if (!NumTotalSamples) { 1333e59e5801SRahman Lavaee if (opts::BasicAggregation) { 1334e59e5801SRahman Lavaee errs() << "HEATMAP-ERROR: no basic event samples detected in profile. " 1335e59e5801SRahman Lavaee "Cannot build heatmap."; 1336e59e5801SRahman Lavaee } else { 1337a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no LBR traces detected in profile. " 13380c13d97eSRahman Lavaee "Cannot build heatmap. Use -nl for building heatmap from " 13390c13d97eSRahman Lavaee "basic events.\n"; 13400c13d97eSRahman Lavaee } 1341a34c753fSRafael Auler exit(1); 1342a34c753fSRafael Auler } 1343a34c753fSRafael Auler 1344a34c753fSRafael Auler outs() << "HEATMAP: building heat map...\n"; 1345a34c753fSRafael Auler 1346a34c753fSRafael Auler for (const auto &LBR : FallthroughLBRs) { 1347a34c753fSRafael Auler const Trace &Trace = LBR.first; 1348a34c753fSRafael Auler const FTInfo &Info = LBR.second; 1349a34c753fSRafael Auler HM.registerAddressRange(Trace.From, Trace.To, Info.InternCount); 1350a34c753fSRafael Auler } 1351a34c753fSRafael Auler 1352a34c753fSRafael Auler if (HM.getNumInvalidRanges()) 1353a34c753fSRafael Auler outs() << "HEATMAP: invalid traces: " << HM.getNumInvalidRanges() << '\n'; 1354a34c753fSRafael Auler 1355a34c753fSRafael Auler if (!HM.size()) { 1356a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no valid traces registered\n"; 1357a34c753fSRafael Auler exit(1); 1358a34c753fSRafael Auler } 1359a34c753fSRafael Auler 13605c2ae5f4SVladislav Khmelevsky HM.print(opts::OutputFilename); 13615c2ae5f4SVladislav Khmelevsky if (opts::OutputFilename == "-") 13625c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename); 1363def464aaSAmir Ayupov else 13645c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename + ".csv"); 1365733dc3e5SRahman Lavaee if (opts::OutputFilename == "-") 1366733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename); 1367733dc3e5SRahman Lavaee else 1368733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv"); 1369a34c753fSRafael Auler 1370a34c753fSRafael Auler return std::error_code(); 1371a34c753fSRafael Auler } 1372a34c753fSRafael Auler 1373860543d9SAmir Ayupov uint64_t DataAggregator::parseLBRSample(const PerfBranchSample &Sample, 1374860543d9SAmir Ayupov bool NeedsSkylakeFix) { 1375860543d9SAmir Ayupov uint64_t NumTraces{0}; 1376860543d9SAmir Ayupov // LBRs are stored in reverse execution order. NextPC refers to the next 1377860543d9SAmir Ayupov // recorded executed PC. 1378860543d9SAmir Ayupov uint64_t NextPC = opts::UseEventPC ? Sample.PC : 0; 1379860543d9SAmir Ayupov uint32_t NumEntry = 0; 1380860543d9SAmir Ayupov for (const LBREntry &LBR : Sample.LBR) { 1381860543d9SAmir Ayupov ++NumEntry; 1382860543d9SAmir Ayupov // Hardware bug workaround: Intel Skylake (which has 32 LBR entries) 1383860543d9SAmir Ayupov // sometimes record entry 32 as an exact copy of entry 31. This will cause 1384860543d9SAmir Ayupov // us to likely record an invalid trace and generate a stale function for 1385860543d9SAmir Ayupov // BAT mode (non BAT disassembles the function and is able to ignore this 1386860543d9SAmir Ayupov // trace at aggregation time). Drop first 2 entries (last two, in 1387860543d9SAmir Ayupov // chronological order) 1388860543d9SAmir Ayupov if (NeedsSkylakeFix && NumEntry <= 2) 1389860543d9SAmir Ayupov continue; 1390860543d9SAmir Ayupov if (NextPC) { 1391860543d9SAmir Ayupov // Record fall-through trace. 1392860543d9SAmir Ayupov const uint64_t TraceFrom = LBR.To; 1393860543d9SAmir Ayupov const uint64_t TraceTo = NextPC; 1394860543d9SAmir Ayupov const BinaryFunction *TraceBF = 1395860543d9SAmir Ayupov getBinaryFunctionContainingAddress(TraceFrom); 1396860543d9SAmir Ayupov if (TraceBF && TraceBF->containsAddress(TraceTo)) { 1397860543d9SAmir Ayupov FTInfo &Info = FallthroughLBRs[Trace(TraceFrom, TraceTo)]; 1398860543d9SAmir Ayupov if (TraceBF->containsAddress(LBR.From)) 1399860543d9SAmir Ayupov ++Info.InternCount; 1400860543d9SAmir Ayupov else 1401860543d9SAmir Ayupov ++Info.ExternCount; 1402860543d9SAmir Ayupov } else { 1403860543d9SAmir Ayupov const BinaryFunction *ToFunc = 1404860543d9SAmir Ayupov getBinaryFunctionContainingAddress(TraceTo); 1405860543d9SAmir Ayupov if (TraceBF && ToFunc) { 1406860543d9SAmir Ayupov LLVM_DEBUG({ 1407860543d9SAmir Ayupov dbgs() << "Invalid trace starting in " << TraceBF->getPrintName() 1408860543d9SAmir Ayupov << formatv(" @ {0:x}", TraceFrom - TraceBF->getAddress()) 1409860543d9SAmir Ayupov << formatv(" and ending @ {0:x}\n", TraceTo); 1410860543d9SAmir Ayupov }); 1411860543d9SAmir Ayupov ++NumInvalidTraces; 1412860543d9SAmir Ayupov } else { 1413860543d9SAmir Ayupov LLVM_DEBUG({ 1414860543d9SAmir Ayupov dbgs() << "Out of range trace starting in " 1415860543d9SAmir Ayupov << (TraceBF ? TraceBF->getPrintName() : "None") 1416860543d9SAmir Ayupov << formatv(" @ {0:x}", 1417860543d9SAmir Ayupov TraceFrom - (TraceBF ? TraceBF->getAddress() : 0)) 1418860543d9SAmir Ayupov << " and ending in " 1419860543d9SAmir Ayupov << (ToFunc ? ToFunc->getPrintName() : "None") 1420860543d9SAmir Ayupov << formatv(" @ {0:x}\n", 1421860543d9SAmir Ayupov TraceTo - (ToFunc ? ToFunc->getAddress() : 0)); 1422860543d9SAmir Ayupov }); 1423860543d9SAmir Ayupov ++NumLongRangeTraces; 1424860543d9SAmir Ayupov } 1425860543d9SAmir Ayupov } 1426860543d9SAmir Ayupov ++NumTraces; 1427860543d9SAmir Ayupov } 1428860543d9SAmir Ayupov NextPC = LBR.From; 1429860543d9SAmir Ayupov 1430860543d9SAmir Ayupov uint64_t From = getBinaryFunctionContainingAddress(LBR.From) ? LBR.From : 0; 1431860543d9SAmir Ayupov uint64_t To = getBinaryFunctionContainingAddress(LBR.To) ? LBR.To : 0; 1432860543d9SAmir Ayupov if (!From && !To) 1433860543d9SAmir Ayupov continue; 1434860543d9SAmir Ayupov BranchInfo &Info = BranchLBRs[Trace(From, To)]; 1435860543d9SAmir Ayupov ++Info.TakenCount; 1436860543d9SAmir Ayupov Info.MispredCount += LBR.Mispred; 1437860543d9SAmir Ayupov } 1438860543d9SAmir Ayupov return NumTraces; 1439860543d9SAmir Ayupov } 1440860543d9SAmir Ayupov 1441a34c753fSRafael Auler std::error_code DataAggregator::parseBranchEvents() { 1442a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n"; 1443a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName, 1444a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1445a34c753fSRafael Auler 1446a34c753fSRafael Auler uint64_t NumTotalSamples = 0; 1447a34c753fSRafael Auler uint64_t NumEntries = 0; 1448a34c753fSRafael Auler uint64_t NumSamples = 0; 1449a34c753fSRafael Auler uint64_t NumSamplesNoLBR = 0; 1450a34c753fSRafael Auler uint64_t NumTraces = 0; 1451a34c753fSRafael Auler bool NeedsSkylakeFix = false; 1452a34c753fSRafael Auler 1453a34c753fSRafael Auler while (hasData() && NumTotalSamples < opts::MaxSamples) { 1454a34c753fSRafael Auler ++NumTotalSamples; 1455a34c753fSRafael Auler 1456a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample(); 1457a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) { 1458a34c753fSRafael Auler if (EC == errc::no_such_process) 1459a34c753fSRafael Auler continue; 1460a34c753fSRafael Auler return EC; 1461a34c753fSRafael Auler } 1462a34c753fSRafael Auler ++NumSamples; 1463a34c753fSRafael Auler 1464a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get(); 1465a34c753fSRafael Auler if (opts::WriteAutoFDOData) 1466a34c753fSRafael Auler ++BasicSamples[Sample.PC]; 1467a34c753fSRafael Auler 1468a34c753fSRafael Auler if (Sample.LBR.empty()) { 1469a34c753fSRafael Auler ++NumSamplesNoLBR; 1470a34c753fSRafael Auler continue; 1471a34c753fSRafael Auler } 1472a34c753fSRafael Auler 1473a34c753fSRafael Auler NumEntries += Sample.LBR.size(); 1474a34c753fSRafael Auler if (BAT && Sample.LBR.size() == 32 && !NeedsSkylakeFix) { 1475a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using Intel Skylake bug workaround\n"; 1476a34c753fSRafael Auler NeedsSkylakeFix = true; 1477a34c753fSRafael Auler } 1478a34c753fSRafael Auler 1479860543d9SAmir Ayupov NumTraces += parseLBRSample(Sample, NeedsSkylakeFix); 1480a34c753fSRafael Auler } 1481a34c753fSRafael Auler 1482*d796f36fSAmir Ayupov for (const Trace &Trace : llvm::make_first_range(BranchLBRs)) 1483*d796f36fSAmir Ayupov for (const uint64_t Addr : {Trace.From, Trace.To}) 1484*d796f36fSAmir Ayupov if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr)) 1485a34c753fSRafael Auler BF->setHasProfileAvailable(); 1486a34c753fSRafael Auler 1487a34c753fSRafael Auler auto printColored = [](raw_ostream &OS, float Percent, float T1, float T2) { 1488a34c753fSRafael Auler OS << " ("; 1489a34c753fSRafael Auler if (OS.has_colors()) { 1490def464aaSAmir Ayupov if (Percent > T2) 1491a34c753fSRafael Auler OS.changeColor(raw_ostream::RED); 1492def464aaSAmir Ayupov else if (Percent > T1) 1493a34c753fSRafael Auler OS.changeColor(raw_ostream::YELLOW); 1494def464aaSAmir Ayupov else 1495a34c753fSRafael Auler OS.changeColor(raw_ostream::GREEN); 1496a34c753fSRafael Auler } 1497a34c753fSRafael Auler OS << format("%.1f%%", Percent); 1498a34c753fSRafael Auler if (OS.has_colors()) 1499a34c753fSRafael Auler OS.resetColor(); 1500a34c753fSRafael Auler OS << ")"; 1501a34c753fSRafael Auler }; 1502a34c753fSRafael Auler 150340c2e0faSMaksim Panchenko outs() << "PERF2BOLT: read " << NumSamples << " samples and " << NumEntries 150440c2e0faSMaksim Panchenko << " LBR entries\n"; 1505a34c753fSRafael Auler if (NumTotalSamples) { 1506a34c753fSRafael Auler if (NumSamples && NumSamplesNoLBR == NumSamples) { 1507a34c753fSRafael Auler // Note: we don't know if perf2bolt is being used to parse memory samples 1508a34c753fSRafael Auler // at this point. In this case, it is OK to parse zero LBRs. 1509a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: all recorded samples for this binary lack " 1510a34c753fSRafael Auler "LBR. Record profile with perf record -j any or run perf2bolt " 1511a34c753fSRafael Auler "in no-LBR mode with -nl (the performance improvement in -nl " 1512a34c753fSRafael Auler "mode may be limited)\n"; 1513a34c753fSRafael Auler } else { 1514a34c753fSRafael Auler const uint64_t IgnoredSamples = NumTotalSamples - NumSamples; 1515a34c753fSRafael Auler const float PercentIgnored = 100.0f * IgnoredSamples / NumTotalSamples; 1516a34c753fSRafael Auler outs() << "PERF2BOLT: " << IgnoredSamples << " samples"; 1517a34c753fSRafael Auler printColored(outs(), PercentIgnored, 20, 50); 1518a34c753fSRafael Auler outs() << " were ignored\n"; 1519def464aaSAmir Ayupov if (PercentIgnored > 50.0f) 1520a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: less than 50% of all recorded samples " 1521a34c753fSRafael Auler "were attributed to the input binary\n"; 1522a34c753fSRafael Auler } 1523a34c753fSRafael Auler } 1524a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: " 1525a34c753fSRafael Auler << NumInvalidTraces; 1526a34c753fSRafael Auler float Perc = 0.0f; 1527a34c753fSRafael Auler if (NumTraces > 0) { 1528a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces; 1529a34c753fSRafael Auler printColored(outs(), Perc, 5, 10); 1530a34c753fSRafael Auler } 1531a34c753fSRafael Auler outs() << "\n"; 1532def464aaSAmir Ayupov if (Perc > 10.0f) 1533a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1534a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1535a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1536a34c753fSRafael Auler "performance.\n\n"; 1537a34c753fSRafael Auler 1538a34c753fSRafael Auler outs() << "PERF2BOLT: out of range traces involving unknown regions: " 1539a34c753fSRafael Auler << NumLongRangeTraces; 1540def464aaSAmir Ayupov if (NumTraces > 0) 1541a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces); 1542a34c753fSRafael Auler outs() << "\n"; 1543a34c753fSRafael Auler 1544a34c753fSRafael Auler if (NumColdSamples > 0) { 1545a34c753fSRafael Auler const float ColdSamples = NumColdSamples * 100.0f / NumTotalSamples; 1546a34c753fSRafael Auler outs() << "PERF2BOLT: " << NumColdSamples 1547a34c753fSRafael Auler << format(" (%.1f%%)", ColdSamples) 1548a34c753fSRafael Auler << " samples recorded in cold regions of split functions.\n"; 1549def464aaSAmir Ayupov if (ColdSamples > 5.0f) 1550a34c753fSRafael Auler outs() 1551a34c753fSRafael Auler << "WARNING: The BOLT-processed binary where samples were collected " 1552a34c753fSRafael Auler "likely used bad data or your service observed a large shift in " 1553a34c753fSRafael Auler "profile. You may want to audit this.\n"; 1554a34c753fSRafael Auler } 1555a34c753fSRafael Auler 1556a34c753fSRafael Auler return std::error_code(); 1557a34c753fSRafael Auler } 1558a34c753fSRafael Auler 1559a34c753fSRafael Auler void DataAggregator::processBranchEvents() { 1560a34c753fSRafael Auler outs() << "PERF2BOLT: processing branch events...\n"; 1561a34c753fSRafael Auler NamedRegionTimer T("processBranch", "Processing branch events", 1562a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1563a34c753fSRafael Auler 1564a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) { 1565a34c753fSRafael Auler const Trace &Loc = AggrLBR.first; 1566a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second; 1567a34c753fSRafael Auler LBREntry First{Loc.From, Loc.From, false}; 1568a34c753fSRafael Auler LBREntry Second{Loc.To, Loc.To, false}; 1569def464aaSAmir Ayupov if (Info.InternCount) 1570a34c753fSRafael Auler doTrace(First, Second, Info.InternCount); 1571a34c753fSRafael Auler if (Info.ExternCount) { 1572a34c753fSRafael Auler First.From = 0; 1573a34c753fSRafael Auler doTrace(First, Second, Info.ExternCount); 1574a34c753fSRafael Auler } 1575a34c753fSRafael Auler } 1576a34c753fSRafael Auler 1577a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) { 1578a34c753fSRafael Auler const Trace &Loc = AggrLBR.first; 1579a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second; 1580a34c753fSRafael Auler doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount); 1581a34c753fSRafael Auler } 1582a34c753fSRafael Auler } 1583a34c753fSRafael Auler 1584a34c753fSRafael Auler std::error_code DataAggregator::parseBasicEvents() { 1585a34c753fSRafael Auler outs() << "PERF2BOLT: parsing basic events (without LBR)...\n"; 1586a34c753fSRafael Auler NamedRegionTimer T("parseBasic", "Parsing basic events", TimerGroupName, 1587a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1588a34c753fSRafael Auler while (hasData()) { 1589a34c753fSRafael Auler ErrorOr<PerfBasicSample> Sample = parseBasicSample(); 1590a34c753fSRafael Auler if (std::error_code EC = Sample.getError()) 1591a34c753fSRafael Auler return EC; 1592a34c753fSRafael Auler 1593a34c753fSRafael Auler if (!Sample->PC) 1594a34c753fSRafael Auler continue; 1595a34c753fSRafael Auler 1596a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC)) 1597a34c753fSRafael Auler BF->setHasProfileAvailable(); 1598a34c753fSRafael Auler 1599a34c753fSRafael Auler ++BasicSamples[Sample->PC]; 1600a34c753fSRafael Auler EventNames.insert(Sample->EventName); 1601a34c753fSRafael Auler } 1602a34c753fSRafael Auler 1603a34c753fSRafael Auler return std::error_code(); 1604a34c753fSRafael Auler } 1605a34c753fSRafael Auler 1606a34c753fSRafael Auler void DataAggregator::processBasicEvents() { 1607a34c753fSRafael Auler outs() << "PERF2BOLT: processing basic events (without LBR)...\n"; 160840c2e0faSMaksim Panchenko NamedRegionTimer T("processBasic", "Processing basic events", TimerGroupName, 160940c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator); 1610a34c753fSRafael Auler uint64_t OutOfRangeSamples = 0; 1611a34c753fSRafael Auler uint64_t NumSamples = 0; 1612a34c753fSRafael Auler for (auto &Sample : BasicSamples) { 1613a34c753fSRafael Auler const uint64_t PC = Sample.first; 1614a34c753fSRafael Auler const uint64_t HitCount = Sample.second; 1615a34c753fSRafael Auler NumSamples += HitCount; 1616a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC); 1617a34c753fSRafael Auler if (!Func) { 1618a34c753fSRafael Auler OutOfRangeSamples += HitCount; 1619a34c753fSRafael Auler continue; 1620a34c753fSRafael Auler } 1621a34c753fSRafael Auler 1622a34c753fSRafael Auler doSample(*Func, PC, HitCount); 1623a34c753fSRafael Auler } 1624a34c753fSRafael Auler outs() << "PERF2BOLT: read " << NumSamples << " samples\n"; 1625a34c753fSRafael Auler 1626a34c753fSRafael Auler outs() << "PERF2BOLT: out of range samples recorded in unknown regions: " 1627a34c753fSRafael Auler << OutOfRangeSamples; 1628a34c753fSRafael Auler float Perc = 0.0f; 1629a34c753fSRafael Auler if (NumSamples > 0) { 1630a34c753fSRafael Auler outs() << " ("; 1631a34c753fSRafael Auler Perc = OutOfRangeSamples * 100.0f / NumSamples; 1632a34c753fSRafael Auler if (outs().has_colors()) { 1633def464aaSAmir Ayupov if (Perc > 60.0f) 1634a34c753fSRafael Auler outs().changeColor(raw_ostream::RED); 1635def464aaSAmir Ayupov else if (Perc > 40.0f) 1636a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW); 1637def464aaSAmir Ayupov else 1638a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN); 1639a34c753fSRafael Auler } 1640a34c753fSRafael Auler outs() << format("%.1f%%", Perc); 1641a34c753fSRafael Auler if (outs().has_colors()) 1642a34c753fSRafael Auler outs().resetColor(); 1643a34c753fSRafael Auler outs() << ")"; 1644a34c753fSRafael Auler } 1645a34c753fSRafael Auler outs() << "\n"; 1646def464aaSAmir Ayupov if (Perc > 80.0f) 1647a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1648a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1649a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1650a34c753fSRafael Auler "performance.\n\n"; 1651a34c753fSRafael Auler } 1652a34c753fSRafael Auler 1653a34c753fSRafael Auler std::error_code DataAggregator::parseMemEvents() { 1654a34c753fSRafael Auler outs() << "PERF2BOLT: parsing memory events...\n"; 1655a34c753fSRafael Auler NamedRegionTimer T("parseMemEvents", "Parsing mem events", TimerGroupName, 1656a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1657a34c753fSRafael Auler while (hasData()) { 1658a34c753fSRafael Auler ErrorOr<PerfMemSample> Sample = parseMemSample(); 1659a34c753fSRafael Auler if (std::error_code EC = Sample.getError()) 1660a34c753fSRafael Auler return EC; 1661a34c753fSRafael Auler 1662a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC)) 1663a34c753fSRafael Auler BF->setHasProfileAvailable(); 1664a34c753fSRafael Auler 1665a34c753fSRafael Auler MemSamples.emplace_back(std::move(Sample.get())); 1666a34c753fSRafael Auler } 1667a34c753fSRafael Auler 1668a34c753fSRafael Auler return std::error_code(); 1669a34c753fSRafael Auler } 1670a34c753fSRafael Auler 1671a34c753fSRafael Auler void DataAggregator::processMemEvents() { 1672a34c753fSRafael Auler NamedRegionTimer T("ProcessMemEvents", "Processing mem events", 1673a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1674a34c753fSRafael Auler for (const PerfMemSample &Sample : MemSamples) { 1675a34c753fSRafael Auler uint64_t PC = Sample.PC; 1676a34c753fSRafael Auler uint64_t Addr = Sample.Addr; 1677a34c753fSRafael Auler StringRef FuncName; 1678a34c753fSRafael Auler StringRef MemName; 1679a34c753fSRafael Auler 1680a34c753fSRafael Auler // Try to resolve symbol for PC 1681a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC); 1682a34c753fSRafael Auler if (!Func) { 1683a34c753fSRafael Auler LLVM_DEBUG(if (PC != 0) { 16844a7966eaSAmir Ayupov dbgs() << formatv("Skipped mem event: {0:x} => {1:x}\n", PC, Addr); 1685a34c753fSRafael Auler }); 1686a34c753fSRafael Auler continue; 1687a34c753fSRafael Auler } 1688a34c753fSRafael Auler 1689a34c753fSRafael Auler FuncName = Func->getOneName(); 1690a34c753fSRafael Auler PC -= Func->getAddress(); 1691a34c753fSRafael Auler 1692a34c753fSRafael Auler // Try to resolve symbol for memory load 1693a34c753fSRafael Auler if (BinaryData *BD = BC->getBinaryDataContainingAddress(Addr)) { 1694a34c753fSRafael Auler MemName = BD->getName(); 1695a34c753fSRafael Auler Addr -= BD->getAddress(); 1696a34c753fSRafael Auler } else if (opts::FilterMemProfile) { 1697a34c753fSRafael Auler // Filter out heap/stack accesses 1698a34c753fSRafael Auler continue; 1699a34c753fSRafael Auler } 1700a34c753fSRafael Auler 1701a34c753fSRafael Auler const Location FuncLoc(!FuncName.empty(), FuncName, PC); 1702a34c753fSRafael Auler const Location AddrLoc(!MemName.empty(), MemName, Addr); 1703a34c753fSRafael Auler 1704a34c753fSRafael Auler FuncMemData *MemData = &NamesToMemEvents[FuncName]; 170573b89e3fSMaksim Panchenko MemData->Name = FuncName; 1706a34c753fSRafael Auler setMemData(*Func, MemData); 1707a34c753fSRafael Auler MemData->update(FuncLoc, AddrLoc); 1708a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Mem event: " << FuncLoc << " = " << AddrLoc << "\n"); 1709a34c753fSRafael Auler } 1710a34c753fSRafael Auler } 1711a34c753fSRafael Auler 1712a34c753fSRafael Auler std::error_code DataAggregator::parsePreAggregatedLBRSamples() { 1713a34c753fSRafael Auler outs() << "PERF2BOLT: parsing pre-aggregated profile...\n"; 1714a34c753fSRafael Auler NamedRegionTimer T("parseAggregated", "Parsing aggregated branch events", 1715a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1716a34c753fSRafael Auler while (hasData()) { 1717a34c753fSRafael Auler ErrorOr<AggregatedLBREntry> AggrEntry = parseAggregatedLBREntry(); 1718a34c753fSRafael Auler if (std::error_code EC = AggrEntry.getError()) 1719a34c753fSRafael Auler return EC; 1720a34c753fSRafael Auler 1721*d796f36fSAmir Ayupov for (const uint64_t Addr : {AggrEntry->From.Offset, AggrEntry->To.Offset}) 1722*d796f36fSAmir Ayupov if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Addr)) 1723a34c753fSRafael Auler BF->setHasProfileAvailable(); 1724a34c753fSRafael Auler 1725a34c753fSRafael Auler AggregatedLBRs.emplace_back(std::move(AggrEntry.get())); 1726a34c753fSRafael Auler } 1727a34c753fSRafael Auler 1728a34c753fSRafael Auler return std::error_code(); 1729a34c753fSRafael Auler } 1730a34c753fSRafael Auler 1731a34c753fSRafael Auler void DataAggregator::processPreAggregated() { 1732a34c753fSRafael Auler outs() << "PERF2BOLT: processing pre-aggregated profile...\n"; 1733a34c753fSRafael Auler NamedRegionTimer T("processAggregated", "Processing aggregated branch events", 1734a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1735a34c753fSRafael Auler 1736a34c753fSRafael Auler uint64_t NumTraces = 0; 1737a34c753fSRafael Auler for (const AggregatedLBREntry &AggrEntry : AggregatedLBRs) { 1738a34c753fSRafael Auler switch (AggrEntry.EntryType) { 1739a34c753fSRafael Auler case AggregatedLBREntry::BRANCH: 1740a34c753fSRafael Auler doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count, 1741a34c753fSRafael Auler AggrEntry.Mispreds); 1742a34c753fSRafael Auler break; 1743a34c753fSRafael Auler case AggregatedLBREntry::FT: 1744a34c753fSRafael Auler case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: { 1745a34c753fSRafael Auler LBREntry First{AggrEntry.EntryType == AggregatedLBREntry::FT 1746a34c753fSRafael Auler ? AggrEntry.From.Offset 1747a34c753fSRafael Auler : 0, 1748a34c753fSRafael Auler AggrEntry.From.Offset, false}; 1749a34c753fSRafael Auler LBREntry Second{AggrEntry.To.Offset, AggrEntry.To.Offset, false}; 1750a34c753fSRafael Auler doTrace(First, Second, AggrEntry.Count); 1751a34c753fSRafael Auler NumTraces += AggrEntry.Count; 1752a34c753fSRafael Auler break; 1753a34c753fSRafael Auler } 1754a34c753fSRafael Auler } 1755a34c753fSRafael Auler } 1756a34c753fSRafael Auler 1757a34c753fSRafael Auler outs() << "PERF2BOLT: read " << AggregatedLBRs.size() 1758a34c753fSRafael Auler << " aggregated LBR entries\n"; 1759a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: " 1760a34c753fSRafael Auler << NumInvalidTraces; 1761a34c753fSRafael Auler float Perc = 0.0f; 1762a34c753fSRafael Auler if (NumTraces > 0) { 1763a34c753fSRafael Auler outs() << " ("; 1764a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces; 1765a34c753fSRafael Auler if (outs().has_colors()) { 1766def464aaSAmir Ayupov if (Perc > 10.0f) 1767a34c753fSRafael Auler outs().changeColor(raw_ostream::RED); 1768def464aaSAmir Ayupov else if (Perc > 5.0f) 1769a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW); 1770def464aaSAmir Ayupov else 1771a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN); 1772a34c753fSRafael Auler } 1773a34c753fSRafael Auler outs() << format("%.1f%%", Perc); 1774a34c753fSRafael Auler if (outs().has_colors()) 1775a34c753fSRafael Auler outs().resetColor(); 1776a34c753fSRafael Auler outs() << ")"; 1777a34c753fSRafael Auler } 1778a34c753fSRafael Auler outs() << "\n"; 1779def464aaSAmir Ayupov if (Perc > 10.0f) 1780a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1781a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1782a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1783a34c753fSRafael Auler "performance.\n\n"; 1784a34c753fSRafael Auler 1785a34c753fSRafael Auler outs() << "PERF2BOLT: Out of range traces involving unknown regions: " 1786a34c753fSRafael Auler << NumLongRangeTraces; 1787def464aaSAmir Ayupov if (NumTraces > 0) 1788a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces); 1789a34c753fSRafael Auler outs() << "\n"; 1790a34c753fSRafael Auler } 1791a34c753fSRafael Auler 1792835a9c28SAmir Ayupov std::optional<int32_t> DataAggregator::parseCommExecEvent() { 1793a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1794a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1795a34c753fSRafael Auler reportError("expected rest of line"); 1796a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1797e324a80fSKazu Hirata return std::nullopt; 1798a34c753fSRafael Auler } 1799a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1800a34c753fSRafael Auler 1801a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_COMM exec"); 1802def464aaSAmir Ayupov if (Pos == StringRef::npos) 1803e324a80fSKazu Hirata return std::nullopt; 1804a34c753fSRafael Auler Line = Line.drop_front(Pos); 1805a34c753fSRafael Auler 1806a34c753fSRafael Auler // Line: 1807a34c753fSRafael Auler // PERF_RECORD_COMM exec: <name>:<pid>/<tid>" 1808a34c753fSRafael Auler StringRef PIDStr = Line.rsplit(':').second.split('/').first; 1809ae585be1SRafael Auler int32_t PID; 1810a34c753fSRafael Auler if (PIDStr.getAsInteger(10, PID)) { 1811a34c753fSRafael Auler reportError("expected PID"); 1812a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n"; 1813e324a80fSKazu Hirata return std::nullopt; 1814a34c753fSRafael Auler } 1815a34c753fSRafael Auler 1816a34c753fSRafael Auler return PID; 1817a34c753fSRafael Auler } 1818a34c753fSRafael Auler 1819a34c753fSRafael Auler namespace { 1820835a9c28SAmir Ayupov std::optional<uint64_t> parsePerfTime(const StringRef TimeStr) { 1821a34c753fSRafael Auler const StringRef SecTimeStr = TimeStr.split('.').first; 1822a34c753fSRafael Auler const StringRef USecTimeStr = TimeStr.split('.').second; 1823a34c753fSRafael Auler uint64_t SecTime; 1824a34c753fSRafael Auler uint64_t USecTime; 1825a34c753fSRafael Auler if (SecTimeStr.getAsInteger(10, SecTime) || 1826def464aaSAmir Ayupov USecTimeStr.getAsInteger(10, USecTime)) 1827e324a80fSKazu Hirata return std::nullopt; 1828a34c753fSRafael Auler return SecTime * 1000000ULL + USecTime; 1829a34c753fSRafael Auler } 1830a34c753fSRafael Auler } 1831a34c753fSRafael Auler 1832835a9c28SAmir Ayupov std::optional<DataAggregator::ForkInfo> DataAggregator::parseForkEvent() { 183340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 183440c2e0faSMaksim Panchenko } 1835a34c753fSRafael Auler 1836a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1837a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1838a34c753fSRafael Auler reportError("expected rest of line"); 1839a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1840e324a80fSKazu Hirata return std::nullopt; 1841a34c753fSRafael Auler } 1842a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1843a34c753fSRafael Auler 1844a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_FORK"); 1845a34c753fSRafael Auler if (Pos == StringRef::npos) { 1846a34c753fSRafael Auler consumeRestOfLine(); 1847e324a80fSKazu Hirata return std::nullopt; 1848a34c753fSRafael Auler } 1849a34c753fSRafael Auler 1850a34c753fSRafael Auler ForkInfo FI; 1851a34c753fSRafael Auler 1852a34c753fSRafael Auler const StringRef TimeStr = 1853a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second; 1854835a9c28SAmir Ayupov if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) { 1855a34c753fSRafael Auler FI.Time = *TimeRes; 1856a34c753fSRafael Auler } 1857a34c753fSRafael Auler 1858a34c753fSRafael Auler Line = Line.drop_front(Pos); 1859a34c753fSRafael Auler 1860a34c753fSRafael Auler // Line: 1861a34c753fSRafael Auler // PERF_RECORD_FORK(<child_pid>:<child_tid>):(<parent_pid>:<parent_tid>) 1862a34c753fSRafael Auler const StringRef ChildPIDStr = Line.split('(').second.split(':').first; 1863a34c753fSRafael Auler if (ChildPIDStr.getAsInteger(10, FI.ChildPID)) { 1864a34c753fSRafael Auler reportError("expected PID"); 1865a34c753fSRafael Auler Diag << "Found: " << ChildPIDStr << "in '" << Line << "'\n"; 1866e324a80fSKazu Hirata return std::nullopt; 1867a34c753fSRafael Auler } 1868a34c753fSRafael Auler 1869a34c753fSRafael Auler const StringRef ParentPIDStr = Line.rsplit('(').second.split(':').first; 1870a34c753fSRafael Auler if (ParentPIDStr.getAsInteger(10, FI.ParentPID)) { 1871a34c753fSRafael Auler reportError("expected PID"); 1872a34c753fSRafael Auler Diag << "Found: " << ParentPIDStr << "in '" << Line << "'\n"; 1873e324a80fSKazu Hirata return std::nullopt; 1874a34c753fSRafael Auler } 1875a34c753fSRafael Auler 1876a34c753fSRafael Auler consumeRestOfLine(); 1877a34c753fSRafael Auler 1878a34c753fSRafael Auler return FI; 1879a34c753fSRafael Auler } 1880a34c753fSRafael Auler 1881a34c753fSRafael Auler ErrorOr<std::pair<StringRef, DataAggregator::MMapInfo>> 1882a34c753fSRafael Auler DataAggregator::parseMMapEvent() { 188340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 188440c2e0faSMaksim Panchenko } 1885a34c753fSRafael Auler 1886a34c753fSRafael Auler MMapInfo ParsedInfo; 1887a34c753fSRafael Auler 1888a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1889a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1890a34c753fSRafael Auler reportError("expected rest of line"); 1891a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1892a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1893a34c753fSRafael Auler } 1894a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1895a34c753fSRafael Auler 1896a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_MMAP2"); 1897a34c753fSRafael Auler if (Pos == StringRef::npos) { 1898a34c753fSRafael Auler consumeRestOfLine(); 1899a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo); 1900a34c753fSRafael Auler } 1901a34c753fSRafael Auler 1902a34c753fSRafael Auler // Line: 1903a34c753fSRafael Auler // {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name> 1904a34c753fSRafael Auler 1905a34c753fSRafael Auler const StringRef TimeStr = 1906a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second; 1907835a9c28SAmir Ayupov if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) 1908a34c753fSRafael Auler ParsedInfo.Time = *TimeRes; 1909a34c753fSRafael Auler 1910a34c753fSRafael Auler Line = Line.drop_front(Pos); 1911a34c753fSRafael Auler 1912a34c753fSRafael Auler // Line: 1913a34c753fSRafael Auler // PERF_RECORD_MMAP2 <pid>/<tid>: [<hexbase>(<hexsize>) .*]: .* <file_name> 1914a34c753fSRafael Auler 1915a34c753fSRafael Auler StringRef FileName = Line.rsplit(FieldSeparator).second; 1916a34c753fSRafael Auler if (FileName.startswith("//") || FileName.startswith("[")) { 1917a34c753fSRafael Auler consumeRestOfLine(); 1918a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo); 1919a34c753fSRafael Auler } 1920a34c753fSRafael Auler FileName = sys::path::filename(FileName); 1921a34c753fSRafael Auler 1922a34c753fSRafael Auler const StringRef PIDStr = Line.split(FieldSeparator).second.split('/').first; 1923a34c753fSRafael Auler if (PIDStr.getAsInteger(10, ParsedInfo.PID)) { 1924a34c753fSRafael Auler reportError("expected PID"); 1925a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n"; 1926a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1927a34c753fSRafael Auler } 1928a34c753fSRafael Auler 1929a34c753fSRafael Auler const StringRef BaseAddressStr = Line.split('[').second.split('(').first; 193077b75ca5SMaksim Panchenko if (BaseAddressStr.getAsInteger(0, ParsedInfo.MMapAddress)) { 1931a34c753fSRafael Auler reportError("expected base address"); 1932a34c753fSRafael Auler Diag << "Found: " << BaseAddressStr << "in '" << Line << "'\n"; 1933a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1934a34c753fSRafael Auler } 1935a34c753fSRafael Auler 1936a34c753fSRafael Auler const StringRef SizeStr = Line.split('(').second.split(')').first; 1937a34c753fSRafael Auler if (SizeStr.getAsInteger(0, ParsedInfo.Size)) { 1938a34c753fSRafael Auler reportError("expected mmaped size"); 1939a34c753fSRafael Auler Diag << "Found: " << SizeStr << "in '" << Line << "'\n"; 1940a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1941a34c753fSRafael Auler } 1942a34c753fSRafael Auler 1943a34c753fSRafael Auler const StringRef OffsetStr = 1944a34c753fSRafael Auler Line.split('@').second.ltrim().split(FieldSeparator).first; 1945a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, ParsedInfo.Offset)) { 1946a34c753fSRafael Auler reportError("expected mmaped page-aligned offset"); 1947a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "in '" << Line << "'\n"; 1948a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1949a34c753fSRafael Auler } 1950a34c753fSRafael Auler 1951a34c753fSRafael Auler consumeRestOfLine(); 1952a34c753fSRafael Auler 1953a34c753fSRafael Auler return std::make_pair(FileName, ParsedInfo); 1954a34c753fSRafael Auler } 1955a34c753fSRafael Auler 1956a34c753fSRafael Auler std::error_code DataAggregator::parseMMapEvents() { 1957a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script mmap events output\n"; 1958a34c753fSRafael Auler NamedRegionTimer T("parseMMapEvents", "Parsing mmap events", TimerGroupName, 1959a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1960a34c753fSRafael Auler 1961a34c753fSRafael Auler std::multimap<StringRef, MMapInfo> GlobalMMapInfo; 1962a34c753fSRafael Auler while (hasData()) { 1963a34c753fSRafael Auler ErrorOr<std::pair<StringRef, MMapInfo>> FileMMapInfoRes = parseMMapEvent(); 1964a34c753fSRafael Auler if (std::error_code EC = FileMMapInfoRes.getError()) 1965a34c753fSRafael Auler return EC; 1966a34c753fSRafael Auler 1967a34c753fSRafael Auler std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get(); 1968a34c753fSRafael Auler if (FileMMapInfo.second.PID == -1) 1969a34c753fSRafael Auler continue; 1970a478a091SAmir Ayupov if (FileMMapInfo.first.equals("(deleted)")) 1971a478a091SAmir Ayupov continue; 1972a34c753fSRafael Auler 1973a34c753fSRafael Auler // Consider only the first mapping of the file for any given PID 1974a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(FileMMapInfo.first); 197517f3cbe3SAmir Ayupov bool PIDExists = llvm::any_of(make_range(Range), [&](const auto &MI) { 197617f3cbe3SAmir Ayupov return MI.second.PID == FileMMapInfo.second.PID; 197717f3cbe3SAmir Ayupov }); 197817f3cbe3SAmir Ayupov 1979a34c753fSRafael Auler if (PIDExists) 1980a34c753fSRafael Auler continue; 1981a34c753fSRafael Auler 1982a34c753fSRafael Auler GlobalMMapInfo.insert(FileMMapInfo); 1983a34c753fSRafael Auler } 1984a34c753fSRafael Auler 1985a34c753fSRafael Auler LLVM_DEBUG({ 1986a34c753fSRafael Auler dbgs() << "FileName -> mmap info:\n"; 1987def464aaSAmir Ayupov for (const std::pair<const StringRef, MMapInfo> &Pair : GlobalMMapInfo) 1988a34c753fSRafael Auler dbgs() << " " << Pair.first << " : " << Pair.second.PID << " [0x" 198977b75ca5SMaksim Panchenko << Twine::utohexstr(Pair.second.MMapAddress) << ", " 1990a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Size) << " @ " 1991a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Offset) << "]\n"; 1992a34c753fSRafael Auler }); 1993a34c753fSRafael Auler 1994a34c753fSRafael Auler StringRef NameToUse = llvm::sys::path::filename(BC->getFilename()); 1995a34c753fSRafael Auler if (GlobalMMapInfo.count(NameToUse) == 0 && !BuildIDBinaryName.empty()) { 1996a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using \"" << BuildIDBinaryName 1997a34c753fSRafael Auler << "\" for profile matching\n"; 1998a34c753fSRafael Auler NameToUse = BuildIDBinaryName; 1999a34c753fSRafael Auler } 2000a34c753fSRafael Auler 2001a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(NameToUse); 200217f3cbe3SAmir Ayupov for (MMapInfo &MMapInfo : llvm::make_second_range(make_range(Range))) { 200377b75ca5SMaksim Panchenko if (BC->HasFixedLoadAddress && MMapInfo.MMapAddress) { 2004a34c753fSRafael Auler // Check that the binary mapping matches one of the segments. 2005f119a248SAmir Ayupov bool MatchFound = llvm::any_of( 2006f119a248SAmir Ayupov llvm::make_second_range(BC->SegmentMapInfo), 2007f119a248SAmir Ayupov [&](SegmentInfo &SegInfo) { 200877b75ca5SMaksim Panchenko // The mapping is page-aligned and hence the MMapAddress could be 2009a34c753fSRafael Auler // different from the segment start address. We cannot know the page 2010a34c753fSRafael Auler // size of the mapping, but we know it should not exceed the segment 2011a34c753fSRafael Auler // alignment value. Hence we are performing an approximate check. 2012f119a248SAmir Ayupov return SegInfo.Address >= MMapInfo.MMapAddress && 2013f119a248SAmir Ayupov SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment; 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; 203377b75ca5SMaksim Panchenko } else { 203477b75ca5SMaksim Panchenko MMapInfo.BaseAddress = *BaseAddress; 203577b75ca5SMaksim Panchenko } 203677b75ca5SMaksim Panchenko } 203777b75ca5SMaksim Panchenko 2038a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)); 2039a34c753fSRafael Auler } 2040a34c753fSRafael Auler 2041a34c753fSRafael Auler if (BinaryMMapInfo.empty()) { 2042a34c753fSRafael Auler if (errs().has_colors()) 2043a34c753fSRafael Auler errs().changeColor(raw_ostream::RED); 2044a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching binary \"" 2045a34c753fSRafael Auler << BC->getFilename() << "\"."; 2046a34c753fSRafael Auler if (!GlobalMMapInfo.empty()) { 2047a34c753fSRafael Auler errs() << " Profile for the following binary name(s) is available:\n"; 2048a34c753fSRafael Auler for (auto I = GlobalMMapInfo.begin(), IE = GlobalMMapInfo.end(); I != IE; 2049def464aaSAmir Ayupov I = GlobalMMapInfo.upper_bound(I->first)) 2050a34c753fSRafael Auler errs() << " " << I->first << '\n'; 2051a34c753fSRafael Auler errs() << "Please rename the input binary.\n"; 2052a34c753fSRafael Auler } else { 2053a34c753fSRafael Auler errs() << " Failed to extract any binary name from a profile.\n"; 2054a34c753fSRafael Auler } 2055a34c753fSRafael Auler if (errs().has_colors()) 2056a34c753fSRafael Auler errs().resetColor(); 2057a34c753fSRafael Auler 2058a34c753fSRafael Auler exit(1); 2059a34c753fSRafael Auler } 2060a34c753fSRafael Auler 2061a34c753fSRafael Auler return std::error_code(); 2062a34c753fSRafael Auler } 2063a34c753fSRafael Auler 2064a34c753fSRafael Auler std::error_code DataAggregator::parseTaskEvents() { 2065a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script task events output\n"; 2066a34c753fSRafael Auler NamedRegionTimer T("parseTaskEvents", "Parsing task events", TimerGroupName, 2067a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 2068a34c753fSRafael Auler 2069a34c753fSRafael Auler while (hasData()) { 2070835a9c28SAmir Ayupov if (std::optional<int32_t> CommInfo = parseCommExecEvent()) { 2071a34c753fSRafael Auler // Remove forked child that ran execve 2072a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*CommInfo); 2073def464aaSAmir Ayupov if (MMapInfoIter != BinaryMMapInfo.end() && MMapInfoIter->second.Forked) 2074a34c753fSRafael Auler BinaryMMapInfo.erase(MMapInfoIter); 2075a34c753fSRafael Auler consumeRestOfLine(); 2076a34c753fSRafael Auler continue; 2077a34c753fSRafael Auler } 2078a34c753fSRafael Auler 2079835a9c28SAmir Ayupov std::optional<ForkInfo> ForkInfo = parseForkEvent(); 2080a34c753fSRafael Auler if (!ForkInfo) 2081a34c753fSRafael Auler continue; 2082a34c753fSRafael Auler 2083a34c753fSRafael Auler if (ForkInfo->ParentPID == ForkInfo->ChildPID) 2084a34c753fSRafael Auler continue; 2085a34c753fSRafael Auler 2086a34c753fSRafael Auler if (ForkInfo->Time == 0) { 2087a34c753fSRafael Auler // Process was forked and mmaped before perf ran. In this case the child 2088a34c753fSRafael Auler // should have its own mmap entry unless it was execve'd. 2089a34c753fSRafael Auler continue; 2090a34c753fSRafael Auler } 2091a34c753fSRafael Auler 2092a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(ForkInfo->ParentPID); 2093a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) 2094a34c753fSRafael Auler continue; 2095a34c753fSRafael Auler 2096a34c753fSRafael Auler MMapInfo MMapInfo = MMapInfoIter->second; 2097a34c753fSRafael Auler MMapInfo.PID = ForkInfo->ChildPID; 2098a34c753fSRafael Auler MMapInfo.Forked = true; 2099a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)); 2100a34c753fSRafael Auler } 2101a34c753fSRafael Auler 2102a34c753fSRafael Auler outs() << "PERF2BOLT: input binary is associated with " 2103a34c753fSRafael Auler << BinaryMMapInfo.size() << " PID(s)\n"; 2104a34c753fSRafael Auler 2105a34c753fSRafael Auler LLVM_DEBUG({ 21064a7966eaSAmir Ayupov for (const MMapInfo &MMI : llvm::make_second_range(BinaryMMapInfo)) 21074a7966eaSAmir Ayupov outs() << formatv(" {0}{1}: ({2:x}: {3:x})\n", MMI.PID, 21084a7966eaSAmir Ayupov (MMI.Forked ? " (forked)" : ""), MMI.MMapAddress, 21094a7966eaSAmir Ayupov MMI.Size); 2110a34c753fSRafael Auler }); 2111a34c753fSRafael Auler 2112a34c753fSRafael Auler return std::error_code(); 2113a34c753fSRafael Auler } 2114a34c753fSRafael Auler 2115835a9c28SAmir Ayupov std::optional<std::pair<StringRef, StringRef>> 2116a34c753fSRafael Auler DataAggregator::parseNameBuildIDPair() { 211740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 211840c2e0faSMaksim Panchenko } 2119a34c753fSRafael Auler 2120a34c753fSRafael Auler ErrorOr<StringRef> BuildIDStr = parseString(FieldSeparator, true); 2121a34c753fSRafael Auler if (std::error_code EC = BuildIDStr.getError()) 2122e324a80fSKazu Hirata return std::nullopt; 2123a34c753fSRafael Auler 2124661577b5SMaksim Panchenko // If one of the strings is missing, don't issue a parsing error, but still 2125661577b5SMaksim Panchenko // do not return a value. 2126e549ac07SRafael Auler consumeAllRemainingFS(); 2127ba9cc653SRafael Auler if (checkNewLine()) 2128e324a80fSKazu Hirata return std::nullopt; 2129661577b5SMaksim Panchenko 2130a34c753fSRafael Auler ErrorOr<StringRef> NameStr = parseString(FieldSeparator, true); 2131a34c753fSRafael Auler if (std::error_code EC = NameStr.getError()) 2132e324a80fSKazu Hirata return std::nullopt; 2133a34c753fSRafael Auler 2134a34c753fSRafael Auler consumeRestOfLine(); 2135a34c753fSRafael Auler return std::make_pair(NameStr.get(), BuildIDStr.get()); 2136a34c753fSRafael Auler } 2137a34c753fSRafael Auler 2138661577b5SMaksim Panchenko bool DataAggregator::hasAllBuildIDs() { 2139661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf; 2140661577b5SMaksim Panchenko 2141661577b5SMaksim Panchenko if (!hasData()) 2142661577b5SMaksim Panchenko return false; 2143661577b5SMaksim Panchenko 2144661577b5SMaksim Panchenko bool HasInvalidEntries = false; 2145661577b5SMaksim Panchenko while (hasData()) { 2146661577b5SMaksim Panchenko if (!parseNameBuildIDPair()) { 2147661577b5SMaksim Panchenko HasInvalidEntries = true; 2148661577b5SMaksim Panchenko break; 2149661577b5SMaksim Panchenko } 2150661577b5SMaksim Panchenko } 2151661577b5SMaksim Panchenko 2152661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf; 2153661577b5SMaksim Panchenko 2154661577b5SMaksim Panchenko return !HasInvalidEntries; 2155661577b5SMaksim Panchenko } 2156661577b5SMaksim Panchenko 2157835a9c28SAmir Ayupov std::optional<StringRef> 2158a34c753fSRafael Auler DataAggregator::getFileNameForBuildID(StringRef FileBuildID) { 2159661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf; 2160661577b5SMaksim Panchenko 2161661577b5SMaksim Panchenko StringRef FileName; 2162a34c753fSRafael Auler while (hasData()) { 2163835a9c28SAmir Ayupov std::optional<std::pair<StringRef, StringRef>> IDPair = 2164835a9c28SAmir Ayupov parseNameBuildIDPair(); 2165661577b5SMaksim Panchenko if (!IDPair) { 2166661577b5SMaksim Panchenko consumeRestOfLine(); 2167661577b5SMaksim Panchenko continue; 2168a34c753fSRafael Auler } 2169661577b5SMaksim Panchenko 2170661577b5SMaksim Panchenko if (IDPair->second.startswith(FileBuildID)) { 2171661577b5SMaksim Panchenko FileName = sys::path::filename(IDPair->first); 2172661577b5SMaksim Panchenko break; 2173661577b5SMaksim Panchenko } 2174661577b5SMaksim Panchenko } 2175661577b5SMaksim Panchenko 2176661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf; 2177661577b5SMaksim Panchenko 2178661577b5SMaksim Panchenko if (!FileName.empty()) 2179661577b5SMaksim Panchenko return FileName; 2180661577b5SMaksim Panchenko 2181e324a80fSKazu Hirata return std::nullopt; 2182a34c753fSRafael Auler } 2183a34c753fSRafael Auler 2184a34c753fSRafael Auler std::error_code 2185a34c753fSRafael Auler DataAggregator::writeAggregatedFile(StringRef OutputFilename) const { 2186a34c753fSRafael Auler std::error_code EC; 2187a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None); 2188a34c753fSRafael Auler if (EC) 2189a34c753fSRafael Auler return EC; 2190a34c753fSRafael Auler 2191a34c753fSRafael Auler bool WriteMemLocs = false; 2192a34c753fSRafael Auler 2193a34c753fSRafael Auler auto writeLocation = [&OutFile, &WriteMemLocs](const Location &Loc) { 2194a34c753fSRafael Auler if (WriteMemLocs) 2195a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "4 " : "3 "); 2196a34c753fSRafael Auler else 2197a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "1 " : "0 "); 2198a34c753fSRafael Auler OutFile << (Loc.Name.empty() ? "[unknown]" : getEscapedName(Loc.Name)) 2199a34c753fSRafael Auler << " " << Twine::utohexstr(Loc.Offset) << FieldSeparator; 2200a34c753fSRafael Auler }; 2201a34c753fSRafael Auler 2202a34c753fSRafael Auler uint64_t BranchValues = 0; 2203a34c753fSRafael Auler uint64_t MemValues = 0; 2204a34c753fSRafael Auler 2205a34c753fSRafael Auler if (BAT) 2206a34c753fSRafael Auler OutFile << "boltedcollection\n"; 2207a34c753fSRafael Auler if (opts::BasicAggregation) { 2208a34c753fSRafael Auler OutFile << "no_lbr"; 220934bcadc3SKazu Hirata for (const StringMapEntry<std::nullopt_t> &Entry : EventNames) 2210a34c753fSRafael Auler OutFile << " " << Entry.getKey(); 2211a34c753fSRafael Auler OutFile << "\n"; 2212a34c753fSRafael Auler 221373b89e3fSMaksim Panchenko for (const auto &KV : NamesToSamples) { 221473b89e3fSMaksim Panchenko const FuncSampleData &FSD = KV.second; 221573b89e3fSMaksim Panchenko for (const SampleInfo &SI : FSD.Data) { 2216a34c753fSRafael Auler writeLocation(SI.Loc); 2217a34c753fSRafael Auler OutFile << SI.Hits << "\n"; 2218a34c753fSRafael Auler ++BranchValues; 2219a34c753fSRafael Auler } 2220a34c753fSRafael Auler } 2221a34c753fSRafael Auler } else { 222273b89e3fSMaksim Panchenko for (const auto &KV : NamesToBranches) { 222373b89e3fSMaksim Panchenko const FuncBranchData &FBD = KV.second; 222473b89e3fSMaksim Panchenko for (const llvm::bolt::BranchInfo &BI : FBD.Data) { 2225a34c753fSRafael Auler writeLocation(BI.From); 2226a34c753fSRafael Auler writeLocation(BI.To); 2227a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n"; 2228a34c753fSRafael Auler ++BranchValues; 2229a34c753fSRafael Auler } 223073b89e3fSMaksim Panchenko for (const llvm::bolt::BranchInfo &BI : FBD.EntryData) { 2231a34c753fSRafael Auler // Do not output if source is a known symbol, since this was already 2232a34c753fSRafael Auler // accounted for in the source function 2233a34c753fSRafael Auler if (BI.From.IsSymbol) 2234a34c753fSRafael Auler continue; 2235a34c753fSRafael Auler writeLocation(BI.From); 2236a34c753fSRafael Auler writeLocation(BI.To); 2237a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n"; 2238a34c753fSRafael Auler ++BranchValues; 2239a34c753fSRafael Auler } 2240a34c753fSRafael Auler } 2241a34c753fSRafael Auler 2242a34c753fSRafael Auler WriteMemLocs = true; 224373b89e3fSMaksim Panchenko for (const auto &KV : NamesToMemEvents) { 224473b89e3fSMaksim Panchenko const FuncMemData &FMD = KV.second; 224573b89e3fSMaksim Panchenko for (const MemInfo &MemEvent : FMD.Data) { 2246a34c753fSRafael Auler writeLocation(MemEvent.Offset); 2247a34c753fSRafael Auler writeLocation(MemEvent.Addr); 2248a34c753fSRafael Auler OutFile << MemEvent.Count << "\n"; 2249a34c753fSRafael Auler ++MemValues; 2250a34c753fSRafael Auler } 2251a34c753fSRafael Auler } 2252a34c753fSRafael Auler } 2253a34c753fSRafael Auler 225440c2e0faSMaksim Panchenko outs() << "PERF2BOLT: wrote " << BranchValues << " objects and " << MemValues 225540c2e0faSMaksim Panchenko << " memory objects to " << OutputFilename << "\n"; 2256a34c753fSRafael Auler 2257a34c753fSRafael Auler return std::error_code(); 2258a34c753fSRafael Auler } 2259a34c753fSRafael Auler 226040c2e0faSMaksim Panchenko void DataAggregator::dump() const { DataReader::dump(); } 2261a34c753fSRafael Auler 2262a34c753fSRafael Auler void DataAggregator::dump(const LBREntry &LBR) const { 2263a34c753fSRafael Auler Diag << "From: " << Twine::utohexstr(LBR.From) 2264a34c753fSRafael Auler << " To: " << Twine::utohexstr(LBR.To) << " Mispred? " << LBR.Mispred 2265a34c753fSRafael Auler << "\n"; 2266a34c753fSRafael Auler } 2267a34c753fSRafael Auler 2268a34c753fSRafael Auler void DataAggregator::dump(const PerfBranchSample &Sample) const { 2269a34c753fSRafael Auler Diag << "Sample LBR entries: " << Sample.LBR.size() << "\n"; 2270def464aaSAmir Ayupov for (const LBREntry &LBR : Sample.LBR) 2271a34c753fSRafael Auler dump(LBR); 2272a34c753fSRafael Auler } 2273a34c753fSRafael Auler 2274a34c753fSRafael Auler void DataAggregator::dump(const PerfMemSample &Sample) const { 2275a34c753fSRafael Auler Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n"; 2276a34c753fSRafael Auler } 2277