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