Path: blob/main/contrib/llvm-project/llvm/tools/llvm-profdata/llvm-profdata.cpp
35258 views
//===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// llvm-profdata merges .profdata files.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ADT/SmallSet.h"13#include "llvm/ADT/SmallVector.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/IR/LLVMContext.h"16#include "llvm/Object/Binary.h"17#include "llvm/ProfileData/InstrProfCorrelator.h"18#include "llvm/ProfileData/InstrProfReader.h"19#include "llvm/ProfileData/InstrProfWriter.h"20#include "llvm/ProfileData/MemProf.h"21#include "llvm/ProfileData/MemProfReader.h"22#include "llvm/ProfileData/ProfileCommon.h"23#include "llvm/ProfileData/SampleProfReader.h"24#include "llvm/ProfileData/SampleProfWriter.h"25#include "llvm/Support/BalancedPartitioning.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/Discriminator.h"28#include "llvm/Support/Errc.h"29#include "llvm/Support/FileSystem.h"30#include "llvm/Support/Format.h"31#include "llvm/Support/FormattedStream.h"32#include "llvm/Support/LLVMDriver.h"33#include "llvm/Support/MD5.h"34#include "llvm/Support/MemoryBuffer.h"35#include "llvm/Support/Path.h"36#include "llvm/Support/Regex.h"37#include "llvm/Support/ThreadPool.h"38#include "llvm/Support/Threading.h"39#include "llvm/Support/VirtualFileSystem.h"40#include "llvm/Support/WithColor.h"41#include "llvm/Support/raw_ostream.h"42#include <algorithm>43#include <cmath>44#include <optional>45#include <queue>4647using namespace llvm;48using ProfCorrelatorKind = InstrProfCorrelator::ProfCorrelatorKind;4950// https://llvm.org/docs/CommandGuide/llvm-profdata.html has documentations51// on each subcommand.52cl::SubCommand ShowSubcommand(53"show",54"Takes a profile data file and displays the profiles. See detailed "55"documentation in "56"https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-show");57cl::SubCommand OrderSubcommand(58"order",59"Reads temporal profiling traces from a profile and outputs a function "60"order that reduces the number of page faults for those traces. See "61"detailed documentation in "62"https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-order");63cl::SubCommand OverlapSubcommand(64"overlap",65"Computes and displays the overlap between two profiles. See detailed "66"documentation in "67"https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-overlap");68cl::SubCommand MergeSubcommand(69"merge",70"Takes several profiles and merge them together. See detailed "71"documentation in "72"https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge");7374namespace {75enum ProfileKinds { instr, sample, memory };76enum FailureMode { warnOnly, failIfAnyAreInvalid, failIfAllAreInvalid };7778enum ProfileFormat {79PF_None = 0,80PF_Text,81PF_Compact_Binary, // Deprecated82PF_Ext_Binary,83PF_GCC,84PF_Binary85};8687enum class ShowFormat { Text, Json, Yaml };88} // namespace8990// Common options.91cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),92cl::init("-"), cl::desc("Output file"),93cl::sub(ShowSubcommand),94cl::sub(OrderSubcommand),95cl::sub(OverlapSubcommand),96cl::sub(MergeSubcommand));97// NOTE: cl::alias must not have cl::sub(), since aliased option's cl::sub()98// will be used. llvm::cl::alias::done() method asserts this condition.99cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),100cl::aliasopt(OutputFilename));101102// Options common to at least two commands.103cl::opt<ProfileKinds> ProfileKind(104cl::desc("Profile kind:"), cl::sub(MergeSubcommand),105cl::sub(OverlapSubcommand), cl::init(instr),106cl::values(clEnumVal(instr, "Instrumentation profile (default)"),107clEnumVal(sample, "Sample profile")));108cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"),109cl::sub(ShowSubcommand),110cl::sub(OrderSubcommand));111cl::opt<unsigned> MaxDbgCorrelationWarnings(112"max-debug-info-correlation-warnings",113cl::desc("The maximum number of warnings to emit when correlating "114"profile from debug info (0 = no limit)"),115cl::sub(MergeSubcommand), cl::sub(ShowSubcommand), cl::init(5));116cl::opt<std::string> ProfiledBinary(117"profiled-binary", cl::init(""),118cl::desc("Path to binary from which the profile was collected."),119cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));120cl::opt<std::string> DebugInfoFilename(121"debug-info", cl::init(""),122cl::desc(123"For show, read and extract profile metadata from debug info and show "124"the functions it found. For merge, use the provided debug info to "125"correlate the raw profile."),126cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));127cl::opt<std::string>128BinaryFilename("binary-file", cl::init(""),129cl::desc("For merge, use the provided unstripped bianry to "130"correlate the raw profile."),131cl::sub(MergeSubcommand));132cl::opt<std::string> FuncNameFilter(133"function",134cl::desc("Only functions matching the filter are shown in the output. For "135"overlapping CSSPGO, this takes a function name with calling "136"context."),137cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand),138cl::sub(MergeSubcommand));139140// TODO: Consider creating a template class (e.g., MergeOption, ShowOption) to141// factor out the common cl::sub in cl::opt constructor for subcommand-specific142// options.143144// Options specific to merge subcommand.145cl::list<std::string> InputFilenames(cl::Positional, cl::sub(MergeSubcommand),146cl::desc("<filename...>"));147cl::list<std::string> WeightedInputFilenames("weighted-input",148cl::sub(MergeSubcommand),149cl::desc("<weight>,<filename>"));150cl::opt<ProfileFormat> OutputFormat(151cl::desc("Format of output profile"), cl::sub(MergeSubcommand),152cl::init(PF_Ext_Binary),153cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding"),154clEnumValN(PF_Ext_Binary, "extbinary",155"Extensible binary encoding "156"(default)"),157clEnumValN(PF_Text, "text", "Text encoding"),158clEnumValN(PF_GCC, "gcc",159"GCC encoding (only meaningful for -sample)")));160cl::opt<std::string>161InputFilenamesFile("input-files", cl::init(""), cl::sub(MergeSubcommand),162cl::desc("Path to file containing newline-separated "163"[<weight>,]<filename> entries"));164cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),165cl::aliasopt(InputFilenamesFile));166cl::opt<bool> DumpInputFileList(167"dump-input-file-list", cl::init(false), cl::Hidden,168cl::sub(MergeSubcommand),169cl::desc("Dump the list of input files and their weights, then exit"));170cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),171cl::sub(MergeSubcommand),172cl::desc("Symbol remapping file"));173cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),174cl::aliasopt(RemappingFile));175cl::opt<bool>176UseMD5("use-md5", cl::init(false), cl::Hidden,177cl::desc("Choose to use MD5 to represent string in name table (only "178"meaningful for -extbinary)"),179cl::sub(MergeSubcommand));180cl::opt<bool> CompressAllSections(181"compress-all-sections", cl::init(false), cl::Hidden,182cl::sub(MergeSubcommand),183cl::desc("Compress all sections when writing the profile (only "184"meaningful for -extbinary)"));185cl::opt<bool> SampleMergeColdContext(186"sample-merge-cold-context", cl::init(false), cl::Hidden,187cl::sub(MergeSubcommand),188cl::desc(189"Merge context sample profiles whose count is below cold threshold"));190cl::opt<bool> SampleTrimColdContext(191"sample-trim-cold-context", cl::init(false), cl::Hidden,192cl::sub(MergeSubcommand),193cl::desc(194"Trim context sample profiles whose count is below cold threshold"));195cl::opt<uint32_t> SampleColdContextFrameDepth(196"sample-frame-depth-for-cold-context", cl::init(1),197cl::sub(MergeSubcommand),198cl::desc("Keep the last K frames while merging cold profile. 1 means the "199"context-less base profile"));200cl::opt<size_t> OutputSizeLimit(201"output-size-limit", cl::init(0), cl::Hidden, cl::sub(MergeSubcommand),202cl::desc("Trim cold functions until profile size is below specified "203"limit in bytes. This uses a heursitic and functions may be "204"excessively trimmed"));205cl::opt<bool> GenPartialProfile(206"gen-partial-profile", cl::init(false), cl::Hidden,207cl::sub(MergeSubcommand),208cl::desc("Generate a partial profile (only meaningful for -extbinary)"));209cl::opt<std::string> SupplInstrWithSample(210"supplement-instr-with-sample", cl::init(""), cl::Hidden,211cl::sub(MergeSubcommand),212cl::desc("Supplement an instr profile with sample profile, to correct "213"the profile unrepresentativeness issue. The sample "214"profile is the input of the flag. Output will be in instr "215"format (The flag only works with -instr)"));216cl::opt<float> ZeroCounterThreshold(217"zero-counter-threshold", cl::init(0.7), cl::Hidden,218cl::sub(MergeSubcommand),219cl::desc("For the function which is cold in instr profile but hot in "220"sample profile, if the ratio of the number of zero counters "221"divided by the total number of counters is above the "222"threshold, the profile of the function will be regarded as "223"being harmful for performance and will be dropped."));224cl::opt<unsigned> SupplMinSizeThreshold(225"suppl-min-size-threshold", cl::init(10), cl::Hidden,226cl::sub(MergeSubcommand),227cl::desc("If the size of a function is smaller than the threshold, "228"assume it can be inlined by PGO early inliner and it won't "229"be adjusted based on sample profile."));230cl::opt<unsigned> InstrProfColdThreshold(231"instr-prof-cold-threshold", cl::init(0), cl::Hidden,232cl::sub(MergeSubcommand),233cl::desc("User specified cold threshold for instr profile which will "234"override the cold threshold got from profile summary. "));235// WARNING: This reservoir size value is propagated to any input indexed236// profiles for simplicity. Changing this value between invocations could237// result in sample bias.238cl::opt<uint64_t> TemporalProfTraceReservoirSize(239"temporal-profile-trace-reservoir-size", cl::init(100),240cl::sub(MergeSubcommand),241cl::desc("The maximum number of stored temporal profile traces (default: "242"100)"));243cl::opt<uint64_t> TemporalProfMaxTraceLength(244"temporal-profile-max-trace-length", cl::init(10000),245cl::sub(MergeSubcommand),246cl::desc("The maximum length of a single temporal profile trace "247"(default: 10000)"));248cl::opt<std::string> FuncNameNegativeFilter(249"no-function", cl::init(""),250cl::sub(MergeSubcommand),251cl::desc("Exclude functions matching the filter from the output."));252253cl::opt<FailureMode>254FailMode("failure-mode", cl::init(failIfAnyAreInvalid),255cl::desc("Failure mode:"), cl::sub(MergeSubcommand),256cl::values(clEnumValN(warnOnly, "warn",257"Do not fail and just print warnings."),258clEnumValN(failIfAnyAreInvalid, "any",259"Fail if any profile is invalid."),260clEnumValN(failIfAllAreInvalid, "all",261"Fail only if all profiles are invalid.")));262263cl::opt<bool> OutputSparse(264"sparse", cl::init(false), cl::sub(MergeSubcommand),265cl::desc("Generate a sparse profile (only meaningful for -instr)"));266cl::opt<unsigned> NumThreads(267"num-threads", cl::init(0), cl::sub(MergeSubcommand),268cl::desc("Number of merge threads to use (default: autodetect)"));269cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),270cl::aliasopt(NumThreads));271272cl::opt<std::string> ProfileSymbolListFile(273"prof-sym-list", cl::init(""), cl::sub(MergeSubcommand),274cl::desc("Path to file containing the list of function symbols "275"used to populate profile symbol list"));276277cl::opt<SampleProfileLayout> ProfileLayout(278"convert-sample-profile-layout",279cl::desc("Convert the generated profile to a profile with a new layout"),280cl::sub(MergeSubcommand), cl::init(SPL_None),281cl::values(282clEnumValN(SPL_Nest, "nest",283"Nested profile, the input should be CS flat profile"),284clEnumValN(SPL_Flat, "flat",285"Profile with nested inlinee flatten out")));286287cl::opt<bool> DropProfileSymbolList(288"drop-profile-symbol-list", cl::init(false), cl::Hidden,289cl::sub(MergeSubcommand),290cl::desc("Drop the profile symbol list when merging AutoFDO profiles "291"(only meaningful for -sample)"));292293cl::opt<bool> KeepVTableSymbols(294"keep-vtable-symbols", cl::init(false), cl::Hidden,295cl::sub(MergeSubcommand),296cl::desc("If true, keep the vtable symbols in indexed profiles"));297298// Temporary support for writing the previous version of the format, to enable299// some forward compatibility.300// TODO: Consider enabling this with future version changes as well, to ease301// deployment of newer versions of llvm-profdata.302cl::opt<bool> DoWritePrevVersion(303"write-prev-version", cl::init(false), cl::Hidden,304cl::desc("Write the previous version of indexed format, to enable "305"some forward compatibility."));306307cl::opt<memprof::IndexedVersion> MemProfVersionRequested(308"memprof-version", cl::Hidden, cl::sub(MergeSubcommand),309cl::desc("Specify the version of the memprof format to use"),310cl::init(memprof::Version0),311cl::values(clEnumValN(memprof::Version0, "0", "version 0"),312clEnumValN(memprof::Version1, "1", "version 1"),313clEnumValN(memprof::Version2, "2", "version 2"),314clEnumValN(memprof::Version3, "3", "version 3")));315316cl::opt<bool> MemProfFullSchema(317"memprof-full-schema", cl::Hidden, cl::sub(MergeSubcommand),318cl::desc("Use the full schema for serialization"), cl::init(false));319320// Options specific to overlap subcommand.321cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,322cl::desc("<base profile file>"),323cl::sub(OverlapSubcommand));324cl::opt<std::string> TestFilename(cl::Positional, cl::Required,325cl::desc("<test profile file>"),326cl::sub(OverlapSubcommand));327328cl::opt<unsigned long long> SimilarityCutoff(329"similarity-cutoff", cl::init(0),330cl::desc("For sample profiles, list function names (with calling context "331"for csspgo) for overlapped functions "332"with similarities below the cutoff (percentage times 10000)."),333cl::sub(OverlapSubcommand));334335cl::opt<bool> IsCS(336"cs", cl::init(false),337cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."),338cl::sub(OverlapSubcommand));339340cl::opt<unsigned long long> OverlapValueCutoff(341"value-cutoff", cl::init(-1),342cl::desc(343"Function level overlap information for every function (with calling "344"context for csspgo) in test "345"profile with max count value greater then the parameter value"),346cl::sub(OverlapSubcommand));347348// Options specific to show subcommand.349cl::opt<bool> ShowCounts("counts", cl::init(false),350cl::desc("Show counter values for shown functions"),351cl::sub(ShowSubcommand));352cl::opt<ShowFormat>353SFormat("show-format", cl::init(ShowFormat::Text),354cl::desc("Emit output in the selected format if supported"),355cl::sub(ShowSubcommand),356cl::values(clEnumValN(ShowFormat::Text, "text",357"emit normal text output (default)"),358clEnumValN(ShowFormat::Json, "json", "emit JSON"),359clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")));360// TODO: Consider replacing this with `--show-format=text-encoding`.361cl::opt<bool>362TextFormat("text", cl::init(false),363cl::desc("Show instr profile data in text dump format"),364cl::sub(ShowSubcommand));365cl::opt<bool>366JsonFormat("json",367cl::desc("Show sample profile data in the JSON format "368"(deprecated, please use --show-format=json)"),369cl::sub(ShowSubcommand));370cl::opt<bool> ShowIndirectCallTargets(371"ic-targets", cl::init(false),372cl::desc("Show indirect call site target values for shown functions"),373cl::sub(ShowSubcommand));374cl::opt<bool> ShowVTables("show-vtables", cl::init(false),375cl::desc("Show vtable names for shown functions"),376cl::sub(ShowSubcommand));377cl::opt<bool> ShowMemOPSizes(378"memop-sizes", cl::init(false),379cl::desc("Show the profiled sizes of the memory intrinsic calls "380"for shown functions"),381cl::sub(ShowSubcommand));382cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),383cl::desc("Show detailed profile summary"),384cl::sub(ShowSubcommand));385cl::list<uint32_t> DetailedSummaryCutoffs(386cl::CommaSeparated, "detailed-summary-cutoffs",387cl::desc(388"Cutoff percentages (times 10000) for generating detailed summary"),389cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand));390cl::opt<bool>391ShowHotFuncList("hot-func-list", cl::init(false),392cl::desc("Show profile summary of a list of hot functions"),393cl::sub(ShowSubcommand));394cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),395cl::desc("Details for each and every function"),396cl::sub(ShowSubcommand));397cl::opt<bool> ShowCS("showcs", cl::init(false),398cl::desc("Show context sensitive counts"),399cl::sub(ShowSubcommand));400cl::opt<ProfileKinds> ShowProfileKind(401cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand),402cl::init(instr),403cl::values(clEnumVal(instr, "Instrumentation profile (default)"),404clEnumVal(sample, "Sample profile"),405clEnumVal(memory, "MemProf memory access profile")));406cl::opt<uint32_t> TopNFunctions(407"topn", cl::init(0),408cl::desc("Show the list of functions with the largest internal counts"),409cl::sub(ShowSubcommand));410cl::opt<uint32_t> ShowValueCutoff(411"value-cutoff", cl::init(0),412cl::desc("Set the count value cutoff. Functions with the maximum count "413"less than this value will not be printed out. (Default is 0)"),414cl::sub(ShowSubcommand));415cl::opt<bool> OnlyListBelow(416"list-below-cutoff", cl::init(false),417cl::desc("Only output names of functions whose max count values are "418"below the cutoff value"),419cl::sub(ShowSubcommand));420cl::opt<bool> ShowProfileSymbolList(421"show-prof-sym-list", cl::init(false),422cl::desc("Show profile symbol list if it exists in the profile. "),423cl::sub(ShowSubcommand));424cl::opt<bool> ShowSectionInfoOnly(425"show-sec-info-only", cl::init(false),426cl::desc("Show the information of each section in the sample profile. "427"The flag is only usable when the sample profile is in "428"extbinary format"),429cl::sub(ShowSubcommand));430cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),431cl::desc("Show binary ids in the profile. "),432cl::sub(ShowSubcommand));433cl::opt<bool> ShowTemporalProfTraces(434"temporal-profile-traces",435cl::desc("Show temporal profile traces in the profile."),436cl::sub(ShowSubcommand));437438cl::opt<bool>439ShowCovered("covered", cl::init(false),440cl::desc("Show only the functions that have been executed."),441cl::sub(ShowSubcommand));442443cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false),444cl::desc("Show profile version. "),445cl::sub(ShowSubcommand));446447// Options specific to order subcommand.448cl::opt<unsigned>449NumTestTraces("num-test-traces", cl::init(0),450cl::desc("Keep aside the last <num-test-traces> traces in "451"the profile when computing the function order and "452"instead use them to evaluate that order"),453cl::sub(OrderSubcommand));454455// We use this string to indicate that there are456// multiple static functions map to the same name.457const std::string DuplicateNameStr = "----";458459static void warn(Twine Message, StringRef Whence = "", StringRef Hint = "") {460WithColor::warning();461if (!Whence.empty())462errs() << Whence << ": ";463errs() << Message << "\n";464if (!Hint.empty())465WithColor::note() << Hint << "\n";466}467468static void warn(Error E, StringRef Whence = "") {469if (E.isA<InstrProfError>()) {470handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {471warn(IPE.message(), Whence);472});473}474}475476static void exitWithError(Twine Message, StringRef Whence = "",477StringRef Hint = "") {478WithColor::error();479if (!Whence.empty())480errs() << Whence << ": ";481errs() << Message << "\n";482if (!Hint.empty())483WithColor::note() << Hint << "\n";484::exit(1);485}486487static void exitWithError(Error E, StringRef Whence = "") {488if (E.isA<InstrProfError>()) {489handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {490instrprof_error instrError = IPE.get();491StringRef Hint = "";492if (instrError == instrprof_error::unrecognized_format) {493// Hint in case user missed specifying the profile type.494Hint = "Perhaps you forgot to use the --sample or --memory option?";495}496exitWithError(IPE.message(), Whence, Hint);497});498return;499}500501exitWithError(toString(std::move(E)), Whence);502}503504static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {505exitWithError(EC.message(), Whence);506}507508static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,509StringRef Whence = "") {510if (FailMode == failIfAnyAreInvalid)511exitWithErrorCode(EC, Whence);512else513warn(EC.message(), Whence);514}515516static void handleMergeWriterError(Error E, StringRef WhenceFile = "",517StringRef WhenceFunction = "",518bool ShowHint = true) {519if (!WhenceFile.empty())520errs() << WhenceFile << ": ";521if (!WhenceFunction.empty())522errs() << WhenceFunction << ": ";523524auto IPE = instrprof_error::success;525E = handleErrors(std::move(E),526[&IPE](std::unique_ptr<InstrProfError> E) -> Error {527IPE = E->get();528return Error(std::move(E));529});530errs() << toString(std::move(E)) << "\n";531532if (ShowHint) {533StringRef Hint = "";534if (IPE != instrprof_error::success) {535switch (IPE) {536case instrprof_error::hash_mismatch:537case instrprof_error::count_mismatch:538case instrprof_error::value_site_count_mismatch:539Hint = "Make sure that all profile data to be merged is generated "540"from the same binary.";541break;542default:543break;544}545}546547if (!Hint.empty())548errs() << Hint << "\n";549}550}551552namespace {553/// A remapper from original symbol names to new symbol names based on a file554/// containing a list of mappings from old name to new name.555class SymbolRemapper {556std::unique_ptr<MemoryBuffer> File;557DenseMap<StringRef, StringRef> RemappingTable;558559public:560/// Build a SymbolRemapper from a file containing a list of old/new symbols.561static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {562auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);563if (!BufOrError)564exitWithErrorCode(BufOrError.getError(), InputFile);565566auto Remapper = std::make_unique<SymbolRemapper>();567Remapper->File = std::move(BufOrError.get());568569for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');570!LineIt.is_at_eof(); ++LineIt) {571std::pair<StringRef, StringRef> Parts = LineIt->split(' ');572if (Parts.first.empty() || Parts.second.empty() ||573Parts.second.count(' ')) {574exitWithError("unexpected line in remapping file",575(InputFile + ":" + Twine(LineIt.line_number())).str(),576"expected 'old_symbol new_symbol'");577}578Remapper->RemappingTable.insert(Parts);579}580return Remapper;581}582583/// Attempt to map the given old symbol into a new symbol.584///585/// \return The new symbol, or \p Name if no such symbol was found.586StringRef operator()(StringRef Name) {587StringRef New = RemappingTable.lookup(Name);588return New.empty() ? Name : New;589}590591FunctionId operator()(FunctionId Name) {592// MD5 name cannot be remapped.593if (!Name.isStringRef())594return Name;595StringRef New = RemappingTable.lookup(Name.stringRef());596return New.empty() ? Name : FunctionId(New);597}598};599}600601struct WeightedFile {602std::string Filename;603uint64_t Weight;604};605typedef SmallVector<WeightedFile, 5> WeightedFileVector;606607/// Keep track of merged data and reported errors.608struct WriterContext {609std::mutex Lock;610InstrProfWriter Writer;611std::vector<std::pair<Error, std::string>> Errors;612std::mutex &ErrLock;613SmallSet<instrprof_error, 4> &WriterErrorCodes;614615WriterContext(bool IsSparse, std::mutex &ErrLock,616SmallSet<instrprof_error, 4> &WriterErrorCodes,617uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)618: Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion,619MemProfVersionRequested, MemProfFullSchema),620ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}621};622623/// Computer the overlap b/w profile BaseFilename and TestFileName,624/// and store the program level result to Overlap.625static void overlapInput(const std::string &BaseFilename,626const std::string &TestFilename, WriterContext *WC,627OverlapStats &Overlap,628const OverlapFuncFilters &FuncFilter,629raw_fd_ostream &OS, bool IsCS) {630auto FS = vfs::getRealFileSystem();631auto ReaderOrErr = InstrProfReader::create(TestFilename, *FS);632if (Error E = ReaderOrErr.takeError()) {633// Skip the empty profiles by returning sliently.634auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));635if (ErrorCode != instrprof_error::empty_raw_profile)636WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),637TestFilename);638return;639}640641auto Reader = std::move(ReaderOrErr.get());642for (auto &I : *Reader) {643OverlapStats FuncOverlap(OverlapStats::FunctionLevel);644FuncOverlap.setFuncInfo(I.Name, I.Hash);645646WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);647FuncOverlap.dump(OS);648}649}650651/// Load an input into a writer context.652static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,653const InstrProfCorrelator *Correlator,654const StringRef ProfiledBinary, WriterContext *WC) {655std::unique_lock<std::mutex> CtxGuard{WC->Lock};656657// Copy the filename, because llvm::ThreadPool copied the input "const658// WeightedFile &" by value, making a reference to the filename within it659// invalid outside of this packaged task.660std::string Filename = Input.Filename;661662using ::llvm::memprof::RawMemProfReader;663if (RawMemProfReader::hasFormat(Input.Filename)) {664auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);665if (!ReaderOrErr) {666exitWithError(ReaderOrErr.takeError(), Input.Filename);667}668std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());669// Check if the profile types can be merged, e.g. clang frontend profiles670// should not be merged with memprof profiles.671if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {672consumeError(std::move(E));673WC->Errors.emplace_back(674make_error<StringError>(675"Cannot merge MemProf profile with Clang generated profile.",676std::error_code()),677Filename);678return;679}680681auto MemProfError = [&](Error E) {682auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));683WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),684Filename);685};686687// Add the frame mappings into the writer context.688const auto &IdToFrame = Reader->getFrameMapping();689for (const auto &I : IdToFrame) {690bool Succeeded = WC->Writer.addMemProfFrame(691/*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);692// If we weren't able to add the frame mappings then it doesn't make sense693// to try to add the records from this profile.694if (!Succeeded)695return;696}697698// Add the call stacks into the writer context.699const auto &CSIdToCallStacks = Reader->getCallStacks();700for (const auto &I : CSIdToCallStacks) {701bool Succeeded = WC->Writer.addMemProfCallStack(702/*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);703// If we weren't able to add the call stacks then it doesn't make sense704// to try to add the records from this profile.705if (!Succeeded)706return;707}708709const auto &FunctionProfileData = Reader->getProfileData();710// Add the memprof records into the writer context.711for (const auto &[GUID, Record] : FunctionProfileData) {712WC->Writer.addMemProfRecord(GUID, Record);713}714return;715}716717auto FS = vfs::getRealFileSystem();718// TODO: This only saves the first non-fatal error from InstrProfReader, and719// then added to WriterContext::Errors. However, this is not extensible, if720// we have more non-fatal errors from InstrProfReader in the future. How721// should this interact with different -failure-mode?722std::optional<std::pair<Error, std::string>> ReaderWarning;723auto Warn = [&](Error E) {724if (ReaderWarning) {725consumeError(std::move(E));726return;727}728// Only show the first time an error occurs in this file.729auto [ErrCode, Msg] = InstrProfError::take(std::move(E));730ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};731};732auto ReaderOrErr =733InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);734if (Error E = ReaderOrErr.takeError()) {735// Skip the empty profiles by returning silently.736auto [ErrCode, Msg] = InstrProfError::take(std::move(E));737if (ErrCode != instrprof_error::empty_raw_profile)738WC->Errors.emplace_back(make_error<InstrProfError>(ErrCode, Msg),739Filename);740return;741}742743auto Reader = std::move(ReaderOrErr.get());744if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {745consumeError(std::move(E));746WC->Errors.emplace_back(747make_error<StringError>(748"Merge IR generated profile with Clang generated profile.",749std::error_code()),750Filename);751return;752}753754for (auto &I : *Reader) {755if (Remapper)756I.Name = (*Remapper)(I.Name);757const StringRef FuncName = I.Name;758bool Reported = false;759WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {760if (Reported) {761consumeError(std::move(E));762return;763}764Reported = true;765// Only show hint the first time an error occurs.766auto [ErrCode, Msg] = InstrProfError::take(std::move(E));767std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};768bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second;769handleMergeWriterError(make_error<InstrProfError>(ErrCode, Msg),770Input.Filename, FuncName, firstTime);771});772}773774if (KeepVTableSymbols) {775const InstrProfSymtab &symtab = Reader->getSymtab();776const auto &VTableNames = symtab.getVTableNames();777778for (const auto &kv : VTableNames)779WC->Writer.addVTableName(kv.getKey());780}781782if (Reader->hasTemporalProfile()) {783auto &Traces = Reader->getTemporalProfTraces(Input.Weight);784if (!Traces.empty())785WC->Writer.addTemporalProfileTraces(786Traces, Reader->getTemporalProfTraceStreamSize());787}788if (Reader->hasError()) {789if (Error E = Reader->getError()) {790WC->Errors.emplace_back(std::move(E), Filename);791return;792}793}794795std::vector<llvm::object::BuildID> BinaryIds;796if (Error E = Reader->readBinaryIds(BinaryIds)) {797WC->Errors.emplace_back(std::move(E), Filename);798return;799}800WC->Writer.addBinaryIds(BinaryIds);801802if (ReaderWarning) {803WC->Errors.emplace_back(std::move(ReaderWarning->first),804ReaderWarning->second);805}806}807808/// Merge the \p Src writer context into \p Dst.809static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {810for (auto &ErrorPair : Src->Errors)811Dst->Errors.push_back(std::move(ErrorPair));812Src->Errors.clear();813814if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind()))815exitWithError(std::move(E));816817Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {818auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));819std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};820bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second;821if (firstTime)822warn(toString(make_error<InstrProfError>(ErrorCode, Msg)));823});824}825826static StringRef827getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) {828return Val.first();829}830831static std::string832getFuncName(const SampleProfileMap::value_type &Val) {833return Val.second.getContext().toString();834}835836template <typename T>837static void filterFunctions(T &ProfileMap) {838bool hasFilter = !FuncNameFilter.empty();839bool hasNegativeFilter = !FuncNameNegativeFilter.empty();840if (!hasFilter && !hasNegativeFilter)841return;842843// If filter starts with '?' it is MSVC mangled name, not a regex.844llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+");845if (hasFilter && FuncNameFilter[0] == '?' &&846ProbablyMSVCMangledName.match(FuncNameFilter))847FuncNameFilter = llvm::Regex::escape(FuncNameFilter);848if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' &&849ProbablyMSVCMangledName.match(FuncNameNegativeFilter))850FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter);851852size_t Count = ProfileMap.size();853llvm::Regex Pattern(FuncNameFilter);854llvm::Regex NegativePattern(FuncNameNegativeFilter);855std::string Error;856if (hasFilter && !Pattern.isValid(Error))857exitWithError(Error);858if (hasNegativeFilter && !NegativePattern.isValid(Error))859exitWithError(Error);860861// Handle MD5 profile, so it is still able to match using the original name.862std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter));863std::string NegativeMD5Name =864std::to_string(llvm::MD5Hash(FuncNameNegativeFilter));865866for (auto I = ProfileMap.begin(); I != ProfileMap.end();) {867auto Tmp = I++;868const auto &FuncName = getFuncName(*Tmp);869// Negative filter has higher precedence than positive filter.870if ((hasNegativeFilter &&871(NegativePattern.match(FuncName) ||872(FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) ||873(hasFilter && !(Pattern.match(FuncName) ||874(FunctionSamples::UseMD5 && MD5Name == FuncName))))875ProfileMap.erase(Tmp);876}877878llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions "879<< "in the original profile are filtered.\n";880}881882static void writeInstrProfile(StringRef OutputFilename,883ProfileFormat OutputFormat,884InstrProfWriter &Writer) {885std::error_code EC;886raw_fd_ostream Output(OutputFilename.data(), EC,887OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF888: sys::fs::OF_None);889if (EC)890exitWithErrorCode(EC, OutputFilename);891892if (OutputFormat == PF_Text) {893if (Error E = Writer.writeText(Output))894warn(std::move(E));895} else {896if (Output.is_displayed())897exitWithError("cannot write a non-text format profile to the terminal");898if (Error E = Writer.write(Output))899warn(std::move(E));900}901}902903static void mergeInstrProfile(const WeightedFileVector &Inputs,904SymbolRemapper *Remapper,905int MaxDbgCorrelationWarnings,906const StringRef ProfiledBinary) {907const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue();908const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue();909if (OutputFormat == PF_Compact_Binary)910exitWithError("Compact Binary is deprecated");911if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary &&912OutputFormat != PF_Text)913exitWithError("unknown format is specified");914915// TODO: Maybe we should support correlation with mixture of different916// correlation modes(w/wo debug-info/object correlation).917if (!DebugInfoFilename.empty() && !BinaryFilename.empty())918exitWithError("Expected only one of -debug-info, -binary-file");919std::string CorrelateFilename;920ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;921if (!DebugInfoFilename.empty()) {922CorrelateFilename = DebugInfoFilename;923CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;924} else if (!BinaryFilename.empty()) {925CorrelateFilename = BinaryFilename;926CorrelateKind = ProfCorrelatorKind::BINARY;927}928929std::unique_ptr<InstrProfCorrelator> Correlator;930if (CorrelateKind != InstrProfCorrelator::NONE) {931if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)932.moveInto(Correlator))933exitWithError(std::move(Err), CorrelateFilename);934if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))935exitWithError(std::move(Err), CorrelateFilename);936}937938std::mutex ErrorLock;939SmallSet<instrprof_error, 4> WriterErrorCodes;940941// If NumThreads is not specified, auto-detect a good default.942if (NumThreads == 0)943NumThreads = std::min(hardware_concurrency().compute_thread_count(),944unsigned((Inputs.size() + 1) / 2));945946// Initialize the writer contexts.947SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;948for (unsigned I = 0; I < NumThreads; ++I)949Contexts.emplace_back(std::make_unique<WriterContext>(950OutputSparse, ErrorLock, WriterErrorCodes, TraceReservoirSize,951MaxTraceLength));952953if (NumThreads == 1) {954for (const auto &Input : Inputs)955loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,956Contexts[0].get());957} else {958DefaultThreadPool Pool(hardware_concurrency(NumThreads));959960// Load the inputs in parallel (N/NumThreads serial steps).961unsigned Ctx = 0;962for (const auto &Input : Inputs) {963Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,964Contexts[Ctx].get());965Ctx = (Ctx + 1) % NumThreads;966}967Pool.wait();968969// Merge the writer contexts together (~ lg(NumThreads) serial steps).970unsigned Mid = Contexts.size() / 2;971unsigned End = Contexts.size();972assert(Mid > 0 && "Expected more than one context");973do {974for (unsigned I = 0; I < Mid; ++I)975Pool.async(mergeWriterContexts, Contexts[I].get(),976Contexts[I + Mid].get());977Pool.wait();978if (End & 1) {979Pool.async(mergeWriterContexts, Contexts[0].get(),980Contexts[End - 1].get());981Pool.wait();982}983End = Mid;984Mid /= 2;985} while (Mid > 0);986}987988// Handle deferred errors encountered during merging. If the number of errors989// is equal to the number of inputs the merge failed.990unsigned NumErrors = 0;991for (std::unique_ptr<WriterContext> &WC : Contexts) {992for (auto &ErrorPair : WC->Errors) {993++NumErrors;994warn(toString(std::move(ErrorPair.first)), ErrorPair.second);995}996}997if ((NumErrors == Inputs.size() && FailMode == failIfAllAreInvalid) ||998(NumErrors > 0 && FailMode == failIfAnyAreInvalid))999exitWithError("no profile can be merged");10001001filterFunctions(Contexts[0]->Writer.getProfileData());10021003writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);1004}10051006/// The profile entry for a function in instrumentation profile.1007struct InstrProfileEntry {1008uint64_t MaxCount = 0;1009uint64_t NumEdgeCounters = 0;1010float ZeroCounterRatio = 0.0;1011InstrProfRecord *ProfRecord;1012InstrProfileEntry(InstrProfRecord *Record);1013InstrProfileEntry() = default;1014};10151016InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {1017ProfRecord = Record;1018uint64_t CntNum = Record->Counts.size();1019uint64_t ZeroCntNum = 0;1020for (size_t I = 0; I < CntNum; ++I) {1021MaxCount = std::max(MaxCount, Record->Counts[I]);1022ZeroCntNum += !Record->Counts[I];1023}1024ZeroCounterRatio = (float)ZeroCntNum / CntNum;1025NumEdgeCounters = CntNum;1026}10271028/// Either set all the counters in the instr profile entry \p IFE to1029/// -1 / -2 /in order to drop the profile or scale up the1030/// counters in \p IFP to be above hot / cold threshold. We use1031/// the ratio of zero counters in the profile of a function to1032/// decide the profile is helpful or harmful for performance,1033/// and to choose whether to scale up or drop it.1034static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot,1035uint64_t HotInstrThreshold,1036uint64_t ColdInstrThreshold,1037float ZeroCounterThreshold) {1038InstrProfRecord *ProfRecord = IFE.ProfRecord;1039if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {1040// If all or most of the counters of the function are zero, the1041// profile is unaccountable and should be dropped. Reset all the1042// counters to be -1 / -2 and PGO profile-use will drop the profile.1043// All counters being -1 also implies that the function is hot so1044// PGO profile-use will also set the entry count metadata to be1045// above hot threshold.1046// All counters being -2 implies that the function is warm so1047// PGO profile-use will also set the entry count metadata to be1048// above cold threshold.1049auto Kind =1050(SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm);1051ProfRecord->setPseudoCount(Kind);1052return;1053}10541055// Scale up the MaxCount to be multiple times above hot / cold threshold.1056const unsigned MultiplyFactor = 3;1057uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold);1058uint64_t Numerator = Threshold * MultiplyFactor;10591060// Make sure Threshold for warm counters is below the HotInstrThreshold.1061if (!SetToHot && Threshold >= HotInstrThreshold) {1062Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2;1063}10641065uint64_t Denominator = IFE.MaxCount;1066if (Numerator <= Denominator)1067return;1068ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {1069warn(toString(make_error<InstrProfError>(E)));1070});1071}10721073const uint64_t ColdPercentileIdx = 15;1074const uint64_t HotPercentileIdx = 11;10751076using sampleprof::FSDiscriminatorPass;10771078// Internal options to set FSDiscriminatorPass. Used in merge and show1079// commands.1080static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(1081"fs-discriminator-pass", cl::init(PassLast), cl::Hidden,1082cl::desc("Zero out the discriminator bits for the FS discrimiantor "1083"pass beyond this value. The enum values are defined in "1084"Support/Discriminator.h"),1085cl::values(clEnumVal(Base, "Use base discriminators only"),1086clEnumVal(Pass1, "Use base and pass 1 discriminators"),1087clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),1088clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),1089clEnumVal(PassLast, "Use all discriminator bits (default)")));10901091static unsigned getDiscriminatorMask() {1092return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));1093}10941095/// Adjust the instr profile in \p WC based on the sample profile in1096/// \p Reader.1097static void1098adjustInstrProfile(std::unique_ptr<WriterContext> &WC,1099std::unique_ptr<sampleprof::SampleProfileReader> &Reader,1100unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,1101unsigned InstrProfColdThreshold) {1102// Function to its entry in instr profile.1103StringMap<InstrProfileEntry> InstrProfileMap;1104StringMap<StringRef> StaticFuncMap;1105InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);11061107auto checkSampleProfileHasFUnique = [&Reader]() {1108for (const auto &PD : Reader->getProfiles()) {1109auto &FContext = PD.second.getContext();1110if (FContext.toString().find(FunctionSamples::UniqSuffix) !=1111std::string::npos) {1112return true;1113}1114}1115return false;1116};11171118bool SampleProfileHasFUnique = checkSampleProfileHasFUnique();11191120auto buildStaticFuncMap = [&StaticFuncMap,1121SampleProfileHasFUnique](const StringRef Name) {1122std::string FilePrefixes[] = {".cpp", "cc", ".c", ".hpp", ".h"};1123size_t PrefixPos = StringRef::npos;1124for (auto &FilePrefix : FilePrefixes) {1125std::string NamePrefix = FilePrefix + GlobalIdentifierDelimiter;1126PrefixPos = Name.find_insensitive(NamePrefix);1127if (PrefixPos == StringRef::npos)1128continue;1129PrefixPos += NamePrefix.size();1130break;1131}11321133if (PrefixPos == StringRef::npos) {1134return;1135}11361137StringRef NewName = Name.drop_front(PrefixPos);1138StringRef FName = Name.substr(0, PrefixPos - 1);1139if (NewName.size() == 0) {1140return;1141}11421143// This name should have a static linkage.1144size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix);1145bool ProfileHasFUnique = (PostfixPos != StringRef::npos);11461147// If sample profile and instrumented profile do not agree on symbol1148// uniqification.1149if (SampleProfileHasFUnique != ProfileHasFUnique) {1150// If instrumented profile uses -funique-internal-linkage-symbols,1151// we need to trim the name.1152if (ProfileHasFUnique) {1153NewName = NewName.substr(0, PostfixPos);1154} else {1155// If sample profile uses -funique-internal-linkage-symbols,1156// we build the map.1157std::string NStr =1158NewName.str() + getUniqueInternalLinkagePostfix(FName);1159NewName = StringRef(NStr);1160StaticFuncMap[NewName] = Name;1161return;1162}1163}11641165if (!StaticFuncMap.contains(NewName)) {1166StaticFuncMap[NewName] = Name;1167} else {1168StaticFuncMap[NewName] = DuplicateNameStr;1169}1170};11711172// We need to flatten the SampleFDO profile as the InstrFDO1173// profile does not have inlined callsite profiles.1174// One caveat is the pre-inlined function -- their samples1175// should be collapsed into the caller function.1176// Here we do a DFS traversal to get the flatten profile1177// info: the sum of entrycount and the max of maxcount.1178// Here is the algorithm:1179// recursive (FS, root_name) {1180// name = FS->getName();1181// get samples for FS;1182// if (InstrProf.find(name) {1183// root_name = name;1184// } else {1185// if (name is in static_func map) {1186// root_name = static_name;1187// }1188// }1189// update the Map entry for root_name;1190// for (subfs: FS) {1191// recursive(subfs, root_name);1192// }1193// }1194//1195// Here is an example.1196//1197// SampleProfile:1198// foo:12345:10001199// 1: 10001200// 2.1: 10001201// 15: 50001202// 4: bar:10001203// 1: 10001204// 2: goo:30001205// 1: 30001206// 8: bar:400001207// 1: 100001208// 2: goo:300001209// 1: 300001210//1211// InstrProfile has two entries:1212// foo1213// bar.cc;bar1214//1215// After BuildMaxSampleMap, we should have the following in FlattenSampleMap:1216// {"foo", {1000, 5000}}1217// {"bar.cc;bar", {11000, 30000}}1218//1219// foo's has an entry count of 1000, and max body count of 5000.1220// bar.cc;bar has an entry count of 11000 (sum two callsites of 1000 and1221// 10000), and max count of 30000 (from the callsite in line 8).1222//1223// Note that goo's count will remain in bar.cc;bar() as it does not have an1224// entry in InstrProfile.1225llvm::StringMap<std::pair<uint64_t, uint64_t>> FlattenSampleMap;1226auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap,1227&InstrProfileMap](const FunctionSamples &FS,1228const StringRef &RootName) {1229auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,1230const StringRef &RootName,1231auto &BuildImpl) -> void {1232std::string NameStr = FS.getFunction().str();1233const StringRef Name = NameStr;1234const StringRef *NewRootName = &RootName;1235uint64_t EntrySample = FS.getHeadSamplesEstimate();1236uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);12371238auto It = InstrProfileMap.find(Name);1239if (It != InstrProfileMap.end()) {1240NewRootName = &Name;1241} else {1242auto NewName = StaticFuncMap.find(Name);1243if (NewName != StaticFuncMap.end()) {1244It = InstrProfileMap.find(NewName->second.str());1245if (NewName->second != DuplicateNameStr) {1246NewRootName = &NewName->second;1247}1248} else {1249// Here the EntrySample is of an inlined function, so we should not1250// update the EntrySample in the map.1251EntrySample = 0;1252}1253}1254EntrySample += FlattenSampleMap[*NewRootName].first;1255MaxBodySample =1256std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample);1257FlattenSampleMap[*NewRootName] =1258std::make_pair(EntrySample, MaxBodySample);12591260for (const auto &C : FS.getCallsiteSamples())1261for (const auto &F : C.second)1262BuildImpl(F.second, *NewRootName, BuildImpl);1263};1264BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl);1265};12661267for (auto &PD : WC->Writer.getProfileData()) {1268// Populate IPBuilder.1269for (const auto &PDV : PD.getValue()) {1270InstrProfRecord Record = PDV.second;1271IPBuilder.addRecord(Record);1272}12731274// If a function has multiple entries in instr profile, skip it.1275if (PD.getValue().size() != 1)1276continue;12771278// Initialize InstrProfileMap.1279InstrProfRecord *R = &PD.getValue().begin()->second;1280StringRef FullName = PD.getKey();1281InstrProfileMap[FullName] = InstrProfileEntry(R);1282buildStaticFuncMap(FullName);1283}12841285for (auto &PD : Reader->getProfiles()) {1286sampleprof::FunctionSamples &FS = PD.second;1287std::string Name = FS.getFunction().str();1288BuildMaxSampleMap(FS, Name);1289}12901291ProfileSummary InstrPS = *IPBuilder.getSummary();1292ProfileSummary SamplePS = Reader->getSummary();12931294// Compute cold thresholds for instr profile and sample profile.1295uint64_t HotSampleThreshold =1296ProfileSummaryBuilder::getEntryForPercentile(1297SamplePS.getDetailedSummary(),1298ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])1299.MinCount;1300uint64_t ColdSampleThreshold =1301ProfileSummaryBuilder::getEntryForPercentile(1302SamplePS.getDetailedSummary(),1303ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])1304.MinCount;1305uint64_t HotInstrThreshold =1306ProfileSummaryBuilder::getEntryForPercentile(1307InstrPS.getDetailedSummary(),1308ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])1309.MinCount;1310uint64_t ColdInstrThreshold =1311InstrProfColdThreshold1312? InstrProfColdThreshold1313: ProfileSummaryBuilder::getEntryForPercentile(1314InstrPS.getDetailedSummary(),1315ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])1316.MinCount;13171318// Find hot/warm functions in sample profile which is cold in instr profile1319// and adjust the profiles of those functions in the instr profile.1320for (const auto &E : FlattenSampleMap) {1321uint64_t SampleMaxCount = std::max(E.second.first, E.second.second);1322if (SampleMaxCount < ColdSampleThreshold)1323continue;1324StringRef Name = E.first();1325auto It = InstrProfileMap.find(Name);1326if (It == InstrProfileMap.end()) {1327auto NewName = StaticFuncMap.find(Name);1328if (NewName != StaticFuncMap.end()) {1329It = InstrProfileMap.find(NewName->second.str());1330if (NewName->second == DuplicateNameStr) {1331WithColor::warning()1332<< "Static function " << Name1333<< " has multiple promoted names, cannot adjust profile.\n";1334}1335}1336}1337if (It == InstrProfileMap.end() ||1338It->second.MaxCount > ColdInstrThreshold ||1339It->second.NumEdgeCounters < SupplMinSizeThreshold)1340continue;1341bool SetToHot = SampleMaxCount >= HotSampleThreshold;1342updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold,1343ColdInstrThreshold, ZeroCounterThreshold);1344}1345}13461347/// The main function to supplement instr profile with sample profile.1348/// \Inputs contains the instr profile. \p SampleFilename specifies the1349/// sample profile. \p OutputFilename specifies the output profile name.1350/// \p OutputFormat specifies the output profile format. \p OutputSparse1351/// specifies whether to generate sparse profile. \p SupplMinSizeThreshold1352/// specifies the minimal size for the functions whose profile will be1353/// adjusted. \p ZeroCounterThreshold is the threshold to check whether1354/// a function contains too many zero counters and whether its profile1355/// should be dropped. \p InstrProfColdThreshold is the user specified1356/// cold threshold which will override the cold threshold got from the1357/// instr profile summary.1358static void supplementInstrProfile(const WeightedFileVector &Inputs,1359StringRef SampleFilename, bool OutputSparse,1360unsigned SupplMinSizeThreshold,1361float ZeroCounterThreshold,1362unsigned InstrProfColdThreshold) {1363if (OutputFilename == "-")1364exitWithError("cannot write indexed profdata format to stdout");1365if (Inputs.size() != 1)1366exitWithError("expect one input to be an instr profile");1367if (Inputs[0].Weight != 1)1368exitWithError("expect instr profile doesn't have weight");13691370StringRef InstrFilename = Inputs[0].Filename;13711372// Read sample profile.1373LLVMContext Context;1374auto FS = vfs::getRealFileSystem();1375auto ReaderOrErr = sampleprof::SampleProfileReader::create(1376SampleFilename.str(), Context, *FS, FSDiscriminatorPassOption);1377if (std::error_code EC = ReaderOrErr.getError())1378exitWithErrorCode(EC, SampleFilename);1379auto Reader = std::move(ReaderOrErr.get());1380if (std::error_code EC = Reader->read())1381exitWithErrorCode(EC, SampleFilename);13821383// Read instr profile.1384std::mutex ErrorLock;1385SmallSet<instrprof_error, 4> WriterErrorCodes;1386auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,1387WriterErrorCodes);1388loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());1389if (WC->Errors.size() > 0)1390exitWithError(std::move(WC->Errors[0].first), InstrFilename);13911392adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,1393InstrProfColdThreshold);1394writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);1395}13961397/// Make a copy of the given function samples with all symbol names remapped1398/// by the provided symbol remapper.1399static sampleprof::FunctionSamples1400remapSamples(const sampleprof::FunctionSamples &Samples,1401SymbolRemapper &Remapper, sampleprof_error &Error) {1402sampleprof::FunctionSamples Result;1403Result.setFunction(Remapper(Samples.getFunction()));1404Result.addTotalSamples(Samples.getTotalSamples());1405Result.addHeadSamples(Samples.getHeadSamples());1406for (const auto &BodySample : Samples.getBodySamples()) {1407uint32_t MaskedDiscriminator =1408BodySample.first.Discriminator & getDiscriminatorMask();1409Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,1410BodySample.second.getSamples());1411for (const auto &Target : BodySample.second.getCallTargets()) {1412Result.addCalledTargetSamples(BodySample.first.LineOffset,1413MaskedDiscriminator,1414Remapper(Target.first), Target.second);1415}1416}1417for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {1418sampleprof::FunctionSamplesMap &Target =1419Result.functionSamplesAt(CallsiteSamples.first);1420for (const auto &Callsite : CallsiteSamples.second) {1421sampleprof::FunctionSamples Remapped =1422remapSamples(Callsite.second, Remapper, Error);1423mergeSampleProfErrors(Error,1424Target[Remapped.getFunction()].merge(Remapped));1425}1426}1427return Result;1428}14291430static sampleprof::SampleProfileFormat FormatMap[] = {1431sampleprof::SPF_None,1432sampleprof::SPF_Text,1433sampleprof::SPF_None,1434sampleprof::SPF_Ext_Binary,1435sampleprof::SPF_GCC,1436sampleprof::SPF_Binary};14371438static std::unique_ptr<MemoryBuffer>1439getInputFileBuf(const StringRef &InputFile) {1440if (InputFile == "")1441return {};14421443auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);1444if (!BufOrError)1445exitWithErrorCode(BufOrError.getError(), InputFile);14461447return std::move(*BufOrError);1448}14491450static void populateProfileSymbolList(MemoryBuffer *Buffer,1451sampleprof::ProfileSymbolList &PSL) {1452if (!Buffer)1453return;14541455SmallVector<StringRef, 32> SymbolVec;1456StringRef Data = Buffer->getBuffer();1457Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);14581459for (StringRef SymbolStr : SymbolVec)1460PSL.add(SymbolStr.trim());1461}14621463static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,1464ProfileFormat OutputFormat,1465MemoryBuffer *Buffer,1466sampleprof::ProfileSymbolList &WriterList,1467bool CompressAllSections, bool UseMD5,1468bool GenPartialProfile) {1469populateProfileSymbolList(Buffer, WriterList);1470if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)1471warn("Profile Symbol list is not empty but the output format is not "1472"ExtBinary format. The list will be lost in the output. ");14731474Writer.setProfileSymbolList(&WriterList);14751476if (CompressAllSections) {1477if (OutputFormat != PF_Ext_Binary)1478warn("-compress-all-section is ignored. Specify -extbinary to enable it");1479else1480Writer.setToCompressAllSections();1481}1482if (UseMD5) {1483if (OutputFormat != PF_Ext_Binary)1484warn("-use-md5 is ignored. Specify -extbinary to enable it");1485else1486Writer.setUseMD5();1487}1488if (GenPartialProfile) {1489if (OutputFormat != PF_Ext_Binary)1490warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");1491else1492Writer.setPartialProfile();1493}1494}14951496static void mergeSampleProfile(const WeightedFileVector &Inputs,1497SymbolRemapper *Remapper,1498StringRef ProfileSymbolListFile,1499size_t OutputSizeLimit) {1500using namespace sampleprof;1501SampleProfileMap ProfileMap;1502SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;1503LLVMContext Context;1504sampleprof::ProfileSymbolList WriterList;1505std::optional<bool> ProfileIsProbeBased;1506std::optional<bool> ProfileIsCS;1507for (const auto &Input : Inputs) {1508auto FS = vfs::getRealFileSystem();1509auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, *FS,1510FSDiscriminatorPassOption);1511if (std::error_code EC = ReaderOrErr.getError()) {1512warnOrExitGivenError(FailMode, EC, Input.Filename);1513continue;1514}15151516// We need to keep the readers around until after all the files are1517// read so that we do not lose the function names stored in each1518// reader's memory. The function names are needed to write out the1519// merged profile map.1520Readers.push_back(std::move(ReaderOrErr.get()));1521const auto Reader = Readers.back().get();1522if (std::error_code EC = Reader->read()) {1523warnOrExitGivenError(FailMode, EC, Input.Filename);1524Readers.pop_back();1525continue;1526}15271528SampleProfileMap &Profiles = Reader->getProfiles();1529if (ProfileIsProbeBased &&1530ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)1531exitWithError(1532"cannot merge probe-based profile with non-probe-based profile");1533ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;1534if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)1535exitWithError("cannot merge CS profile with non-CS profile");1536ProfileIsCS = FunctionSamples::ProfileIsCS;1537for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();1538I != E; ++I) {1539sampleprof_error Result = sampleprof_error::success;1540FunctionSamples Remapped =1541Remapper ? remapSamples(I->second, *Remapper, Result)1542: FunctionSamples();1543FunctionSamples &Samples = Remapper ? Remapped : I->second;1544SampleContext FContext = Samples.getContext();1545mergeSampleProfErrors(Result,1546ProfileMap[FContext].merge(Samples, Input.Weight));1547if (Result != sampleprof_error::success) {1548std::error_code EC = make_error_code(Result);1549handleMergeWriterError(errorCodeToError(EC), Input.Filename,1550FContext.toString());1551}1552}15531554if (!DropProfileSymbolList) {1555std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =1556Reader->getProfileSymbolList();1557if (ReaderList)1558WriterList.merge(*ReaderList);1559}1560}15611562if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {1563// Use threshold calculated from profile summary unless specified.1564SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);1565auto Summary = Builder.computeSummaryForProfiles(ProfileMap);1566uint64_t SampleProfColdThreshold =1567ProfileSummaryBuilder::getColdCountThreshold(1568(Summary->getDetailedSummary()));15691570// Trim and merge cold context profile using cold threshold above;1571SampleContextTrimmer(ProfileMap)1572.trimAndMergeColdContextProfiles(1573SampleProfColdThreshold, SampleTrimColdContext,1574SampleMergeColdContext, SampleColdContextFrameDepth, false);1575}15761577if (ProfileLayout == llvm::sampleprof::SPL_Flat) {1578ProfileConverter::flattenProfile(ProfileMap, FunctionSamples::ProfileIsCS);1579ProfileIsCS = FunctionSamples::ProfileIsCS = false;1580} else if (ProfileIsCS && ProfileLayout == llvm::sampleprof::SPL_Nest) {1581ProfileConverter CSConverter(ProfileMap);1582CSConverter.convertCSProfiles();1583ProfileIsCS = FunctionSamples::ProfileIsCS = false;1584}15851586filterFunctions(ProfileMap);15871588auto WriterOrErr =1589SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);1590if (std::error_code EC = WriterOrErr.getError())1591exitWithErrorCode(EC, OutputFilename);15921593auto Writer = std::move(WriterOrErr.get());1594// WriterList will have StringRef refering to string in Buffer.1595// Make sure Buffer lives as long as WriterList.1596auto Buffer = getInputFileBuf(ProfileSymbolListFile);1597handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,1598CompressAllSections, UseMD5, GenPartialProfile);15991600// If OutputSizeLimit is 0 (default), it is the same as write().1601if (std::error_code EC =1602Writer->writeWithSizeLimit(ProfileMap, OutputSizeLimit))1603exitWithErrorCode(EC);1604}16051606static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {1607StringRef WeightStr, FileName;1608std::tie(WeightStr, FileName) = WeightedFilename.split(',');16091610uint64_t Weight;1611if (WeightStr.getAsInteger(10, Weight) || Weight < 1)1612exitWithError("input weight must be a positive integer");16131614return {std::string(FileName), Weight};1615}16161617static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {1618StringRef Filename = WF.Filename;1619uint64_t Weight = WF.Weight;16201621// If it's STDIN just pass it on.1622if (Filename == "-") {1623WNI.push_back({std::string(Filename), Weight});1624return;1625}16261627llvm::sys::fs::file_status Status;1628llvm::sys::fs::status(Filename, Status);1629if (!llvm::sys::fs::exists(Status))1630exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),1631Filename);1632// If it's a source file, collect it.1633if (llvm::sys::fs::is_regular_file(Status)) {1634WNI.push_back({std::string(Filename), Weight});1635return;1636}16371638if (llvm::sys::fs::is_directory(Status)) {1639std::error_code EC;1640for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;1641F != E && !EC; F.increment(EC)) {1642if (llvm::sys::fs::is_regular_file(F->path())) {1643addWeightedInput(WNI, {F->path(), Weight});1644}1645}1646if (EC)1647exitWithErrorCode(EC, Filename);1648}1649}16501651static void parseInputFilenamesFile(MemoryBuffer *Buffer,1652WeightedFileVector &WFV) {1653if (!Buffer)1654return;16551656SmallVector<StringRef, 8> Entries;1657StringRef Data = Buffer->getBuffer();1658Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);1659for (const StringRef &FileWeightEntry : Entries) {1660StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");1661// Skip comments.1662if (SanitizedEntry.starts_with("#"))1663continue;1664// If there's no comma, it's an unweighted profile.1665else if (!SanitizedEntry.contains(','))1666addWeightedInput(WFV, {std::string(SanitizedEntry), 1});1667else1668addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));1669}1670}16711672static int merge_main(StringRef ProgName) {1673WeightedFileVector WeightedInputs;1674for (StringRef Filename : InputFilenames)1675addWeightedInput(WeightedInputs, {std::string(Filename), 1});1676for (StringRef WeightedFilename : WeightedInputFilenames)1677addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));16781679// Make sure that the file buffer stays alive for the duration of the1680// weighted input vector's lifetime.1681auto Buffer = getInputFileBuf(InputFilenamesFile);1682parseInputFilenamesFile(Buffer.get(), WeightedInputs);16831684if (WeightedInputs.empty())1685exitWithError("no input files specified. See " + ProgName + " merge -help");16861687if (DumpInputFileList) {1688for (auto &WF : WeightedInputs)1689outs() << WF.Weight << "," << WF.Filename << "\n";1690return 0;1691}16921693std::unique_ptr<SymbolRemapper> Remapper;1694if (!RemappingFile.empty())1695Remapper = SymbolRemapper::create(RemappingFile);16961697if (!SupplInstrWithSample.empty()) {1698if (ProfileKind != instr)1699exitWithError(1700"-supplement-instr-with-sample can only work with -instr. ");17011702supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse,1703SupplMinSizeThreshold, ZeroCounterThreshold,1704InstrProfColdThreshold);1705return 0;1706}17071708if (ProfileKind == instr)1709mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings,1710ProfiledBinary);1711else1712mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile,1713OutputSizeLimit);1714return 0;1715}17161717/// Computer the overlap b/w profile BaseFilename and profile TestFilename.1718static void overlapInstrProfile(const std::string &BaseFilename,1719const std::string &TestFilename,1720const OverlapFuncFilters &FuncFilter,1721raw_fd_ostream &OS, bool IsCS) {1722std::mutex ErrorLock;1723SmallSet<instrprof_error, 4> WriterErrorCodes;1724WriterContext Context(false, ErrorLock, WriterErrorCodes);1725WeightedFile WeightedInput{BaseFilename, 1};1726OverlapStats Overlap;1727Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);1728if (E)1729exitWithError(std::move(E), "error in getting profile count sums");1730if (Overlap.Base.CountSum < 1.0f) {1731OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";1732exit(0);1733}1734if (Overlap.Test.CountSum < 1.0f) {1735OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";1736exit(0);1737}1738loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);1739overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,1740IsCS);1741Overlap.dump(OS);1742}17431744namespace {1745struct SampleOverlapStats {1746SampleContext BaseName;1747SampleContext TestName;1748// Number of overlap units1749uint64_t OverlapCount = 0;1750// Total samples of overlap units1751uint64_t OverlapSample = 0;1752// Number of and total samples of units that only present in base or test1753// profile1754uint64_t BaseUniqueCount = 0;1755uint64_t BaseUniqueSample = 0;1756uint64_t TestUniqueCount = 0;1757uint64_t TestUniqueSample = 0;1758// Number of units and total samples in base or test profile1759uint64_t BaseCount = 0;1760uint64_t BaseSample = 0;1761uint64_t TestCount = 0;1762uint64_t TestSample = 0;1763// Number of and total samples of units that present in at least one profile1764uint64_t UnionCount = 0;1765uint64_t UnionSample = 0;1766// Weighted similarity1767double Similarity = 0.0;1768// For SampleOverlapStats instances representing functions, weights of the1769// function in base and test profiles1770double BaseWeight = 0.0;1771double TestWeight = 0.0;17721773SampleOverlapStats() = default;1774};1775} // end anonymous namespace17761777namespace {1778struct FuncSampleStats {1779uint64_t SampleSum = 0;1780uint64_t MaxSample = 0;1781uint64_t HotBlockCount = 0;1782FuncSampleStats() = default;1783FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,1784uint64_t HotBlockCount)1785: SampleSum(SampleSum), MaxSample(MaxSample),1786HotBlockCount(HotBlockCount) {}1787};1788} // end anonymous namespace17891790namespace {1791enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };17921793// Class for updating merging steps for two sorted maps. The class should be1794// instantiated with a map iterator type.1795template <class T> class MatchStep {1796public:1797MatchStep() = delete;17981799MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)1800: FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),1801SecondEnd(SecondEnd), Status(MS_None) {}18021803bool areBothFinished() const {1804return (FirstIter == FirstEnd && SecondIter == SecondEnd);1805}18061807bool isFirstFinished() const { return FirstIter == FirstEnd; }18081809bool isSecondFinished() const { return SecondIter == SecondEnd; }18101811/// Advance one step based on the previous match status unless the previous1812/// status is MS_None. Then update Status based on the comparison between two1813/// container iterators at the current step. If the previous status is1814/// MS_None, it means two iterators are at the beginning and no comparison has1815/// been made, so we simply update Status without advancing the iterators.1816void updateOneStep();18171818T getFirstIter() const { return FirstIter; }18191820T getSecondIter() const { return SecondIter; }18211822MatchStatus getMatchStatus() const { return Status; }18231824private:1825// Current iterator and end iterator of the first container.1826T FirstIter;1827T FirstEnd;1828// Current iterator and end iterator of the second container.1829T SecondIter;1830T SecondEnd;1831// Match status of the current step.1832MatchStatus Status;1833};1834} // end anonymous namespace18351836template <class T> void MatchStep<T>::updateOneStep() {1837switch (Status) {1838case MS_Match:1839++FirstIter;1840++SecondIter;1841break;1842case MS_FirstUnique:1843++FirstIter;1844break;1845case MS_SecondUnique:1846++SecondIter;1847break;1848case MS_None:1849break;1850}18511852// Update Status according to iterators at the current step.1853if (areBothFinished())1854return;1855if (FirstIter != FirstEnd &&1856(SecondIter == SecondEnd || FirstIter->first < SecondIter->first))1857Status = MS_FirstUnique;1858else if (SecondIter != SecondEnd &&1859(FirstIter == FirstEnd || SecondIter->first < FirstIter->first))1860Status = MS_SecondUnique;1861else1862Status = MS_Match;1863}18641865// Return the sum of line/block samples, the max line/block sample, and the1866// number of line/block samples above the given threshold in a function1867// including its inlinees.1868static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,1869FuncSampleStats &FuncStats,1870uint64_t HotThreshold) {1871for (const auto &L : Func.getBodySamples()) {1872uint64_t Sample = L.second.getSamples();1873FuncStats.SampleSum += Sample;1874FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);1875if (Sample >= HotThreshold)1876++FuncStats.HotBlockCount;1877}18781879for (const auto &C : Func.getCallsiteSamples()) {1880for (const auto &F : C.second)1881getFuncSampleStats(F.second, FuncStats, HotThreshold);1882}1883}18841885/// Predicate that determines if a function is hot with a given threshold. We1886/// keep it separate from its callsites for possible extension in the future.1887static bool isFunctionHot(const FuncSampleStats &FuncStats,1888uint64_t HotThreshold) {1889// We intentionally compare the maximum sample count in a function with the1890// HotThreshold to get an approximate determination on hot functions.1891return (FuncStats.MaxSample >= HotThreshold);1892}18931894namespace {1895class SampleOverlapAggregator {1896public:1897SampleOverlapAggregator(const std::string &BaseFilename,1898const std::string &TestFilename,1899double LowSimilarityThreshold, double Epsilon,1900const OverlapFuncFilters &FuncFilter)1901: BaseFilename(BaseFilename), TestFilename(TestFilename),1902LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),1903FuncFilter(FuncFilter) {}19041905/// Detect 0-sample input profile and report to output stream. This interface1906/// should be called after loadProfiles().1907bool detectZeroSampleProfile(raw_fd_ostream &OS) const;19081909/// Write out function-level similarity statistics for functions specified by1910/// options --function, --value-cutoff, and --similarity-cutoff.1911void dumpFuncSimilarity(raw_fd_ostream &OS) const;19121913/// Write out program-level similarity and overlap statistics.1914void dumpProgramSummary(raw_fd_ostream &OS) const;19151916/// Write out hot-function and hot-block statistics for base_profile,1917/// test_profile, and their overlap. For both cases, the overlap HO is1918/// calculated as follows:1919/// Given the number of functions (or blocks) that are hot in both profiles1920/// HCommon and the number of functions (or blocks) that are hot in at1921/// least one profile HUnion, HO = HCommon / HUnion.1922void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;19231924/// This function tries matching functions in base and test profiles. For each1925/// pair of matched functions, it aggregates the function-level1926/// similarity into a profile-level similarity. It also dump function-level1927/// similarity information of functions specified by --function,1928/// --value-cutoff, and --similarity-cutoff options. The program-level1929/// similarity PS is computed as follows:1930/// Given function-level similarity FS(A) for all function A, the1931/// weight of function A in base profile WB(A), and the weight of function1932/// A in test profile WT(A), compute PS(base_profile, test_profile) =1933/// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.01934/// meaning no-overlap.1935void computeSampleProfileOverlap(raw_fd_ostream &OS);19361937/// Initialize ProfOverlap with the sum of samples in base and test1938/// profiles. This function also computes and keeps the sum of samples and1939/// max sample counts of each function in BaseStats and TestStats for later1940/// use to avoid re-computations.1941void initializeSampleProfileOverlap();19421943/// Load profiles specified by BaseFilename and TestFilename.1944std::error_code loadProfiles();19451946using FuncSampleStatsMap =1947std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;19481949private:1950SampleOverlapStats ProfOverlap;1951SampleOverlapStats HotFuncOverlap;1952SampleOverlapStats HotBlockOverlap;1953std::string BaseFilename;1954std::string TestFilename;1955std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;1956std::unique_ptr<sampleprof::SampleProfileReader> TestReader;1957// BaseStats and TestStats hold FuncSampleStats for each function, with1958// function name as the key.1959FuncSampleStatsMap BaseStats;1960FuncSampleStatsMap TestStats;1961// Low similarity threshold in floating point number1962double LowSimilarityThreshold;1963// Block samples above BaseHotThreshold or TestHotThreshold are considered hot1964// for tracking hot blocks.1965uint64_t BaseHotThreshold;1966uint64_t TestHotThreshold;1967// A small threshold used to round the results of floating point accumulations1968// to resolve imprecision.1969const double Epsilon;1970std::multimap<double, SampleOverlapStats, std::greater<double>>1971FuncSimilarityDump;1972// FuncFilter carries specifications in options --value-cutoff and1973// --function.1974OverlapFuncFilters FuncFilter;1975// Column offsets for printing the function-level details table.1976static const unsigned int TestWeightCol = 15;1977static const unsigned int SimilarityCol = 30;1978static const unsigned int OverlapCol = 43;1979static const unsigned int BaseUniqueCol = 53;1980static const unsigned int TestUniqueCol = 67;1981static const unsigned int BaseSampleCol = 81;1982static const unsigned int TestSampleCol = 96;1983static const unsigned int FuncNameCol = 111;19841985/// Return a similarity of two line/block sample counters in the same1986/// function in base and test profiles. The line/block-similarity BS(i) is1987/// computed as follows:1988/// For an offsets i, given the sample count at i in base profile BB(i),1989/// the sample count at i in test profile BT(i), the sum of sample counts1990/// in this function in base profile SB, and the sum of sample counts in1991/// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -1992/// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.1993double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,1994const SampleOverlapStats &FuncOverlap) const;19951996void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,1997uint64_t HotBlockCount);19981999void getHotFunctions(const FuncSampleStatsMap &ProfStats,2000FuncSampleStatsMap &HotFunc,2001uint64_t HotThreshold) const;20022003void computeHotFuncOverlap();20042005/// This function updates statistics in FuncOverlap, HotBlockOverlap, and2006/// Difference for two sample units in a matched function according to the2007/// given match status.2008void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,2009uint64_t HotBlockCount,2010SampleOverlapStats &FuncOverlap,2011double &Difference, MatchStatus Status);20122013/// This function updates statistics in FuncOverlap, HotBlockOverlap, and2014/// Difference for unmatched callees that only present in one profile in a2015/// matched caller function.2016void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,2017SampleOverlapStats &FuncOverlap,2018double &Difference, MatchStatus Status);20192020/// This function updates sample overlap statistics of an overlap function in2021/// base and test profile. It also calculates a function-internal similarity2022/// FIS as follows:2023/// For offsets i that have samples in at least one profile in this2024/// function A, given BS(i) returned by computeBlockSimilarity(), compute2025/// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with2026/// 0.0 meaning no overlap.2027double computeSampleFunctionInternalOverlap(2028const sampleprof::FunctionSamples &BaseFunc,2029const sampleprof::FunctionSamples &TestFunc,2030SampleOverlapStats &FuncOverlap);20312032/// Function-level similarity (FS) is a weighted value over function internal2033/// similarity (FIS). This function computes a function's FS from its FIS by2034/// applying the weight.2035double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,2036uint64_t TestFuncSample) const;20372038/// The function-level similarity FS(A) for a function A is computed as2039/// follows:2040/// Compute a function-internal similarity FIS(A) by2041/// computeSampleFunctionInternalOverlap(). Then, with the weight of2042/// function A in base profile WB(A), and the weight of function A in test2043/// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))2044/// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.2045double2046computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,2047const sampleprof::FunctionSamples *TestFunc,2048SampleOverlapStats *FuncOverlap,2049uint64_t BaseFuncSample,2050uint64_t TestFuncSample);20512052/// Profile-level similarity (PS) is a weighted aggregate over function-level2053/// similarities (FS). This method weights the FS value by the function2054/// weights in the base and test profiles for the aggregation.2055double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,2056uint64_t TestFuncSample) const;2057};2058} // end anonymous namespace20592060bool SampleOverlapAggregator::detectZeroSampleProfile(2061raw_fd_ostream &OS) const {2062bool HaveZeroSample = false;2063if (ProfOverlap.BaseSample == 0) {2064OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";2065HaveZeroSample = true;2066}2067if (ProfOverlap.TestSample == 0) {2068OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";2069HaveZeroSample = true;2070}2071return HaveZeroSample;2072}20732074double SampleOverlapAggregator::computeBlockSimilarity(2075uint64_t BaseSample, uint64_t TestSample,2076const SampleOverlapStats &FuncOverlap) const {2077double BaseFrac = 0.0;2078double TestFrac = 0.0;2079if (FuncOverlap.BaseSample > 0)2080BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;2081if (FuncOverlap.TestSample > 0)2082TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;2083return 1.0 - std::fabs(BaseFrac - TestFrac);2084}20852086void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,2087uint64_t TestSample,2088uint64_t HotBlockCount) {2089bool IsBaseHot = (BaseSample >= BaseHotThreshold);2090bool IsTestHot = (TestSample >= TestHotThreshold);2091if (!IsBaseHot && !IsTestHot)2092return;20932094HotBlockOverlap.UnionCount += HotBlockCount;2095if (IsBaseHot)2096HotBlockOverlap.BaseCount += HotBlockCount;2097if (IsTestHot)2098HotBlockOverlap.TestCount += HotBlockCount;2099if (IsBaseHot && IsTestHot)2100HotBlockOverlap.OverlapCount += HotBlockCount;2101}21022103void SampleOverlapAggregator::getHotFunctions(2104const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,2105uint64_t HotThreshold) const {2106for (const auto &F : ProfStats) {2107if (isFunctionHot(F.second, HotThreshold))2108HotFunc.emplace(F.first, F.second);2109}2110}21112112void SampleOverlapAggregator::computeHotFuncOverlap() {2113FuncSampleStatsMap BaseHotFunc;2114getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);2115HotFuncOverlap.BaseCount = BaseHotFunc.size();21162117FuncSampleStatsMap TestHotFunc;2118getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);2119HotFuncOverlap.TestCount = TestHotFunc.size();2120HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;21212122for (const auto &F : BaseHotFunc) {2123if (TestHotFunc.count(F.first))2124++HotFuncOverlap.OverlapCount;2125else2126++HotFuncOverlap.UnionCount;2127}2128}21292130void SampleOverlapAggregator::updateOverlapStatsForFunction(2131uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,2132SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {2133assert(Status != MS_None &&2134"Match status should be updated before updating overlap statistics");2135if (Status == MS_FirstUnique) {2136TestSample = 0;2137FuncOverlap.BaseUniqueSample += BaseSample;2138} else if (Status == MS_SecondUnique) {2139BaseSample = 0;2140FuncOverlap.TestUniqueSample += TestSample;2141} else {2142++FuncOverlap.OverlapCount;2143}21442145FuncOverlap.UnionSample += std::max(BaseSample, TestSample);2146FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);2147Difference +=21481.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);2149updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);2150}21512152void SampleOverlapAggregator::updateForUnmatchedCallee(2153const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,2154double &Difference, MatchStatus Status) {2155assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&2156"Status must be either of the two unmatched cases");2157FuncSampleStats FuncStats;2158if (Status == MS_FirstUnique) {2159getFuncSampleStats(Func, FuncStats, BaseHotThreshold);2160updateOverlapStatsForFunction(FuncStats.SampleSum, 0,2161FuncStats.HotBlockCount, FuncOverlap,2162Difference, Status);2163} else {2164getFuncSampleStats(Func, FuncStats, TestHotThreshold);2165updateOverlapStatsForFunction(0, FuncStats.SampleSum,2166FuncStats.HotBlockCount, FuncOverlap,2167Difference, Status);2168}2169}21702171double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(2172const sampleprof::FunctionSamples &BaseFunc,2173const sampleprof::FunctionSamples &TestFunc,2174SampleOverlapStats &FuncOverlap) {21752176using namespace sampleprof;21772178double Difference = 0;21792180// Accumulate Difference for regular line/block samples in the function.2181// We match them through sort-merge join algorithm because2182// FunctionSamples::getBodySamples() returns a map of sample counters ordered2183// by their offsets.2184MatchStep<BodySampleMap::const_iterator> BlockIterStep(2185BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),2186TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());2187BlockIterStep.updateOneStep();2188while (!BlockIterStep.areBothFinished()) {2189uint64_t BaseSample =2190BlockIterStep.isFirstFinished()2191? 02192: BlockIterStep.getFirstIter()->second.getSamples();2193uint64_t TestSample =2194BlockIterStep.isSecondFinished()2195? 02196: BlockIterStep.getSecondIter()->second.getSamples();2197updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,2198Difference, BlockIterStep.getMatchStatus());21992200BlockIterStep.updateOneStep();2201}22022203// Accumulate Difference for callsite lines in the function. We match2204// them through sort-merge algorithm because2205// FunctionSamples::getCallsiteSamples() returns a map of callsite records2206// ordered by their offsets.2207MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(2208BaseFunc.getCallsiteSamples().cbegin(),2209BaseFunc.getCallsiteSamples().cend(),2210TestFunc.getCallsiteSamples().cbegin(),2211TestFunc.getCallsiteSamples().cend());2212CallsiteIterStep.updateOneStep();2213while (!CallsiteIterStep.areBothFinished()) {2214MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();2215assert(CallsiteStepStatus != MS_None &&2216"Match status should be updated before entering loop body");22172218if (CallsiteStepStatus != MS_Match) {2219auto Callsite = (CallsiteStepStatus == MS_FirstUnique)2220? CallsiteIterStep.getFirstIter()2221: CallsiteIterStep.getSecondIter();2222for (const auto &F : Callsite->second)2223updateForUnmatchedCallee(F.second, FuncOverlap, Difference,2224CallsiteStepStatus);2225} else {2226// There may be multiple inlinees at the same offset, so we need to try2227// matching all of them. This match is implemented through sort-merge2228// algorithm because callsite records at the same offset are ordered by2229// function names.2230MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(2231CallsiteIterStep.getFirstIter()->second.cbegin(),2232CallsiteIterStep.getFirstIter()->second.cend(),2233CallsiteIterStep.getSecondIter()->second.cbegin(),2234CallsiteIterStep.getSecondIter()->second.cend());2235CalleeIterStep.updateOneStep();2236while (!CalleeIterStep.areBothFinished()) {2237MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();2238if (CalleeStepStatus != MS_Match) {2239auto Callee = (CalleeStepStatus == MS_FirstUnique)2240? CalleeIterStep.getFirstIter()2241: CalleeIterStep.getSecondIter();2242updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,2243CalleeStepStatus);2244} else {2245// An inlined function can contain other inlinees inside, so compute2246// the Difference recursively.2247Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(2248CalleeIterStep.getFirstIter()->second,2249CalleeIterStep.getSecondIter()->second,2250FuncOverlap);2251}2252CalleeIterStep.updateOneStep();2253}2254}2255CallsiteIterStep.updateOneStep();2256}22572258// Difference reflects the total differences of line/block samples in this2259// function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to2260// reflect the similarity between function profiles in [0.0f to 1.0f].2261return (2.0 - Difference) / 2;2262}22632264double SampleOverlapAggregator::weightForFuncSimilarity(2265double FuncInternalSimilarity, uint64_t BaseFuncSample,2266uint64_t TestFuncSample) const {2267// Compute the weight as the distance between the function weights in two2268// profiles.2269double BaseFrac = 0.0;2270double TestFrac = 0.0;2271assert(ProfOverlap.BaseSample > 0 &&2272"Total samples in base profile should be greater than 0");2273BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;2274assert(ProfOverlap.TestSample > 0 &&2275"Total samples in test profile should be greater than 0");2276TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;2277double WeightDistance = std::fabs(BaseFrac - TestFrac);22782279// Take WeightDistance into the similarity.2280return FuncInternalSimilarity * (1 - WeightDistance);2281}22822283double2284SampleOverlapAggregator::weightByImportance(double FuncSimilarity,2285uint64_t BaseFuncSample,2286uint64_t TestFuncSample) const {22872288double BaseFrac = 0.0;2289double TestFrac = 0.0;2290assert(ProfOverlap.BaseSample > 0 &&2291"Total samples in base profile should be greater than 0");2292BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;2293assert(ProfOverlap.TestSample > 0 &&2294"Total samples in test profile should be greater than 0");2295TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;2296return FuncSimilarity * (BaseFrac + TestFrac);2297}22982299double SampleOverlapAggregator::computeSampleFunctionOverlap(2300const sampleprof::FunctionSamples *BaseFunc,2301const sampleprof::FunctionSamples *TestFunc,2302SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,2303uint64_t TestFuncSample) {2304// Default function internal similarity before weighted, meaning two functions2305// has no overlap.2306const double DefaultFuncInternalSimilarity = 0;2307double FuncSimilarity;2308double FuncInternalSimilarity;23092310// If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.2311// In this case, we use DefaultFuncInternalSimilarity as the function internal2312// similarity.2313if (!BaseFunc || !TestFunc) {2314FuncInternalSimilarity = DefaultFuncInternalSimilarity;2315} else {2316assert(FuncOverlap != nullptr &&2317"FuncOverlap should be provided in this case");2318FuncInternalSimilarity = computeSampleFunctionInternalOverlap(2319*BaseFunc, *TestFunc, *FuncOverlap);2320// Now, FuncInternalSimilarity may be a little less than 0 due to2321// imprecision of floating point accumulations. Make it zero if the2322// difference is below Epsilon.2323FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)2324? 02325: FuncInternalSimilarity;2326}2327FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,2328BaseFuncSample, TestFuncSample);2329return FuncSimilarity;2330}23312332void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {2333using namespace sampleprof;23342335std::unordered_map<SampleContext, const FunctionSamples *,2336SampleContext::Hash>2337BaseFuncProf;2338const auto &BaseProfiles = BaseReader->getProfiles();2339for (const auto &BaseFunc : BaseProfiles) {2340BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));2341}2342ProfOverlap.UnionCount = BaseFuncProf.size();23432344const auto &TestProfiles = TestReader->getProfiles();2345for (const auto &TestFunc : TestProfiles) {2346SampleOverlapStats FuncOverlap;2347FuncOverlap.TestName = TestFunc.second.getContext();2348assert(TestStats.count(FuncOverlap.TestName) &&2349"TestStats should have records for all functions in test profile "2350"except inlinees");2351FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;23522353bool Matched = false;2354const auto Match = BaseFuncProf.find(FuncOverlap.TestName);2355if (Match == BaseFuncProf.end()) {2356const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];2357++ProfOverlap.TestUniqueCount;2358ProfOverlap.TestUniqueSample += FuncStats.SampleSum;2359FuncOverlap.TestUniqueSample = FuncStats.SampleSum;23602361updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);23622363double FuncSimilarity = computeSampleFunctionOverlap(2364nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);2365ProfOverlap.Similarity +=2366weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);23672368++ProfOverlap.UnionCount;2369ProfOverlap.UnionSample += FuncStats.SampleSum;2370} else {2371++ProfOverlap.OverlapCount;23722373// Two functions match with each other. Compute function-level overlap and2374// aggregate them into profile-level overlap.2375FuncOverlap.BaseName = Match->second->getContext();2376assert(BaseStats.count(FuncOverlap.BaseName) &&2377"BaseStats should have records for all functions in base profile "2378"except inlinees");2379FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;23802381FuncOverlap.Similarity = computeSampleFunctionOverlap(2382Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,2383FuncOverlap.TestSample);2384ProfOverlap.Similarity +=2385weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,2386FuncOverlap.TestSample);2387ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;2388ProfOverlap.UnionSample += FuncOverlap.UnionSample;23892390// Accumulate the percentage of base unique and test unique samples into2391// ProfOverlap.2392ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;2393ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;23942395// Remove matched base functions for later reporting functions not found2396// in test profile.2397BaseFuncProf.erase(Match);2398Matched = true;2399}24002401// Print function-level similarity information if specified by options.2402assert(TestStats.count(FuncOverlap.TestName) &&2403"TestStats should have records for all functions in test profile "2404"except inlinees");2405if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||2406(Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||2407(Matched && !FuncFilter.NameFilter.empty() &&2408FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=2409std::string::npos)) {2410assert(ProfOverlap.BaseSample > 0 &&2411"Total samples in base profile should be greater than 0");2412FuncOverlap.BaseWeight =2413static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;2414assert(ProfOverlap.TestSample > 0 &&2415"Total samples in test profile should be greater than 0");2416FuncOverlap.TestWeight =2417static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;2418FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);2419}2420}24212422// Traverse through functions in base profile but not in test profile.2423for (const auto &F : BaseFuncProf) {2424assert(BaseStats.count(F.second->getContext()) &&2425"BaseStats should have records for all functions in base profile "2426"except inlinees");2427const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];2428++ProfOverlap.BaseUniqueCount;2429ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;24302431updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);24322433double FuncSimilarity = computeSampleFunctionOverlap(2434nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);2435ProfOverlap.Similarity +=2436weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);24372438ProfOverlap.UnionSample += FuncStats.SampleSum;2439}24402441// Now, ProfSimilarity may be a little greater than 1 due to imprecision2442// of floating point accumulations. Make it 1.0 if the difference is below2443// Epsilon.2444ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)2445? 12446: ProfOverlap.Similarity;24472448computeHotFuncOverlap();2449}24502451void SampleOverlapAggregator::initializeSampleProfileOverlap() {2452const auto &BaseProf = BaseReader->getProfiles();2453for (const auto &I : BaseProf) {2454++ProfOverlap.BaseCount;2455FuncSampleStats FuncStats;2456getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);2457ProfOverlap.BaseSample += FuncStats.SampleSum;2458BaseStats.emplace(I.second.getContext(), FuncStats);2459}24602461const auto &TestProf = TestReader->getProfiles();2462for (const auto &I : TestProf) {2463++ProfOverlap.TestCount;2464FuncSampleStats FuncStats;2465getFuncSampleStats(I.second, FuncStats, TestHotThreshold);2466ProfOverlap.TestSample += FuncStats.SampleSum;2467TestStats.emplace(I.second.getContext(), FuncStats);2468}24692470ProfOverlap.BaseName = StringRef(BaseFilename);2471ProfOverlap.TestName = StringRef(TestFilename);2472}24732474void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {2475using namespace sampleprof;24762477if (FuncSimilarityDump.empty())2478return;24792480formatted_raw_ostream FOS(OS);2481FOS << "Function-level details:\n";2482FOS << "Base weight";2483FOS.PadToColumn(TestWeightCol);2484FOS << "Test weight";2485FOS.PadToColumn(SimilarityCol);2486FOS << "Similarity";2487FOS.PadToColumn(OverlapCol);2488FOS << "Overlap";2489FOS.PadToColumn(BaseUniqueCol);2490FOS << "Base unique";2491FOS.PadToColumn(TestUniqueCol);2492FOS << "Test unique";2493FOS.PadToColumn(BaseSampleCol);2494FOS << "Base samples";2495FOS.PadToColumn(TestSampleCol);2496FOS << "Test samples";2497FOS.PadToColumn(FuncNameCol);2498FOS << "Function name\n";2499for (const auto &F : FuncSimilarityDump) {2500double OverlapPercent =2501F.second.UnionSample > 02502? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample2503: 0;2504double BaseUniquePercent =2505F.second.BaseSample > 02506? static_cast<double>(F.second.BaseUniqueSample) /2507F.second.BaseSample2508: 0;2509double TestUniquePercent =2510F.second.TestSample > 02511? static_cast<double>(F.second.TestUniqueSample) /2512F.second.TestSample2513: 0;25142515FOS << format("%.2f%%", F.second.BaseWeight * 100);2516FOS.PadToColumn(TestWeightCol);2517FOS << format("%.2f%%", F.second.TestWeight * 100);2518FOS.PadToColumn(SimilarityCol);2519FOS << format("%.2f%%", F.second.Similarity * 100);2520FOS.PadToColumn(OverlapCol);2521FOS << format("%.2f%%", OverlapPercent * 100);2522FOS.PadToColumn(BaseUniqueCol);2523FOS << format("%.2f%%", BaseUniquePercent * 100);2524FOS.PadToColumn(TestUniqueCol);2525FOS << format("%.2f%%", TestUniquePercent * 100);2526FOS.PadToColumn(BaseSampleCol);2527FOS << F.second.BaseSample;2528FOS.PadToColumn(TestSampleCol);2529FOS << F.second.TestSample;2530FOS.PadToColumn(FuncNameCol);2531FOS << F.second.TestName.toString() << "\n";2532}2533}25342535void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {2536OS << "Profile overlap infomation for base_profile: "2537<< ProfOverlap.BaseName.toString()2538<< " and test_profile: " << ProfOverlap.TestName.toString()2539<< "\nProgram level:\n";25402541OS << " Whole program profile similarity: "2542<< format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";25432544assert(ProfOverlap.UnionSample > 0 &&2545"Total samples in two profile should be greater than 0");2546double OverlapPercent =2547static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;2548assert(ProfOverlap.BaseSample > 0 &&2549"Total samples in base profile should be greater than 0");2550double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /2551ProfOverlap.BaseSample;2552assert(ProfOverlap.TestSample > 0 &&2553"Total samples in test profile should be greater than 0");2554double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /2555ProfOverlap.TestSample;25562557OS << " Whole program sample overlap: "2558<< format("%.3f%%", OverlapPercent * 100) << "\n";2559OS << " percentage of samples unique in base profile: "2560<< format("%.3f%%", BaseUniquePercent * 100) << "\n";2561OS << " percentage of samples unique in test profile: "2562<< format("%.3f%%", TestUniquePercent * 100) << "\n";2563OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n"2564<< " total samples in test profile: " << ProfOverlap.TestSample << "\n";25652566assert(ProfOverlap.UnionCount > 0 &&2567"There should be at least one function in two input profiles");2568double FuncOverlapPercent =2569static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;2570OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)2571<< "\n";2572OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n";2573OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount2574<< "\n";2575OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount2576<< "\n";2577}25782579void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(2580raw_fd_ostream &OS) const {2581assert(HotFuncOverlap.UnionCount > 0 &&2582"There should be at least one hot function in two input profiles");2583OS << " Hot-function overlap: "2584<< format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /2585HotFuncOverlap.UnionCount * 100)2586<< "\n";2587OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";2588OS << " hot functions unique in base profile: "2589<< HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";2590OS << " hot functions unique in test profile: "2591<< HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";25922593assert(HotBlockOverlap.UnionCount > 0 &&2594"There should be at least one hot block in two input profiles");2595OS << " Hot-block overlap: "2596<< format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /2597HotBlockOverlap.UnionCount * 100)2598<< "\n";2599OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";2600OS << " hot blocks unique in base profile: "2601<< HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";2602OS << " hot blocks unique in test profile: "2603<< HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";2604}26052606std::error_code SampleOverlapAggregator::loadProfiles() {2607using namespace sampleprof;26082609LLVMContext Context;2610auto FS = vfs::getRealFileSystem();2611auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, *FS,2612FSDiscriminatorPassOption);2613if (std::error_code EC = BaseReaderOrErr.getError())2614exitWithErrorCode(EC, BaseFilename);26152616auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, *FS,2617FSDiscriminatorPassOption);2618if (std::error_code EC = TestReaderOrErr.getError())2619exitWithErrorCode(EC, TestFilename);26202621BaseReader = std::move(BaseReaderOrErr.get());2622TestReader = std::move(TestReaderOrErr.get());26232624if (std::error_code EC = BaseReader->read())2625exitWithErrorCode(EC, BaseFilename);2626if (std::error_code EC = TestReader->read())2627exitWithErrorCode(EC, TestFilename);2628if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())2629exitWithError(2630"cannot compare probe-based profile with non-probe-based profile");2631if (BaseReader->profileIsCS() != TestReader->profileIsCS())2632exitWithError("cannot compare CS profile with non-CS profile");26332634// Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in2635// profile summary.2636ProfileSummary &BasePS = BaseReader->getSummary();2637ProfileSummary &TestPS = TestReader->getSummary();2638BaseHotThreshold =2639ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());2640TestHotThreshold =2641ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());26422643return std::error_code();2644}26452646void overlapSampleProfile(const std::string &BaseFilename,2647const std::string &TestFilename,2648const OverlapFuncFilters &FuncFilter,2649uint64_t SimilarityCutoff, raw_fd_ostream &OS) {2650using namespace sampleprof;26512652// We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics2653// report 2--3 places after decimal point in percentage numbers.2654SampleOverlapAggregator OverlapAggr(2655BaseFilename, TestFilename,2656static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);2657if (std::error_code EC = OverlapAggr.loadProfiles())2658exitWithErrorCode(EC);26592660OverlapAggr.initializeSampleProfileOverlap();2661if (OverlapAggr.detectZeroSampleProfile(OS))2662return;26632664OverlapAggr.computeSampleProfileOverlap(OS);26652666OverlapAggr.dumpProgramSummary(OS);2667OverlapAggr.dumpHotFuncAndBlockOverlap(OS);2668OverlapAggr.dumpFuncSimilarity(OS);2669}26702671static int overlap_main() {2672std::error_code EC;2673raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);2674if (EC)2675exitWithErrorCode(EC, OutputFilename);26762677if (ProfileKind == instr)2678overlapInstrProfile(BaseFilename, TestFilename,2679OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},2680OS, IsCS);2681else2682overlapSampleProfile(BaseFilename, TestFilename,2683OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},2684SimilarityCutoff, OS);26852686return 0;2687}26882689namespace {2690struct ValueSitesStats {2691ValueSitesStats() = default;2692uint64_t TotalNumValueSites = 0;2693uint64_t TotalNumValueSitesWithValueProfile = 0;2694uint64_t TotalNumValues = 0;2695std::vector<unsigned> ValueSitesHistogram;2696};2697} // namespace26982699static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,2700ValueSitesStats &Stats, raw_fd_ostream &OS,2701InstrProfSymtab *Symtab) {2702uint32_t NS = Func.getNumValueSites(VK);2703Stats.TotalNumValueSites += NS;2704for (size_t I = 0; I < NS; ++I) {2705auto VD = Func.getValueArrayForSite(VK, I);2706uint32_t NV = VD.size();2707if (NV == 0)2708continue;2709Stats.TotalNumValues += NV;2710Stats.TotalNumValueSitesWithValueProfile++;2711if (NV > Stats.ValueSitesHistogram.size())2712Stats.ValueSitesHistogram.resize(NV, 0);2713Stats.ValueSitesHistogram[NV - 1]++;27142715uint64_t SiteSum = 0;2716for (const auto &V : VD)2717SiteSum += V.Count;2718if (SiteSum == 0)2719SiteSum = 1;27202721for (const auto &V : VD) {2722OS << "\t[ " << format("%2u", I) << ", ";2723if (Symtab == nullptr)2724OS << format("%4" PRIu64, V.Value);2725else2726OS << Symtab->getFuncOrVarName(V.Value);2727OS << ", " << format("%10" PRId64, V.Count) << " ] ("2728<< format("%.2f%%", (V.Count * 100.0 / SiteSum)) << ")\n";2729}2730}2731}27322733static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,2734ValueSitesStats &Stats) {2735OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";2736OS << " Total number of sites with values: "2737<< Stats.TotalNumValueSitesWithValueProfile << "\n";2738OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";27392740OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";2741for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {2742if (Stats.ValueSitesHistogram[I] > 0)2743OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";2744}2745}27462747static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) {2748if (SFormat == ShowFormat::Json)2749exitWithError("JSON output is not supported for instr profiles");2750if (SFormat == ShowFormat::Yaml)2751exitWithError("YAML output is not supported for instr profiles");2752auto FS = vfs::getRealFileSystem();2753auto ReaderOrErr = InstrProfReader::create(Filename, *FS);2754std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);2755if (ShowDetailedSummary && Cutoffs.empty()) {2756Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;2757}2758InstrProfSummaryBuilder Builder(std::move(Cutoffs));2759if (Error E = ReaderOrErr.takeError())2760exitWithError(std::move(E), Filename);27612762auto Reader = std::move(ReaderOrErr.get());2763bool IsIRInstr = Reader->isIRLevelProfile();2764size_t ShownFunctions = 0;2765size_t BelowCutoffFunctions = 0;2766int NumVPKind = IPVK_Last - IPVK_First + 1;2767std::vector<ValueSitesStats> VPStats(NumVPKind);27682769auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,2770const std::pair<std::string, uint64_t> &v2) {2771return v1.second > v2.second;2772};27732774std::priority_queue<std::pair<std::string, uint64_t>,2775std::vector<std::pair<std::string, uint64_t>>,2776decltype(MinCmp)>2777HottestFuncs(MinCmp);27782779if (!TextFormat && OnlyListBelow) {2780OS << "The list of functions with the maximum counter less than "2781<< ShowValueCutoff << ":\n";2782}27832784// Add marker so that IR-level instrumentation round-trips properly.2785if (TextFormat && IsIRInstr)2786OS << ":ir\n";27872788for (const auto &Func : *Reader) {2789if (Reader->isIRLevelProfile()) {2790bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);2791if (FuncIsCS != ShowCS)2792continue;2793}2794bool Show = ShowAllFunctions ||2795(!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter));27962797bool doTextFormatDump = (Show && TextFormat);27982799if (doTextFormatDump) {2800InstrProfSymtab &Symtab = Reader->getSymtab();2801InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,2802OS);2803continue;2804}28052806assert(Func.Counts.size() > 0 && "function missing entry counter");2807Builder.addRecord(Func);28082809if (ShowCovered) {2810if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))2811OS << Func.Name << "\n";2812continue;2813}28142815uint64_t FuncMax = 0;2816uint64_t FuncSum = 0;28172818auto PseudoKind = Func.getCountPseudoKind();2819if (PseudoKind != InstrProfRecord::NotPseudo) {2820if (Show) {2821if (!ShownFunctions)2822OS << "Counters:\n";2823++ShownFunctions;2824OS << " " << Func.Name << ":\n"2825<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"2826<< " Counters: " << Func.Counts.size();2827if (PseudoKind == InstrProfRecord::PseudoHot)2828OS << " <PseudoHot>\n";2829else if (PseudoKind == InstrProfRecord::PseudoWarm)2830OS << " <PseudoWarm>\n";2831else2832llvm_unreachable("Unknown PseudoKind");2833}2834continue;2835}28362837for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {2838FuncMax = std::max(FuncMax, Func.Counts[I]);2839FuncSum += Func.Counts[I];2840}28412842if (FuncMax < ShowValueCutoff) {2843++BelowCutoffFunctions;2844if (OnlyListBelow) {2845OS << " " << Func.Name << ": (Max = " << FuncMax2846<< " Sum = " << FuncSum << ")\n";2847}2848continue;2849} else if (OnlyListBelow)2850continue;28512852if (TopNFunctions) {2853if (HottestFuncs.size() == TopNFunctions) {2854if (HottestFuncs.top().second < FuncMax) {2855HottestFuncs.pop();2856HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));2857}2858} else2859HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));2860}28612862if (Show) {2863if (!ShownFunctions)2864OS << "Counters:\n";28652866++ShownFunctions;28672868OS << " " << Func.Name << ":\n"2869<< " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"2870<< " Counters: " << Func.Counts.size() << "\n";2871if (!IsIRInstr)2872OS << " Function count: " << Func.Counts[0] << "\n";28732874if (ShowIndirectCallTargets)2875OS << " Indirect Call Site Count: "2876<< Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";28772878if (ShowVTables)2879OS << " Number of instrumented vtables: "2880<< Func.getNumValueSites(IPVK_VTableTarget) << "\n";28812882uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);2883if (ShowMemOPSizes && NumMemOPCalls > 0)2884OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls2885<< "\n";28862887if (ShowCounts) {2888OS << " Block counts: [";2889size_t Start = (IsIRInstr ? 0 : 1);2890for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {2891OS << (I == Start ? "" : ", ") << Func.Counts[I];2892}2893OS << "]\n";2894}28952896if (ShowIndirectCallTargets) {2897OS << " Indirect Target Results:\n";2898traverseAllValueSites(Func, IPVK_IndirectCallTarget,2899VPStats[IPVK_IndirectCallTarget], OS,2900&(Reader->getSymtab()));2901}29022903if (ShowVTables) {2904OS << " VTable Results:\n";2905traverseAllValueSites(Func, IPVK_VTableTarget,2906VPStats[IPVK_VTableTarget], OS,2907&(Reader->getSymtab()));2908}29092910if (ShowMemOPSizes && NumMemOPCalls > 0) {2911OS << " Memory Intrinsic Size Results:\n";2912traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,2913nullptr);2914}2915}2916}2917if (Reader->hasError())2918exitWithError(Reader->getError(), Filename);29192920if (TextFormat || ShowCovered)2921return 0;2922std::unique_ptr<ProfileSummary> PS(Builder.getSummary());2923bool IsIR = Reader->isIRLevelProfile();2924OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");2925if (IsIR)2926OS << " entry_first = " << Reader->instrEntryBBEnabled();2927OS << "\n";2928if (ShowAllFunctions || !FuncNameFilter.empty())2929OS << "Functions shown: " << ShownFunctions << "\n";2930OS << "Total functions: " << PS->getNumFunctions() << "\n";2931if (ShowValueCutoff > 0) {2932OS << "Number of functions with maximum count (< " << ShowValueCutoff2933<< "): " << BelowCutoffFunctions << "\n";2934OS << "Number of functions with maximum count (>= " << ShowValueCutoff2935<< "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";2936}2937OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";2938OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";29392940if (TopNFunctions) {2941std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;2942while (!HottestFuncs.empty()) {2943SortedHottestFuncs.emplace_back(HottestFuncs.top());2944HottestFuncs.pop();2945}2946OS << "Top " << TopNFunctions2947<< " functions with the largest internal block counts: \n";2948for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))2949OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";2950}29512952if (ShownFunctions && ShowIndirectCallTargets) {2953OS << "Statistics for indirect call sites profile:\n";2954showValueSitesStats(OS, IPVK_IndirectCallTarget,2955VPStats[IPVK_IndirectCallTarget]);2956}29572958if (ShownFunctions && ShowVTables) {2959OS << "Statistics for vtable profile:\n";2960showValueSitesStats(OS, IPVK_VTableTarget, VPStats[IPVK_VTableTarget]);2961}29622963if (ShownFunctions && ShowMemOPSizes) {2964OS << "Statistics for memory intrinsic calls sizes profile:\n";2965showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);2966}29672968if (ShowDetailedSummary) {2969OS << "Total number of blocks: " << PS->getNumCounts() << "\n";2970OS << "Total count: " << PS->getTotalCount() << "\n";2971PS->printDetailedSummary(OS);2972}29732974if (ShowBinaryIds)2975if (Error E = Reader->printBinaryIds(OS))2976exitWithError(std::move(E), Filename);29772978if (ShowProfileVersion)2979OS << "Profile version: " << Reader->getVersion() << "\n";29802981if (ShowTemporalProfTraces) {2982auto &Traces = Reader->getTemporalProfTraces();2983OS << "Temporal Profile Traces (samples=" << Traces.size()2984<< " seen=" << Reader->getTemporalProfTraceStreamSize() << "):\n";2985for (unsigned i = 0; i < Traces.size(); i++) {2986OS << " Temporal Profile Trace " << i << " (weight=" << Traces[i].Weight2987<< " count=" << Traces[i].FunctionNameRefs.size() << "):\n";2988for (auto &NameRef : Traces[i].FunctionNameRefs)2989OS << " " << Reader->getSymtab().getFuncOrVarName(NameRef) << "\n";2990}2991}29922993return 0;2994}29952996static void showSectionInfo(sampleprof::SampleProfileReader *Reader,2997raw_fd_ostream &OS) {2998if (!Reader->dumpSectionInfo(OS)) {2999WithColor::warning() << "-show-sec-info-only is only supported for "3000<< "sample profile in extbinary format and is "3001<< "ignored for other formats.\n";3002return;3003}3004}30053006namespace {3007struct HotFuncInfo {3008std::string FuncName;3009uint64_t TotalCount = 0;3010double TotalCountPercent = 0.0f;3011uint64_t MaxCount = 0;3012uint64_t EntryCount = 0;30133014HotFuncInfo() = default;30153016HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)3017: FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),3018MaxCount(MS), EntryCount(ES) {}3019};3020} // namespace30213022// Print out detailed information about hot functions in PrintValues vector.3023// Users specify titles and offset of every columns through ColumnTitle and3024// ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same3025// and at least 4. Besides, users can optionally give a HotFuncMetric string to3026// print out or let it be an empty string.3027static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,3028const std::vector<int> &ColumnOffset,3029const std::vector<HotFuncInfo> &PrintValues,3030uint64_t HotFuncCount, uint64_t TotalFuncCount,3031uint64_t HotProfCount, uint64_t TotalProfCount,3032const std::string &HotFuncMetric,3033uint32_t TopNFunctions, raw_fd_ostream &OS) {3034assert(ColumnOffset.size() == ColumnTitle.size() &&3035"ColumnOffset and ColumnTitle should have the same size");3036assert(ColumnTitle.size() >= 4 &&3037"ColumnTitle should have at least 4 elements");3038assert(TotalFuncCount > 0 &&3039"There should be at least one function in the profile");3040double TotalProfPercent = 0;3041if (TotalProfCount > 0)3042TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;30433044formatted_raw_ostream FOS(OS);3045FOS << HotFuncCount << " out of " << TotalFuncCount3046<< " functions with profile ("3047<< format("%.2f%%",3048(static_cast<double>(HotFuncCount) / TotalFuncCount * 100))3049<< ") are considered hot functions";3050if (!HotFuncMetric.empty())3051FOS << " (" << HotFuncMetric << ")";3052FOS << ".\n";3053FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("3054<< format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";30553056for (size_t I = 0; I < ColumnTitle.size(); ++I) {3057FOS.PadToColumn(ColumnOffset[I]);3058FOS << ColumnTitle[I];3059}3060FOS << "\n";30613062uint32_t Count = 0;3063for (const auto &R : PrintValues) {3064if (TopNFunctions && (Count++ == TopNFunctions))3065break;3066FOS.PadToColumn(ColumnOffset[0]);3067FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";3068FOS.PadToColumn(ColumnOffset[1]);3069FOS << R.MaxCount;3070FOS.PadToColumn(ColumnOffset[2]);3071FOS << R.EntryCount;3072FOS.PadToColumn(ColumnOffset[3]);3073FOS << R.FuncName << "\n";3074}3075}30763077static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,3078ProfileSummary &PS, uint32_t TopN,3079raw_fd_ostream &OS) {3080using namespace sampleprof;30813082const uint32_t HotFuncCutoff = 990000;3083auto &SummaryVector = PS.getDetailedSummary();3084uint64_t MinCountThreshold = 0;3085for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {3086if (SummaryEntry.Cutoff == HotFuncCutoff) {3087MinCountThreshold = SummaryEntry.MinCount;3088break;3089}3090}30913092// Traverse all functions in the profile and keep only hot functions.3093// The following loop also calculates the sum of total samples of all3094// functions.3095std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,3096std::greater<uint64_t>>3097HotFunc;3098uint64_t ProfileTotalSample = 0;3099uint64_t HotFuncSample = 0;3100uint64_t HotFuncCount = 0;31013102for (const auto &I : Profiles) {3103FuncSampleStats FuncStats;3104const FunctionSamples &FuncProf = I.second;3105ProfileTotalSample += FuncProf.getTotalSamples();3106getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);31073108if (isFunctionHot(FuncStats, MinCountThreshold)) {3109HotFunc.emplace(FuncProf.getTotalSamples(),3110std::make_pair(&(I.second), FuncStats.MaxSample));3111HotFuncSample += FuncProf.getTotalSamples();3112++HotFuncCount;3113}3114}31153116std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",3117"Entry sample", "Function name"};3118std::vector<int> ColumnOffset{0, 24, 42, 58};3119std::string Metric =3120std::string("max sample >= ") + std::to_string(MinCountThreshold);3121std::vector<HotFuncInfo> PrintValues;3122for (const auto &FuncPair : HotFunc) {3123const FunctionSamples &Func = *FuncPair.second.first;3124double TotalSamplePercent =3125(ProfileTotalSample > 0)3126? (Func.getTotalSamples() * 100.0) / ProfileTotalSample3127: 0;3128PrintValues.emplace_back(3129HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),3130TotalSamplePercent, FuncPair.second.second,3131Func.getHeadSamplesEstimate()));3132}3133dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,3134Profiles.size(), HotFuncSample, ProfileTotalSample,3135Metric, TopN, OS);31363137return 0;3138}31393140static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {3141if (SFormat == ShowFormat::Yaml)3142exitWithError("YAML output is not supported for sample profiles");3143using namespace sampleprof;3144LLVMContext Context;3145auto FS = vfs::getRealFileSystem();3146auto ReaderOrErr = SampleProfileReader::create(Filename, Context, *FS,3147FSDiscriminatorPassOption);3148if (std::error_code EC = ReaderOrErr.getError())3149exitWithErrorCode(EC, Filename);31503151auto Reader = std::move(ReaderOrErr.get());3152if (ShowSectionInfoOnly) {3153showSectionInfo(Reader.get(), OS);3154return 0;3155}31563157if (std::error_code EC = Reader->read())3158exitWithErrorCode(EC, Filename);31593160if (ShowAllFunctions || FuncNameFilter.empty()) {3161if (SFormat == ShowFormat::Json)3162Reader->dumpJson(OS);3163else3164Reader->dump(OS);3165} else {3166if (SFormat == ShowFormat::Json)3167exitWithError(3168"the JSON format is supported only when all functions are to "3169"be printed");31703171// TODO: parse context string to support filtering by contexts.3172FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter));3173Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS);3174}31753176if (ShowProfileSymbolList) {3177std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =3178Reader->getProfileSymbolList();3179ReaderList->dump(OS);3180}31813182if (ShowDetailedSummary) {3183auto &PS = Reader->getSummary();3184PS.printSummary(OS);3185PS.printDetailedSummary(OS);3186}31873188if (ShowHotFuncList || TopNFunctions)3189showHotFunctionList(Reader->getProfiles(), Reader->getSummary(),3190TopNFunctions, OS);31913192return 0;3193}31943195static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {3196if (SFormat == ShowFormat::Json)3197exitWithError("JSON output is not supported for MemProf");3198auto ReaderOr = llvm::memprof::RawMemProfReader::create(3199Filename, ProfiledBinary, /*KeepNames=*/true);3200if (Error E = ReaderOr.takeError())3201// Since the error can be related to the profile or the binary we do not3202// pass whence. Instead additional context is provided where necessary in3203// the error message.3204exitWithError(std::move(E), /*Whence*/ "");32053206std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(3207ReaderOr.get().release());32083209Reader->printYAML(OS);3210return 0;3211}32123213static int showDebugInfoCorrelation(const std::string &Filename,3214ShowFormat SFormat, raw_fd_ostream &OS) {3215if (SFormat == ShowFormat::Json)3216exitWithError("JSON output is not supported for debug info correlation");3217std::unique_ptr<InstrProfCorrelator> Correlator;3218if (auto Err =3219InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)3220.moveInto(Correlator))3221exitWithError(std::move(Err), Filename);3222if (SFormat == ShowFormat::Yaml) {3223if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))3224exitWithError(std::move(Err), Filename);3225return 0;3226}32273228if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))3229exitWithError(std::move(Err), Filename);32303231InstrProfSymtab Symtab;3232if (auto Err = Symtab.create(3233StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))3234exitWithError(std::move(Err), Filename);32353236if (ShowProfileSymbolList)3237Symtab.dumpNames(OS);3238// TODO: Read "Profile Data Type" from debug info to compute and show how many3239// counters the section holds.3240if (ShowDetailedSummary)3241OS << "Counters section size: 0x"3242<< Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";3243OS << "Found " << Correlator->getDataSize() << " functions\n";32443245return 0;3246}32473248static int show_main(StringRef ProgName) {3249if (Filename.empty() && DebugInfoFilename.empty())3250exitWithError(3251"the positional argument '<profdata-file>' is required unless '--" +3252DebugInfoFilename.ArgStr + "' is provided");32533254if (Filename == OutputFilename) {3255errs() << ProgName3256<< " show: Input file name cannot be the same as the output file "3257"name!\n";3258return 1;3259}3260if (JsonFormat)3261SFormat = ShowFormat::Json;32623263std::error_code EC;3264raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);3265if (EC)3266exitWithErrorCode(EC, OutputFilename);32673268if (ShowAllFunctions && !FuncNameFilter.empty())3269WithColor::warning() << "-function argument ignored: showing all functions\n";32703271if (!DebugInfoFilename.empty())3272return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);32733274if (ShowProfileKind == instr)3275return showInstrProfile(SFormat, OS);3276if (ShowProfileKind == sample)3277return showSampleProfile(SFormat, OS);3278return showMemProfProfile(SFormat, OS);3279}32803281static int order_main() {3282std::error_code EC;3283raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);3284if (EC)3285exitWithErrorCode(EC, OutputFilename);3286auto FS = vfs::getRealFileSystem();3287auto ReaderOrErr = InstrProfReader::create(Filename, *FS);3288if (Error E = ReaderOrErr.takeError())3289exitWithError(std::move(E), Filename);32903291auto Reader = std::move(ReaderOrErr.get());3292for (auto &I : *Reader) {3293// Read all entries3294(void)I;3295}3296ArrayRef Traces = Reader->getTemporalProfTraces();3297if (NumTestTraces && NumTestTraces >= Traces.size())3298exitWithError(3299"--" + NumTestTraces.ArgStr +3300" must be smaller than the total number of traces: expected: < " +3301Twine(Traces.size()) + ", actual: " + Twine(NumTestTraces));3302ArrayRef TestTraces = Traces.take_back(NumTestTraces);3303Traces = Traces.drop_back(NumTestTraces);33043305std::vector<BPFunctionNode> Nodes;3306TemporalProfTraceTy::createBPFunctionNodes(Traces, Nodes);3307BalancedPartitioningConfig Config;3308BalancedPartitioning BP(Config);3309BP.run(Nodes);33103311OS << "# Ordered " << Nodes.size() << " functions\n";3312if (!TestTraces.empty()) {3313// Since we don't know the symbol sizes, we assume 32 functions per page.3314DenseMap<BPFunctionNode::IDT, unsigned> IdToPageNumber;3315for (auto &Node : Nodes)3316IdToPageNumber[Node.Id] = IdToPageNumber.size() / 32;33173318SmallSet<unsigned, 0> TouchedPages;3319unsigned Area = 0;3320for (auto &Trace : TestTraces) {3321for (auto Id : Trace.FunctionNameRefs) {3322auto It = IdToPageNumber.find(Id);3323if (It == IdToPageNumber.end())3324continue;3325TouchedPages.insert(It->getSecond());3326Area += TouchedPages.size();3327}3328TouchedPages.clear();3329}3330OS << "# Total area under the page fault curve: " << (float)Area << "\n";3331}3332OS << "# Warning: Mach-O may prefix symbols with \"_\" depending on the "3333"linkage and this output does not take that into account. Some "3334"post-processing may be required before passing to the linker via "3335"-order_file.\n";3336for (auto &N : Nodes) {3337auto [Filename, ParsedFuncName] =3338getParsedIRPGOName(Reader->getSymtab().getFuncOrVarName(N.Id));3339if (!Filename.empty())3340OS << "# " << Filename << "\n";3341OS << ParsedFuncName << "\n";3342}3343return 0;3344}33453346int llvm_profdata_main(int argc, char **argvNonConst,3347const llvm::ToolContext &) {3348const char **argv = const_cast<const char **>(argvNonConst);33493350StringRef ProgName(sys::path::filename(argv[0]));33513352if (argc < 2) {3353errs() << ProgName3354<< ": No subcommand specified! Run llvm-profata --help for usage.\n";3355return 1;3356}33573358cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");33593360if (ShowSubcommand)3361return show_main(ProgName);33623363if (OrderSubcommand)3364return order_main();33653366if (OverlapSubcommand)3367return overlap_main();33683369if (MergeSubcommand)3370return merge_main(ProgName);33713372errs() << ProgName3373<< ": Unknown command. Run llvm-profdata --help for usage.\n";3374return 1;3375}337633773378