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