| 1 | //===- bolt/tools/merge-fdata/merge-fdata.cpp -----------------------------===// |
| 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 | // Tool for merging profile in fdata format: |
| 10 | // |
| 11 | // $ merge-fdata 1.fdata 2.fdata 3.fdata > merged.fdata |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "bolt/Profile/ProfileYAMLMapping.h" |
| 16 | #include "llvm/ADT/STLExtras.h" |
| 17 | #include "llvm/ADT/StringMap.h" |
| 18 | #include "llvm/Support/CommandLine.h" |
| 19 | #include "llvm/Support/FileSystem.h" |
| 20 | #include "llvm/Support/ManagedStatic.h" |
| 21 | #include "llvm/Support/PrettyStackTrace.h" |
| 22 | #include "llvm/Support/Signals.h" |
| 23 | #include "llvm/Support/ThreadPool.h" |
| 24 | #include <algorithm> |
| 25 | #include <fstream> |
| 26 | #include <mutex> |
| 27 | #include <unordered_map> |
| 28 | |
| 29 | using namespace llvm; |
| 30 | using namespace llvm::yaml::bolt; |
| 31 | |
| 32 | namespace opts { |
| 33 | |
| 34 | static cl::OptionCategory MergeFdataCategory("merge-fdata options" ); |
| 35 | |
| 36 | enum SortType : char { |
| 37 | ST_NONE, |
| 38 | ST_EXEC_COUNT, /// Sort based on function execution count. |
| 39 | ST_TOTAL_BRANCHES, /// Sort based on all branches in the function. |
| 40 | }; |
| 41 | |
| 42 | static cl::list<std::string> |
| 43 | InputDataFilenames( |
| 44 | cl::Positional, |
| 45 | cl::CommaSeparated, |
| 46 | cl::desc("<fdata1> [<fdata2>]..." ), |
| 47 | cl::OneOrMore, |
| 48 | cl::cat(MergeFdataCategory)); |
| 49 | |
| 50 | static cl::opt<SortType> |
| 51 | PrintFunctionList("print" , |
| 52 | cl::desc("print the list of objects with count to stderr" ), |
| 53 | cl::init(Val: ST_NONE), |
| 54 | cl::values(clEnumValN(ST_NONE, |
| 55 | "none" , |
| 56 | "do not print objects/functions" ), |
| 57 | clEnumValN(ST_EXEC_COUNT, |
| 58 | "exec" , |
| 59 | "print functions sorted by execution count" ), |
| 60 | clEnumValN(ST_TOTAL_BRANCHES, |
| 61 | "branches" , |
| 62 | "print functions sorted by total branch count" )), |
| 63 | cl::cat(MergeFdataCategory)); |
| 64 | |
| 65 | static cl::opt<bool> |
| 66 | SuppressMergedDataOutput("q" , |
| 67 | cl::desc("do not print merged data to stdout" ), |
| 68 | cl::init(Val: false), |
| 69 | cl::Optional, |
| 70 | cl::cat(MergeFdataCategory)); |
| 71 | |
| 72 | static cl::opt<std::string> |
| 73 | OutputFilePath("o" , |
| 74 | cl::value_desc("file" ), |
| 75 | cl::desc("Write output to <file>" ), |
| 76 | cl::cat(MergeFdataCategory)); |
| 77 | |
| 78 | } // namespace opts |
| 79 | |
| 80 | namespace { |
| 81 | |
| 82 | static StringRef ToolName; |
| 83 | |
| 84 | static void report_error(StringRef Message, std::error_code EC) { |
| 85 | assert(EC); |
| 86 | errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n" ; |
| 87 | exit(status: 1); |
| 88 | } |
| 89 | |
| 90 | static void report_error(Twine Message, StringRef CustomError) { |
| 91 | errs() << ToolName << ": '" << Message << "': " << CustomError << ".\n" ; |
| 92 | exit(status: 1); |
| 93 | } |
| 94 | |
| 95 | static raw_fd_ostream &output() { |
| 96 | if (opts::OutputFilePath.empty() || opts::OutputFilePath == "-" ) |
| 97 | return outs(); |
| 98 | else { |
| 99 | std::error_code EC; |
| 100 | static raw_fd_ostream Output(opts::OutputFilePath, EC); |
| 101 | if (EC) |
| 102 | report_error(Message: opts::OutputFilePath, EC); |
| 103 | return Output; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | void (BinaryProfileHeader &, |
| 108 | const BinaryProfileHeader &) { |
| 109 | if (MergedHeader.FileName.empty()) |
| 110 | MergedHeader.FileName = Header.FileName; |
| 111 | |
| 112 | if (!MergedHeader.FileName.empty() && |
| 113 | MergedHeader.FileName != Header.FileName) |
| 114 | errs() << "WARNING: merging profile from a binary for " << Header.FileName |
| 115 | << " into a profile for binary " << MergedHeader.FileName << '\n'; |
| 116 | |
| 117 | if (MergedHeader.Id.empty()) |
| 118 | MergedHeader.Id = Header.Id; |
| 119 | |
| 120 | if (!MergedHeader.Id.empty() && (MergedHeader.Id != Header.Id)) |
| 121 | errs() << "WARNING: build-ids in merged profiles do not match\n" ; |
| 122 | |
| 123 | // Cannot merge samples profile with LBR profile. |
| 124 | if (!MergedHeader.Flags) |
| 125 | MergedHeader.Flags = Header.Flags; |
| 126 | |
| 127 | constexpr auto Mask = llvm::bolt::BinaryFunction::PF_BRANCH | |
| 128 | llvm::bolt::BinaryFunction::PF_BASIC; |
| 129 | if ((MergedHeader.Flags & Mask) != (Header.Flags & Mask)) { |
| 130 | errs() << "ERROR: cannot merge LBR profile with non-LBR profile\n" ; |
| 131 | exit(status: 1); |
| 132 | } |
| 133 | MergedHeader.Flags = MergedHeader.Flags | Header.Flags; |
| 134 | |
| 135 | if (!Header.Origin.empty()) { |
| 136 | if (MergedHeader.Origin.empty()) |
| 137 | MergedHeader.Origin = Header.Origin; |
| 138 | else if (MergedHeader.Origin != Header.Origin) |
| 139 | MergedHeader.Origin += "; " + Header.Origin; |
| 140 | } |
| 141 | |
| 142 | if (MergedHeader.EventNames.empty()) |
| 143 | MergedHeader.EventNames = Header.EventNames; |
| 144 | |
| 145 | if (MergedHeader.EventNames != Header.EventNames) { |
| 146 | errs() << "WARNING: merging profiles with different sampling events\n" ; |
| 147 | MergedHeader.EventNames += "," + Header.EventNames; |
| 148 | } |
| 149 | |
| 150 | if (MergedHeader.HashFunction != Header.HashFunction) |
| 151 | report_error(Message: "merge conflict" , |
| 152 | CustomError: "cannot merge profiles with different hash functions" ); |
| 153 | } |
| 154 | |
| 155 | void mergeBasicBlockProfile(BinaryBasicBlockProfile &MergedBB, |
| 156 | BinaryBasicBlockProfile &&BB, |
| 157 | const BinaryFunctionProfile &BF) { |
| 158 | // Verify that the blocks match. |
| 159 | if (BB.NumInstructions != MergedBB.NumInstructions) |
| 160 | report_error(Message: BF.Name + " : BB #" + Twine(BB.Index), |
| 161 | CustomError: "number of instructions in block mismatch" ); |
| 162 | if (BB.Hash != MergedBB.Hash) |
| 163 | report_error(Message: BF.Name + " : BB #" + Twine(BB.Index), |
| 164 | CustomError: "basic block hash mismatch" ); |
| 165 | |
| 166 | // Update the execution count. |
| 167 | MergedBB.ExecCount += BB.ExecCount; |
| 168 | |
| 169 | // Update the event count. |
| 170 | MergedBB.EventCount += BB.EventCount; |
| 171 | |
| 172 | // Merge calls sites. |
| 173 | std::unordered_map<uint32_t, CallSiteInfo *> CSByOffset; |
| 174 | for (CallSiteInfo &CS : BB.CallSites) |
| 175 | CSByOffset.emplace(args: std::make_pair(x&: CS.Offset, y: &CS)); |
| 176 | |
| 177 | for (CallSiteInfo &MergedCS : MergedBB.CallSites) { |
| 178 | auto CSI = CSByOffset.find(x: MergedCS.Offset); |
| 179 | if (CSI == CSByOffset.end()) |
| 180 | continue; |
| 181 | yaml::bolt::CallSiteInfo &CS = *CSI->second; |
| 182 | if (CS != MergedCS) |
| 183 | continue; |
| 184 | |
| 185 | MergedCS.Count += CS.Count; |
| 186 | MergedCS.Mispreds += CS.Mispreds; |
| 187 | |
| 188 | CSByOffset.erase(position: CSI); |
| 189 | } |
| 190 | |
| 191 | // Append the rest of call sites. |
| 192 | for (std::pair<const uint32_t, CallSiteInfo *> CSI : CSByOffset) |
| 193 | MergedBB.CallSites.emplace_back(args: std::move(*CSI.second)); |
| 194 | |
| 195 | // Merge successor info. |
| 196 | std::vector<SuccessorInfo *> SIByIndex(BF.NumBasicBlocks); |
| 197 | for (SuccessorInfo &SI : BB.Successors) { |
| 198 | if (SI.Index >= BF.NumBasicBlocks) |
| 199 | report_error(Message: BF.Name, CustomError: "bad successor index" ); |
| 200 | SIByIndex[SI.Index] = &SI; |
| 201 | } |
| 202 | for (SuccessorInfo &MergedSI : MergedBB.Successors) { |
| 203 | if (!SIByIndex[MergedSI.Index]) |
| 204 | continue; |
| 205 | SuccessorInfo &SI = *SIByIndex[MergedSI.Index]; |
| 206 | |
| 207 | MergedSI.Count += SI.Count; |
| 208 | MergedSI.Mispreds += SI.Mispreds; |
| 209 | |
| 210 | SIByIndex[MergedSI.Index] = nullptr; |
| 211 | } |
| 212 | for (SuccessorInfo *SI : SIByIndex) |
| 213 | if (SI) |
| 214 | MergedBB.Successors.emplace_back(args: std::move(*SI)); |
| 215 | } |
| 216 | |
| 217 | void mergeFunctionProfile(BinaryFunctionProfile &MergedBF, |
| 218 | BinaryFunctionProfile &&BF) { |
| 219 | // Validate that we are merging the correct function. |
| 220 | if (BF.NumBasicBlocks != MergedBF.NumBasicBlocks) |
| 221 | report_error(Message: BF.Name, CustomError: "number of basic blocks mismatch" ); |
| 222 | if (BF.Id != MergedBF.Id) |
| 223 | report_error(Message: BF.Name, CustomError: "ID mismatch" ); |
| 224 | if (BF.Hash != MergedBF.Hash) |
| 225 | report_error(Message: BF.Name, CustomError: "hash mismatch" ); |
| 226 | |
| 227 | // Update the execution count. |
| 228 | MergedBF.ExecCount += BF.ExecCount; |
| 229 | |
| 230 | // Merge basic blocks profile. |
| 231 | std::vector<BinaryBasicBlockProfile *> BlockByIndex(BF.NumBasicBlocks); |
| 232 | for (BinaryBasicBlockProfile &BB : BF.Blocks) { |
| 233 | if (BB.Index >= BF.NumBasicBlocks) |
| 234 | report_error(Message: BF.Name + " : BB #" + Twine(BB.Index), |
| 235 | CustomError: "bad basic block index" ); |
| 236 | BlockByIndex[BB.Index] = &BB; |
| 237 | } |
| 238 | for (BinaryBasicBlockProfile &MergedBB : MergedBF.Blocks) { |
| 239 | if (!BlockByIndex[MergedBB.Index]) |
| 240 | continue; |
| 241 | BinaryBasicBlockProfile &BB = *BlockByIndex[MergedBB.Index]; |
| 242 | |
| 243 | mergeBasicBlockProfile(MergedBB, BB: std::move(BB), BF: MergedBF); |
| 244 | |
| 245 | // Ignore this block in the future. |
| 246 | BlockByIndex[MergedBB.Index] = nullptr; |
| 247 | } |
| 248 | |
| 249 | // Append blocks unique to BF (i.e. those that are not in MergedBF). |
| 250 | for (BinaryBasicBlockProfile *BB : BlockByIndex) |
| 251 | if (BB) |
| 252 | MergedBF.Blocks.emplace_back(args: std::move(*BB)); |
| 253 | } |
| 254 | |
| 255 | bool isYAML(const StringRef Filename) { |
| 256 | ErrorOr<std::unique_ptr<MemoryBuffer>> MB = |
| 257 | MemoryBuffer::getFileOrSTDIN(Filename); |
| 258 | if (std::error_code EC = MB.getError()) |
| 259 | report_error(Message: Filename, EC); |
| 260 | StringRef Buffer = MB.get()->getBuffer(); |
| 261 | if (Buffer.starts_with(Prefix: "---\n" )) |
| 262 | return true; |
| 263 | return false; |
| 264 | } |
| 265 | |
| 266 | void mergeLegacyProfiles(const SmallVectorImpl<std::string> &Filenames) { |
| 267 | errs() << "Using legacy profile format.\n" ; |
| 268 | std::optional<bool> BoltedCollection; |
| 269 | std::optional<bool> NoLBRCollection; |
| 270 | std::mutex BoltedCollectionMutex; |
| 271 | struct CounterTy { |
| 272 | uint64_t Exec{0}; |
| 273 | uint64_t Mispred{0}; |
| 274 | CounterTy &operator+=(const CounterTy &O) { |
| 275 | Exec += O.Exec; |
| 276 | Mispred += O.Mispred; |
| 277 | return *this; |
| 278 | } |
| 279 | CounterTy operator+(const CounterTy &O) { return *this += O; } |
| 280 | }; |
| 281 | typedef StringMap<CounterTy> ProfileTy; |
| 282 | |
| 283 | auto ParseProfile = [&](const std::string &Filename, auto &Profiles) { |
| 284 | const llvm::thread::id tid = llvm::this_thread::get_id(); |
| 285 | |
| 286 | if (isYAML(Filename)) |
| 287 | report_error(Message: Filename, CustomError: "cannot mix YAML and legacy formats" ); |
| 288 | |
| 289 | std::ifstream FdataFile(Filename, std::ios::in); |
| 290 | std::string FdataLine; |
| 291 | std::getline(is&: FdataFile, str&: FdataLine); |
| 292 | |
| 293 | auto checkMode = [&](const std::string &Key, std::optional<bool> &Flag) { |
| 294 | const bool KeyIsSet = FdataLine.rfind(str: Key, pos: 0) == 0; |
| 295 | |
| 296 | if (!Flag.has_value()) |
| 297 | Flag = KeyIsSet; |
| 298 | else if (*Flag != KeyIsSet) |
| 299 | report_error(Message: Filename, CustomError: "cannot mix profile with and without " + Key); |
| 300 | if (KeyIsSet) |
| 301 | // Advance line |
| 302 | std::getline(is&: FdataFile, str&: FdataLine); |
| 303 | }; |
| 304 | |
| 305 | ProfileTy *Profile; |
| 306 | { |
| 307 | std::lock_guard<std::mutex> Lock(BoltedCollectionMutex); |
| 308 | // Check if the string "boltedcollection" is in the first line |
| 309 | checkMode("boltedcollection" , BoltedCollection); |
| 310 | // Check if the string "no_lbr" is in the first line |
| 311 | // (or second line if BoltedCollection is true) |
| 312 | checkMode("no_lbr" , NoLBRCollection); |
| 313 | Profile = &Profiles[tid]; |
| 314 | } |
| 315 | |
| 316 | do { |
| 317 | StringRef Line(FdataLine); |
| 318 | CounterTy Count; |
| 319 | auto [Signature, ExecCount] = Line.rsplit(Separator: ' '); |
| 320 | if (ExecCount.getAsInteger(Radix: 10, Result&: Count.Exec)) |
| 321 | report_error(Message: Filename, CustomError: "Malformed / corrupted execution count" ); |
| 322 | // Only LBR profile has misprediction field |
| 323 | if (!NoLBRCollection.value_or(u: false)) { |
| 324 | auto [SignatureLBR, MispredCount] = Signature.rsplit(Separator: ' '); |
| 325 | Signature = SignatureLBR; |
| 326 | if (MispredCount.getAsInteger(Radix: 10, Result&: Count.Mispred)) |
| 327 | report_error(Message: Filename, CustomError: "Malformed / corrupted misprediction count" ); |
| 328 | } |
| 329 | |
| 330 | Count += Profile->lookup(Key: Signature); |
| 331 | Profile->insert_or_assign(Key: Signature, Val&: Count); |
| 332 | } while (std::getline(is&: FdataFile, str&: FdataLine)); |
| 333 | }; |
| 334 | |
| 335 | // The final reduction has non-trivial cost, make sure each thread has at |
| 336 | // least 4 tasks. |
| 337 | ThreadPoolStrategy S = optimal_concurrency( |
| 338 | TaskCount: std::max(a: Filenames.size() / 4, b: static_cast<size_t>(1))); |
| 339 | DefaultThreadPool Pool(S); |
| 340 | DenseMap<llvm::thread::id, ProfileTy> ParsedProfiles( |
| 341 | Pool.getMaxConcurrency()); |
| 342 | for (const auto &Filename : Filenames) |
| 343 | Pool.async(F&: ParseProfile, ArgList: std::cref(t: Filename), ArgList: std::ref(t&: ParsedProfiles)); |
| 344 | Pool.wait(); |
| 345 | |
| 346 | ProfileTy MergedProfile; |
| 347 | for (const auto &[Thread, Profile] : ParsedProfiles) |
| 348 | for (const auto &[Key, Value] : Profile) { |
| 349 | CounterTy Count = MergedProfile.lookup(Key) + Value; |
| 350 | MergedProfile.insert_or_assign(Key, Val&: Count); |
| 351 | } |
| 352 | |
| 353 | if (BoltedCollection.value_or(u: false)) |
| 354 | output() << "boltedcollection\n" ; |
| 355 | if (NoLBRCollection.value_or(u: false)) |
| 356 | output() << "no_lbr\n" ; |
| 357 | for (const auto &[Key, Value] : MergedProfile) { |
| 358 | output() << Key << " " ; |
| 359 | if (!NoLBRCollection.value_or(u: false)) |
| 360 | output() << Value.Mispred << " " ; |
| 361 | output() << Value.Exec << "\n" ; |
| 362 | } |
| 363 | |
| 364 | errs() << "Profile from " << Filenames.size() << " files merged.\n" ; |
| 365 | } |
| 366 | |
| 367 | } // anonymous namespace |
| 368 | |
| 369 | int main(int argc, char **argv) { |
| 370 | // Print a stack trace if we signal out. |
| 371 | sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]); |
| 372 | PrettyStackTraceProgram X(argc, argv); |
| 373 | |
| 374 | llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. |
| 375 | |
| 376 | cl::HideUnrelatedOptions(Category&: opts::MergeFdataCategory); |
| 377 | |
| 378 | cl::ParseCommandLineOptions(argc, argv, |
| 379 | Overview: "merge multiple fdata into a single file" ); |
| 380 | |
| 381 | ToolName = argv[0]; |
| 382 | |
| 383 | // Recursively expand input directories into input file lists. |
| 384 | SmallVector<std::string> Inputs; |
| 385 | for (std::string &InputDataFilename : opts::InputDataFilenames) { |
| 386 | if (!llvm::sys::fs::exists(Path: InputDataFilename)) |
| 387 | report_error(Message: InputDataFilename, |
| 388 | EC: std::make_error_code(e: std::errc::no_such_file_or_directory)); |
| 389 | if (llvm::sys::fs::is_regular_file(Path: InputDataFilename)) |
| 390 | Inputs.emplace_back(Args&: InputDataFilename); |
| 391 | else if (llvm::sys::fs::is_directory(Path: InputDataFilename)) { |
| 392 | std::error_code EC; |
| 393 | for (llvm::sys::fs::recursive_directory_iterator F(InputDataFilename, EC), |
| 394 | E; |
| 395 | F != E && !EC; F.increment(ec&: EC)) |
| 396 | if (llvm::sys::fs::is_regular_file(Path: F->path())) |
| 397 | Inputs.emplace_back(Args: F->path()); |
| 398 | if (EC) |
| 399 | report_error(Message: InputDataFilename, EC); |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | if (!isYAML(Filename: Inputs.front())) { |
| 404 | mergeLegacyProfiles(Filenames: Inputs); |
| 405 | return 0; |
| 406 | } |
| 407 | |
| 408 | // Merged header. |
| 409 | BinaryProfileHeader ; |
| 410 | MergedHeader.Version = 1; |
| 411 | |
| 412 | // Merged information for all functions. |
| 413 | StringMap<BinaryFunctionProfile> MergedBFs; |
| 414 | |
| 415 | bool = true; |
| 416 | for (std::string &InputDataFilename : Inputs) { |
| 417 | ErrorOr<std::unique_ptr<MemoryBuffer>> MB = |
| 418 | MemoryBuffer::getFileOrSTDIN(Filename: InputDataFilename); |
| 419 | if (std::error_code EC = MB.getError()) |
| 420 | report_error(Message: InputDataFilename, EC); |
| 421 | yaml::Input YamlInput(MB.get()->getBuffer()); |
| 422 | YamlInput.setAllowUnknownKeys(true); |
| 423 | |
| 424 | errs() << "Merging data from " << InputDataFilename << "...\n" ; |
| 425 | |
| 426 | BinaryProfile BP; |
| 427 | YamlInput >> BP; |
| 428 | if (YamlInput.error()) |
| 429 | report_error(Message: InputDataFilename, EC: YamlInput.error()); |
| 430 | |
| 431 | // Sanity check. |
| 432 | if (BP.Header.Version != 1) { |
| 433 | errs() << "Unable to merge data from profile using version " |
| 434 | << BP.Header.Version << '\n'; |
| 435 | exit(status: 1); |
| 436 | } |
| 437 | |
| 438 | // Merge the header. |
| 439 | if (FirstHeader) { |
| 440 | MergedHeader = BP.Header; |
| 441 | FirstHeader = false; |
| 442 | } else { |
| 443 | mergeProfileHeaders(MergedHeader, Header: BP.Header); |
| 444 | } |
| 445 | |
| 446 | // Do the function merge. |
| 447 | for (BinaryFunctionProfile &BF : BP.Functions) { |
| 448 | if (!MergedBFs.count(Key: BF.Name)) { |
| 449 | MergedBFs.insert(KV: std::make_pair(x&: BF.Name, y&: BF)); |
| 450 | continue; |
| 451 | } |
| 452 | |
| 453 | BinaryFunctionProfile &MergedBF = MergedBFs.find(Key: BF.Name)->second; |
| 454 | mergeFunctionProfile(MergedBF, BF: std::move(BF)); |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | if (!opts::SuppressMergedDataOutput) { |
| 459 | yaml::Output YamlOut(output()); |
| 460 | |
| 461 | BinaryProfile MergedProfile; |
| 462 | MergedProfile.Header = MergedHeader; |
| 463 | MergedProfile.Functions.resize(new_size: MergedBFs.size()); |
| 464 | llvm::copy(Range: llvm::make_second_range(c&: MergedBFs), |
| 465 | Out: MergedProfile.Functions.begin()); |
| 466 | |
| 467 | // For consistency, sort functions by their IDs. |
| 468 | llvm::sort(C&: MergedProfile.Functions, |
| 469 | Comp: [](const BinaryFunctionProfile &A, |
| 470 | const BinaryFunctionProfile &B) { return A.Id < B.Id; }); |
| 471 | |
| 472 | YamlOut << MergedProfile; |
| 473 | } |
| 474 | |
| 475 | errs() << "Data for " << MergedBFs.size() |
| 476 | << " unique objects successfully merged.\n" ; |
| 477 | |
| 478 | if (opts::PrintFunctionList != opts::ST_NONE) { |
| 479 | // List of function names with execution count. |
| 480 | std::vector<std::pair<uint64_t, StringRef>> FunctionList(MergedBFs.size()); |
| 481 | using CountFuncType = std::function<std::pair<uint64_t, StringRef>( |
| 482 | const StringMapEntry<BinaryFunctionProfile> &)>; |
| 483 | CountFuncType ExecCountFunc = |
| 484 | [](const StringMapEntry<BinaryFunctionProfile> &V) { |
| 485 | return std::make_pair(x: V.second.ExecCount, y: StringRef(V.second.Name)); |
| 486 | }; |
| 487 | CountFuncType BranchCountFunc = |
| 488 | [](const StringMapEntry<BinaryFunctionProfile> &V) { |
| 489 | // Return total branch count. |
| 490 | uint64_t BranchCount = 0; |
| 491 | for (const BinaryBasicBlockProfile &BI : V.second.Blocks) |
| 492 | for (const SuccessorInfo &SI : BI.Successors) |
| 493 | BranchCount += SI.Count; |
| 494 | return std::make_pair(x&: BranchCount, y: StringRef(V.second.Name)); |
| 495 | }; |
| 496 | |
| 497 | CountFuncType CountFunc = (opts::PrintFunctionList == opts::ST_EXEC_COUNT) |
| 498 | ? ExecCountFunc |
| 499 | : BranchCountFunc; |
| 500 | llvm::transform(Range&: MergedBFs, d_first: FunctionList.begin(), F: CountFunc); |
| 501 | llvm::stable_sort(Range: reverse(C&: FunctionList)); |
| 502 | errs() << "Functions sorted by " |
| 503 | << (opts::PrintFunctionList == opts::ST_EXEC_COUNT ? "execution" |
| 504 | : "total branch" ) |
| 505 | << " count:\n" ; |
| 506 | for (std::pair<uint64_t, StringRef> &FI : FunctionList) |
| 507 | errs() << FI.second << " : " << FI.first << '\n'; |
| 508 | } |
| 509 | |
| 510 | return 0; |
| 511 | } |
| 512 | |