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 202a34c753fSRafael Auler sys::Wait(TaskEventsPPI.PI, 1, false, &Error); 203a34c753fSRafael Auler sys::Wait(MMapEventsPPI.PI, 1, false, &Error); 204a34c753fSRafael Auler sys::Wait(MainEventsPPI.PI, 1, false, &Error); 205a34c753fSRafael Auler sys::Wait(MemEventsPPI.PI, 1, false, &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 219a34c753fSRafael Auler char *WritableArgsString = strdup(ArgsString); 220a34c753fSRafael Auler char *Str = WritableArgsString; 221a34c753fSRafael Auler do { 222a34c753fSRafael Auler Argv.push_back(Str); 223a34c753fSRafael Auler while (*Str && *Str != ' ') 224a34c753fSRafael Auler ++Str; 225a34c753fSRafael Auler if (!*Str) 226a34c753fSRafael Auler break; 227a34c753fSRafael Auler *Str++ = 0; 228a34c753fSRafael Auler } while (true); 229a34c753fSRafael Auler 230a34c753fSRafael Auler Argv.push_back("-f"); 231a34c753fSRafael Auler Argv.push_back("-i"); 232a34c753fSRafael Auler Argv.push_back(Filename.c_str()); 233a34c753fSRafael Auler 234a34c753fSRafael Auler if (std::error_code Errc = 235a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "out", PPI.StdoutPath)) { 23640c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StdoutPath 23740c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n"; 238a34c753fSRafael Auler exit(1); 239a34c753fSRafael Auler } 240a34c753fSRafael Auler TempFiles.push_back(PPI.StdoutPath.data()); 241a34c753fSRafael Auler 242a34c753fSRafael Auler if (std::error_code Errc = 243a34c753fSRafael Auler sys::fs::createTemporaryFile("perf.script", "err", PPI.StderrPath)) { 24440c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to create temporary file " << PPI.StderrPath 24540c2e0faSMaksim Panchenko << " with error " << Errc.message() << "\n"; 246a34c753fSRafael Auler exit(1); 247a34c753fSRafael Auler } 248a34c753fSRafael Auler TempFiles.push_back(PPI.StderrPath.data()); 249a34c753fSRafael Auler 2501028b165SKazu Hirata std::optional<StringRef> Redirects[] = { 251e324a80fSKazu Hirata std::nullopt, // Stdin 252a34c753fSRafael Auler StringRef(PPI.StdoutPath.data()), // Stdout 253a34c753fSRafael Auler StringRef(PPI.StderrPath.data())}; // Stderr 254a34c753fSRafael Auler 255a34c753fSRafael Auler LLVM_DEBUG({ 256a34c753fSRafael Auler dbgs() << "Launching perf: "; 257a34c753fSRafael Auler for (StringRef Arg : Argv) 258a34c753fSRafael Auler dbgs() << Arg << " "; 259a34c753fSRafael Auler dbgs() << " 1> " << PPI.StdoutPath.data() << " 2> " << PPI.StderrPath.data() 260a34c753fSRafael Auler << "\n"; 261a34c753fSRafael Auler }); 262a34c753fSRafael Auler 263def464aaSAmir Ayupov if (Wait) 264a34c753fSRafael Auler PPI.PI.ReturnCode = sys::ExecuteAndWait(PerfPath.data(), Argv, 265e324a80fSKazu Hirata /*envp*/ std::nullopt, Redirects); 266def464aaSAmir Ayupov else 267e324a80fSKazu Hirata PPI.PI = sys::ExecuteNoWait(PerfPath.data(), Argv, /*envp*/ std::nullopt, 268a34c753fSRafael Auler Redirects); 269a34c753fSRafael Auler 270a34c753fSRafael Auler free(WritableArgsString); 271a34c753fSRafael Auler } 272a34c753fSRafael Auler 273a34c753fSRafael Auler void DataAggregator::processFileBuildID(StringRef FileBuildID) { 274a34c753fSRafael Auler PerfProcessInfo BuildIDProcessInfo; 275a34c753fSRafael Auler launchPerfProcess("buildid list", 276a34c753fSRafael Auler BuildIDProcessInfo, 277a34c753fSRafael Auler "buildid-list", 278a34c753fSRafael Auler /*Wait = */true); 279a34c753fSRafael Auler 280a34c753fSRafael Auler if (BuildIDProcessInfo.PI.ReturnCode != 0) { 281a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 282a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StderrPath.data()); 283a34c753fSRafael Auler StringRef ErrBuf = (*MB)->getBuffer(); 284a34c753fSRafael Auler 285a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << BuildIDProcessInfo.PI.ReturnCode 286a34c753fSRafael Auler << '\n'; 287a34c753fSRafael Auler errs() << ErrBuf; 288a34c753fSRafael Auler return; 289a34c753fSRafael Auler } 290a34c753fSRafael Auler 291a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 292a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(BuildIDProcessInfo.StdoutPath.data()); 293a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 294a34c753fSRafael Auler errs() << "Cannot open " << BuildIDProcessInfo.StdoutPath.data() << ": " 295a34c753fSRafael Auler << EC.message() << "\n"; 296a34c753fSRafael Auler return; 297a34c753fSRafael Auler } 298a34c753fSRafael Auler 299d914486aSAmir Ayupov FileBuf = std::move(*MB); 300a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 301a34c753fSRafael Auler 302*835a9c28SAmir Ayupov std::optional<StringRef> FileName = getFileNameForBuildID(FileBuildID); 303a34c753fSRafael Auler if (!FileName) { 304661577b5SMaksim Panchenko if (hasAllBuildIDs()) { 305a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: failed to match build-id from perf output. " 306a34c753fSRafael Auler "This indicates the input binary supplied for data aggregation " 307a34c753fSRafael Auler "is not the same recorded by perf when collecting profiling " 308a34c753fSRafael Auler "data, or there were no samples recorded for the binary. " 309a34c753fSRafael Auler "Use -ignore-build-id option to override.\n"; 310def464aaSAmir Ayupov if (!opts::IgnoreBuildID) 311a34c753fSRafael Auler abort(); 312661577b5SMaksim Panchenko } else { 313661577b5SMaksim Panchenko errs() << "PERF2BOLT-WARNING: build-id will not be checked because perf " 314661577b5SMaksim Panchenko "data was recorded without it\n"; 315661577b5SMaksim Panchenko return; 316661577b5SMaksim Panchenko } 317a34c753fSRafael Auler } else if (*FileName != llvm::sys::path::filename(BC->getFilename())) { 318a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: build-id matched a different file name\n"; 319a34c753fSRafael Auler BuildIDBinaryName = std::string(*FileName); 320a34c753fSRafael Auler } else { 321a34c753fSRafael Auler outs() << "PERF2BOLT: matched build-id and file name\n"; 322a34c753fSRafael Auler } 323a34c753fSRafael Auler } 324a34c753fSRafael Auler 325a34c753fSRafael Auler bool DataAggregator::checkPerfDataMagic(StringRef FileName) { 326a34c753fSRafael Auler if (opts::ReadPreAggregated) 327a34c753fSRafael Auler return true; 328a34c753fSRafael Auler 329ae585be1SRafael Auler Expected<sys::fs::file_t> FD = sys::fs::openNativeFileForRead(FileName); 3300f915826SMaksim Panchenko if (!FD) { 3310f915826SMaksim Panchenko consumeError(FD.takeError()); 332a34c753fSRafael Auler return false; 3330f915826SMaksim Panchenko } 334a34c753fSRafael Auler 335a34c753fSRafael Auler char Buf[7] = {0, 0, 0, 0, 0, 0, 0}; 336a34c753fSRafael Auler 337ae585be1SRafael Auler auto Close = make_scope_exit([&] { sys::fs::closeFile(*FD); }); 338ae585be1SRafael Auler Expected<size_t> BytesRead = sys::fs::readNativeFileSlice( 339ae585be1SRafael Auler *FD, makeMutableArrayRef(Buf, sizeof(Buf)), 0); 3400f915826SMaksim Panchenko if (!BytesRead) { 3410f915826SMaksim Panchenko consumeError(BytesRead.takeError()); 3420f915826SMaksim Panchenko return false; 3430f915826SMaksim Panchenko } 3440f915826SMaksim Panchenko 3450f915826SMaksim Panchenko if (*BytesRead != 7) 346a34c753fSRafael Auler return false; 347a34c753fSRafael Auler 348a34c753fSRafael Auler if (strncmp(Buf, "PERFILE", 7) == 0) 349a34c753fSRafael Auler return true; 350a34c753fSRafael Auler return false; 351a34c753fSRafael Auler } 352a34c753fSRafael Auler 353a34c753fSRafael Auler void DataAggregator::parsePreAggregated() { 354a34c753fSRafael Auler std::string Error; 355a34c753fSRafael Auler 356a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 357a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Filename); 358a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 359a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: cannot open " << Filename << ": " 360a34c753fSRafael Auler << EC.message() << "\n"; 361a34c753fSRafael Auler exit(1); 362a34c753fSRafael Auler } 363a34c753fSRafael Auler 364d914486aSAmir Ayupov FileBuf = std::move(*MB); 365a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 366a34c753fSRafael Auler Col = 0; 367a34c753fSRafael Auler Line = 1; 368a34c753fSRafael Auler if (parsePreAggregatedLBRSamples()) { 369a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n"; 370a34c753fSRafael Auler exit(1); 371a34c753fSRafael Auler } 372a34c753fSRafael Auler } 373a34c753fSRafael Auler 374a34c753fSRafael Auler std::error_code DataAggregator::writeAutoFDOData(StringRef OutputFilename) { 375a34c753fSRafael Auler outs() << "PERF2BOLT: writing data for autofdo tools...\n"; 37640c2e0faSMaksim Panchenko NamedRegionTimer T("writeAutoFDO", "Processing branch events", TimerGroupName, 37740c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator); 378a34c753fSRafael Auler 379a34c753fSRafael Auler std::error_code EC; 380a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None); 381a34c753fSRafael Auler if (EC) 382a34c753fSRafael Auler return EC; 383a34c753fSRafael Auler 384a34c753fSRafael Auler // Format: 385a34c753fSRafael Auler // number of unique traces 386a34c753fSRafael Auler // from_1-to_1:count_1 387a34c753fSRafael Auler // from_2-to_2:count_2 388a34c753fSRafael Auler // ...... 389a34c753fSRafael Auler // from_n-to_n:count_n 390a34c753fSRafael Auler // number of unique sample addresses 391a34c753fSRafael Auler // addr_1:count_1 392a34c753fSRafael Auler // addr_2:count_2 393a34c753fSRafael Auler // ...... 394a34c753fSRafael Auler // addr_n:count_n 395a34c753fSRafael Auler // number of unique LBR entries 396a34c753fSRafael Auler // src_1->dst_1:count_1 397a34c753fSRafael Auler // src_2->dst_2:count_2 398a34c753fSRafael Auler // ...... 399a34c753fSRafael Auler // src_n->dst_n:count_n 400a34c753fSRafael Auler 401a34c753fSRafael Auler const uint64_t FirstAllocAddress = this->BC->FirstAllocAddress; 402a34c753fSRafael Auler 403a34c753fSRafael Auler // AutoFDO addresses are relative to the first allocated loadable program 404a34c753fSRafael Auler // segment 405a34c753fSRafael Auler auto filterAddress = [&FirstAllocAddress](uint64_t Address) -> uint64_t { 406a34c753fSRafael Auler if (Address < FirstAllocAddress) 407a34c753fSRafael Auler return 0; 408a34c753fSRafael Auler return Address - FirstAllocAddress; 409a34c753fSRafael Auler }; 410a34c753fSRafael Auler 411a34c753fSRafael Auler OutFile << FallthroughLBRs.size() << "\n"; 412a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) { 413a34c753fSRafael Auler const Trace &Trace = AggrLBR.first; 414a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second; 415a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "-" 416a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":" 417a34c753fSRafael Auler << (Info.InternCount + Info.ExternCount) << "\n"; 418a34c753fSRafael Auler } 419a34c753fSRafael Auler 420a34c753fSRafael Auler OutFile << BasicSamples.size() << "\n"; 421a34c753fSRafael Auler for (const auto &Sample : BasicSamples) { 422a34c753fSRafael Auler uint64_t PC = Sample.first; 423a34c753fSRafael Auler uint64_t HitCount = Sample.second; 424a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(PC)) << ":" << HitCount << "\n"; 425a34c753fSRafael Auler } 426a34c753fSRafael Auler 427a34c753fSRafael Auler OutFile << BranchLBRs.size() << "\n"; 428a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) { 429a34c753fSRafael Auler const Trace &Trace = AggrLBR.first; 430a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second; 431a34c753fSRafael Auler OutFile << Twine::utohexstr(filterAddress(Trace.From)) << "->" 432a34c753fSRafael Auler << Twine::utohexstr(filterAddress(Trace.To)) << ":" 433a34c753fSRafael Auler << Info.TakenCount << "\n"; 434a34c753fSRafael Auler } 435a34c753fSRafael Auler 436a34c753fSRafael Auler outs() << "PERF2BOLT: wrote " << FallthroughLBRs.size() << " unique traces, " 437a34c753fSRafael Auler << BasicSamples.size() << " sample addresses and " << BranchLBRs.size() 438a34c753fSRafael Auler << " unique branches to " << OutputFilename << "\n"; 439a34c753fSRafael Auler 440a34c753fSRafael Auler return std::error_code(); 441a34c753fSRafael Auler } 442a34c753fSRafael Auler 443a34c753fSRafael Auler void DataAggregator::filterBinaryMMapInfo() { 444a34c753fSRafael Auler if (opts::FilterPID) { 445a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(opts::FilterPID); 446a34c753fSRafael Auler if (MMapInfoIter != BinaryMMapInfo.end()) { 447a34c753fSRafael Auler MMapInfo MMap = MMapInfoIter->second; 448a34c753fSRafael Auler BinaryMMapInfo.clear(); 449a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMap.PID, MMap)); 450a34c753fSRafael Auler } else { 451a34c753fSRafael Auler if (errs().has_colors()) 452a34c753fSRafael Auler errs().changeColor(raw_ostream::RED); 453a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching PID \"" 45440c2e0faSMaksim Panchenko << opts::FilterPID << "\"" 45540c2e0faSMaksim Panchenko << " for binary \"" << BC->getFilename() << "\"."; 456a34c753fSRafael Auler assert(!BinaryMMapInfo.empty() && "No memory map for matching binary"); 457a34c753fSRafael Auler errs() << " Profile for the following process is available:\n"; 458def464aaSAmir Ayupov for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo) 459a34c753fSRafael Auler outs() << " " << MMI.second.PID 460a34c753fSRafael Auler << (MMI.second.Forked ? " (forked)\n" : "\n"); 461def464aaSAmir Ayupov 462a34c753fSRafael Auler if (errs().has_colors()) 463a34c753fSRafael Auler errs().resetColor(); 464a34c753fSRafael Auler 465a34c753fSRafael Auler exit(1); 466a34c753fSRafael Auler } 467a34c753fSRafael Auler } 468a34c753fSRafael Auler } 469a34c753fSRafael Auler 470a34c753fSRafael Auler Error DataAggregator::preprocessProfile(BinaryContext &BC) { 471a34c753fSRafael Auler this->BC = &BC; 472a34c753fSRafael Auler 473a34c753fSRafael Auler if (opts::ReadPreAggregated) { 474a34c753fSRafael Auler parsePreAggregated(); 475a34c753fSRafael Auler return Error::success(); 476a34c753fSRafael Auler } 477a34c753fSRafael Auler 478a34c753fSRafael Auler if (Optional<StringRef> FileBuildID = BC.getFileBuildID()) { 479a34c753fSRafael Auler outs() << "BOLT-INFO: binary build-id is: " << *FileBuildID << "\n"; 480a34c753fSRafael Auler processFileBuildID(*FileBuildID); 481a34c753fSRafael Auler } else { 482a34c753fSRafael Auler errs() << "BOLT-WARNING: build-id will not be checked because we could " 483a34c753fSRafael Auler "not read one from input binary\n"; 484a34c753fSRafael Auler } 485a34c753fSRafael Auler 486a34c753fSRafael Auler auto prepareToParse = [&](StringRef Name, PerfProcessInfo &Process) { 487a34c753fSRafael Auler std::string Error; 488a34c753fSRafael Auler outs() << "PERF2BOLT: waiting for perf " << Name 489a34c753fSRafael Auler << " collection to finish...\n"; 490a34c753fSRafael Auler sys::ProcessInfo PI = sys::Wait(Process.PI, 0, true, &Error); 491a34c753fSRafael Auler 492a34c753fSRafael Auler if (!Error.empty()) { 493a34c753fSRafael Auler errs() << "PERF-ERROR: " << PerfPath << ": " << Error << "\n"; 494a34c753fSRafael Auler deleteTempFiles(); 495a34c753fSRafael Auler exit(1); 496a34c753fSRafael Auler } 497a34c753fSRafael Auler 498a34c753fSRafael Auler if (PI.ReturnCode != 0) { 499a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorMB = 500a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StderrPath.data()); 501a34c753fSRafael Auler StringRef ErrBuf = (*ErrorMB)->getBuffer(); 502a34c753fSRafael Auler 503a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << PI.ReturnCode << "\n"; 504a34c753fSRafael Auler errs() << ErrBuf; 505a34c753fSRafael Auler deleteTempFiles(); 506a34c753fSRafael Auler exit(1); 507a34c753fSRafael Auler } 508a34c753fSRafael Auler 509a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 510a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Process.StdoutPath.data()); 511a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 512a34c753fSRafael Auler errs() << "Cannot open " << Process.StdoutPath.data() << ": " 513a34c753fSRafael Auler << EC.message() << "\n"; 514a34c753fSRafael Auler deleteTempFiles(); 515a34c753fSRafael Auler exit(1); 516a34c753fSRafael Auler } 517a34c753fSRafael Auler 518d914486aSAmir Ayupov FileBuf = std::move(*MB); 519a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 520a34c753fSRafael Auler Col = 0; 521a34c753fSRafael Auler Line = 1; 522a34c753fSRafael Auler }; 523a34c753fSRafael Auler 524a34c753fSRafael Auler if (opts::LinuxKernelMode) { 525a34c753fSRafael Auler // Current MMap parsing logic does not work with linux kernel. 526a34c753fSRafael Auler // MMap entries for linux kernel uses PERF_RECORD_MMAP 527a34c753fSRafael Auler // format instead of typical PERF_RECORD_MMAP2 format. 528a34c753fSRafael Auler // Since linux kernel address mapping is absolute (same as 529a34c753fSRafael Auler // in the ELF file), we avoid parsing MMap in linux kernel mode. 530a34c753fSRafael Auler // While generating optimized linux kernel binary, we may need 531a34c753fSRafael Auler // to parse MMap entries. 532a34c753fSRafael Auler 533a34c753fSRafael Auler // In linux kernel mode, we analyze and optimize 534a34c753fSRafael Auler // all linux kernel binary instructions, irrespective 535a34c753fSRafael Auler // of whether they are due to system calls or due to 536a34c753fSRafael Auler // interrupts. Therefore, we cannot ignore interrupt 537a34c753fSRafael Auler // in Linux kernel mode. 538a34c753fSRafael Auler opts::IgnoreInterruptLBR = false; 539a34c753fSRafael Auler } else { 540a34c753fSRafael Auler prepareToParse("mmap events", MMapEventsPPI); 541def464aaSAmir Ayupov if (parseMMapEvents()) 542a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse mmap events\n"; 543a34c753fSRafael Auler } 544a34c753fSRafael Auler 545a34c753fSRafael Auler prepareToParse("task events", TaskEventsPPI); 546def464aaSAmir Ayupov if (parseTaskEvents()) 547a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse task events\n"; 548a34c753fSRafael Auler 549a34c753fSRafael Auler filterBinaryMMapInfo(); 550a34c753fSRafael Auler prepareToParse("events", MainEventsPPI); 551a34c753fSRafael Auler 552a34c753fSRafael Auler if (opts::HeatmapMode) { 553a34c753fSRafael Auler if (std::error_code EC = printLBRHeatMap()) { 554a34c753fSRafael Auler errs() << "ERROR: failed to print heat map: " << EC.message() << '\n'; 555a34c753fSRafael Auler exit(1); 556a34c753fSRafael Auler } 557a34c753fSRafael Auler exit(0); 558a34c753fSRafael Auler } 559a34c753fSRafael Auler 560a34c753fSRafael Auler if ((!opts::BasicAggregation && parseBranchEvents()) || 561def464aaSAmir Ayupov (opts::BasicAggregation && parseBasicEvents())) 562a34c753fSRafael Auler errs() << "PERF2BOLT: failed to parse samples\n"; 563a34c753fSRafael Auler 564a34c753fSRafael Auler // We can finish early if the goal is just to generate data for autofdo 565a34c753fSRafael Auler if (opts::WriteAutoFDOData) { 566def464aaSAmir Ayupov if (std::error_code EC = writeAutoFDOData(opts::OutputFilename)) 567a34c753fSRafael Auler errs() << "Error writing autofdo data to file: " << EC.message() << "\n"; 568def464aaSAmir Ayupov 569a34c753fSRafael Auler deleteTempFiles(); 570a34c753fSRafael Auler exit(0); 571a34c753fSRafael Auler } 572a34c753fSRafael Auler 573a34c753fSRafael Auler // Special handling for memory events 574a34c753fSRafael Auler std::string Error; 575a34c753fSRafael Auler sys::ProcessInfo PI = sys::Wait(MemEventsPPI.PI, 0, true, &Error); 576a34c753fSRafael Auler if (PI.ReturnCode != 0) { 577a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 578a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(MemEventsPPI.StderrPath.data()); 579a34c753fSRafael Auler StringRef ErrBuf = (*MB)->getBuffer(); 580a34c753fSRafael Auler 581a34c753fSRafael Auler deleteTempFiles(); 582a34c753fSRafael Auler 583a34c753fSRafael Auler Regex NoData("Samples for '.*' event do not have ADDR attribute set. " 584a34c753fSRafael Auler "Cannot print 'addr' field."); 585a34c753fSRafael Auler if (!NoData.match(ErrBuf)) { 586a34c753fSRafael Auler errs() << "PERF-ERROR: return code " << PI.ReturnCode << "\n"; 587a34c753fSRafael Auler errs() << ErrBuf; 588a34c753fSRafael Auler exit(1); 589a34c753fSRafael Auler } 590a34c753fSRafael Auler return Error::success(); 591a34c753fSRafael Auler } 592a34c753fSRafael Auler 593a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 594a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(MemEventsPPI.StdoutPath.data()); 595a34c753fSRafael Auler if (std::error_code EC = MB.getError()) { 596a34c753fSRafael Auler errs() << "Cannot open " << MemEventsPPI.StdoutPath.data() << ": " 597a34c753fSRafael Auler << EC.message() << "\n"; 598a34c753fSRafael Auler deleteTempFiles(); 599a34c753fSRafael Auler exit(1); 600a34c753fSRafael Auler } 601a34c753fSRafael Auler 602d914486aSAmir Ayupov FileBuf = std::move(*MB); 603a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer(); 604a34c753fSRafael Auler Col = 0; 605a34c753fSRafael Auler Line = 1; 606def464aaSAmir Ayupov if (const std::error_code EC = parseMemEvents()) 60740c2e0faSMaksim Panchenko errs() << "PERF2BOLT: failed to parse memory events: " << EC.message() 60840c2e0faSMaksim Panchenko << '\n'; 609a34c753fSRafael Auler 610a34c753fSRafael Auler deleteTempFiles(); 611a34c753fSRafael Auler 612a34c753fSRafael Auler return Error::success(); 613a34c753fSRafael Auler } 614a34c753fSRafael Auler 615a34c753fSRafael Auler Error DataAggregator::readProfile(BinaryContext &BC) { 616a34c753fSRafael Auler processProfile(BC); 617a34c753fSRafael Auler 618a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) { 619a34c753fSRafael Auler BinaryFunction &Function = BFI.second; 620a34c753fSRafael Auler convertBranchData(Function); 621a34c753fSRafael Auler } 622a34c753fSRafael Auler 62339336fc0SAmir Ayupov if (opts::AggregateOnly && 62439336fc0SAmir Ayupov opts::ProfileFormat == opts::ProfileFormatKind::PF_Fdata) { 625def464aaSAmir Ayupov if (std::error_code EC = writeAggregatedFile(opts::OutputFilename)) 626a34c753fSRafael Auler report_error("cannot create output data file", EC); 627a34c753fSRafael Auler } 628a34c753fSRafael Auler 629a34c753fSRafael Auler return Error::success(); 630a34c753fSRafael Auler } 631a34c753fSRafael Auler 632a34c753fSRafael Auler bool DataAggregator::mayHaveProfileData(const BinaryFunction &Function) { 633a34c753fSRafael Auler return Function.hasProfileAvailable(); 634a34c753fSRafael Auler } 635a34c753fSRafael Auler 636a34c753fSRafael Auler void DataAggregator::processProfile(BinaryContext &BC) { 637a34c753fSRafael Auler if (opts::ReadPreAggregated) 638a34c753fSRafael Auler processPreAggregated(); 639a34c753fSRafael Auler else if (opts::BasicAggregation) 640a34c753fSRafael Auler processBasicEvents(); 641a34c753fSRafael Auler else 642a34c753fSRafael Auler processBranchEvents(); 643a34c753fSRafael Auler 644a34c753fSRafael Auler processMemEvents(); 645a34c753fSRafael Auler 646a34c753fSRafael Auler // Mark all functions with registered events as having a valid profile. 647a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) { 648a34c753fSRafael Auler BinaryFunction &BF = BFI.second; 649a34c753fSRafael Auler if (getBranchData(BF)) { 650a34c753fSRafael Auler const auto Flags = opts::BasicAggregation ? BinaryFunction::PF_SAMPLE 651a34c753fSRafael Auler : BinaryFunction::PF_LBR; 652a34c753fSRafael Auler BF.markProfiled(Flags); 653a34c753fSRafael Auler } 654a34c753fSRafael Auler } 655a34c753fSRafael Auler 656a34c753fSRafael Auler // Release intermediate storage. 657a34c753fSRafael Auler clear(BranchLBRs); 658a34c753fSRafael Auler clear(FallthroughLBRs); 659a34c753fSRafael Auler clear(AggregatedLBRs); 660a34c753fSRafael Auler clear(BasicSamples); 661a34c753fSRafael Auler clear(MemSamples); 662a34c753fSRafael Auler } 663a34c753fSRafael Auler 664a34c753fSRafael Auler BinaryFunction * 665a34c753fSRafael Auler DataAggregator::getBinaryFunctionContainingAddress(uint64_t Address) const { 666a34c753fSRafael Auler if (!BC->containsAddress(Address)) 667a34c753fSRafael Auler return nullptr; 668a34c753fSRafael Auler 669a34c753fSRafael Auler return BC->getBinaryFunctionContainingAddress(Address, /*CheckPastEnd=*/false, 670a34c753fSRafael Auler /*UseMaxSize=*/true); 671a34c753fSRafael Auler } 672a34c753fSRafael Auler 673a34c753fSRafael Auler StringRef DataAggregator::getLocationName(BinaryFunction &Func, 674a34c753fSRafael Auler uint64_t Count) { 675a34c753fSRafael Auler if (!BAT) 676a34c753fSRafael Auler return Func.getOneName(); 677a34c753fSRafael Auler 678a34c753fSRafael Auler const BinaryFunction *OrigFunc = &Func; 679a34c753fSRafael Auler if (const uint64_t HotAddr = BAT->fetchParentAddress(Func.getAddress())) { 680a34c753fSRafael Auler NumColdSamples += Count; 681a34c753fSRafael Auler BinaryFunction *HotFunc = getBinaryFunctionContainingAddress(HotAddr); 682a34c753fSRafael Auler if (HotFunc) 683a34c753fSRafael Auler OrigFunc = HotFunc; 684a34c753fSRafael Auler } 685a34c753fSRafael Auler // If it is a local function, prefer the name containing the file name where 686a34c753fSRafael Auler // the local function was declared 687a34c753fSRafael Auler for (StringRef AlternativeName : OrigFunc->getNames()) { 688a34c753fSRafael Auler size_t FileNameIdx = AlternativeName.find('/'); 689a34c753fSRafael Auler // Confirm the alternative name has the pattern Symbol/FileName/1 before 690a34c753fSRafael Auler // using it 691a34c753fSRafael Auler if (FileNameIdx == StringRef::npos || 692a34c753fSRafael Auler AlternativeName.find('/', FileNameIdx + 1) == StringRef::npos) 693a34c753fSRafael Auler continue; 694a34c753fSRafael Auler return AlternativeName; 695a34c753fSRafael Auler } 696a34c753fSRafael Auler return OrigFunc->getOneName(); 697a34c753fSRafael Auler } 698a34c753fSRafael Auler 699a34c753fSRafael Auler bool DataAggregator::doSample(BinaryFunction &Func, uint64_t Address, 700a34c753fSRafael Auler uint64_t Count) { 701a34c753fSRafael Auler auto I = NamesToSamples.find(Func.getOneName()); 702a34c753fSRafael Auler if (I == NamesToSamples.end()) { 703a34c753fSRafael Auler bool Success; 704a34c753fSRafael Auler StringRef LocName = getLocationName(Func, Count); 70540c2e0faSMaksim Panchenko std::tie(I, Success) = NamesToSamples.insert( 70640c2e0faSMaksim Panchenko std::make_pair(Func.getOneName(), 707a34c753fSRafael Auler FuncSampleData(LocName, FuncSampleData::ContainerTy()))); 708a34c753fSRafael Auler } 709a34c753fSRafael Auler 710a34c753fSRafael Auler Address -= Func.getAddress(); 711a34c753fSRafael Auler if (BAT) 712fc0ced73SRafael Auler Address = BAT->translate(Func.getAddress(), Address, /*IsBranchSrc=*/false); 713a34c753fSRafael Auler 714a34c753fSRafael Auler I->second.bumpCount(Address, Count); 715a34c753fSRafael Auler return true; 716a34c753fSRafael Auler } 717a34c753fSRafael Auler 718a34c753fSRafael Auler bool DataAggregator::doIntraBranch(BinaryFunction &Func, uint64_t From, 719a34c753fSRafael Auler uint64_t To, uint64_t Count, 720a34c753fSRafael Auler uint64_t Mispreds) { 721a34c753fSRafael Auler FuncBranchData *AggrData = getBranchData(Func); 722a34c753fSRafael Auler if (!AggrData) { 723a34c753fSRafael Auler AggrData = &NamesToBranches[Func.getOneName()]; 724a34c753fSRafael Auler AggrData->Name = getLocationName(Func, Count); 725a34c753fSRafael Auler setBranchData(Func, AggrData); 726a34c753fSRafael Auler } 727a34c753fSRafael Auler 728a34c753fSRafael Auler From -= Func.getAddress(); 729a34c753fSRafael Auler To -= Func.getAddress(); 730a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: bumpBranchCount: " << Func.getPrintName() 731a34c753fSRafael Auler << " @ " << Twine::utohexstr(From) << " -> " 732a34c753fSRafael Auler << Func.getPrintName() << " @ " << Twine::utohexstr(To) 733a34c753fSRafael Auler << '\n'); 734a34c753fSRafael Auler if (BAT) { 735fc0ced73SRafael Auler From = BAT->translate(Func.getAddress(), From, /*IsBranchSrc=*/true); 736fc0ced73SRafael Auler To = BAT->translate(Func.getAddress(), To, /*IsBranchSrc=*/false); 737a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "BOLT-DEBUG: BAT translation on bumpBranchCount: " 738a34c753fSRafael Auler << Func.getPrintName() << " @ " << Twine::utohexstr(From) 739a34c753fSRafael Auler << " -> " << Func.getPrintName() << " @ " 740a34c753fSRafael Auler << Twine::utohexstr(To) << '\n'); 741a34c753fSRafael Auler } 742a34c753fSRafael Auler 743a34c753fSRafael Auler AggrData->bumpBranchCount(From, To, Count, Mispreds); 744a34c753fSRafael Auler return true; 745a34c753fSRafael Auler } 746a34c753fSRafael Auler 747a34c753fSRafael Auler bool DataAggregator::doInterBranch(BinaryFunction *FromFunc, 748a34c753fSRafael Auler BinaryFunction *ToFunc, uint64_t From, 749a34c753fSRafael Auler uint64_t To, uint64_t Count, 750a34c753fSRafael Auler uint64_t Mispreds) { 751a34c753fSRafael Auler FuncBranchData *FromAggrData = nullptr; 752a34c753fSRafael Auler FuncBranchData *ToAggrData = nullptr; 753a34c753fSRafael Auler StringRef SrcFunc; 754a34c753fSRafael Auler StringRef DstFunc; 755a34c753fSRafael Auler if (FromFunc) { 756a34c753fSRafael Auler SrcFunc = getLocationName(*FromFunc, Count); 757a34c753fSRafael Auler FromAggrData = getBranchData(*FromFunc); 758a34c753fSRafael Auler if (!FromAggrData) { 759a34c753fSRafael Auler FromAggrData = &NamesToBranches[FromFunc->getOneName()]; 760a34c753fSRafael Auler FromAggrData->Name = SrcFunc; 761a34c753fSRafael Auler setBranchData(*FromFunc, FromAggrData); 762a34c753fSRafael Auler } 763a34c753fSRafael Auler From -= FromFunc->getAddress(); 764a34c753fSRafael Auler if (BAT) 765fc0ced73SRafael Auler From = BAT->translate(FromFunc->getAddress(), From, /*IsBranchSrc=*/true); 766a34c753fSRafael Auler 767a34c753fSRafael Auler recordExit(*FromFunc, From, Mispreds, Count); 768a34c753fSRafael Auler } 769a34c753fSRafael Auler if (ToFunc) { 770a34c753fSRafael Auler DstFunc = getLocationName(*ToFunc, 0); 771a34c753fSRafael Auler ToAggrData = getBranchData(*ToFunc); 772a34c753fSRafael Auler if (!ToAggrData) { 773a34c753fSRafael Auler ToAggrData = &NamesToBranches[ToFunc->getOneName()]; 774a34c753fSRafael Auler ToAggrData->Name = DstFunc; 775a34c753fSRafael Auler setBranchData(*ToFunc, ToAggrData); 776a34c753fSRafael Auler } 777a34c753fSRafael Auler To -= ToFunc->getAddress(); 778a34c753fSRafael Auler if (BAT) 779fc0ced73SRafael Auler To = BAT->translate(ToFunc->getAddress(), To, /*IsBranchSrc=*/false); 780a34c753fSRafael Auler 781a34c753fSRafael Auler recordEntry(*ToFunc, To, Mispreds, Count); 782a34c753fSRafael Auler } 783a34c753fSRafael Auler 784a34c753fSRafael Auler if (FromAggrData) 785a34c753fSRafael Auler FromAggrData->bumpCallCount(From, Location(!DstFunc.empty(), DstFunc, To), 786a34c753fSRafael Auler Count, Mispreds); 787a34c753fSRafael Auler if (ToAggrData) 788a34c753fSRafael Auler ToAggrData->bumpEntryCount(Location(!SrcFunc.empty(), SrcFunc, From), To, 789a34c753fSRafael Auler Count, Mispreds); 790a34c753fSRafael Auler return true; 791a34c753fSRafael Auler } 792a34c753fSRafael Auler 793a34c753fSRafael Auler bool DataAggregator::doBranch(uint64_t From, uint64_t To, uint64_t Count, 794a34c753fSRafael Auler uint64_t Mispreds) { 795a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(From); 796a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(To); 797a34c753fSRafael Auler if (!FromFunc && !ToFunc) 798a34c753fSRafael Auler return false; 799a34c753fSRafael Auler 800a34c753fSRafael Auler if (FromFunc == ToFunc) { 801a34c753fSRafael Auler recordBranch(*FromFunc, From - FromFunc->getAddress(), 802a34c753fSRafael Auler To - FromFunc->getAddress(), Count, Mispreds); 803a34c753fSRafael Auler return doIntraBranch(*FromFunc, From, To, Count, Mispreds); 804a34c753fSRafael Auler } 805a34c753fSRafael Auler 806a34c753fSRafael Auler return doInterBranch(FromFunc, ToFunc, From, To, Count, Mispreds); 807a34c753fSRafael Auler } 808a34c753fSRafael Auler 809a34c753fSRafael Auler bool DataAggregator::doTrace(const LBREntry &First, const LBREntry &Second, 810a34c753fSRafael Auler uint64_t Count) { 811a34c753fSRafael Auler BinaryFunction *FromFunc = getBinaryFunctionContainingAddress(First.To); 812a34c753fSRafael Auler BinaryFunction *ToFunc = getBinaryFunctionContainingAddress(Second.From); 813a34c753fSRafael Auler if (!FromFunc || !ToFunc) { 814a34c753fSRafael Auler LLVM_DEBUG( 815a34c753fSRafael Auler dbgs() << "Out of range 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 NumLongRangeTraces += Count; 821a34c753fSRafael Auler return false; 822a34c753fSRafael Auler } 823a34c753fSRafael Auler if (FromFunc != ToFunc) { 824a34c753fSRafael Auler NumInvalidTraces += Count; 825a34c753fSRafael Auler LLVM_DEBUG( 826a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName() 827a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress()) 828a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ " 829a34c753fSRafael Auler << ToFunc->getPrintName() << " @ " 830a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n'); 831a34c753fSRafael Auler return false; 832a34c753fSRafael Auler } 833a34c753fSRafael Auler 8343d573fdbSAmir Ayupov std::optional<BoltAddressTranslation::FallthroughListTy> FTs = 835fc0ced73SRafael Auler BAT ? BAT->getFallthroughsInTrace(FromFunc->getAddress(), First.To, 836fc0ced73SRafael Auler Second.From) 837a34c753fSRafael Auler : getFallthroughsInTrace(*FromFunc, First, Second, Count); 838a34c753fSRafael Auler if (!FTs) { 839a34c753fSRafael Auler LLVM_DEBUG( 840a34c753fSRafael Auler dbgs() << "Invalid trace starting in " << FromFunc->getPrintName() 841a34c753fSRafael Auler << " @ " << Twine::utohexstr(First.To - FromFunc->getAddress()) 842a34c753fSRafael Auler << " and ending in " << ToFunc->getPrintName() << " @ " 843a34c753fSRafael Auler << ToFunc->getPrintName() << " @ " 844a34c753fSRafael Auler << Twine::utohexstr(Second.From - ToFunc->getAddress()) << '\n'); 845a34c753fSRafael Auler NumInvalidTraces += Count; 846a34c753fSRafael Auler return false; 847a34c753fSRafael Auler } 848a34c753fSRafael Auler 849a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Processing " << FTs->size() << " fallthroughs for " 850a34c753fSRafael Auler << FromFunc->getPrintName() << ":" 851a34c753fSRafael Auler << Twine::utohexstr(First.To) << " to " 852a34c753fSRafael Auler << Twine::utohexstr(Second.From) << ".\n"); 853def464aaSAmir Ayupov for (const std::pair<uint64_t, uint64_t> &Pair : *FTs) 854a34c753fSRafael Auler doIntraBranch(*FromFunc, Pair.first + FromFunc->getAddress(), 855a34c753fSRafael Auler Pair.second + FromFunc->getAddress(), Count, false); 856a34c753fSRafael Auler 857a34c753fSRafael Auler return true; 858a34c753fSRafael Auler } 859a34c753fSRafael Auler 860a34c753fSRafael Auler bool DataAggregator::recordTrace( 861a34c753fSRafael Auler BinaryFunction &BF, 862a34c753fSRafael Auler const LBREntry &FirstLBR, 863a34c753fSRafael Auler const LBREntry &SecondLBR, 864a34c753fSRafael Auler uint64_t Count, 865a34c753fSRafael Auler SmallVector<std::pair<uint64_t, uint64_t>, 16> *Branches) const { 866a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext(); 867a34c753fSRafael Auler 868a34c753fSRafael Auler if (!BF.isSimple()) 869a34c753fSRafael Auler return false; 870a34c753fSRafael Auler 871a34c753fSRafael Auler assert(BF.hasCFG() && "can only record traces in CFG state"); 872a34c753fSRafael Auler 873a34c753fSRafael Auler // Offsets of the trace within this function. 874a34c753fSRafael Auler const uint64_t From = FirstLBR.To - BF.getAddress(); 875a34c753fSRafael Auler const uint64_t To = SecondLBR.From - BF.getAddress(); 876a34c753fSRafael Auler 877a34c753fSRafael Auler if (From > To) 878a34c753fSRafael Auler return false; 879a34c753fSRafael Auler 880d5c03defSFabian Parzefall const BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From); 881d5c03defSFabian Parzefall const BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To); 882a34c753fSRafael Auler 883a34c753fSRafael Auler if (!FromBB || !ToBB) 884a34c753fSRafael Auler return false; 885a34c753fSRafael Auler 886a34c753fSRafael Auler // Adjust FromBB if the first LBR is a return from the last instruction in 887a34c753fSRafael Auler // the previous block (that instruction should be a call). 888a34c753fSRafael Auler if (From == FromBB->getOffset() && !BF.containsAddress(FirstLBR.From) && 889a34c753fSRafael Auler !FromBB->isEntryPoint() && !FromBB->isLandingPad()) { 890d5c03defSFabian Parzefall const BinaryBasicBlock *PrevBB = 891d5c03defSFabian Parzefall BF.getLayout().getBlock(FromBB->getIndex() - 1); 892a34c753fSRafael Auler if (PrevBB->getSuccessor(FromBB->getLabel())) { 893a34c753fSRafael Auler const MCInst *Instr = PrevBB->getLastNonPseudoInstr(); 894def464aaSAmir Ayupov if (Instr && BC.MIB->isCall(*Instr)) 895a34c753fSRafael Auler FromBB = PrevBB; 896def464aaSAmir Ayupov else 897a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR (no call): " << FirstLBR 898a34c753fSRafael Auler << '\n'); 899a34c753fSRafael Auler } else { 900a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "invalid incoming LBR: " << FirstLBR << '\n'); 901a34c753fSRafael Auler } 902a34c753fSRafael Auler } 903a34c753fSRafael Auler 904a34c753fSRafael Auler // Fill out information for fall-through edges. The From and To could be 905a34c753fSRafael Auler // within the same basic block, e.g. when two call instructions are in the 906a34c753fSRafael Auler // same block. In this case we skip the processing. 907def464aaSAmir Ayupov if (FromBB == ToBB) 908a34c753fSRafael Auler return true; 909a34c753fSRafael Auler 910a34c753fSRafael Auler // Process blocks in the original layout order. 9118477bc67SFabian Parzefall BinaryBasicBlock *BB = BF.getLayout().getBlock(FromBB->getIndex()); 912a34c753fSRafael Auler assert(BB == FromBB && "index mismatch"); 913a34c753fSRafael Auler while (BB != ToBB) { 9148477bc67SFabian Parzefall BinaryBasicBlock *NextBB = BF.getLayout().getBlock(BB->getIndex() + 1); 915a34c753fSRafael Auler assert((NextBB && NextBB->getOffset() > BB->getOffset()) && "bad layout"); 916a34c753fSRafael Auler 917a34c753fSRafael Auler // Check for bad LBRs. 918a34c753fSRafael Auler if (!BB->getSuccessor(NextBB->getLabel())) { 919a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "no fall-through for the trace:\n" 920a34c753fSRafael Auler << " " << FirstLBR << '\n' 921a34c753fSRafael Auler << " " << SecondLBR << '\n'); 922a34c753fSRafael Auler return false; 923a34c753fSRafael Auler } 924a34c753fSRafael Auler 925a34c753fSRafael Auler // Record fall-through jumps 926a34c753fSRafael Auler BinaryBasicBlock::BinaryBranchInfo &BI = BB->getBranchInfo(*NextBB); 927a34c753fSRafael Auler BI.Count += Count; 928a34c753fSRafael Auler 929a34c753fSRafael Auler if (Branches) { 930a34c753fSRafael Auler const MCInst *Instr = BB->getLastNonPseudoInstr(); 931a34c753fSRafael Auler uint64_t Offset = 0; 932def464aaSAmir Ayupov if (Instr) 933a9cd49d5SAmir Ayupov Offset = BC.MIB->getOffsetWithDefault(*Instr, 0); 934def464aaSAmir Ayupov else 935a34c753fSRafael Auler Offset = BB->getOffset(); 936def464aaSAmir Ayupov 937a34c753fSRafael Auler Branches->emplace_back(Offset, NextBB->getOffset()); 938a34c753fSRafael Auler } 939a34c753fSRafael Auler 940a34c753fSRafael Auler BB = NextBB; 941a34c753fSRafael Auler } 942a34c753fSRafael Auler 943a34c753fSRafael Auler return true; 944a34c753fSRafael Auler } 945a34c753fSRafael Auler 9463d573fdbSAmir Ayupov std::optional<SmallVector<std::pair<uint64_t, uint64_t>, 16>> 947a34c753fSRafael Auler DataAggregator::getFallthroughsInTrace(BinaryFunction &BF, 948a34c753fSRafael Auler const LBREntry &FirstLBR, 949a34c753fSRafael Auler const LBREntry &SecondLBR, 950a34c753fSRafael Auler uint64_t Count) const { 951a34c753fSRafael Auler SmallVector<std::pair<uint64_t, uint64_t>, 16> Res; 952a34c753fSRafael Auler 953a34c753fSRafael Auler if (!recordTrace(BF, FirstLBR, SecondLBR, Count, &Res)) 954e324a80fSKazu Hirata return std::nullopt; 955a34c753fSRafael Auler 956a34c753fSRafael Auler return Res; 957a34c753fSRafael Auler } 958a34c753fSRafael Auler 959a34c753fSRafael Auler bool DataAggregator::recordEntry(BinaryFunction &BF, uint64_t To, bool Mispred, 960a34c753fSRafael Auler uint64_t Count) const { 961a34c753fSRafael Auler if (To > BF.getSize()) 962a34c753fSRafael Auler return false; 963a34c753fSRafael Auler 964a34c753fSRafael Auler if (!BF.hasProfile()) 965a34c753fSRafael Auler BF.ExecutionCount = 0; 966a34c753fSRafael Auler 967a34c753fSRafael Auler BinaryBasicBlock *EntryBB = nullptr; 968a34c753fSRafael Auler if (To == 0) { 969a34c753fSRafael Auler BF.ExecutionCount += Count; 970a34c753fSRafael Auler if (!BF.empty()) 971a34c753fSRafael Auler EntryBB = &BF.front(); 972a34c753fSRafael Auler } else if (BinaryBasicBlock *BB = BF.getBasicBlockAtOffset(To)) { 973a34c753fSRafael Auler if (BB->isEntryPoint()) 974a34c753fSRafael Auler EntryBB = BB; 975a34c753fSRafael Auler } 976a34c753fSRafael Auler 977a34c753fSRafael Auler if (EntryBB) 978a34c753fSRafael Auler EntryBB->setExecutionCount(EntryBB->getKnownExecutionCount() + Count); 979a34c753fSRafael Auler 980a34c753fSRafael Auler return true; 981a34c753fSRafael Auler } 982a34c753fSRafael Auler 98340c2e0faSMaksim Panchenko bool DataAggregator::recordExit(BinaryFunction &BF, uint64_t From, bool Mispred, 98440c2e0faSMaksim Panchenko uint64_t Count) const { 985a34c753fSRafael Auler if (!BF.isSimple() || From > BF.getSize()) 986a34c753fSRafael Auler return false; 987a34c753fSRafael Auler 988a34c753fSRafael Auler if (!BF.hasProfile()) 989a34c753fSRafael Auler BF.ExecutionCount = 0; 990a34c753fSRafael Auler 991a34c753fSRafael Auler return true; 992a34c753fSRafael Auler } 993a34c753fSRafael Auler 994a34c753fSRafael Auler ErrorOr<LBREntry> DataAggregator::parseLBREntry() { 995a34c753fSRafael Auler LBREntry Res; 996a34c753fSRafael Auler ErrorOr<StringRef> FromStrRes = parseString('/'); 997a34c753fSRafael Auler if (std::error_code EC = FromStrRes.getError()) 998a34c753fSRafael Auler return EC; 999a34c753fSRafael Auler StringRef OffsetStr = FromStrRes.get(); 1000a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.From)) { 1001a34c753fSRafael Auler reportError("expected hexadecimal number with From address"); 1002a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n"; 1003a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1004a34c753fSRafael Auler } 1005a34c753fSRafael Auler 1006a34c753fSRafael Auler ErrorOr<StringRef> ToStrRes = parseString('/'); 1007a34c753fSRafael Auler if (std::error_code EC = ToStrRes.getError()) 1008a34c753fSRafael Auler return EC; 1009a34c753fSRafael Auler OffsetStr = ToStrRes.get(); 1010a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, Res.To)) { 1011a34c753fSRafael Auler reportError("expected hexadecimal number with To address"); 1012a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "\n"; 1013a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1014a34c753fSRafael Auler } 1015a34c753fSRafael Auler 1016a34c753fSRafael Auler ErrorOr<StringRef> MispredStrRes = parseString('/'); 1017a34c753fSRafael Auler if (std::error_code EC = MispredStrRes.getError()) 1018a34c753fSRafael Auler return EC; 1019a34c753fSRafael Auler StringRef MispredStr = MispredStrRes.get(); 1020a34c753fSRafael Auler if (MispredStr.size() != 1 || 1021a34c753fSRafael Auler (MispredStr[0] != 'P' && MispredStr[0] != 'M' && MispredStr[0] != '-')) { 1022a34c753fSRafael Auler reportError("expected single char for mispred bit"); 1023a34c753fSRafael Auler Diag << "Found: " << MispredStr << "\n"; 1024a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1025a34c753fSRafael Auler } 1026a34c753fSRafael Auler Res.Mispred = MispredStr[0] == 'M'; 1027a34c753fSRafael Auler 102840c2e0faSMaksim Panchenko static bool MispredWarning = true; 1029a34c753fSRafael Auler if (MispredStr[0] == '-' && MispredWarning) { 1030a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: misprediction bit is missing in profile\n"; 1031a34c753fSRafael Auler MispredWarning = false; 1032a34c753fSRafael Auler } 1033a34c753fSRafael Auler 1034a34c753fSRafael Auler ErrorOr<StringRef> Rest = parseString(FieldSeparator, true); 1035a34c753fSRafael Auler if (std::error_code EC = Rest.getError()) 1036a34c753fSRafael Auler return EC; 1037a34c753fSRafael Auler if (Rest.get().size() < 5) { 1038a34c753fSRafael Auler reportError("expected rest of LBR entry"); 1039a34c753fSRafael Auler Diag << "Found: " << Rest.get() << "\n"; 1040a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1041a34c753fSRafael Auler } 1042a34c753fSRafael Auler return Res; 1043a34c753fSRafael Auler } 1044a34c753fSRafael Auler 1045a34c753fSRafael Auler bool DataAggregator::checkAndConsumeFS() { 1046def464aaSAmir Ayupov if (ParsingBuf[0] != FieldSeparator) 1047a34c753fSRafael Auler return false; 1048def464aaSAmir Ayupov 1049a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1); 1050a34c753fSRafael Auler Col += 1; 1051a34c753fSRafael Auler return true; 1052a34c753fSRafael Auler } 1053a34c753fSRafael Auler 1054a34c753fSRafael Auler void DataAggregator::consumeRestOfLine() { 1055a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of('\n'); 1056a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1057a34c753fSRafael Auler ParsingBuf = StringRef(); 1058a34c753fSRafael Auler Col = 0; 1059a34c753fSRafael Auler Line += 1; 1060a34c753fSRafael Auler return; 1061a34c753fSRafael Auler } 1062a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(LineEnd + 1); 1063a34c753fSRafael Auler Col = 0; 1064a34c753fSRafael Auler Line += 1; 1065a34c753fSRafael Auler } 1066a34c753fSRafael Auler 1067ba9cc653SRafael Auler bool DataAggregator::checkNewLine() { 1068ba9cc653SRafael Auler return ParsingBuf[0] == '\n'; 1069ba9cc653SRafael Auler } 1070ba9cc653SRafael Auler 1071a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBranchSample> DataAggregator::parseBranchSample() { 1072a34c753fSRafael Auler PerfBranchSample Res; 1073a34c753fSRafael Auler 107440c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 107540c2e0faSMaksim Panchenko } 1076a34c753fSRafael Auler 1077a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1078a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1079a34c753fSRafael Auler return EC; 1080a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1081a34c753fSRafael Auler if (!opts::LinuxKernelMode && MMapInfoIter == BinaryMMapInfo.end()) { 1082a34c753fSRafael Auler consumeRestOfLine(); 1083a34c753fSRafael Auler return make_error_code(errc::no_such_process); 1084a34c753fSRafael Auler } 1085a34c753fSRafael Auler 108640c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 108740c2e0faSMaksim Panchenko } 1088a34c753fSRafael Auler 1089a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true); 1090a34c753fSRafael Auler if (std::error_code EC = PCRes.getError()) 1091a34c753fSRafael Auler return EC; 1092a34c753fSRafael Auler Res.PC = PCRes.get(); 1093a34c753fSRafael Auler 1094a34c753fSRafael Auler if (checkAndConsumeNewLine()) 1095a34c753fSRafael Auler return Res; 1096a34c753fSRafael Auler 1097a34c753fSRafael Auler while (!checkAndConsumeNewLine()) { 1098a34c753fSRafael Auler checkAndConsumeFS(); 1099a34c753fSRafael Auler 1100a34c753fSRafael Auler ErrorOr<LBREntry> LBRRes = parseLBREntry(); 1101a34c753fSRafael Auler if (std::error_code EC = LBRRes.getError()) 1102a34c753fSRafael Auler return EC; 1103a34c753fSRafael Auler LBREntry LBR = LBRRes.get(); 1104a34c753fSRafael Auler if (ignoreKernelInterrupt(LBR)) 1105a34c753fSRafael Auler continue; 1106a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1107a34c753fSRafael Auler adjustLBR(LBR, MMapInfoIter->second); 1108a34c753fSRafael Auler Res.LBR.push_back(LBR); 1109a34c753fSRafael Auler } 1110a34c753fSRafael Auler 1111a34c753fSRafael Auler return Res; 1112a34c753fSRafael Auler } 1113a34c753fSRafael Auler 1114a34c753fSRafael Auler ErrorOr<DataAggregator::PerfBasicSample> DataAggregator::parseBasicSample() { 111540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 111640c2e0faSMaksim Panchenko } 1117a34c753fSRafael Auler 1118a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1119a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1120a34c753fSRafael Auler return EC; 1121a34c753fSRafael Auler 1122a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1123a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) { 1124a34c753fSRafael Auler consumeRestOfLine(); 1125a34c753fSRafael Auler return PerfBasicSample{StringRef(), 0}; 1126a34c753fSRafael Auler } 1127a34c753fSRafael Auler 112840c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 112940c2e0faSMaksim Panchenko } 1130a34c753fSRafael Auler 1131a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator); 1132a34c753fSRafael Auler if (std::error_code EC = Event.getError()) 1133a34c753fSRafael Auler return EC; 1134a34c753fSRafael Auler 113540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 113640c2e0faSMaksim Panchenko } 1137a34c753fSRafael Auler 1138a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator, true); 1139def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError()) 1140a34c753fSRafael Auler return EC; 1141a34c753fSRafael Auler 1142a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1143a34c753fSRafael Auler reportError("expected end of line"); 1144a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1145a34c753fSRafael Auler } 1146a34c753fSRafael Auler 1147a34c753fSRafael Auler uint64_t Address = *AddrRes; 1148a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1149a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second); 1150a34c753fSRafael Auler 1151a34c753fSRafael Auler return PerfBasicSample{Event.get(), Address}; 1152a34c753fSRafael Auler } 1153a34c753fSRafael Auler 1154a34c753fSRafael Auler ErrorOr<DataAggregator::PerfMemSample> DataAggregator::parseMemSample() { 1155a34c753fSRafael Auler PerfMemSample Res{0, 0}; 1156a34c753fSRafael Auler 115740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 115840c2e0faSMaksim Panchenko } 1159a34c753fSRafael Auler 1160a34c753fSRafael Auler ErrorOr<int64_t> PIDRes = parseNumberField(FieldSeparator, true); 1161a34c753fSRafael Auler if (std::error_code EC = PIDRes.getError()) 1162a34c753fSRafael Auler return EC; 1163a34c753fSRafael Auler 1164a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*PIDRes); 1165a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) { 1166a34c753fSRafael Auler consumeRestOfLine(); 1167a34c753fSRafael Auler return Res; 1168a34c753fSRafael Auler } 1169a34c753fSRafael Auler 117040c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 117140c2e0faSMaksim Panchenko } 1172a34c753fSRafael Auler 1173a34c753fSRafael Auler ErrorOr<StringRef> Event = parseString(FieldSeparator); 1174a34c753fSRafael Auler if (std::error_code EC = Event.getError()) 1175a34c753fSRafael Auler return EC; 117620f0f15aSKazu Hirata if (!Event.get().contains("mem-loads")) { 1177a34c753fSRafael Auler consumeRestOfLine(); 1178a34c753fSRafael Auler return Res; 1179a34c753fSRafael Auler } 1180a34c753fSRafael Auler 118140c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 118240c2e0faSMaksim Panchenko } 1183a34c753fSRafael Auler 1184a34c753fSRafael Auler ErrorOr<uint64_t> AddrRes = parseHexField(FieldSeparator); 1185def464aaSAmir Ayupov if (std::error_code EC = AddrRes.getError()) 1186a34c753fSRafael Auler return EC; 1187a34c753fSRafael Auler 118840c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 118940c2e0faSMaksim Panchenko } 1190a34c753fSRafael Auler 1191a34c753fSRafael Auler ErrorOr<uint64_t> PCRes = parseHexField(FieldSeparator, true); 1192a34c753fSRafael Auler if (std::error_code EC = PCRes.getError()) { 1193a34c753fSRafael Auler consumeRestOfLine(); 1194a34c753fSRafael Auler return EC; 1195a34c753fSRafael Auler } 1196a34c753fSRafael Auler 1197a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1198a34c753fSRafael Auler reportError("expected end of line"); 1199a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1200a34c753fSRafael Auler } 1201a34c753fSRafael Auler 1202a34c753fSRafael Auler uint64_t Address = *AddrRes; 1203a34c753fSRafael Auler if (!BC->HasFixedLoadAddress) 1204a34c753fSRafael Auler adjustAddress(Address, MMapInfoIter->second); 1205a34c753fSRafael Auler 1206a34c753fSRafael Auler return PerfMemSample{PCRes.get(), Address}; 1207a34c753fSRafael Auler } 1208a34c753fSRafael Auler 1209a34c753fSRafael Auler ErrorOr<Location> DataAggregator::parseLocationOrOffset() { 1210a34c753fSRafael Auler auto parseOffset = [this]() -> ErrorOr<Location> { 1211a34c753fSRafael Auler ErrorOr<uint64_t> Res = parseHexField(FieldSeparator); 1212a34c753fSRafael Auler if (std::error_code EC = Res.getError()) 1213a34c753fSRafael Auler return EC; 1214a34c753fSRafael Auler return Location(Res.get()); 1215a34c753fSRafael Auler }; 1216a34c753fSRafael Auler 1217a34c753fSRafael Auler size_t Sep = ParsingBuf.find_first_of(" \n"); 1218a34c753fSRafael Auler if (Sep == StringRef::npos) 1219a34c753fSRafael Auler return parseOffset(); 1220a34c753fSRafael Auler StringRef LookAhead = ParsingBuf.substr(0, Sep); 1221a34c753fSRafael Auler if (LookAhead.find_first_of(":") == StringRef::npos) 1222a34c753fSRafael Auler return parseOffset(); 1223a34c753fSRafael Auler 1224a34c753fSRafael Auler ErrorOr<StringRef> BuildID = parseString(':'); 1225a34c753fSRafael Auler if (std::error_code EC = BuildID.getError()) 1226a34c753fSRafael Auler return EC; 1227a34c753fSRafael Auler ErrorOr<uint64_t> Offset = parseHexField(FieldSeparator); 1228a34c753fSRafael Auler if (std::error_code EC = Offset.getError()) 1229a34c753fSRafael Auler return EC; 1230a34c753fSRafael Auler return Location(true, BuildID.get(), Offset.get()); 1231a34c753fSRafael Auler } 1232a34c753fSRafael Auler 1233a34c753fSRafael Auler ErrorOr<DataAggregator::AggregatedLBREntry> 1234a34c753fSRafael Auler DataAggregator::parseAggregatedLBREntry() { 123540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 123640c2e0faSMaksim Panchenko } 1237a34c753fSRafael Auler 1238a34c753fSRafael Auler ErrorOr<StringRef> TypeOrErr = parseString(FieldSeparator); 1239a34c753fSRafael Auler if (std::error_code EC = TypeOrErr.getError()) 1240a34c753fSRafael Auler return EC; 1241a34c753fSRafael Auler auto Type = AggregatedLBREntry::BRANCH; 1242a34c753fSRafael Auler if (TypeOrErr.get() == "B") { 1243a34c753fSRafael Auler Type = AggregatedLBREntry::BRANCH; 1244a34c753fSRafael Auler } else if (TypeOrErr.get() == "F") { 1245a34c753fSRafael Auler Type = AggregatedLBREntry::FT; 1246a34c753fSRafael Auler } else if (TypeOrErr.get() == "f") { 1247a34c753fSRafael Auler Type = AggregatedLBREntry::FT_EXTERNAL_ORIGIN; 1248a34c753fSRafael Auler } else { 1249a34c753fSRafael Auler reportError("expected B, F or f"); 1250a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1251a34c753fSRafael Auler } 1252a34c753fSRafael Auler 125340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 125440c2e0faSMaksim Panchenko } 1255a34c753fSRafael Auler ErrorOr<Location> From = parseLocationOrOffset(); 1256a34c753fSRafael Auler if (std::error_code EC = From.getError()) 1257a34c753fSRafael Auler return EC; 1258a34c753fSRafael Auler 125940c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 126040c2e0faSMaksim Panchenko } 1261a34c753fSRafael Auler ErrorOr<Location> To = parseLocationOrOffset(); 1262a34c753fSRafael Auler if (std::error_code EC = To.getError()) 1263a34c753fSRafael Auler return EC; 1264a34c753fSRafael Auler 126540c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 126640c2e0faSMaksim Panchenko } 1267a34c753fSRafael Auler ErrorOr<int64_t> Frequency = 1268a34c753fSRafael Auler parseNumberField(FieldSeparator, Type != AggregatedLBREntry::BRANCH); 1269a34c753fSRafael Auler if (std::error_code EC = Frequency.getError()) 1270a34c753fSRafael Auler return EC; 1271a34c753fSRafael Auler 1272a34c753fSRafael Auler uint64_t Mispreds = 0; 1273a34c753fSRafael Auler if (Type == AggregatedLBREntry::BRANCH) { 127440c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 127540c2e0faSMaksim Panchenko } 1276a34c753fSRafael Auler ErrorOr<int64_t> MispredsOrErr = parseNumberField(FieldSeparator, true); 1277a34c753fSRafael Auler if (std::error_code EC = MispredsOrErr.getError()) 1278a34c753fSRafael Auler return EC; 1279a34c753fSRafael Auler Mispreds = static_cast<uint64_t>(MispredsOrErr.get()); 1280a34c753fSRafael Auler } 1281a34c753fSRafael Auler 1282a34c753fSRafael Auler if (!checkAndConsumeNewLine()) { 1283a34c753fSRafael Auler reportError("expected end of line"); 1284a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1285a34c753fSRafael Auler } 1286a34c753fSRafael Auler 1287a34c753fSRafael Auler return AggregatedLBREntry{From.get(), To.get(), 1288a34c753fSRafael Auler static_cast<uint64_t>(Frequency.get()), Mispreds, 1289a34c753fSRafael Auler Type}; 1290a34c753fSRafael Auler } 1291a34c753fSRafael Auler 1292a34c753fSRafael Auler bool DataAggregator::ignoreKernelInterrupt(LBREntry &LBR) const { 1293a34c753fSRafael Auler return opts::IgnoreInterruptLBR && 1294a34c753fSRafael Auler (LBR.From >= KernelBaseAddr || LBR.To >= KernelBaseAddr); 1295a34c753fSRafael Auler } 1296a34c753fSRafael Auler 1297a34c753fSRafael Auler std::error_code DataAggregator::printLBRHeatMap() { 1298a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n"; 1299a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName, 1300a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1301a34c753fSRafael Auler 1302a34c753fSRafael Auler if (opts::LinuxKernelMode) { 1303a34c753fSRafael Auler opts::HeatmapMaxAddress = 0xffffffffffffffff; 1304a34c753fSRafael Auler opts::HeatmapMinAddress = KernelBaseAddr; 1305a34c753fSRafael Auler } 1306a34c753fSRafael Auler Heatmap HM(opts::HeatmapBlock, opts::HeatmapMinAddress, 1307733dc3e5SRahman Lavaee opts::HeatmapMaxAddress, getTextSections(BC)); 1308a34c753fSRafael Auler uint64_t NumTotalSamples = 0; 1309a34c753fSRafael Auler 13100c13d97eSRahman Lavaee if (opts::BasicAggregation) { 1311e59e5801SRahman Lavaee while (hasData()) { 13120c13d97eSRahman Lavaee ErrorOr<PerfBasicSample> SampleRes = parseBasicSample(); 13130c13d97eSRahman Lavaee if (std::error_code EC = SampleRes.getError()) { 13140c13d97eSRahman Lavaee if (EC == errc::no_such_process) 13150c13d97eSRahman Lavaee continue; 13160c13d97eSRahman Lavaee return EC; 13170c13d97eSRahman Lavaee } 13180c13d97eSRahman Lavaee PerfBasicSample &Sample = SampleRes.get(); 13190c13d97eSRahman Lavaee HM.registerAddress(Sample.PC); 13200c13d97eSRahman Lavaee NumTotalSamples++; 1321e59e5801SRahman Lavaee } 1322e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " basic samples\n"; 13230c13d97eSRahman Lavaee } else { 1324e59e5801SRahman Lavaee while (hasData()) { 1325a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample(); 1326a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) { 1327a34c753fSRafael Auler if (EC == errc::no_such_process) 1328a34c753fSRafael Auler continue; 1329a34c753fSRafael Auler return EC; 1330a34c753fSRafael Auler } 1331a34c753fSRafael Auler 1332a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get(); 1333a34c753fSRafael Auler 1334a34c753fSRafael Auler // LBRs are stored in reverse execution order. NextLBR refers to the next 1335a34c753fSRafael Auler // executed branch record. 1336a34c753fSRafael Auler const LBREntry *NextLBR = nullptr; 1337a34c753fSRafael Auler for (const LBREntry &LBR : Sample.LBR) { 1338a34c753fSRafael Auler if (NextLBR) { 1339a34c753fSRafael Auler // Record fall-through trace. 1340a34c753fSRafael Auler const uint64_t TraceFrom = LBR.To; 1341a34c753fSRafael Auler const uint64_t TraceTo = NextLBR->From; 1342a34c753fSRafael Auler ++FallthroughLBRs[Trace(TraceFrom, TraceTo)].InternCount; 1343a34c753fSRafael Auler } 1344a34c753fSRafael Auler NextLBR = &LBR; 1345a34c753fSRafael Auler } 1346a34c753fSRafael Auler if (!Sample.LBR.empty()) { 1347a34c753fSRafael Auler HM.registerAddress(Sample.LBR.front().To); 1348a34c753fSRafael Auler HM.registerAddress(Sample.LBR.back().From); 1349a34c753fSRafael Auler } 1350a34c753fSRafael Auler NumTotalSamples += Sample.LBR.size(); 1351a34c753fSRafael Auler } 1352e59e5801SRahman Lavaee outs() << "HEATMAP: read " << NumTotalSamples << " LBR samples\n"; 1353e59e5801SRahman Lavaee outs() << "HEATMAP: " << FallthroughLBRs.size() << " unique traces\n"; 13540c13d97eSRahman Lavaee } 1355a34c753fSRafael Auler 1356a34c753fSRafael Auler if (!NumTotalSamples) { 1357e59e5801SRahman Lavaee if (opts::BasicAggregation) { 1358e59e5801SRahman Lavaee errs() << "HEATMAP-ERROR: no basic event samples detected in profile. " 1359e59e5801SRahman Lavaee "Cannot build heatmap."; 1360e59e5801SRahman Lavaee } else { 1361a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no LBR traces detected in profile. " 13620c13d97eSRahman Lavaee "Cannot build heatmap. Use -nl for building heatmap from " 13630c13d97eSRahman Lavaee "basic events.\n"; 13640c13d97eSRahman Lavaee } 1365a34c753fSRafael Auler exit(1); 1366a34c753fSRafael Auler } 1367a34c753fSRafael Auler 1368a34c753fSRafael Auler outs() << "HEATMAP: building heat map...\n"; 1369a34c753fSRafael Auler 1370a34c753fSRafael Auler for (const auto &LBR : FallthroughLBRs) { 1371a34c753fSRafael Auler const Trace &Trace = LBR.first; 1372a34c753fSRafael Auler const FTInfo &Info = LBR.second; 1373a34c753fSRafael Auler HM.registerAddressRange(Trace.From, Trace.To, Info.InternCount); 1374a34c753fSRafael Auler } 1375a34c753fSRafael Auler 1376a34c753fSRafael Auler if (HM.getNumInvalidRanges()) 1377a34c753fSRafael Auler outs() << "HEATMAP: invalid traces: " << HM.getNumInvalidRanges() << '\n'; 1378a34c753fSRafael Auler 1379a34c753fSRafael Auler if (!HM.size()) { 1380a34c753fSRafael Auler errs() << "HEATMAP-ERROR: no valid traces registered\n"; 1381a34c753fSRafael Auler exit(1); 1382a34c753fSRafael Auler } 1383a34c753fSRafael Auler 13845c2ae5f4SVladislav Khmelevsky HM.print(opts::OutputFilename); 13855c2ae5f4SVladislav Khmelevsky if (opts::OutputFilename == "-") 13865c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename); 1387def464aaSAmir Ayupov else 13885c2ae5f4SVladislav Khmelevsky HM.printCDF(opts::OutputFilename + ".csv"); 1389733dc3e5SRahman Lavaee if (opts::OutputFilename == "-") 1390733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename); 1391733dc3e5SRahman Lavaee else 1392733dc3e5SRahman Lavaee HM.printSectionHotness(opts::OutputFilename + "-section-hotness.csv"); 1393a34c753fSRafael Auler 1394a34c753fSRafael Auler return std::error_code(); 1395a34c753fSRafael Auler } 1396a34c753fSRafael Auler 1397a34c753fSRafael Auler std::error_code DataAggregator::parseBranchEvents() { 1398a34c753fSRafael Auler outs() << "PERF2BOLT: parse branch events...\n"; 1399a34c753fSRafael Auler NamedRegionTimer T("parseBranch", "Parsing branch events", TimerGroupName, 1400a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1401a34c753fSRafael Auler 1402a34c753fSRafael Auler uint64_t NumTotalSamples = 0; 1403a34c753fSRafael Auler uint64_t NumEntries = 0; 1404a34c753fSRafael Auler uint64_t NumSamples = 0; 1405a34c753fSRafael Auler uint64_t NumSamplesNoLBR = 0; 1406a34c753fSRafael Auler uint64_t NumTraces = 0; 1407a34c753fSRafael Auler bool NeedsSkylakeFix = false; 1408a34c753fSRafael Auler 1409a34c753fSRafael Auler while (hasData() && NumTotalSamples < opts::MaxSamples) { 1410a34c753fSRafael Auler ++NumTotalSamples; 1411a34c753fSRafael Auler 1412a34c753fSRafael Auler ErrorOr<PerfBranchSample> SampleRes = parseBranchSample(); 1413a34c753fSRafael Auler if (std::error_code EC = SampleRes.getError()) { 1414a34c753fSRafael Auler if (EC == errc::no_such_process) 1415a34c753fSRafael Auler continue; 1416a34c753fSRafael Auler return EC; 1417a34c753fSRafael Auler } 1418a34c753fSRafael Auler ++NumSamples; 1419a34c753fSRafael Auler 1420a34c753fSRafael Auler PerfBranchSample &Sample = SampleRes.get(); 1421a34c753fSRafael Auler if (opts::WriteAutoFDOData) 1422a34c753fSRafael Auler ++BasicSamples[Sample.PC]; 1423a34c753fSRafael Auler 1424a34c753fSRafael Auler if (Sample.LBR.empty()) { 1425a34c753fSRafael Auler ++NumSamplesNoLBR; 1426a34c753fSRafael Auler continue; 1427a34c753fSRafael Auler } 1428a34c753fSRafael Auler 1429a34c753fSRafael Auler NumEntries += Sample.LBR.size(); 1430a34c753fSRafael Auler if (BAT && Sample.LBR.size() == 32 && !NeedsSkylakeFix) { 1431a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using Intel Skylake bug workaround\n"; 1432a34c753fSRafael Auler NeedsSkylakeFix = true; 1433a34c753fSRafael Auler } 1434a34c753fSRafael Auler 1435a34c753fSRafael Auler // LBRs are stored in reverse execution order. NextPC refers to the next 1436a34c753fSRafael Auler // recorded executed PC. 1437a34c753fSRafael Auler uint64_t NextPC = opts::UseEventPC ? Sample.PC : 0; 1438a34c753fSRafael Auler uint32_t NumEntry = 0; 1439a34c753fSRafael Auler for (const LBREntry &LBR : Sample.LBR) { 1440a34c753fSRafael Auler ++NumEntry; 1441a34c753fSRafael Auler // Hardware bug workaround: Intel Skylake (which has 32 LBR entries) 1442a34c753fSRafael Auler // sometimes record entry 32 as an exact copy of entry 31. This will cause 1443a34c753fSRafael Auler // us to likely record an invalid trace and generate a stale function for 1444a34c753fSRafael Auler // BAT mode (non BAT disassembles the function and is able to ignore this 1445a34c753fSRafael Auler // trace at aggregation time). Drop first 2 entries (last two, in 1446a34c753fSRafael Auler // chronological order) 1447a34c753fSRafael Auler if (NeedsSkylakeFix && NumEntry <= 2) 1448a34c753fSRafael Auler continue; 1449a34c753fSRafael Auler if (NextPC) { 1450a34c753fSRafael Auler // Record fall-through trace. 1451a34c753fSRafael Auler const uint64_t TraceFrom = LBR.To; 1452a34c753fSRafael Auler const uint64_t TraceTo = NextPC; 1453a34c753fSRafael Auler const BinaryFunction *TraceBF = 1454a34c753fSRafael Auler getBinaryFunctionContainingAddress(TraceFrom); 1455a34c753fSRafael Auler if (TraceBF && TraceBF->containsAddress(TraceTo)) { 1456a34c753fSRafael Auler FTInfo &Info = FallthroughLBRs[Trace(TraceFrom, TraceTo)]; 1457def464aaSAmir Ayupov if (TraceBF->containsAddress(LBR.From)) 1458a34c753fSRafael Auler ++Info.InternCount; 1459def464aaSAmir Ayupov else 1460a34c753fSRafael Auler ++Info.ExternCount; 1461a34c753fSRafael Auler } else { 1462a34c753fSRafael Auler if (TraceBF && getBinaryFunctionContainingAddress(TraceTo)) { 1463a34c753fSRafael Auler LLVM_DEBUG(dbgs() 1464a34c753fSRafael Auler << "Invalid trace starting in " 1465a34c753fSRafael Auler << TraceBF->getPrintName() << " @ " 1466a34c753fSRafael Auler << Twine::utohexstr(TraceFrom - TraceBF->getAddress()) 1467a34c753fSRafael Auler << " and ending @ " << Twine::utohexstr(TraceTo) 1468a34c753fSRafael Auler << '\n'); 1469a34c753fSRafael Auler ++NumInvalidTraces; 1470a34c753fSRafael Auler } else { 1471a34c753fSRafael Auler LLVM_DEBUG(dbgs() 1472a34c753fSRafael Auler << "Out of range trace starting in " 1473a34c753fSRafael Auler << (TraceBF ? TraceBF->getPrintName() : "None") << " @ " 1474a34c753fSRafael Auler << Twine::utohexstr( 1475a34c753fSRafael Auler TraceFrom - (TraceBF ? TraceBF->getAddress() : 0)) 1476a34c753fSRafael Auler << " and ending in " 1477a34c753fSRafael Auler << (getBinaryFunctionContainingAddress(TraceTo) 1478a34c753fSRafael Auler ? getBinaryFunctionContainingAddress(TraceTo) 1479a34c753fSRafael Auler ->getPrintName() 1480a34c753fSRafael Auler : "None") 1481a34c753fSRafael Auler << " @ " 1482a34c753fSRafael Auler << Twine::utohexstr( 1483a34c753fSRafael Auler TraceTo - 1484a34c753fSRafael Auler (getBinaryFunctionContainingAddress(TraceTo) 1485a34c753fSRafael Auler ? getBinaryFunctionContainingAddress(TraceTo) 1486a34c753fSRafael Auler ->getAddress() 1487a34c753fSRafael Auler : 0)) 1488a34c753fSRafael Auler << '\n'); 1489a34c753fSRafael Auler ++NumLongRangeTraces; 1490a34c753fSRafael Auler } 1491a34c753fSRafael Auler } 1492a34c753fSRafael Auler ++NumTraces; 1493a34c753fSRafael Auler } 1494a34c753fSRafael Auler NextPC = LBR.From; 1495a34c753fSRafael Auler 1496a34c753fSRafael Auler uint64_t From = LBR.From; 1497a34c753fSRafael Auler if (!getBinaryFunctionContainingAddress(From)) 1498a34c753fSRafael Auler From = 0; 1499a34c753fSRafael Auler uint64_t To = LBR.To; 1500a34c753fSRafael Auler if (!getBinaryFunctionContainingAddress(To)) 1501a34c753fSRafael Auler To = 0; 1502a34c753fSRafael Auler if (!From && !To) 1503a34c753fSRafael Auler continue; 1504a34c753fSRafael Auler BranchInfo &Info = BranchLBRs[Trace(From, To)]; 1505a34c753fSRafael Auler ++Info.TakenCount; 1506a34c753fSRafael Auler Info.MispredCount += LBR.Mispred; 1507a34c753fSRafael Auler } 1508a34c753fSRafael Auler } 1509a34c753fSRafael Auler 1510a34c753fSRafael Auler for (const auto &LBR : BranchLBRs) { 1511a34c753fSRafael Auler const Trace &Trace = LBR.first; 1512a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Trace.From)) 1513a34c753fSRafael Auler BF->setHasProfileAvailable(); 1514a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Trace.To)) 1515a34c753fSRafael Auler BF->setHasProfileAvailable(); 1516a34c753fSRafael Auler } 1517a34c753fSRafael Auler 1518a34c753fSRafael Auler auto printColored = [](raw_ostream &OS, float Percent, float T1, float T2) { 1519a34c753fSRafael Auler OS << " ("; 1520a34c753fSRafael Auler if (OS.has_colors()) { 1521def464aaSAmir Ayupov if (Percent > T2) 1522a34c753fSRafael Auler OS.changeColor(raw_ostream::RED); 1523def464aaSAmir Ayupov else if (Percent > T1) 1524a34c753fSRafael Auler OS.changeColor(raw_ostream::YELLOW); 1525def464aaSAmir Ayupov else 1526a34c753fSRafael Auler OS.changeColor(raw_ostream::GREEN); 1527a34c753fSRafael Auler } 1528a34c753fSRafael Auler OS << format("%.1f%%", Percent); 1529a34c753fSRafael Auler if (OS.has_colors()) 1530a34c753fSRafael Auler OS.resetColor(); 1531a34c753fSRafael Auler OS << ")"; 1532a34c753fSRafael Auler }; 1533a34c753fSRafael Auler 153440c2e0faSMaksim Panchenko outs() << "PERF2BOLT: read " << NumSamples << " samples and " << NumEntries 153540c2e0faSMaksim Panchenko << " LBR entries\n"; 1536a34c753fSRafael Auler if (NumTotalSamples) { 1537a34c753fSRafael Auler if (NumSamples && NumSamplesNoLBR == NumSamples) { 1538a34c753fSRafael Auler // Note: we don't know if perf2bolt is being used to parse memory samples 1539a34c753fSRafael Auler // at this point. In this case, it is OK to parse zero LBRs. 1540a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: all recorded samples for this binary lack " 1541a34c753fSRafael Auler "LBR. Record profile with perf record -j any or run perf2bolt " 1542a34c753fSRafael Auler "in no-LBR mode with -nl (the performance improvement in -nl " 1543a34c753fSRafael Auler "mode may be limited)\n"; 1544a34c753fSRafael Auler } else { 1545a34c753fSRafael Auler const uint64_t IgnoredSamples = NumTotalSamples - NumSamples; 1546a34c753fSRafael Auler const float PercentIgnored = 100.0f * IgnoredSamples / NumTotalSamples; 1547a34c753fSRafael Auler outs() << "PERF2BOLT: " << IgnoredSamples << " samples"; 1548a34c753fSRafael Auler printColored(outs(), PercentIgnored, 20, 50); 1549a34c753fSRafael Auler outs() << " were ignored\n"; 1550def464aaSAmir Ayupov if (PercentIgnored > 50.0f) 1551a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: less than 50% of all recorded samples " 1552a34c753fSRafael Auler "were attributed to the input binary\n"; 1553a34c753fSRafael Auler } 1554a34c753fSRafael Auler } 1555a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: " 1556a34c753fSRafael Auler << NumInvalidTraces; 1557a34c753fSRafael Auler float Perc = 0.0f; 1558a34c753fSRafael Auler if (NumTraces > 0) { 1559a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces; 1560a34c753fSRafael Auler printColored(outs(), Perc, 5, 10); 1561a34c753fSRafael Auler } 1562a34c753fSRafael Auler outs() << "\n"; 1563def464aaSAmir Ayupov if (Perc > 10.0f) 1564a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1565a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1566a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1567a34c753fSRafael Auler "performance.\n\n"; 1568a34c753fSRafael Auler 1569a34c753fSRafael Auler outs() << "PERF2BOLT: out of range traces involving unknown regions: " 1570a34c753fSRafael Auler << NumLongRangeTraces; 1571def464aaSAmir Ayupov if (NumTraces > 0) 1572a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces); 1573a34c753fSRafael Auler outs() << "\n"; 1574a34c753fSRafael Auler 1575a34c753fSRafael Auler if (NumColdSamples > 0) { 1576a34c753fSRafael Auler const float ColdSamples = NumColdSamples * 100.0f / NumTotalSamples; 1577a34c753fSRafael Auler outs() << "PERF2BOLT: " << NumColdSamples 1578a34c753fSRafael Auler << format(" (%.1f%%)", ColdSamples) 1579a34c753fSRafael Auler << " samples recorded in cold regions of split functions.\n"; 1580def464aaSAmir Ayupov if (ColdSamples > 5.0f) 1581a34c753fSRafael Auler outs() 1582a34c753fSRafael Auler << "WARNING: The BOLT-processed binary where samples were collected " 1583a34c753fSRafael Auler "likely used bad data or your service observed a large shift in " 1584a34c753fSRafael Auler "profile. You may want to audit this.\n"; 1585a34c753fSRafael Auler } 1586a34c753fSRafael Auler 1587a34c753fSRafael Auler return std::error_code(); 1588a34c753fSRafael Auler } 1589a34c753fSRafael Auler 1590a34c753fSRafael Auler void DataAggregator::processBranchEvents() { 1591a34c753fSRafael Auler outs() << "PERF2BOLT: processing branch events...\n"; 1592a34c753fSRafael Auler NamedRegionTimer T("processBranch", "Processing branch events", 1593a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1594a34c753fSRafael Auler 1595a34c753fSRafael Auler for (const auto &AggrLBR : FallthroughLBRs) { 1596a34c753fSRafael Auler const Trace &Loc = AggrLBR.first; 1597a34c753fSRafael Auler const FTInfo &Info = AggrLBR.second; 1598a34c753fSRafael Auler LBREntry First{Loc.From, Loc.From, false}; 1599a34c753fSRafael Auler LBREntry Second{Loc.To, Loc.To, false}; 1600def464aaSAmir Ayupov if (Info.InternCount) 1601a34c753fSRafael Auler doTrace(First, Second, Info.InternCount); 1602a34c753fSRafael Auler if (Info.ExternCount) { 1603a34c753fSRafael Auler First.From = 0; 1604a34c753fSRafael Auler doTrace(First, Second, Info.ExternCount); 1605a34c753fSRafael Auler } 1606a34c753fSRafael Auler } 1607a34c753fSRafael Auler 1608a34c753fSRafael Auler for (const auto &AggrLBR : BranchLBRs) { 1609a34c753fSRafael Auler const Trace &Loc = AggrLBR.first; 1610a34c753fSRafael Auler const BranchInfo &Info = AggrLBR.second; 1611a34c753fSRafael Auler doBranch(Loc.From, Loc.To, Info.TakenCount, Info.MispredCount); 1612a34c753fSRafael Auler } 1613a34c753fSRafael Auler } 1614a34c753fSRafael Auler 1615a34c753fSRafael Auler std::error_code DataAggregator::parseBasicEvents() { 1616a34c753fSRafael Auler outs() << "PERF2BOLT: parsing basic events (without LBR)...\n"; 1617a34c753fSRafael Auler NamedRegionTimer T("parseBasic", "Parsing basic events", TimerGroupName, 1618a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1619a34c753fSRafael Auler while (hasData()) { 1620a34c753fSRafael Auler ErrorOr<PerfBasicSample> Sample = parseBasicSample(); 1621a34c753fSRafael Auler if (std::error_code EC = Sample.getError()) 1622a34c753fSRafael Auler return EC; 1623a34c753fSRafael Auler 1624a34c753fSRafael Auler if (!Sample->PC) 1625a34c753fSRafael Auler continue; 1626a34c753fSRafael Auler 1627a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC)) 1628a34c753fSRafael Auler BF->setHasProfileAvailable(); 1629a34c753fSRafael Auler 1630a34c753fSRafael Auler ++BasicSamples[Sample->PC]; 1631a34c753fSRafael Auler EventNames.insert(Sample->EventName); 1632a34c753fSRafael Auler } 1633a34c753fSRafael Auler 1634a34c753fSRafael Auler return std::error_code(); 1635a34c753fSRafael Auler } 1636a34c753fSRafael Auler 1637a34c753fSRafael Auler void DataAggregator::processBasicEvents() { 1638a34c753fSRafael Auler outs() << "PERF2BOLT: processing basic events (without LBR)...\n"; 163940c2e0faSMaksim Panchenko NamedRegionTimer T("processBasic", "Processing basic events", TimerGroupName, 164040c2e0faSMaksim Panchenko TimerGroupDesc, opts::TimeAggregator); 1641a34c753fSRafael Auler uint64_t OutOfRangeSamples = 0; 1642a34c753fSRafael Auler uint64_t NumSamples = 0; 1643a34c753fSRafael Auler for (auto &Sample : BasicSamples) { 1644a34c753fSRafael Auler const uint64_t PC = Sample.first; 1645a34c753fSRafael Auler const uint64_t HitCount = Sample.second; 1646a34c753fSRafael Auler NumSamples += HitCount; 1647a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC); 1648a34c753fSRafael Auler if (!Func) { 1649a34c753fSRafael Auler OutOfRangeSamples += HitCount; 1650a34c753fSRafael Auler continue; 1651a34c753fSRafael Auler } 1652a34c753fSRafael Auler 1653a34c753fSRafael Auler doSample(*Func, PC, HitCount); 1654a34c753fSRafael Auler } 1655a34c753fSRafael Auler outs() << "PERF2BOLT: read " << NumSamples << " samples\n"; 1656a34c753fSRafael Auler 1657a34c753fSRafael Auler outs() << "PERF2BOLT: out of range samples recorded in unknown regions: " 1658a34c753fSRafael Auler << OutOfRangeSamples; 1659a34c753fSRafael Auler float Perc = 0.0f; 1660a34c753fSRafael Auler if (NumSamples > 0) { 1661a34c753fSRafael Auler outs() << " ("; 1662a34c753fSRafael Auler Perc = OutOfRangeSamples * 100.0f / NumSamples; 1663a34c753fSRafael Auler if (outs().has_colors()) { 1664def464aaSAmir Ayupov if (Perc > 60.0f) 1665a34c753fSRafael Auler outs().changeColor(raw_ostream::RED); 1666def464aaSAmir Ayupov else if (Perc > 40.0f) 1667a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW); 1668def464aaSAmir Ayupov else 1669a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN); 1670a34c753fSRafael Auler } 1671a34c753fSRafael Auler outs() << format("%.1f%%", Perc); 1672a34c753fSRafael Auler if (outs().has_colors()) 1673a34c753fSRafael Auler outs().resetColor(); 1674a34c753fSRafael Auler outs() << ")"; 1675a34c753fSRafael Auler } 1676a34c753fSRafael Auler outs() << "\n"; 1677def464aaSAmir Ayupov if (Perc > 80.0f) 1678a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1679a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1680a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1681a34c753fSRafael Auler "performance.\n\n"; 1682a34c753fSRafael Auler } 1683a34c753fSRafael Auler 1684a34c753fSRafael Auler std::error_code DataAggregator::parseMemEvents() { 1685a34c753fSRafael Auler outs() << "PERF2BOLT: parsing memory events...\n"; 1686a34c753fSRafael Auler NamedRegionTimer T("parseMemEvents", "Parsing mem events", TimerGroupName, 1687a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1688a34c753fSRafael Auler while (hasData()) { 1689a34c753fSRafael Auler ErrorOr<PerfMemSample> Sample = parseMemSample(); 1690a34c753fSRafael Auler if (std::error_code EC = Sample.getError()) 1691a34c753fSRafael Auler return EC; 1692a34c753fSRafael Auler 1693a34c753fSRafael Auler if (BinaryFunction *BF = getBinaryFunctionContainingAddress(Sample->PC)) 1694a34c753fSRafael Auler BF->setHasProfileAvailable(); 1695a34c753fSRafael Auler 1696a34c753fSRafael Auler MemSamples.emplace_back(std::move(Sample.get())); 1697a34c753fSRafael Auler } 1698a34c753fSRafael Auler 1699a34c753fSRafael Auler return std::error_code(); 1700a34c753fSRafael Auler } 1701a34c753fSRafael Auler 1702a34c753fSRafael Auler void DataAggregator::processMemEvents() { 1703a34c753fSRafael Auler NamedRegionTimer T("ProcessMemEvents", "Processing mem events", 1704a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1705a34c753fSRafael Auler for (const PerfMemSample &Sample : MemSamples) { 1706a34c753fSRafael Auler uint64_t PC = Sample.PC; 1707a34c753fSRafael Auler uint64_t Addr = Sample.Addr; 1708a34c753fSRafael Auler StringRef FuncName; 1709a34c753fSRafael Auler StringRef MemName; 1710a34c753fSRafael Auler 1711a34c753fSRafael Auler // Try to resolve symbol for PC 1712a34c753fSRafael Auler BinaryFunction *Func = getBinaryFunctionContainingAddress(PC); 1713a34c753fSRafael Auler if (!Func) { 1714a34c753fSRafael Auler LLVM_DEBUG(if (PC != 0) { 1715a34c753fSRafael Auler dbgs() << "Skipped mem event: 0x" << Twine::utohexstr(PC) << " => 0x" 1716a34c753fSRafael Auler << Twine::utohexstr(Addr) << "\n"; 1717a34c753fSRafael Auler }); 1718a34c753fSRafael Auler continue; 1719a34c753fSRafael Auler } 1720a34c753fSRafael Auler 1721a34c753fSRafael Auler FuncName = Func->getOneName(); 1722a34c753fSRafael Auler PC -= Func->getAddress(); 1723a34c753fSRafael Auler 1724a34c753fSRafael Auler // Try to resolve symbol for memory load 1725a34c753fSRafael Auler if (BinaryData *BD = BC->getBinaryDataContainingAddress(Addr)) { 1726a34c753fSRafael Auler MemName = BD->getName(); 1727a34c753fSRafael Auler Addr -= BD->getAddress(); 1728a34c753fSRafael Auler } else if (opts::FilterMemProfile) { 1729a34c753fSRafael Auler // Filter out heap/stack accesses 1730a34c753fSRafael Auler continue; 1731a34c753fSRafael Auler } 1732a34c753fSRafael Auler 1733a34c753fSRafael Auler const Location FuncLoc(!FuncName.empty(), FuncName, PC); 1734a34c753fSRafael Auler const Location AddrLoc(!MemName.empty(), MemName, Addr); 1735a34c753fSRafael Auler 1736a34c753fSRafael Auler FuncMemData *MemData = &NamesToMemEvents[FuncName]; 1737a34c753fSRafael Auler setMemData(*Func, MemData); 1738a34c753fSRafael Auler MemData->update(FuncLoc, AddrLoc); 1739a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "Mem event: " << FuncLoc << " = " << AddrLoc << "\n"); 1740a34c753fSRafael Auler } 1741a34c753fSRafael Auler } 1742a34c753fSRafael Auler 1743a34c753fSRafael Auler std::error_code DataAggregator::parsePreAggregatedLBRSamples() { 1744a34c753fSRafael Auler outs() << "PERF2BOLT: parsing pre-aggregated profile...\n"; 1745a34c753fSRafael Auler NamedRegionTimer T("parseAggregated", "Parsing aggregated branch events", 1746a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1747a34c753fSRafael Auler while (hasData()) { 1748a34c753fSRafael Auler ErrorOr<AggregatedLBREntry> AggrEntry = parseAggregatedLBREntry(); 1749a34c753fSRafael Auler if (std::error_code EC = AggrEntry.getError()) 1750a34c753fSRafael Auler return EC; 1751a34c753fSRafael Auler 1752a34c753fSRafael Auler if (BinaryFunction *BF = 1753a34c753fSRafael Auler getBinaryFunctionContainingAddress(AggrEntry->From.Offset)) 1754a34c753fSRafael Auler BF->setHasProfileAvailable(); 1755a34c753fSRafael Auler if (BinaryFunction *BF = 1756a34c753fSRafael Auler getBinaryFunctionContainingAddress(AggrEntry->To.Offset)) 1757a34c753fSRafael Auler BF->setHasProfileAvailable(); 1758a34c753fSRafael Auler 1759a34c753fSRafael Auler AggregatedLBRs.emplace_back(std::move(AggrEntry.get())); 1760a34c753fSRafael Auler } 1761a34c753fSRafael Auler 1762a34c753fSRafael Auler return std::error_code(); 1763a34c753fSRafael Auler } 1764a34c753fSRafael Auler 1765a34c753fSRafael Auler void DataAggregator::processPreAggregated() { 1766a34c753fSRafael Auler outs() << "PERF2BOLT: processing pre-aggregated profile...\n"; 1767a34c753fSRafael Auler NamedRegionTimer T("processAggregated", "Processing aggregated branch events", 1768a34c753fSRafael Auler TimerGroupName, TimerGroupDesc, opts::TimeAggregator); 1769a34c753fSRafael Auler 1770a34c753fSRafael Auler uint64_t NumTraces = 0; 1771a34c753fSRafael Auler for (const AggregatedLBREntry &AggrEntry : AggregatedLBRs) { 1772a34c753fSRafael Auler switch (AggrEntry.EntryType) { 1773a34c753fSRafael Auler case AggregatedLBREntry::BRANCH: 1774a34c753fSRafael Auler doBranch(AggrEntry.From.Offset, AggrEntry.To.Offset, AggrEntry.Count, 1775a34c753fSRafael Auler AggrEntry.Mispreds); 1776a34c753fSRafael Auler break; 1777a34c753fSRafael Auler case AggregatedLBREntry::FT: 1778a34c753fSRafael Auler case AggregatedLBREntry::FT_EXTERNAL_ORIGIN: { 1779a34c753fSRafael Auler LBREntry First{AggrEntry.EntryType == AggregatedLBREntry::FT 1780a34c753fSRafael Auler ? AggrEntry.From.Offset 1781a34c753fSRafael Auler : 0, 1782a34c753fSRafael Auler AggrEntry.From.Offset, false}; 1783a34c753fSRafael Auler LBREntry Second{AggrEntry.To.Offset, AggrEntry.To.Offset, false}; 1784a34c753fSRafael Auler doTrace(First, Second, AggrEntry.Count); 1785a34c753fSRafael Auler NumTraces += AggrEntry.Count; 1786a34c753fSRafael Auler break; 1787a34c753fSRafael Auler } 1788a34c753fSRafael Auler } 1789a34c753fSRafael Auler } 1790a34c753fSRafael Auler 1791a34c753fSRafael Auler outs() << "PERF2BOLT: read " << AggregatedLBRs.size() 1792a34c753fSRafael Auler << " aggregated LBR entries\n"; 1793a34c753fSRafael Auler outs() << "PERF2BOLT: traces mismatching disassembled function contents: " 1794a34c753fSRafael Auler << NumInvalidTraces; 1795a34c753fSRafael Auler float Perc = 0.0f; 1796a34c753fSRafael Auler if (NumTraces > 0) { 1797a34c753fSRafael Auler outs() << " ("; 1798a34c753fSRafael Auler Perc = NumInvalidTraces * 100.0f / NumTraces; 1799a34c753fSRafael Auler if (outs().has_colors()) { 1800def464aaSAmir Ayupov if (Perc > 10.0f) 1801a34c753fSRafael Auler outs().changeColor(raw_ostream::RED); 1802def464aaSAmir Ayupov else if (Perc > 5.0f) 1803a34c753fSRafael Auler outs().changeColor(raw_ostream::YELLOW); 1804def464aaSAmir Ayupov else 1805a34c753fSRafael Auler outs().changeColor(raw_ostream::GREEN); 1806a34c753fSRafael Auler } 1807a34c753fSRafael Auler outs() << format("%.1f%%", Perc); 1808a34c753fSRafael Auler if (outs().has_colors()) 1809a34c753fSRafael Auler outs().resetColor(); 1810a34c753fSRafael Auler outs() << ")"; 1811a34c753fSRafael Auler } 1812a34c753fSRafael Auler outs() << "\n"; 1813def464aaSAmir Ayupov if (Perc > 10.0f) 1814a34c753fSRafael Auler outs() << "\n !! WARNING !! This high mismatch ratio indicates the input " 1815a34c753fSRafael Auler "binary is probably not the same binary used during profiling " 1816a34c753fSRafael Auler "collection. The generated data may be ineffective for improving " 1817a34c753fSRafael Auler "performance.\n\n"; 1818a34c753fSRafael Auler 1819a34c753fSRafael Auler outs() << "PERF2BOLT: Out of range traces involving unknown regions: " 1820a34c753fSRafael Auler << NumLongRangeTraces; 1821def464aaSAmir Ayupov if (NumTraces > 0) 1822a34c753fSRafael Auler outs() << format(" (%.1f%%)", NumLongRangeTraces * 100.0f / NumTraces); 1823a34c753fSRafael Auler outs() << "\n"; 1824a34c753fSRafael Auler } 1825a34c753fSRafael Auler 1826*835a9c28SAmir Ayupov std::optional<int32_t> DataAggregator::parseCommExecEvent() { 1827a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1828a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1829a34c753fSRafael Auler reportError("expected rest of line"); 1830a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1831e324a80fSKazu Hirata return std::nullopt; 1832a34c753fSRafael Auler } 1833a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1834a34c753fSRafael Auler 1835a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_COMM exec"); 1836def464aaSAmir Ayupov if (Pos == StringRef::npos) 1837e324a80fSKazu Hirata return std::nullopt; 1838a34c753fSRafael Auler Line = Line.drop_front(Pos); 1839a34c753fSRafael Auler 1840a34c753fSRafael Auler // Line: 1841a34c753fSRafael Auler // PERF_RECORD_COMM exec: <name>:<pid>/<tid>" 1842a34c753fSRafael Auler StringRef PIDStr = Line.rsplit(':').second.split('/').first; 1843ae585be1SRafael Auler int32_t PID; 1844a34c753fSRafael Auler if (PIDStr.getAsInteger(10, PID)) { 1845a34c753fSRafael Auler reportError("expected PID"); 1846a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n"; 1847e324a80fSKazu Hirata return std::nullopt; 1848a34c753fSRafael Auler } 1849a34c753fSRafael Auler 1850a34c753fSRafael Auler return PID; 1851a34c753fSRafael Auler } 1852a34c753fSRafael Auler 1853a34c753fSRafael Auler namespace { 1854*835a9c28SAmir Ayupov std::optional<uint64_t> parsePerfTime(const StringRef TimeStr) { 1855a34c753fSRafael Auler const StringRef SecTimeStr = TimeStr.split('.').first; 1856a34c753fSRafael Auler const StringRef USecTimeStr = TimeStr.split('.').second; 1857a34c753fSRafael Auler uint64_t SecTime; 1858a34c753fSRafael Auler uint64_t USecTime; 1859a34c753fSRafael Auler if (SecTimeStr.getAsInteger(10, SecTime) || 1860def464aaSAmir Ayupov USecTimeStr.getAsInteger(10, USecTime)) 1861e324a80fSKazu Hirata return std::nullopt; 1862a34c753fSRafael Auler return SecTime * 1000000ULL + USecTime; 1863a34c753fSRafael Auler } 1864a34c753fSRafael Auler } 1865a34c753fSRafael Auler 1866*835a9c28SAmir Ayupov std::optional<DataAggregator::ForkInfo> DataAggregator::parseForkEvent() { 186740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 186840c2e0faSMaksim Panchenko } 1869a34c753fSRafael Auler 1870a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1871a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1872a34c753fSRafael Auler reportError("expected rest of line"); 1873a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1874e324a80fSKazu Hirata return std::nullopt; 1875a34c753fSRafael Auler } 1876a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1877a34c753fSRafael Auler 1878a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_FORK"); 1879a34c753fSRafael Auler if (Pos == StringRef::npos) { 1880a34c753fSRafael Auler consumeRestOfLine(); 1881e324a80fSKazu Hirata return std::nullopt; 1882a34c753fSRafael Auler } 1883a34c753fSRafael Auler 1884a34c753fSRafael Auler ForkInfo FI; 1885a34c753fSRafael Auler 1886a34c753fSRafael Auler const StringRef TimeStr = 1887a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second; 1888*835a9c28SAmir Ayupov if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) { 1889a34c753fSRafael Auler FI.Time = *TimeRes; 1890a34c753fSRafael Auler } 1891a34c753fSRafael Auler 1892a34c753fSRafael Auler Line = Line.drop_front(Pos); 1893a34c753fSRafael Auler 1894a34c753fSRafael Auler // Line: 1895a34c753fSRafael Auler // PERF_RECORD_FORK(<child_pid>:<child_tid>):(<parent_pid>:<parent_tid>) 1896a34c753fSRafael Auler const StringRef ChildPIDStr = Line.split('(').second.split(':').first; 1897a34c753fSRafael Auler if (ChildPIDStr.getAsInteger(10, FI.ChildPID)) { 1898a34c753fSRafael Auler reportError("expected PID"); 1899a34c753fSRafael Auler Diag << "Found: " << ChildPIDStr << "in '" << Line << "'\n"; 1900e324a80fSKazu Hirata return std::nullopt; 1901a34c753fSRafael Auler } 1902a34c753fSRafael Auler 1903a34c753fSRafael Auler const StringRef ParentPIDStr = Line.rsplit('(').second.split(':').first; 1904a34c753fSRafael Auler if (ParentPIDStr.getAsInteger(10, FI.ParentPID)) { 1905a34c753fSRafael Auler reportError("expected PID"); 1906a34c753fSRafael Auler Diag << "Found: " << ParentPIDStr << "in '" << Line << "'\n"; 1907e324a80fSKazu Hirata return std::nullopt; 1908a34c753fSRafael Auler } 1909a34c753fSRafael Auler 1910a34c753fSRafael Auler consumeRestOfLine(); 1911a34c753fSRafael Auler 1912a34c753fSRafael Auler return FI; 1913a34c753fSRafael Auler } 1914a34c753fSRafael Auler 1915a34c753fSRafael Auler ErrorOr<std::pair<StringRef, DataAggregator::MMapInfo>> 1916a34c753fSRafael Auler DataAggregator::parseMMapEvent() { 191740c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 191840c2e0faSMaksim Panchenko } 1919a34c753fSRafael Auler 1920a34c753fSRafael Auler MMapInfo ParsedInfo; 1921a34c753fSRafael Auler 1922a34c753fSRafael Auler size_t LineEnd = ParsingBuf.find_first_of("\n"); 1923a34c753fSRafael Auler if (LineEnd == StringRef::npos) { 1924a34c753fSRafael Auler reportError("expected rest of line"); 1925a34c753fSRafael Auler Diag << "Found: " << ParsingBuf << "\n"; 1926a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1927a34c753fSRafael Auler } 1928a34c753fSRafael Auler StringRef Line = ParsingBuf.substr(0, LineEnd); 1929a34c753fSRafael Auler 1930a34c753fSRafael Auler size_t Pos = Line.find("PERF_RECORD_MMAP2"); 1931a34c753fSRafael Auler if (Pos == StringRef::npos) { 1932a34c753fSRafael Auler consumeRestOfLine(); 1933a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo); 1934a34c753fSRafael Auler } 1935a34c753fSRafael Auler 1936a34c753fSRafael Auler // Line: 1937a34c753fSRafael Auler // {<name> .* <sec>.<usec>: }PERF_RECORD_MMAP2 <pid>/<tid>: .* <file_name> 1938a34c753fSRafael Auler 1939a34c753fSRafael Auler const StringRef TimeStr = 1940a34c753fSRafael Auler Line.substr(0, Pos).rsplit(':').first.rsplit(FieldSeparator).second; 1941*835a9c28SAmir Ayupov if (std::optional<uint64_t> TimeRes = parsePerfTime(TimeStr)) 1942a34c753fSRafael Auler ParsedInfo.Time = *TimeRes; 1943a34c753fSRafael Auler 1944a34c753fSRafael Auler Line = Line.drop_front(Pos); 1945a34c753fSRafael Auler 1946a34c753fSRafael Auler // Line: 1947a34c753fSRafael Auler // PERF_RECORD_MMAP2 <pid>/<tid>: [<hexbase>(<hexsize>) .*]: .* <file_name> 1948a34c753fSRafael Auler 1949a34c753fSRafael Auler StringRef FileName = Line.rsplit(FieldSeparator).second; 1950a34c753fSRafael Auler if (FileName.startswith("//") || FileName.startswith("[")) { 1951a34c753fSRafael Auler consumeRestOfLine(); 1952a34c753fSRafael Auler return std::make_pair(StringRef(), ParsedInfo); 1953a34c753fSRafael Auler } 1954a34c753fSRafael Auler FileName = sys::path::filename(FileName); 1955a34c753fSRafael Auler 1956a34c753fSRafael Auler const StringRef PIDStr = Line.split(FieldSeparator).second.split('/').first; 1957a34c753fSRafael Auler if (PIDStr.getAsInteger(10, ParsedInfo.PID)) { 1958a34c753fSRafael Auler reportError("expected PID"); 1959a34c753fSRafael Auler Diag << "Found: " << PIDStr << "in '" << Line << "'\n"; 1960a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1961a34c753fSRafael Auler } 1962a34c753fSRafael Auler 1963a34c753fSRafael Auler const StringRef BaseAddressStr = Line.split('[').second.split('(').first; 196477b75ca5SMaksim Panchenko if (BaseAddressStr.getAsInteger(0, ParsedInfo.MMapAddress)) { 1965a34c753fSRafael Auler reportError("expected base address"); 1966a34c753fSRafael Auler Diag << "Found: " << BaseAddressStr << "in '" << Line << "'\n"; 1967a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1968a34c753fSRafael Auler } 1969a34c753fSRafael Auler 1970a34c753fSRafael Auler const StringRef SizeStr = Line.split('(').second.split(')').first; 1971a34c753fSRafael Auler if (SizeStr.getAsInteger(0, ParsedInfo.Size)) { 1972a34c753fSRafael Auler reportError("expected mmaped size"); 1973a34c753fSRafael Auler Diag << "Found: " << SizeStr << "in '" << Line << "'\n"; 1974a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1975a34c753fSRafael Auler } 1976a34c753fSRafael Auler 1977a34c753fSRafael Auler const StringRef OffsetStr = 1978a34c753fSRafael Auler Line.split('@').second.ltrim().split(FieldSeparator).first; 1979a34c753fSRafael Auler if (OffsetStr.getAsInteger(0, ParsedInfo.Offset)) { 1980a34c753fSRafael Auler reportError("expected mmaped page-aligned offset"); 1981a34c753fSRafael Auler Diag << "Found: " << OffsetStr << "in '" << Line << "'\n"; 1982a34c753fSRafael Auler return make_error_code(llvm::errc::io_error); 1983a34c753fSRafael Auler } 1984a34c753fSRafael Auler 1985a34c753fSRafael Auler consumeRestOfLine(); 1986a34c753fSRafael Auler 1987a34c753fSRafael Auler return std::make_pair(FileName, ParsedInfo); 1988a34c753fSRafael Auler } 1989a34c753fSRafael Auler 1990a34c753fSRafael Auler std::error_code DataAggregator::parseMMapEvents() { 1991a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script mmap events output\n"; 1992a34c753fSRafael Auler NamedRegionTimer T("parseMMapEvents", "Parsing mmap events", TimerGroupName, 1993a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 1994a34c753fSRafael Auler 1995a34c753fSRafael Auler std::multimap<StringRef, MMapInfo> GlobalMMapInfo; 1996a34c753fSRafael Auler while (hasData()) { 1997a34c753fSRafael Auler ErrorOr<std::pair<StringRef, MMapInfo>> FileMMapInfoRes = parseMMapEvent(); 1998a34c753fSRafael Auler if (std::error_code EC = FileMMapInfoRes.getError()) 1999a34c753fSRafael Auler return EC; 2000a34c753fSRafael Auler 2001a34c753fSRafael Auler std::pair<StringRef, MMapInfo> FileMMapInfo = FileMMapInfoRes.get(); 2002a34c753fSRafael Auler if (FileMMapInfo.second.PID == -1) 2003a34c753fSRafael Auler continue; 2004a34c753fSRafael Auler 2005a34c753fSRafael Auler // Consider only the first mapping of the file for any given PID 2006a34c753fSRafael Auler bool PIDExists = false; 2007a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(FileMMapInfo.first); 2008a34c753fSRafael Auler for (auto MI = Range.first; MI != Range.second; ++MI) { 2009a34c753fSRafael Auler if (MI->second.PID == FileMMapInfo.second.PID) { 2010a34c753fSRafael Auler PIDExists = true; 2011a34c753fSRafael Auler break; 2012a34c753fSRafael Auler } 2013a34c753fSRafael Auler } 2014a34c753fSRafael Auler if (PIDExists) 2015a34c753fSRafael Auler continue; 2016a34c753fSRafael Auler 2017a34c753fSRafael Auler GlobalMMapInfo.insert(FileMMapInfo); 2018a34c753fSRafael Auler } 2019a34c753fSRafael Auler 2020a34c753fSRafael Auler LLVM_DEBUG({ 2021a34c753fSRafael Auler dbgs() << "FileName -> mmap info:\n"; 2022def464aaSAmir Ayupov for (const std::pair<const StringRef, MMapInfo> &Pair : GlobalMMapInfo) 2023a34c753fSRafael Auler dbgs() << " " << Pair.first << " : " << Pair.second.PID << " [0x" 202477b75ca5SMaksim Panchenko << Twine::utohexstr(Pair.second.MMapAddress) << ", " 2025a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Size) << " @ " 2026a34c753fSRafael Auler << Twine::utohexstr(Pair.second.Offset) << "]\n"; 2027a34c753fSRafael Auler }); 2028a34c753fSRafael Auler 2029a34c753fSRafael Auler StringRef NameToUse = llvm::sys::path::filename(BC->getFilename()); 2030a34c753fSRafael Auler if (GlobalMMapInfo.count(NameToUse) == 0 && !BuildIDBinaryName.empty()) { 2031a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: using \"" << BuildIDBinaryName 2032a34c753fSRafael Auler << "\" for profile matching\n"; 2033a34c753fSRafael Auler NameToUse = BuildIDBinaryName; 2034a34c753fSRafael Auler } 2035a34c753fSRafael Auler 2036a34c753fSRafael Auler auto Range = GlobalMMapInfo.equal_range(NameToUse); 2037a34c753fSRafael Auler for (auto I = Range.first; I != Range.second; ++I) { 203877b75ca5SMaksim Panchenko MMapInfo &MMapInfo = I->second; 203977b75ca5SMaksim Panchenko if (BC->HasFixedLoadAddress && MMapInfo.MMapAddress) { 2040a34c753fSRafael Auler // Check that the binary mapping matches one of the segments. 2041f119a248SAmir Ayupov bool MatchFound = llvm::any_of( 2042f119a248SAmir Ayupov llvm::make_second_range(BC->SegmentMapInfo), 2043f119a248SAmir Ayupov [&](SegmentInfo &SegInfo) { 204477b75ca5SMaksim Panchenko // The mapping is page-aligned and hence the MMapAddress could be 2045a34c753fSRafael Auler // different from the segment start address. We cannot know the page 2046a34c753fSRafael Auler // size of the mapping, but we know it should not exceed the segment 2047a34c753fSRafael Auler // alignment value. Hence we are performing an approximate check. 2048f119a248SAmir Ayupov return SegInfo.Address >= MMapInfo.MMapAddress && 2049f119a248SAmir Ayupov SegInfo.Address - MMapInfo.MMapAddress < SegInfo.Alignment; 2050f119a248SAmir Ayupov }); 2051a34c753fSRafael Auler if (!MatchFound) { 2052a34c753fSRafael Auler errs() << "PERF2BOLT-WARNING: ignoring mapping of " << NameToUse 205377b75ca5SMaksim Panchenko << " at 0x" << Twine::utohexstr(MMapInfo.MMapAddress) << '\n'; 2054a34c753fSRafael Auler continue; 2055a34c753fSRafael Auler } 2056a34c753fSRafael Auler } 2057a34c753fSRafael Auler 205877b75ca5SMaksim Panchenko // Set base address for shared objects. 205977b75ca5SMaksim Panchenko if (!BC->HasFixedLoadAddress) { 206077b75ca5SMaksim Panchenko Optional<uint64_t> BaseAddress = 206177b75ca5SMaksim Panchenko BC->getBaseAddressForMapping(MMapInfo.MMapAddress, MMapInfo.Offset); 206277b75ca5SMaksim Panchenko if (!BaseAddress) { 206377b75ca5SMaksim Panchenko errs() << "PERF2BOLT-WARNING: unable to find base address of the " 206477b75ca5SMaksim Panchenko "binary when memory mapped at 0x" 206577b75ca5SMaksim Panchenko << Twine::utohexstr(MMapInfo.MMapAddress) 206677b75ca5SMaksim Panchenko << " using file offset 0x" << Twine::utohexstr(MMapInfo.Offset) 206777b75ca5SMaksim Panchenko << ". Ignoring profile data for this mapping\n"; 206877b75ca5SMaksim Panchenko continue; 206977b75ca5SMaksim Panchenko } else { 207077b75ca5SMaksim Panchenko MMapInfo.BaseAddress = *BaseAddress; 207177b75ca5SMaksim Panchenko } 207277b75ca5SMaksim Panchenko } 207377b75ca5SMaksim Panchenko 2074a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)); 2075a34c753fSRafael Auler } 2076a34c753fSRafael Auler 2077a34c753fSRafael Auler if (BinaryMMapInfo.empty()) { 2078a34c753fSRafael Auler if (errs().has_colors()) 2079a34c753fSRafael Auler errs().changeColor(raw_ostream::RED); 2080a34c753fSRafael Auler errs() << "PERF2BOLT-ERROR: could not find a profile matching binary \"" 2081a34c753fSRafael Auler << BC->getFilename() << "\"."; 2082a34c753fSRafael Auler if (!GlobalMMapInfo.empty()) { 2083a34c753fSRafael Auler errs() << " Profile for the following binary name(s) is available:\n"; 2084a34c753fSRafael Auler for (auto I = GlobalMMapInfo.begin(), IE = GlobalMMapInfo.end(); I != IE; 2085def464aaSAmir Ayupov I = GlobalMMapInfo.upper_bound(I->first)) 2086a34c753fSRafael Auler errs() << " " << I->first << '\n'; 2087a34c753fSRafael Auler errs() << "Please rename the input binary.\n"; 2088a34c753fSRafael Auler } else { 2089a34c753fSRafael Auler errs() << " Failed to extract any binary name from a profile.\n"; 2090a34c753fSRafael Auler } 2091a34c753fSRafael Auler if (errs().has_colors()) 2092a34c753fSRafael Auler errs().resetColor(); 2093a34c753fSRafael Auler 2094a34c753fSRafael Auler exit(1); 2095a34c753fSRafael Auler } 2096a34c753fSRafael Auler 2097a34c753fSRafael Auler return std::error_code(); 2098a34c753fSRafael Auler } 2099a34c753fSRafael Auler 2100a34c753fSRafael Auler std::error_code DataAggregator::parseTaskEvents() { 2101a34c753fSRafael Auler outs() << "PERF2BOLT: parsing perf-script task events output\n"; 2102a34c753fSRafael Auler NamedRegionTimer T("parseTaskEvents", "Parsing task events", TimerGroupName, 2103a34c753fSRafael Auler TimerGroupDesc, opts::TimeAggregator); 2104a34c753fSRafael Auler 2105a34c753fSRafael Auler while (hasData()) { 2106*835a9c28SAmir Ayupov if (std::optional<int32_t> CommInfo = parseCommExecEvent()) { 2107a34c753fSRafael Auler // Remove forked child that ran execve 2108a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(*CommInfo); 2109def464aaSAmir Ayupov if (MMapInfoIter != BinaryMMapInfo.end() && MMapInfoIter->second.Forked) 2110a34c753fSRafael Auler BinaryMMapInfo.erase(MMapInfoIter); 2111a34c753fSRafael Auler consumeRestOfLine(); 2112a34c753fSRafael Auler continue; 2113a34c753fSRafael Auler } 2114a34c753fSRafael Auler 2115*835a9c28SAmir Ayupov std::optional<ForkInfo> ForkInfo = parseForkEvent(); 2116a34c753fSRafael Auler if (!ForkInfo) 2117a34c753fSRafael Auler continue; 2118a34c753fSRafael Auler 2119a34c753fSRafael Auler if (ForkInfo->ParentPID == ForkInfo->ChildPID) 2120a34c753fSRafael Auler continue; 2121a34c753fSRafael Auler 2122a34c753fSRafael Auler if (ForkInfo->Time == 0) { 2123a34c753fSRafael Auler // Process was forked and mmaped before perf ran. In this case the child 2124a34c753fSRafael Auler // should have its own mmap entry unless it was execve'd. 2125a34c753fSRafael Auler continue; 2126a34c753fSRafael Auler } 2127a34c753fSRafael Auler 2128a34c753fSRafael Auler auto MMapInfoIter = BinaryMMapInfo.find(ForkInfo->ParentPID); 2129a34c753fSRafael Auler if (MMapInfoIter == BinaryMMapInfo.end()) 2130a34c753fSRafael Auler continue; 2131a34c753fSRafael Auler 2132a34c753fSRafael Auler MMapInfo MMapInfo = MMapInfoIter->second; 2133a34c753fSRafael Auler MMapInfo.PID = ForkInfo->ChildPID; 2134a34c753fSRafael Auler MMapInfo.Forked = true; 2135a34c753fSRafael Auler BinaryMMapInfo.insert(std::make_pair(MMapInfo.PID, MMapInfo)); 2136a34c753fSRafael Auler } 2137a34c753fSRafael Auler 2138a34c753fSRafael Auler outs() << "PERF2BOLT: input binary is associated with " 2139a34c753fSRafael Auler << BinaryMMapInfo.size() << " PID(s)\n"; 2140a34c753fSRafael Auler 2141a34c753fSRafael Auler LLVM_DEBUG({ 2142def464aaSAmir Ayupov for (std::pair<const uint64_t, MMapInfo> &MMI : BinaryMMapInfo) 2143a34c753fSRafael Auler outs() << " " << MMI.second.PID << (MMI.second.Forked ? " (forked)" : "") 214477b75ca5SMaksim Panchenko << ": (0x" << Twine::utohexstr(MMI.second.MMapAddress) << ": 0x" 214540c2e0faSMaksim Panchenko << Twine::utohexstr(MMI.second.Size) << ")\n"; 2146a34c753fSRafael Auler }); 2147a34c753fSRafael Auler 2148a34c753fSRafael Auler return std::error_code(); 2149a34c753fSRafael Auler } 2150a34c753fSRafael Auler 2151*835a9c28SAmir Ayupov std::optional<std::pair<StringRef, StringRef>> 2152a34c753fSRafael Auler DataAggregator::parseNameBuildIDPair() { 215340c2e0faSMaksim Panchenko while (checkAndConsumeFS()) { 215440c2e0faSMaksim Panchenko } 2155a34c753fSRafael Auler 2156a34c753fSRafael Auler ErrorOr<StringRef> BuildIDStr = parseString(FieldSeparator, true); 2157a34c753fSRafael Auler if (std::error_code EC = BuildIDStr.getError()) 2158e324a80fSKazu Hirata return std::nullopt; 2159a34c753fSRafael Auler 2160661577b5SMaksim Panchenko // If one of the strings is missing, don't issue a parsing error, but still 2161661577b5SMaksim Panchenko // do not return a value. 2162e549ac07SRafael Auler consumeAllRemainingFS(); 2163ba9cc653SRafael Auler if (checkNewLine()) 2164e324a80fSKazu Hirata return std::nullopt; 2165661577b5SMaksim Panchenko 2166a34c753fSRafael Auler ErrorOr<StringRef> NameStr = parseString(FieldSeparator, true); 2167a34c753fSRafael Auler if (std::error_code EC = NameStr.getError()) 2168e324a80fSKazu Hirata return std::nullopt; 2169a34c753fSRafael Auler 2170a34c753fSRafael Auler consumeRestOfLine(); 2171a34c753fSRafael Auler return std::make_pair(NameStr.get(), BuildIDStr.get()); 2172a34c753fSRafael Auler } 2173a34c753fSRafael Auler 2174661577b5SMaksim Panchenko bool DataAggregator::hasAllBuildIDs() { 2175661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf; 2176661577b5SMaksim Panchenko 2177661577b5SMaksim Panchenko if (!hasData()) 2178661577b5SMaksim Panchenko return false; 2179661577b5SMaksim Panchenko 2180661577b5SMaksim Panchenko bool HasInvalidEntries = false; 2181661577b5SMaksim Panchenko while (hasData()) { 2182661577b5SMaksim Panchenko if (!parseNameBuildIDPair()) { 2183661577b5SMaksim Panchenko HasInvalidEntries = true; 2184661577b5SMaksim Panchenko break; 2185661577b5SMaksim Panchenko } 2186661577b5SMaksim Panchenko } 2187661577b5SMaksim Panchenko 2188661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf; 2189661577b5SMaksim Panchenko 2190661577b5SMaksim Panchenko return !HasInvalidEntries; 2191661577b5SMaksim Panchenko } 2192661577b5SMaksim Panchenko 2193*835a9c28SAmir Ayupov std::optional<StringRef> 2194a34c753fSRafael Auler DataAggregator::getFileNameForBuildID(StringRef FileBuildID) { 2195661577b5SMaksim Panchenko const StringRef SavedParsingBuf = ParsingBuf; 2196661577b5SMaksim Panchenko 2197661577b5SMaksim Panchenko StringRef FileName; 2198a34c753fSRafael Auler while (hasData()) { 2199*835a9c28SAmir Ayupov std::optional<std::pair<StringRef, StringRef>> IDPair = 2200*835a9c28SAmir Ayupov parseNameBuildIDPair(); 2201661577b5SMaksim Panchenko if (!IDPair) { 2202661577b5SMaksim Panchenko consumeRestOfLine(); 2203661577b5SMaksim Panchenko continue; 2204a34c753fSRafael Auler } 2205661577b5SMaksim Panchenko 2206661577b5SMaksim Panchenko if (IDPair->second.startswith(FileBuildID)) { 2207661577b5SMaksim Panchenko FileName = sys::path::filename(IDPair->first); 2208661577b5SMaksim Panchenko break; 2209661577b5SMaksim Panchenko } 2210661577b5SMaksim Panchenko } 2211661577b5SMaksim Panchenko 2212661577b5SMaksim Panchenko ParsingBuf = SavedParsingBuf; 2213661577b5SMaksim Panchenko 2214661577b5SMaksim Panchenko if (!FileName.empty()) 2215661577b5SMaksim Panchenko return FileName; 2216661577b5SMaksim Panchenko 2217e324a80fSKazu Hirata return std::nullopt; 2218a34c753fSRafael Auler } 2219a34c753fSRafael Auler 2220a34c753fSRafael Auler std::error_code 2221a34c753fSRafael Auler DataAggregator::writeAggregatedFile(StringRef OutputFilename) const { 2222a34c753fSRafael Auler std::error_code EC; 2223a34c753fSRafael Auler raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::OpenFlags::OF_None); 2224a34c753fSRafael Auler if (EC) 2225a34c753fSRafael Auler return EC; 2226a34c753fSRafael Auler 2227a34c753fSRafael Auler bool WriteMemLocs = false; 2228a34c753fSRafael Auler 2229a34c753fSRafael Auler auto writeLocation = [&OutFile, &WriteMemLocs](const Location &Loc) { 2230a34c753fSRafael Auler if (WriteMemLocs) 2231a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "4 " : "3 "); 2232a34c753fSRafael Auler else 2233a34c753fSRafael Auler OutFile << (Loc.IsSymbol ? "1 " : "0 "); 2234a34c753fSRafael Auler OutFile << (Loc.Name.empty() ? "[unknown]" : getEscapedName(Loc.Name)) 2235a34c753fSRafael Auler << " " << Twine::utohexstr(Loc.Offset) << FieldSeparator; 2236a34c753fSRafael Auler }; 2237a34c753fSRafael Auler 2238a34c753fSRafael Auler uint64_t BranchValues = 0; 2239a34c753fSRafael Auler uint64_t MemValues = 0; 2240a34c753fSRafael Auler 2241a34c753fSRafael Auler if (BAT) 2242a34c753fSRafael Auler OutFile << "boltedcollection\n"; 2243a34c753fSRafael Auler if (opts::BasicAggregation) { 2244a34c753fSRafael Auler OutFile << "no_lbr"; 224534bcadc3SKazu Hirata for (const StringMapEntry<std::nullopt_t> &Entry : EventNames) 2246a34c753fSRafael Auler OutFile << " " << Entry.getKey(); 2247a34c753fSRafael Auler OutFile << "\n"; 2248a34c753fSRafael Auler 2249a34c753fSRafael Auler for (const StringMapEntry<FuncSampleData> &Func : NamesToSamples) { 2250a34c753fSRafael Auler for (const SampleInfo &SI : Func.getValue().Data) { 2251a34c753fSRafael Auler writeLocation(SI.Loc); 2252a34c753fSRafael Auler OutFile << SI.Hits << "\n"; 2253a34c753fSRafael Auler ++BranchValues; 2254a34c753fSRafael Auler } 2255a34c753fSRafael Auler } 2256a34c753fSRafael Auler } else { 2257a34c753fSRafael Auler for (const StringMapEntry<FuncBranchData> &Func : NamesToBranches) { 2258a34c753fSRafael Auler for (const llvm::bolt::BranchInfo &BI : Func.getValue().Data) { 2259a34c753fSRafael Auler writeLocation(BI.From); 2260a34c753fSRafael Auler writeLocation(BI.To); 2261a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n"; 2262a34c753fSRafael Auler ++BranchValues; 2263a34c753fSRafael Auler } 2264a34c753fSRafael Auler for (const llvm::bolt::BranchInfo &BI : Func.getValue().EntryData) { 2265a34c753fSRafael Auler // Do not output if source is a known symbol, since this was already 2266a34c753fSRafael Auler // accounted for in the source function 2267a34c753fSRafael Auler if (BI.From.IsSymbol) 2268a34c753fSRafael Auler continue; 2269a34c753fSRafael Auler writeLocation(BI.From); 2270a34c753fSRafael Auler writeLocation(BI.To); 2271a34c753fSRafael Auler OutFile << BI.Mispreds << " " << BI.Branches << "\n"; 2272a34c753fSRafael Auler ++BranchValues; 2273a34c753fSRafael Auler } 2274a34c753fSRafael Auler } 2275a34c753fSRafael Auler 2276a34c753fSRafael Auler WriteMemLocs = true; 2277a34c753fSRafael Auler for (const StringMapEntry<FuncMemData> &Func : NamesToMemEvents) { 2278a34c753fSRafael Auler for (const MemInfo &MemEvent : Func.getValue().Data) { 2279a34c753fSRafael Auler writeLocation(MemEvent.Offset); 2280a34c753fSRafael Auler writeLocation(MemEvent.Addr); 2281a34c753fSRafael Auler OutFile << MemEvent.Count << "\n"; 2282a34c753fSRafael Auler ++MemValues; 2283a34c753fSRafael Auler } 2284a34c753fSRafael Auler } 2285a34c753fSRafael Auler } 2286a34c753fSRafael Auler 228740c2e0faSMaksim Panchenko outs() << "PERF2BOLT: wrote " << BranchValues << " objects and " << MemValues 228840c2e0faSMaksim Panchenko << " memory objects to " << OutputFilename << "\n"; 2289a34c753fSRafael Auler 2290a34c753fSRafael Auler return std::error_code(); 2291a34c753fSRafael Auler } 2292a34c753fSRafael Auler 229340c2e0faSMaksim Panchenko void DataAggregator::dump() const { DataReader::dump(); } 2294a34c753fSRafael Auler 2295a34c753fSRafael Auler void DataAggregator::dump(const LBREntry &LBR) const { 2296a34c753fSRafael Auler Diag << "From: " << Twine::utohexstr(LBR.From) 2297a34c753fSRafael Auler << " To: " << Twine::utohexstr(LBR.To) << " Mispred? " << LBR.Mispred 2298a34c753fSRafael Auler << "\n"; 2299a34c753fSRafael Auler } 2300a34c753fSRafael Auler 2301a34c753fSRafael Auler void DataAggregator::dump(const PerfBranchSample &Sample) const { 2302a34c753fSRafael Auler Diag << "Sample LBR entries: " << Sample.LBR.size() << "\n"; 2303def464aaSAmir Ayupov for (const LBREntry &LBR : Sample.LBR) 2304a34c753fSRafael Auler dump(LBR); 2305a34c753fSRafael Auler } 2306a34c753fSRafael Auler 2307a34c753fSRafael Auler void DataAggregator::dump(const PerfMemSample &Sample) const { 2308a34c753fSRafael Auler Diag << "Sample mem entries: " << Sample.PC << ": " << Sample.Addr << "\n"; 2309a34c753fSRafael Auler } 2310