10b57cec5SDimitry Andric //===- llvm-profdata.cpp - LLVM profile data tool -------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // llvm-profdata merges .profdata files.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h"
140b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
150b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
160b57cec5SDimitry Andric #include "llvm/IR/LLVMContext.h"
170eae32dcSDimitry Andric #include "llvm/Object/Binary.h"
180eae32dcSDimitry Andric #include "llvm/ProfileData/InstrProfCorrelator.h"
190b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
200b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfWriter.h"
2181ad6265SDimitry Andric #include "llvm/ProfileData/MemProf.h"
220b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.h"
234824e7fdSDimitry Andric #include "llvm/ProfileData/RawMemProfReader.h"
240b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProfReader.h"
250b57cec5SDimitry Andric #include "llvm/ProfileData/SampleProfWriter.h"
2606c3fb27SDimitry Andric #include "llvm/Support/BalancedPartitioning.h"
270b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
28fe6060f1SDimitry Andric #include "llvm/Support/Discriminator.h"
290b57cec5SDimitry Andric #include "llvm/Support/Errc.h"
300b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
310b57cec5SDimitry Andric #include "llvm/Support/Format.h"
325ffd83dbSDimitry Andric #include "llvm/Support/FormattedStream.h"
3306c3fb27SDimitry Andric #include "llvm/Support/LLVMDriver.h"
34bdd1243dSDimitry Andric #include "llvm/Support/MD5.h"
350b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
360b57cec5SDimitry Andric #include "llvm/Support/Path.h"
377a6dacacSDimitry Andric #include "llvm/Support/Regex.h"
380b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
395ffd83dbSDimitry Andric #include "llvm/Support/Threading.h"
4006c3fb27SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
410b57cec5SDimitry Andric #include "llvm/Support/WithColor.h"
420b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
430b57cec5SDimitry Andric #include <algorithm>
44bdd1243dSDimitry Andric #include <cmath>
45bdd1243dSDimitry Andric #include <optional>
4681ad6265SDimitry Andric #include <queue>
470b57cec5SDimitry Andric
480b57cec5SDimitry Andric using namespace llvm;
495f757f3fSDimitry Andric using ProfCorrelatorKind = InstrProfCorrelator::ProfCorrelatorKind;
500b57cec5SDimitry Andric
515f757f3fSDimitry Andric // https://llvm.org/docs/CommandGuide/llvm-profdata.html has documentations
525f757f3fSDimitry Andric // on each subcommand.
535f757f3fSDimitry Andric cl::SubCommand ShowSubcommand(
545f757f3fSDimitry Andric "show",
555f757f3fSDimitry Andric "Takes a profile data file and displays the profiles. See detailed "
565f757f3fSDimitry Andric "documentation in "
575f757f3fSDimitry Andric "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-show");
585f757f3fSDimitry Andric cl::SubCommand OrderSubcommand(
595f757f3fSDimitry Andric "order",
605f757f3fSDimitry Andric "Reads temporal profiling traces from a profile and outputs a function "
615f757f3fSDimitry Andric "order that reduces the number of page faults for those traces. See "
625f757f3fSDimitry Andric "detailed documentation in "
635f757f3fSDimitry Andric "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-order");
645f757f3fSDimitry Andric cl::SubCommand OverlapSubcommand(
655f757f3fSDimitry Andric "overlap",
665f757f3fSDimitry Andric "Computes and displays the overlap between two profiles. See detailed "
675f757f3fSDimitry Andric "documentation in "
685f757f3fSDimitry Andric "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-overlap");
695f757f3fSDimitry Andric cl::SubCommand MergeSubcommand(
705f757f3fSDimitry Andric "merge",
715f757f3fSDimitry Andric "Takes several profiles and merge them together. See detailed "
725f757f3fSDimitry Andric "documentation in "
735f757f3fSDimitry Andric "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge");
745f757f3fSDimitry Andric
755f757f3fSDimitry Andric namespace {
765f757f3fSDimitry Andric enum ProfileKinds { instr, sample, memory };
775f757f3fSDimitry Andric enum FailureMode { warnOnly, failIfAnyAreInvalid, failIfAllAreInvalid };
785f757f3fSDimitry Andric } // namespace
79bdd1243dSDimitry Andric
800b57cec5SDimitry Andric enum ProfileFormat {
810b57cec5SDimitry Andric PF_None = 0,
820b57cec5SDimitry Andric PF_Text,
8306c3fb27SDimitry Andric PF_Compact_Binary, // Deprecated
848bcb0991SDimitry Andric PF_Ext_Binary,
850b57cec5SDimitry Andric PF_GCC,
860b57cec5SDimitry Andric PF_Binary
870b57cec5SDimitry Andric };
880b57cec5SDimitry Andric
89bdd1243dSDimitry Andric enum class ShowFormat { Text, Json, Yaml };
90bdd1243dSDimitry Andric
915f757f3fSDimitry Andric // Common options.
925f757f3fSDimitry Andric cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
935f757f3fSDimitry Andric cl::init("-"), cl::desc("Output file"),
945f757f3fSDimitry Andric cl::sub(ShowSubcommand),
955f757f3fSDimitry Andric cl::sub(OrderSubcommand),
965f757f3fSDimitry Andric cl::sub(OverlapSubcommand),
975f757f3fSDimitry Andric cl::sub(MergeSubcommand));
985f757f3fSDimitry Andric // NOTE: cl::alias must not have cl::sub(), since aliased option's cl::sub()
995f757f3fSDimitry Andric // will be used. llvm::cl::alias::done() method asserts this condition.
1005f757f3fSDimitry Andric cl::alias OutputFilenameA("o", cl::desc("Alias for --output"),
1015f757f3fSDimitry Andric cl::aliasopt(OutputFilename));
1025f757f3fSDimitry Andric
1035f757f3fSDimitry Andric // Options common to at least two commands.
1045f757f3fSDimitry Andric cl::opt<ProfileKinds> ProfileKind(
1055f757f3fSDimitry Andric cl::desc("Profile kind:"), cl::sub(MergeSubcommand),
1065f757f3fSDimitry Andric cl::sub(OverlapSubcommand), cl::init(instr),
1075f757f3fSDimitry Andric cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
1085f757f3fSDimitry Andric clEnumVal(sample, "Sample profile")));
1095f757f3fSDimitry Andric cl::opt<std::string> Filename(cl::Positional, cl::desc("<profdata-file>"),
1105f757f3fSDimitry Andric cl::sub(ShowSubcommand),
1115f757f3fSDimitry Andric cl::sub(OrderSubcommand));
1125f757f3fSDimitry Andric cl::opt<unsigned> MaxDbgCorrelationWarnings(
1135f757f3fSDimitry Andric "max-debug-info-correlation-warnings",
1145f757f3fSDimitry Andric cl::desc("The maximum number of warnings to emit when correlating "
1155f757f3fSDimitry Andric "profile from debug info (0 = no limit)"),
1165f757f3fSDimitry Andric cl::sub(MergeSubcommand), cl::sub(ShowSubcommand), cl::init(5));
1175f757f3fSDimitry Andric cl::opt<std::string> ProfiledBinary(
1185f757f3fSDimitry Andric "profiled-binary", cl::init(""),
1195f757f3fSDimitry Andric cl::desc("Path to binary from which the profile was collected."),
1205f757f3fSDimitry Andric cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
1215f757f3fSDimitry Andric cl::opt<std::string> DebugInfoFilename(
1225f757f3fSDimitry Andric "debug-info", cl::init(""),
1235f757f3fSDimitry Andric cl::desc(
1245f757f3fSDimitry Andric "For show, read and extract profile metadata from debug info and show "
1255f757f3fSDimitry Andric "the functions it found. For merge, use the provided debug info to "
1265f757f3fSDimitry Andric "correlate the raw profile."),
1275f757f3fSDimitry Andric cl::sub(ShowSubcommand), cl::sub(MergeSubcommand));
1285f757f3fSDimitry Andric cl::opt<std::string>
1295f757f3fSDimitry Andric BinaryFilename("binary-file", cl::init(""),
1305f757f3fSDimitry Andric cl::desc("For merge, use the provided unstripped bianry to "
1315f757f3fSDimitry Andric "correlate the raw profile."),
1325f757f3fSDimitry Andric cl::sub(MergeSubcommand));
1335f757f3fSDimitry Andric cl::opt<std::string> FuncNameFilter(
1345f757f3fSDimitry Andric "function",
1357a6dacacSDimitry Andric cl::desc("Only functions matching the filter are shown in the output. For "
1367a6dacacSDimitry Andric "overlapping CSSPGO, this takes a function name with calling "
1377a6dacacSDimitry Andric "context."),
1387a6dacacSDimitry Andric cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand),
1397a6dacacSDimitry Andric cl::sub(MergeSubcommand));
1405f757f3fSDimitry Andric
1415f757f3fSDimitry Andric // TODO: Consider creating a template class (e.g., MergeOption, ShowOption) to
1425f757f3fSDimitry Andric // factor out the common cl::sub in cl::opt constructor for subcommand-specific
1435f757f3fSDimitry Andric // options.
1445f757f3fSDimitry Andric
1455f757f3fSDimitry Andric // Options specific to merge subcommand.
1465f757f3fSDimitry Andric cl::list<std::string> InputFilenames(cl::Positional, cl::sub(MergeSubcommand),
1475f757f3fSDimitry Andric cl::desc("<filename...>"));
1485f757f3fSDimitry Andric cl::list<std::string> WeightedInputFilenames("weighted-input",
1495f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1505f757f3fSDimitry Andric cl::desc("<weight>,<filename>"));
1515f757f3fSDimitry Andric cl::opt<ProfileFormat> OutputFormat(
1525f757f3fSDimitry Andric cl::desc("Format of output profile"), cl::sub(MergeSubcommand),
1535f757f3fSDimitry Andric cl::init(PF_Ext_Binary),
1545f757f3fSDimitry Andric cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding"),
1555f757f3fSDimitry Andric clEnumValN(PF_Ext_Binary, "extbinary",
1565f757f3fSDimitry Andric "Extensible binary encoding "
1575f757f3fSDimitry Andric "(default)"),
1585f757f3fSDimitry Andric clEnumValN(PF_Text, "text", "Text encoding"),
1595f757f3fSDimitry Andric clEnumValN(PF_GCC, "gcc",
1605f757f3fSDimitry Andric "GCC encoding (only meaningful for -sample)")));
1615f757f3fSDimitry Andric cl::opt<std::string>
1625f757f3fSDimitry Andric InputFilenamesFile("input-files", cl::init(""), cl::sub(MergeSubcommand),
1635f757f3fSDimitry Andric cl::desc("Path to file containing newline-separated "
1645f757f3fSDimitry Andric "[<weight>,]<filename> entries"));
1655f757f3fSDimitry Andric cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"),
1665f757f3fSDimitry Andric cl::aliasopt(InputFilenamesFile));
1675f757f3fSDimitry Andric cl::opt<bool> DumpInputFileList(
1685f757f3fSDimitry Andric "dump-input-file-list", cl::init(false), cl::Hidden,
1695f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1705f757f3fSDimitry Andric cl::desc("Dump the list of input files and their weights, then exit"));
1715f757f3fSDimitry Andric cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"),
1725f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1735f757f3fSDimitry Andric cl::desc("Symbol remapping file"));
1745f757f3fSDimitry Andric cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"),
1755f757f3fSDimitry Andric cl::aliasopt(RemappingFile));
1765f757f3fSDimitry Andric cl::opt<bool>
1775f757f3fSDimitry Andric UseMD5("use-md5", cl::init(false), cl::Hidden,
1785f757f3fSDimitry Andric cl::desc("Choose to use MD5 to represent string in name table (only "
1795f757f3fSDimitry Andric "meaningful for -extbinary)"),
1805f757f3fSDimitry Andric cl::sub(MergeSubcommand));
1815f757f3fSDimitry Andric cl::opt<bool> CompressAllSections(
1825f757f3fSDimitry Andric "compress-all-sections", cl::init(false), cl::Hidden,
1835f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1845f757f3fSDimitry Andric cl::desc("Compress all sections when writing the profile (only "
1855f757f3fSDimitry Andric "meaningful for -extbinary)"));
1865f757f3fSDimitry Andric cl::opt<bool> SampleMergeColdContext(
1875f757f3fSDimitry Andric "sample-merge-cold-context", cl::init(false), cl::Hidden,
1885f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1895f757f3fSDimitry Andric cl::desc(
1905f757f3fSDimitry Andric "Merge context sample profiles whose count is below cold threshold"));
1915f757f3fSDimitry Andric cl::opt<bool> SampleTrimColdContext(
1925f757f3fSDimitry Andric "sample-trim-cold-context", cl::init(false), cl::Hidden,
1935f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1945f757f3fSDimitry Andric cl::desc(
1955f757f3fSDimitry Andric "Trim context sample profiles whose count is below cold threshold"));
1965f757f3fSDimitry Andric cl::opt<uint32_t> SampleColdContextFrameDepth(
1975f757f3fSDimitry Andric "sample-frame-depth-for-cold-context", cl::init(1),
1985f757f3fSDimitry Andric cl::sub(MergeSubcommand),
1995f757f3fSDimitry Andric cl::desc("Keep the last K frames while merging cold profile. 1 means the "
2005f757f3fSDimitry Andric "context-less base profile"));
2015f757f3fSDimitry Andric cl::opt<size_t> OutputSizeLimit(
2025f757f3fSDimitry Andric "output-size-limit", cl::init(0), cl::Hidden, cl::sub(MergeSubcommand),
2035f757f3fSDimitry Andric cl::desc("Trim cold functions until profile size is below specified "
2045f757f3fSDimitry Andric "limit in bytes. This uses a heursitic and functions may be "
2055f757f3fSDimitry Andric "excessively trimmed"));
2065f757f3fSDimitry Andric cl::opt<bool> GenPartialProfile(
2075f757f3fSDimitry Andric "gen-partial-profile", cl::init(false), cl::Hidden,
2085f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2095f757f3fSDimitry Andric cl::desc("Generate a partial profile (only meaningful for -extbinary)"));
2105f757f3fSDimitry Andric cl::opt<std::string> SupplInstrWithSample(
2115f757f3fSDimitry Andric "supplement-instr-with-sample", cl::init(""), cl::Hidden,
2125f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2135f757f3fSDimitry Andric cl::desc("Supplement an instr profile with sample profile, to correct "
2145f757f3fSDimitry Andric "the profile unrepresentativeness issue. The sample "
2155f757f3fSDimitry Andric "profile is the input of the flag. Output will be in instr "
2165f757f3fSDimitry Andric "format (The flag only works with -instr)"));
2175f757f3fSDimitry Andric cl::opt<float> ZeroCounterThreshold(
2185f757f3fSDimitry Andric "zero-counter-threshold", cl::init(0.7), cl::Hidden,
2195f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2205f757f3fSDimitry Andric cl::desc("For the function which is cold in instr profile but hot in "
2215f757f3fSDimitry Andric "sample profile, if the ratio of the number of zero counters "
2225f757f3fSDimitry Andric "divided by the total number of counters is above the "
2235f757f3fSDimitry Andric "threshold, the profile of the function will be regarded as "
2245f757f3fSDimitry Andric "being harmful for performance and will be dropped."));
2255f757f3fSDimitry Andric cl::opt<unsigned> SupplMinSizeThreshold(
2265f757f3fSDimitry Andric "suppl-min-size-threshold", cl::init(10), cl::Hidden,
2275f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2285f757f3fSDimitry Andric cl::desc("If the size of a function is smaller than the threshold, "
2295f757f3fSDimitry Andric "assume it can be inlined by PGO early inliner and it won't "
2305f757f3fSDimitry Andric "be adjusted based on sample profile."));
2315f757f3fSDimitry Andric cl::opt<unsigned> InstrProfColdThreshold(
2325f757f3fSDimitry Andric "instr-prof-cold-threshold", cl::init(0), cl::Hidden,
2335f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2345f757f3fSDimitry Andric cl::desc("User specified cold threshold for instr profile which will "
2355f757f3fSDimitry Andric "override the cold threshold got from profile summary. "));
2365f757f3fSDimitry Andric // WARNING: This reservoir size value is propagated to any input indexed
2375f757f3fSDimitry Andric // profiles for simplicity. Changing this value between invocations could
2385f757f3fSDimitry Andric // result in sample bias.
2395f757f3fSDimitry Andric cl::opt<uint64_t> TemporalProfTraceReservoirSize(
2405f757f3fSDimitry Andric "temporal-profile-trace-reservoir-size", cl::init(100),
2415f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2425f757f3fSDimitry Andric cl::desc("The maximum number of stored temporal profile traces (default: "
2435f757f3fSDimitry Andric "100)"));
2445f757f3fSDimitry Andric cl::opt<uint64_t> TemporalProfMaxTraceLength(
2455f757f3fSDimitry Andric "temporal-profile-max-trace-length", cl::init(10000),
2465f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2475f757f3fSDimitry Andric cl::desc("The maximum length of a single temporal profile trace "
2485f757f3fSDimitry Andric "(default: 10000)"));
2497a6dacacSDimitry Andric cl::opt<std::string> FuncNameNegativeFilter(
2507a6dacacSDimitry Andric "no-function", cl::init(""),
2517a6dacacSDimitry Andric cl::sub(MergeSubcommand),
2527a6dacacSDimitry Andric cl::desc("Exclude functions matching the filter from the output."));
2535f757f3fSDimitry Andric
2545f757f3fSDimitry Andric cl::opt<FailureMode>
2555f757f3fSDimitry Andric FailMode("failure-mode", cl::init(failIfAnyAreInvalid),
2565f757f3fSDimitry Andric cl::desc("Failure mode:"), cl::sub(MergeSubcommand),
2575f757f3fSDimitry Andric cl::values(clEnumValN(warnOnly, "warn",
2585f757f3fSDimitry Andric "Do not fail and just print warnings."),
2595f757f3fSDimitry Andric clEnumValN(failIfAnyAreInvalid, "any",
2605f757f3fSDimitry Andric "Fail if any profile is invalid."),
2615f757f3fSDimitry Andric clEnumValN(failIfAllAreInvalid, "all",
2625f757f3fSDimitry Andric "Fail only if all profiles are invalid.")));
2635f757f3fSDimitry Andric
2645f757f3fSDimitry Andric cl::opt<bool> OutputSparse(
2655f757f3fSDimitry Andric "sparse", cl::init(false), cl::sub(MergeSubcommand),
2665f757f3fSDimitry Andric cl::desc("Generate a sparse profile (only meaningful for -instr)"));
2675f757f3fSDimitry Andric cl::opt<unsigned> NumThreads(
2685f757f3fSDimitry Andric "num-threads", cl::init(0), cl::sub(MergeSubcommand),
2695f757f3fSDimitry Andric cl::desc("Number of merge threads to use (default: autodetect)"));
2705f757f3fSDimitry Andric cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
2715f757f3fSDimitry Andric cl::aliasopt(NumThreads));
2725f757f3fSDimitry Andric
2735f757f3fSDimitry Andric cl::opt<std::string> ProfileSymbolListFile(
2745f757f3fSDimitry Andric "prof-sym-list", cl::init(""), cl::sub(MergeSubcommand),
2755f757f3fSDimitry Andric cl::desc("Path to file containing the list of function symbols "
2765f757f3fSDimitry Andric "used to populate profile symbol list"));
2775f757f3fSDimitry Andric
2785f757f3fSDimitry Andric cl::opt<SampleProfileLayout> ProfileLayout(
2795f757f3fSDimitry Andric "convert-sample-profile-layout",
2805f757f3fSDimitry Andric cl::desc("Convert the generated profile to a profile with a new layout"),
2815f757f3fSDimitry Andric cl::sub(MergeSubcommand), cl::init(SPL_None),
2825f757f3fSDimitry Andric cl::values(
2835f757f3fSDimitry Andric clEnumValN(SPL_Nest, "nest",
2845f757f3fSDimitry Andric "Nested profile, the input should be CS flat profile"),
2855f757f3fSDimitry Andric clEnumValN(SPL_Flat, "flat",
2865f757f3fSDimitry Andric "Profile with nested inlinee flatten out")));
2875f757f3fSDimitry Andric
2885f757f3fSDimitry Andric cl::opt<bool> DropProfileSymbolList(
2895f757f3fSDimitry Andric "drop-profile-symbol-list", cl::init(false), cl::Hidden,
2905f757f3fSDimitry Andric cl::sub(MergeSubcommand),
2915f757f3fSDimitry Andric cl::desc("Drop the profile symbol list when merging AutoFDO profiles "
2925f757f3fSDimitry Andric "(only meaningful for -sample)"));
2935f757f3fSDimitry Andric
2945f757f3fSDimitry Andric // Options specific to overlap subcommand.
2955f757f3fSDimitry Andric cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
2965f757f3fSDimitry Andric cl::desc("<base profile file>"),
2975f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
2985f757f3fSDimitry Andric cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
2995f757f3fSDimitry Andric cl::desc("<test profile file>"),
3005f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3015f757f3fSDimitry Andric
3025f757f3fSDimitry Andric cl::opt<unsigned long long> SimilarityCutoff(
3035f757f3fSDimitry Andric "similarity-cutoff", cl::init(0),
3045f757f3fSDimitry Andric cl::desc("For sample profiles, list function names (with calling context "
3055f757f3fSDimitry Andric "for csspgo) for overlapped functions "
3065f757f3fSDimitry Andric "with similarities below the cutoff (percentage times 10000)."),
3075f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3085f757f3fSDimitry Andric
3095f757f3fSDimitry Andric cl::opt<bool> IsCS(
3105f757f3fSDimitry Andric "cs", cl::init(false),
3115f757f3fSDimitry Andric cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."),
3125f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3135f757f3fSDimitry Andric
3145f757f3fSDimitry Andric cl::opt<unsigned long long> OverlapValueCutoff(
3155f757f3fSDimitry Andric "value-cutoff", cl::init(-1),
3165f757f3fSDimitry Andric cl::desc(
3175f757f3fSDimitry Andric "Function level overlap information for every function (with calling "
3185f757f3fSDimitry Andric "context for csspgo) in test "
3195f757f3fSDimitry Andric "profile with max count value greater then the parameter value"),
3205f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3215f757f3fSDimitry Andric
3225f757f3fSDimitry Andric // Options unique to show subcommand.
3235f757f3fSDimitry Andric cl::opt<bool> ShowCounts("counts", cl::init(false),
3245f757f3fSDimitry Andric cl::desc("Show counter values for shown functions"),
3255f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3265f757f3fSDimitry Andric cl::opt<ShowFormat>
3275f757f3fSDimitry Andric SFormat("show-format", cl::init(ShowFormat::Text),
3285f757f3fSDimitry Andric cl::desc("Emit output in the selected format if supported"),
3295f757f3fSDimitry Andric cl::sub(ShowSubcommand),
3305f757f3fSDimitry Andric cl::values(clEnumValN(ShowFormat::Text, "text",
3315f757f3fSDimitry Andric "emit normal text output (default)"),
3325f757f3fSDimitry Andric clEnumValN(ShowFormat::Json, "json", "emit JSON"),
3335f757f3fSDimitry Andric clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")));
3345f757f3fSDimitry Andric // TODO: Consider replacing this with `--show-format=text-encoding`.
3355f757f3fSDimitry Andric cl::opt<bool>
3365f757f3fSDimitry Andric TextFormat("text", cl::init(false),
3375f757f3fSDimitry Andric cl::desc("Show instr profile data in text dump format"),
3385f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3395f757f3fSDimitry Andric cl::opt<bool>
3405f757f3fSDimitry Andric JsonFormat("json",
3415f757f3fSDimitry Andric cl::desc("Show sample profile data in the JSON format "
3425f757f3fSDimitry Andric "(deprecated, please use --show-format=json)"),
3435f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3445f757f3fSDimitry Andric cl::opt<bool> ShowIndirectCallTargets(
3455f757f3fSDimitry Andric "ic-targets", cl::init(false),
3465f757f3fSDimitry Andric cl::desc("Show indirect call site target values for shown functions"),
3475f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3485f757f3fSDimitry Andric cl::opt<bool> ShowMemOPSizes(
3495f757f3fSDimitry Andric "memop-sizes", cl::init(false),
3505f757f3fSDimitry Andric cl::desc("Show the profiled sizes of the memory intrinsic calls "
3515f757f3fSDimitry Andric "for shown functions"),
3525f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3535f757f3fSDimitry Andric cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
3545f757f3fSDimitry Andric cl::desc("Show detailed profile summary"),
3555f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3565f757f3fSDimitry Andric cl::list<uint32_t> DetailedSummaryCutoffs(
3575f757f3fSDimitry Andric cl::CommaSeparated, "detailed-summary-cutoffs",
3585f757f3fSDimitry Andric cl::desc(
3595f757f3fSDimitry Andric "Cutoff percentages (times 10000) for generating detailed summary"),
3605f757f3fSDimitry Andric cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand));
3615f757f3fSDimitry Andric cl::opt<bool>
3625f757f3fSDimitry Andric ShowHotFuncList("hot-func-list", cl::init(false),
3635f757f3fSDimitry Andric cl::desc("Show profile summary of a list of hot functions"),
3645f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3655f757f3fSDimitry Andric cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
3665f757f3fSDimitry Andric cl::desc("Details for each and every function"),
3675f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3685f757f3fSDimitry Andric cl::opt<bool> ShowCS("showcs", cl::init(false),
3695f757f3fSDimitry Andric cl::desc("Show context sensitive counts"),
3705f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3715f757f3fSDimitry Andric cl::opt<ProfileKinds> ShowProfileKind(
3725f757f3fSDimitry Andric cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand),
3735f757f3fSDimitry Andric cl::init(instr),
3745f757f3fSDimitry Andric cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
3755f757f3fSDimitry Andric clEnumVal(sample, "Sample profile"),
3765f757f3fSDimitry Andric clEnumVal(memory, "MemProf memory access profile")));
3775f757f3fSDimitry Andric cl::opt<uint32_t> TopNFunctions(
3785f757f3fSDimitry Andric "topn", cl::init(0),
3795f757f3fSDimitry Andric cl::desc("Show the list of functions with the largest internal counts"),
3805f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3815f757f3fSDimitry Andric cl::opt<uint32_t> ShowValueCutoff(
3825f757f3fSDimitry Andric "value-cutoff", cl::init(0),
3835f757f3fSDimitry Andric cl::desc("Set the count value cutoff. Functions with the maximum count "
3845f757f3fSDimitry Andric "less than this value will not be printed out. (Default is 0)"),
3855f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3865f757f3fSDimitry Andric cl::opt<bool> OnlyListBelow(
3875f757f3fSDimitry Andric "list-below-cutoff", cl::init(false),
3885f757f3fSDimitry Andric cl::desc("Only output names of functions whose max count values are "
3895f757f3fSDimitry Andric "below the cutoff value"),
3905f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3915f757f3fSDimitry Andric cl::opt<bool> ShowProfileSymbolList(
3925f757f3fSDimitry Andric "show-prof-sym-list", cl::init(false),
3935f757f3fSDimitry Andric cl::desc("Show profile symbol list if it exists in the profile. "),
3945f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3955f757f3fSDimitry Andric cl::opt<bool> ShowSectionInfoOnly(
3965f757f3fSDimitry Andric "show-sec-info-only", cl::init(false),
3975f757f3fSDimitry Andric cl::desc("Show the information of each section in the sample profile. "
3985f757f3fSDimitry Andric "The flag is only usable when the sample profile is in "
3995f757f3fSDimitry Andric "extbinary format"),
4005f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4015f757f3fSDimitry Andric cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
4025f757f3fSDimitry Andric cl::desc("Show binary ids in the profile. "),
4035f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4045f757f3fSDimitry Andric cl::opt<bool> ShowTemporalProfTraces(
4055f757f3fSDimitry Andric "temporal-profile-traces",
4065f757f3fSDimitry Andric cl::desc("Show temporal profile traces in the profile."),
4075f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4085f757f3fSDimitry Andric
4095f757f3fSDimitry Andric cl::opt<bool>
4105f757f3fSDimitry Andric ShowCovered("covered", cl::init(false),
4115f757f3fSDimitry Andric cl::desc("Show only the functions that have been executed."),
4125f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4135f757f3fSDimitry Andric
4145f757f3fSDimitry Andric cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false),
4155f757f3fSDimitry Andric cl::desc("Show profile version. "),
4165f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4175f757f3fSDimitry Andric
4185f757f3fSDimitry Andric // We use this string to indicate that there are
4195f757f3fSDimitry Andric // multiple static functions map to the same name.
4205f757f3fSDimitry Andric const std::string DuplicateNameStr = "----";
4215f757f3fSDimitry Andric
warn(Twine Message,std::string Whence="",std::string Hint="")4220b57cec5SDimitry Andric static void warn(Twine Message, std::string Whence = "",
4230b57cec5SDimitry Andric std::string Hint = "") {
4240b57cec5SDimitry Andric WithColor::warning();
4250b57cec5SDimitry Andric if (!Whence.empty())
4260b57cec5SDimitry Andric errs() << Whence << ": ";
4270b57cec5SDimitry Andric errs() << Message << "\n";
4280b57cec5SDimitry Andric if (!Hint.empty())
4290b57cec5SDimitry Andric WithColor::note() << Hint << "\n";
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric
warn(Error E,StringRef Whence="")432fe6060f1SDimitry Andric static void warn(Error E, StringRef Whence = "") {
433fe6060f1SDimitry Andric if (E.isA<InstrProfError>()) {
434fe6060f1SDimitry Andric handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
435fe6060f1SDimitry Andric warn(IPE.message(), std::string(Whence), std::string(""));
436fe6060f1SDimitry Andric });
437fe6060f1SDimitry Andric }
438fe6060f1SDimitry Andric }
439fe6060f1SDimitry Andric
exitWithError(Twine Message,std::string Whence="",std::string Hint="")4400b57cec5SDimitry Andric static void exitWithError(Twine Message, std::string Whence = "",
4410b57cec5SDimitry Andric std::string Hint = "") {
4420b57cec5SDimitry Andric WithColor::error();
4430b57cec5SDimitry Andric if (!Whence.empty())
4440b57cec5SDimitry Andric errs() << Whence << ": ";
4450b57cec5SDimitry Andric errs() << Message << "\n";
4460b57cec5SDimitry Andric if (!Hint.empty())
4470b57cec5SDimitry Andric WithColor::note() << Hint << "\n";
4480b57cec5SDimitry Andric ::exit(1);
4490b57cec5SDimitry Andric }
4500b57cec5SDimitry Andric
exitWithError(Error E,StringRef Whence="")4510b57cec5SDimitry Andric static void exitWithError(Error E, StringRef Whence = "") {
4520b57cec5SDimitry Andric if (E.isA<InstrProfError>()) {
4530b57cec5SDimitry Andric handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
4540b57cec5SDimitry Andric instrprof_error instrError = IPE.get();
4550b57cec5SDimitry Andric StringRef Hint = "";
4560b57cec5SDimitry Andric if (instrError == instrprof_error::unrecognized_format) {
4574824e7fdSDimitry Andric // Hint in case user missed specifying the profile type.
4584824e7fdSDimitry Andric Hint = "Perhaps you forgot to use the --sample or --memory option?";
4590b57cec5SDimitry Andric }
4605ffd83dbSDimitry Andric exitWithError(IPE.message(), std::string(Whence), std::string(Hint));
4610b57cec5SDimitry Andric });
46281ad6265SDimitry Andric return;
4630b57cec5SDimitry Andric }
4640b57cec5SDimitry Andric
4655ffd83dbSDimitry Andric exitWithError(toString(std::move(E)), std::string(Whence));
4660b57cec5SDimitry Andric }
4670b57cec5SDimitry Andric
exitWithErrorCode(std::error_code EC,StringRef Whence="")4680b57cec5SDimitry Andric static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
4695ffd83dbSDimitry Andric exitWithError(EC.message(), std::string(Whence));
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")4728bcb0991SDimitry Andric static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
4738bcb0991SDimitry Andric StringRef Whence = "") {
4748bcb0991SDimitry Andric if (FailMode == failIfAnyAreInvalid)
4758bcb0991SDimitry Andric exitWithErrorCode(EC, Whence);
4768bcb0991SDimitry Andric else
4775ffd83dbSDimitry Andric warn(EC.message(), std::string(Whence));
4780b57cec5SDimitry Andric }
4790b57cec5SDimitry Andric
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)4800b57cec5SDimitry Andric static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
4810b57cec5SDimitry Andric StringRef WhenceFunction = "",
4820b57cec5SDimitry Andric bool ShowHint = true) {
4830b57cec5SDimitry Andric if (!WhenceFile.empty())
4840b57cec5SDimitry Andric errs() << WhenceFile << ": ";
4850b57cec5SDimitry Andric if (!WhenceFunction.empty())
4860b57cec5SDimitry Andric errs() << WhenceFunction << ": ";
4870b57cec5SDimitry Andric
4880b57cec5SDimitry Andric auto IPE = instrprof_error::success;
4890b57cec5SDimitry Andric E = handleErrors(std::move(E),
4900b57cec5SDimitry Andric [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
4910b57cec5SDimitry Andric IPE = E->get();
4920b57cec5SDimitry Andric return Error(std::move(E));
4930b57cec5SDimitry Andric });
4940b57cec5SDimitry Andric errs() << toString(std::move(E)) << "\n";
4950b57cec5SDimitry Andric
4960b57cec5SDimitry Andric if (ShowHint) {
4970b57cec5SDimitry Andric StringRef Hint = "";
4980b57cec5SDimitry Andric if (IPE != instrprof_error::success) {
4990b57cec5SDimitry Andric switch (IPE) {
5000b57cec5SDimitry Andric case instrprof_error::hash_mismatch:
5010b57cec5SDimitry Andric case instrprof_error::count_mismatch:
5020b57cec5SDimitry Andric case instrprof_error::value_site_count_mismatch:
5030b57cec5SDimitry Andric Hint = "Make sure that all profile data to be merged is generated "
5040b57cec5SDimitry Andric "from the same binary.";
5050b57cec5SDimitry Andric break;
5060b57cec5SDimitry Andric default:
5070b57cec5SDimitry Andric break;
5080b57cec5SDimitry Andric }
5090b57cec5SDimitry Andric }
5100b57cec5SDimitry Andric
5110b57cec5SDimitry Andric if (!Hint.empty())
5120b57cec5SDimitry Andric errs() << Hint << "\n";
5130b57cec5SDimitry Andric }
5140b57cec5SDimitry Andric }
5150b57cec5SDimitry Andric
5160b57cec5SDimitry Andric namespace {
5170b57cec5SDimitry Andric /// A remapper from original symbol names to new symbol names based on a file
5180b57cec5SDimitry Andric /// containing a list of mappings from old name to new name.
5190b57cec5SDimitry Andric class SymbolRemapper {
5200b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> File;
5210b57cec5SDimitry Andric DenseMap<StringRef, StringRef> RemappingTable;
5220b57cec5SDimitry Andric
5230b57cec5SDimitry Andric public:
5240b57cec5SDimitry Andric /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)5250b57cec5SDimitry Andric static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
5260b57cec5SDimitry Andric auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
5270b57cec5SDimitry Andric if (!BufOrError)
5280b57cec5SDimitry Andric exitWithErrorCode(BufOrError.getError(), InputFile);
5290b57cec5SDimitry Andric
5308bcb0991SDimitry Andric auto Remapper = std::make_unique<SymbolRemapper>();
5310b57cec5SDimitry Andric Remapper->File = std::move(BufOrError.get());
5320b57cec5SDimitry Andric
5330b57cec5SDimitry Andric for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
5340b57cec5SDimitry Andric !LineIt.is_at_eof(); ++LineIt) {
5350b57cec5SDimitry Andric std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
5360b57cec5SDimitry Andric if (Parts.first.empty() || Parts.second.empty() ||
5370b57cec5SDimitry Andric Parts.second.count(' ')) {
5380b57cec5SDimitry Andric exitWithError("unexpected line in remapping file",
5390b57cec5SDimitry Andric (InputFile + ":" + Twine(LineIt.line_number())).str(),
5400b57cec5SDimitry Andric "expected 'old_symbol new_symbol'");
5410b57cec5SDimitry Andric }
5420b57cec5SDimitry Andric Remapper->RemappingTable.insert(Parts);
5430b57cec5SDimitry Andric }
5440b57cec5SDimitry Andric return Remapper;
5450b57cec5SDimitry Andric }
5460b57cec5SDimitry Andric
5470b57cec5SDimitry Andric /// Attempt to map the given old symbol into a new symbol.
5480b57cec5SDimitry Andric ///
5490b57cec5SDimitry Andric /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)5500b57cec5SDimitry Andric StringRef operator()(StringRef Name) {
5510b57cec5SDimitry Andric StringRef New = RemappingTable.lookup(Name);
5520b57cec5SDimitry Andric return New.empty() ? Name : New;
5530b57cec5SDimitry Andric }
5545f757f3fSDimitry Andric
operator ()(FunctionId Name)5555f757f3fSDimitry Andric FunctionId operator()(FunctionId Name) {
5565f757f3fSDimitry Andric // MD5 name cannot be remapped.
5575f757f3fSDimitry Andric if (!Name.isStringRef())
5585f757f3fSDimitry Andric return Name;
5595f757f3fSDimitry Andric StringRef New = RemappingTable.lookup(Name.stringRef());
5605f757f3fSDimitry Andric return New.empty() ? Name : FunctionId(New);
5615f757f3fSDimitry Andric }
5620b57cec5SDimitry Andric };
5630b57cec5SDimitry Andric }
5640b57cec5SDimitry Andric
5650b57cec5SDimitry Andric struct WeightedFile {
5660b57cec5SDimitry Andric std::string Filename;
5670b57cec5SDimitry Andric uint64_t Weight;
5680b57cec5SDimitry Andric };
5690b57cec5SDimitry Andric typedef SmallVector<WeightedFile, 5> WeightedFileVector;
5700b57cec5SDimitry Andric
5710b57cec5SDimitry Andric /// Keep track of merged data and reported errors.
5720b57cec5SDimitry Andric struct WriterContext {
5730b57cec5SDimitry Andric std::mutex Lock;
5740b57cec5SDimitry Andric InstrProfWriter Writer;
5758bcb0991SDimitry Andric std::vector<std::pair<Error, std::string>> Errors;
5760b57cec5SDimitry Andric std::mutex &ErrLock;
5770b57cec5SDimitry Andric SmallSet<instrprof_error, 4> &WriterErrorCodes;
5780b57cec5SDimitry Andric
WriterContextWriterContext5790b57cec5SDimitry Andric WriterContext(bool IsSparse, std::mutex &ErrLock,
58006c3fb27SDimitry Andric SmallSet<instrprof_error, 4> &WriterErrorCodes,
58106c3fb27SDimitry Andric uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)
58206c3fb27SDimitry Andric : Writer(IsSparse, ReservoirSize, MaxTraceLength), ErrLock(ErrLock),
58306c3fb27SDimitry Andric WriterErrorCodes(WriterErrorCodes) {}
5840b57cec5SDimitry Andric };
5850b57cec5SDimitry Andric
5860b57cec5SDimitry Andric /// Computer the overlap b/w profile BaseFilename and TestFileName,
5870b57cec5SDimitry Andric /// and store the program level result to Overlap.
overlapInput(const std::string & BaseFilename,const std::string & TestFilename,WriterContext * WC,OverlapStats & Overlap,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)5880b57cec5SDimitry Andric static void overlapInput(const std::string &BaseFilename,
5890b57cec5SDimitry Andric const std::string &TestFilename, WriterContext *WC,
5900b57cec5SDimitry Andric OverlapStats &Overlap,
5910b57cec5SDimitry Andric const OverlapFuncFilters &FuncFilter,
5920b57cec5SDimitry Andric raw_fd_ostream &OS, bool IsCS) {
59306c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
59406c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(TestFilename, *FS);
5950b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError()) {
5960b57cec5SDimitry Andric // Skip the empty profiles by returning sliently.
59706c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
59806c3fb27SDimitry Andric if (ErrorCode != instrprof_error::empty_raw_profile)
59906c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
60006c3fb27SDimitry Andric TestFilename);
6010b57cec5SDimitry Andric return;
6020b57cec5SDimitry Andric }
6030b57cec5SDimitry Andric
6040b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
6050b57cec5SDimitry Andric for (auto &I : *Reader) {
6060b57cec5SDimitry Andric OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
6070b57cec5SDimitry Andric FuncOverlap.setFuncInfo(I.Name, I.Hash);
6080b57cec5SDimitry Andric
6090b57cec5SDimitry Andric WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
6100b57cec5SDimitry Andric FuncOverlap.dump(OS);
6110b57cec5SDimitry Andric }
6120b57cec5SDimitry Andric }
6130b57cec5SDimitry Andric
6140b57cec5SDimitry Andric /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)6150b57cec5SDimitry Andric static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
6160eae32dcSDimitry Andric const InstrProfCorrelator *Correlator,
61781ad6265SDimitry Andric const StringRef ProfiledBinary, WriterContext *WC) {
6180b57cec5SDimitry Andric std::unique_lock<std::mutex> CtxGuard{WC->Lock};
6190b57cec5SDimitry Andric
6200b57cec5SDimitry Andric // Copy the filename, because llvm::ThreadPool copied the input "const
6210b57cec5SDimitry Andric // WeightedFile &" by value, making a reference to the filename within it
6220b57cec5SDimitry Andric // invalid outside of this packaged task.
6238bcb0991SDimitry Andric std::string Filename = Input.Filename;
6240b57cec5SDimitry Andric
62581ad6265SDimitry Andric using ::llvm::memprof::RawMemProfReader;
62681ad6265SDimitry Andric if (RawMemProfReader::hasFormat(Input.Filename)) {
62781ad6265SDimitry Andric auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
62881ad6265SDimitry Andric if (!ReaderOrErr) {
62981ad6265SDimitry Andric exitWithError(ReaderOrErr.takeError(), Input.Filename);
63081ad6265SDimitry Andric }
63181ad6265SDimitry Andric std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
63281ad6265SDimitry Andric // Check if the profile types can be merged, e.g. clang frontend profiles
63381ad6265SDimitry Andric // should not be merged with memprof profiles.
63481ad6265SDimitry Andric if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
63581ad6265SDimitry Andric consumeError(std::move(E));
63681ad6265SDimitry Andric WC->Errors.emplace_back(
63781ad6265SDimitry Andric make_error<StringError>(
63881ad6265SDimitry Andric "Cannot merge MemProf profile with Clang generated profile.",
63981ad6265SDimitry Andric std::error_code()),
64081ad6265SDimitry Andric Filename);
64181ad6265SDimitry Andric return;
64281ad6265SDimitry Andric }
64381ad6265SDimitry Andric
64481ad6265SDimitry Andric auto MemProfError = [&](Error E) {
64506c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
64606c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
64706c3fb27SDimitry Andric Filename);
64881ad6265SDimitry Andric };
64981ad6265SDimitry Andric
65081ad6265SDimitry Andric // Add the frame mappings into the writer context.
65181ad6265SDimitry Andric const auto &IdToFrame = Reader->getFrameMapping();
65281ad6265SDimitry Andric for (const auto &I : IdToFrame) {
65381ad6265SDimitry Andric bool Succeeded = WC->Writer.addMemProfFrame(
65481ad6265SDimitry Andric /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
65581ad6265SDimitry Andric // If we weren't able to add the frame mappings then it doesn't make sense
65681ad6265SDimitry Andric // to try to add the records from this profile.
65781ad6265SDimitry Andric if (!Succeeded)
65881ad6265SDimitry Andric return;
65981ad6265SDimitry Andric }
66081ad6265SDimitry Andric const auto &FunctionProfileData = Reader->getProfileData();
66181ad6265SDimitry Andric // Add the memprof records into the writer context.
66281ad6265SDimitry Andric for (const auto &I : FunctionProfileData) {
66381ad6265SDimitry Andric WC->Writer.addMemProfRecord(/*Id=*/I.first, /*Record=*/I.second);
66481ad6265SDimitry Andric }
66581ad6265SDimitry Andric return;
66681ad6265SDimitry Andric }
66781ad6265SDimitry Andric
66806c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
6695f757f3fSDimitry Andric // TODO: This only saves the first non-fatal error from InstrProfReader, and
6705f757f3fSDimitry Andric // then added to WriterContext::Errors. However, this is not extensible, if
6715f757f3fSDimitry Andric // we have more non-fatal errors from InstrProfReader in the future. How
6725f757f3fSDimitry Andric // should this interact with different -failure-mode?
6735f757f3fSDimitry Andric std::optional<std::pair<Error, std::string>> ReaderWarning;
6745f757f3fSDimitry Andric auto Warn = [&](Error E) {
6755f757f3fSDimitry Andric if (ReaderWarning) {
6765f757f3fSDimitry Andric consumeError(std::move(E));
6775f757f3fSDimitry Andric return;
6785f757f3fSDimitry Andric }
6795f757f3fSDimitry Andric // Only show the first time an error occurs in this file.
6805f757f3fSDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
6815f757f3fSDimitry Andric ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};
6825f757f3fSDimitry Andric };
6835f757f3fSDimitry Andric auto ReaderOrErr =
6845f757f3fSDimitry Andric InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);
6850b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError()) {
68606c3fb27SDimitry Andric // Skip the empty profiles by returning silently.
68706c3fb27SDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
68806c3fb27SDimitry Andric if (ErrCode != instrprof_error::empty_raw_profile)
68906c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrCode, Msg),
69006c3fb27SDimitry Andric Filename);
6910b57cec5SDimitry Andric return;
6920b57cec5SDimitry Andric }
6930b57cec5SDimitry Andric
6940b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
6951fd87a68SDimitry Andric if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
696fe6060f1SDimitry Andric consumeError(std::move(E));
6978bcb0991SDimitry Andric WC->Errors.emplace_back(
6988bcb0991SDimitry Andric make_error<StringError>(
6990b57cec5SDimitry Andric "Merge IR generated profile with Clang generated profile.",
7008bcb0991SDimitry Andric std::error_code()),
7018bcb0991SDimitry Andric Filename);
7020b57cec5SDimitry Andric return;
7030b57cec5SDimitry Andric }
7040b57cec5SDimitry Andric
7050b57cec5SDimitry Andric for (auto &I : *Reader) {
7060b57cec5SDimitry Andric if (Remapper)
7070b57cec5SDimitry Andric I.Name = (*Remapper)(I.Name);
7080b57cec5SDimitry Andric const StringRef FuncName = I.Name;
7090b57cec5SDimitry Andric bool Reported = false;
7100b57cec5SDimitry Andric WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
7110b57cec5SDimitry Andric if (Reported) {
7120b57cec5SDimitry Andric consumeError(std::move(E));
7130b57cec5SDimitry Andric return;
7140b57cec5SDimitry Andric }
7150b57cec5SDimitry Andric Reported = true;
7160b57cec5SDimitry Andric // Only show hint the first time an error occurs.
71706c3fb27SDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7180b57cec5SDimitry Andric std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
71906c3fb27SDimitry Andric bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second;
72006c3fb27SDimitry Andric handleMergeWriterError(make_error<InstrProfError>(ErrCode, Msg),
72106c3fb27SDimitry Andric Input.Filename, FuncName, firstTime);
7220b57cec5SDimitry Andric });
7230b57cec5SDimitry Andric }
724bdd1243dSDimitry Andric
72506c3fb27SDimitry Andric if (Reader->hasTemporalProfile()) {
72606c3fb27SDimitry Andric auto &Traces = Reader->getTemporalProfTraces(Input.Weight);
72706c3fb27SDimitry Andric if (!Traces.empty())
72806c3fb27SDimitry Andric WC->Writer.addTemporalProfileTraces(
72906c3fb27SDimitry Andric Traces, Reader->getTemporalProfTraceStreamSize());
73006c3fb27SDimitry Andric }
731bdd1243dSDimitry Andric if (Reader->hasError()) {
7325f757f3fSDimitry Andric if (Error E = Reader->getError()) {
7338bcb0991SDimitry Andric WC->Errors.emplace_back(std::move(E), Filename);
7345f757f3fSDimitry Andric return;
7355f757f3fSDimitry Andric }
7360b57cec5SDimitry Andric }
7370b57cec5SDimitry Andric
738bdd1243dSDimitry Andric std::vector<llvm::object::BuildID> BinaryIds;
7395f757f3fSDimitry Andric if (Error E = Reader->readBinaryIds(BinaryIds)) {
740bdd1243dSDimitry Andric WC->Errors.emplace_back(std::move(E), Filename);
7415f757f3fSDimitry Andric return;
7425f757f3fSDimitry Andric }
743bdd1243dSDimitry Andric WC->Writer.addBinaryIds(BinaryIds);
7445f757f3fSDimitry Andric
7455f757f3fSDimitry Andric if (ReaderWarning) {
7465f757f3fSDimitry Andric WC->Errors.emplace_back(std::move(ReaderWarning->first),
7475f757f3fSDimitry Andric ReaderWarning->second);
7485f757f3fSDimitry Andric }
749bdd1243dSDimitry Andric }
750bdd1243dSDimitry Andric
7510b57cec5SDimitry Andric /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)7520b57cec5SDimitry Andric static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
7538bcb0991SDimitry Andric for (auto &ErrorPair : Src->Errors)
7548bcb0991SDimitry Andric Dst->Errors.push_back(std::move(ErrorPair));
7558bcb0991SDimitry Andric Src->Errors.clear();
7560b57cec5SDimitry Andric
757bdd1243dSDimitry Andric if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind()))
758bdd1243dSDimitry Andric exitWithError(std::move(E));
759bdd1243dSDimitry Andric
7600b57cec5SDimitry Andric Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
76106c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
7628bcb0991SDimitry Andric std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
76306c3fb27SDimitry Andric bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second;
7648bcb0991SDimitry Andric if (firstTime)
76506c3fb27SDimitry Andric warn(toString(make_error<InstrProfError>(ErrorCode, Msg)));
7660b57cec5SDimitry Andric });
7670b57cec5SDimitry Andric }
7680b57cec5SDimitry Andric
7697a6dacacSDimitry Andric static StringRef
getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type & Val)7707a6dacacSDimitry Andric getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) {
7717a6dacacSDimitry Andric return Val.first();
7727a6dacacSDimitry Andric }
7737a6dacacSDimitry Andric
7747a6dacacSDimitry Andric static std::string
getFuncName(const SampleProfileMap::value_type & Val)7757a6dacacSDimitry Andric getFuncName(const SampleProfileMap::value_type &Val) {
7767a6dacacSDimitry Andric return Val.second.getContext().toString();
7777a6dacacSDimitry Andric }
7787a6dacacSDimitry Andric
7797a6dacacSDimitry Andric template <typename T>
filterFunctions(T & ProfileMap)7807a6dacacSDimitry Andric static void filterFunctions(T &ProfileMap) {
7817a6dacacSDimitry Andric bool hasFilter = !FuncNameFilter.empty();
7827a6dacacSDimitry Andric bool hasNegativeFilter = !FuncNameNegativeFilter.empty();
7837a6dacacSDimitry Andric if (!hasFilter && !hasNegativeFilter)
7847a6dacacSDimitry Andric return;
7857a6dacacSDimitry Andric
7867a6dacacSDimitry Andric // If filter starts with '?' it is MSVC mangled name, not a regex.
7877a6dacacSDimitry Andric llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+");
7887a6dacacSDimitry Andric if (hasFilter && FuncNameFilter[0] == '?' &&
7897a6dacacSDimitry Andric ProbablyMSVCMangledName.match(FuncNameFilter))
7907a6dacacSDimitry Andric FuncNameFilter = llvm::Regex::escape(FuncNameFilter);
7917a6dacacSDimitry Andric if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' &&
7927a6dacacSDimitry Andric ProbablyMSVCMangledName.match(FuncNameNegativeFilter))
7937a6dacacSDimitry Andric FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter);
7947a6dacacSDimitry Andric
7957a6dacacSDimitry Andric size_t Count = ProfileMap.size();
7967a6dacacSDimitry Andric llvm::Regex Pattern(FuncNameFilter);
7977a6dacacSDimitry Andric llvm::Regex NegativePattern(FuncNameNegativeFilter);
7987a6dacacSDimitry Andric std::string Error;
7997a6dacacSDimitry Andric if (hasFilter && !Pattern.isValid(Error))
8007a6dacacSDimitry Andric exitWithError(Error);
8017a6dacacSDimitry Andric if (hasNegativeFilter && !NegativePattern.isValid(Error))
8027a6dacacSDimitry Andric exitWithError(Error);
8037a6dacacSDimitry Andric
8047a6dacacSDimitry Andric // Handle MD5 profile, so it is still able to match using the original name.
8057a6dacacSDimitry Andric std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter));
8067a6dacacSDimitry Andric std::string NegativeMD5Name =
8077a6dacacSDimitry Andric std::to_string(llvm::MD5Hash(FuncNameNegativeFilter));
8087a6dacacSDimitry Andric
8097a6dacacSDimitry Andric for (auto I = ProfileMap.begin(); I != ProfileMap.end();) {
8107a6dacacSDimitry Andric auto Tmp = I++;
8117a6dacacSDimitry Andric const auto &FuncName = getFuncName(*Tmp);
8127a6dacacSDimitry Andric // Negative filter has higher precedence than positive filter.
8137a6dacacSDimitry Andric if ((hasNegativeFilter &&
8147a6dacacSDimitry Andric (NegativePattern.match(FuncName) ||
8157a6dacacSDimitry Andric (FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) ||
8167a6dacacSDimitry Andric (hasFilter && !(Pattern.match(FuncName) ||
8177a6dacacSDimitry Andric (FunctionSamples::UseMD5 && MD5Name == FuncName))))
8187a6dacacSDimitry Andric ProfileMap.erase(Tmp);
8197a6dacacSDimitry Andric }
8207a6dacacSDimitry Andric
8217a6dacacSDimitry Andric llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions "
8227a6dacacSDimitry Andric << "in the original profile are filtered.\n";
8237a6dacacSDimitry Andric }
8247a6dacacSDimitry Andric
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)8255ffd83dbSDimitry Andric static void writeInstrProfile(StringRef OutputFilename,
8265ffd83dbSDimitry Andric ProfileFormat OutputFormat,
8275ffd83dbSDimitry Andric InstrProfWriter &Writer) {
8285ffd83dbSDimitry Andric std::error_code EC;
829e8d8bef9SDimitry Andric raw_fd_ostream Output(OutputFilename.data(), EC,
830fe6060f1SDimitry Andric OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
831e8d8bef9SDimitry Andric : sys::fs::OF_None);
8325ffd83dbSDimitry Andric if (EC)
8335ffd83dbSDimitry Andric exitWithErrorCode(EC, OutputFilename);
8345ffd83dbSDimitry Andric
8355ffd83dbSDimitry Andric if (OutputFormat == PF_Text) {
8365ffd83dbSDimitry Andric if (Error E = Writer.writeText(Output))
837fe6060f1SDimitry Andric warn(std::move(E));
8385ffd83dbSDimitry Andric } else {
839fe6060f1SDimitry Andric if (Output.is_displayed())
840fe6060f1SDimitry Andric exitWithError("cannot write a non-text format profile to the terminal");
841fe6060f1SDimitry Andric if (Error E = Writer.write(Output))
842fe6060f1SDimitry Andric warn(std::move(E));
8435ffd83dbSDimitry Andric }
8445ffd83dbSDimitry Andric }
8455ffd83dbSDimitry Andric
mergeInstrProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,int MaxDbgCorrelationWarnings,const StringRef ProfiledBinary)8465f757f3fSDimitry Andric static void mergeInstrProfile(const WeightedFileVector &Inputs,
8475f757f3fSDimitry Andric SymbolRemapper *Remapper,
8485f757f3fSDimitry Andric int MaxDbgCorrelationWarnings,
84981ad6265SDimitry Andric const StringRef ProfiledBinary) {
8505f757f3fSDimitry Andric const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue();
8515f757f3fSDimitry Andric const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue();
85206c3fb27SDimitry Andric if (OutputFormat == PF_Compact_Binary)
85306c3fb27SDimitry Andric exitWithError("Compact Binary is deprecated");
85406c3fb27SDimitry Andric if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary &&
85506c3fb27SDimitry Andric OutputFormat != PF_Text)
856fe6060f1SDimitry Andric exitWithError("unknown format is specified");
8570b57cec5SDimitry Andric
8585f757f3fSDimitry Andric // TODO: Maybe we should support correlation with mixture of different
8595f757f3fSDimitry Andric // correlation modes(w/wo debug-info/object correlation).
8605f757f3fSDimitry Andric if (!DebugInfoFilename.empty() && !BinaryFilename.empty())
8615f757f3fSDimitry Andric exitWithError("Expected only one of -debug-info, -binary-file");
8625f757f3fSDimitry Andric std::string CorrelateFilename;
8635f757f3fSDimitry Andric ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;
8640eae32dcSDimitry Andric if (!DebugInfoFilename.empty()) {
8655f757f3fSDimitry Andric CorrelateFilename = DebugInfoFilename;
8665f757f3fSDimitry Andric CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;
8675f757f3fSDimitry Andric } else if (!BinaryFilename.empty()) {
8685f757f3fSDimitry Andric CorrelateFilename = BinaryFilename;
8695f757f3fSDimitry Andric CorrelateKind = ProfCorrelatorKind::BINARY;
8705f757f3fSDimitry Andric }
8715f757f3fSDimitry Andric
8725f757f3fSDimitry Andric std::unique_ptr<InstrProfCorrelator> Correlator;
8735f757f3fSDimitry Andric if (CorrelateKind != InstrProfCorrelator::NONE) {
8745f757f3fSDimitry Andric if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)
8755f757f3fSDimitry Andric .moveInto(Correlator))
8765f757f3fSDimitry Andric exitWithError(std::move(Err), CorrelateFilename);
8775f757f3fSDimitry Andric if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
8785f757f3fSDimitry Andric exitWithError(std::move(Err), CorrelateFilename);
8790eae32dcSDimitry Andric }
8800eae32dcSDimitry Andric
8810b57cec5SDimitry Andric std::mutex ErrorLock;
8820b57cec5SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
8830b57cec5SDimitry Andric
8840b57cec5SDimitry Andric // If NumThreads is not specified, auto-detect a good default.
8850b57cec5SDimitry Andric if (NumThreads == 0)
8865ffd83dbSDimitry Andric NumThreads = std::min(hardware_concurrency().compute_thread_count(),
8875ffd83dbSDimitry Andric unsigned((Inputs.size() + 1) / 2));
8880b57cec5SDimitry Andric
8890b57cec5SDimitry Andric // Initialize the writer contexts.
8900b57cec5SDimitry Andric SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
8910b57cec5SDimitry Andric for (unsigned I = 0; I < NumThreads; ++I)
8928bcb0991SDimitry Andric Contexts.emplace_back(std::make_unique<WriterContext>(
89306c3fb27SDimitry Andric OutputSparse, ErrorLock, WriterErrorCodes, TraceReservoirSize,
89406c3fb27SDimitry Andric MaxTraceLength));
8950b57cec5SDimitry Andric
8960b57cec5SDimitry Andric if (NumThreads == 1) {
8970b57cec5SDimitry Andric for (const auto &Input : Inputs)
89881ad6265SDimitry Andric loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
89981ad6265SDimitry Andric Contexts[0].get());
9000b57cec5SDimitry Andric } else {
9015ffd83dbSDimitry Andric ThreadPool Pool(hardware_concurrency(NumThreads));
9020b57cec5SDimitry Andric
9030b57cec5SDimitry Andric // Load the inputs in parallel (N/NumThreads serial steps).
9040b57cec5SDimitry Andric unsigned Ctx = 0;
9050b57cec5SDimitry Andric for (const auto &Input : Inputs) {
90681ad6265SDimitry Andric Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
9070eae32dcSDimitry Andric Contexts[Ctx].get());
9080b57cec5SDimitry Andric Ctx = (Ctx + 1) % NumThreads;
9090b57cec5SDimitry Andric }
9100b57cec5SDimitry Andric Pool.wait();
9110b57cec5SDimitry Andric
9120b57cec5SDimitry Andric // Merge the writer contexts together (~ lg(NumThreads) serial steps).
9130b57cec5SDimitry Andric unsigned Mid = Contexts.size() / 2;
9140b57cec5SDimitry Andric unsigned End = Contexts.size();
9150b57cec5SDimitry Andric assert(Mid > 0 && "Expected more than one context");
9160b57cec5SDimitry Andric do {
9170b57cec5SDimitry Andric for (unsigned I = 0; I < Mid; ++I)
9180b57cec5SDimitry Andric Pool.async(mergeWriterContexts, Contexts[I].get(),
9190b57cec5SDimitry Andric Contexts[I + Mid].get());
9200b57cec5SDimitry Andric Pool.wait();
9210b57cec5SDimitry Andric if (End & 1) {
9220b57cec5SDimitry Andric Pool.async(mergeWriterContexts, Contexts[0].get(),
9230b57cec5SDimitry Andric Contexts[End - 1].get());
9240b57cec5SDimitry Andric Pool.wait();
9250b57cec5SDimitry Andric }
9260b57cec5SDimitry Andric End = Mid;
9270b57cec5SDimitry Andric Mid /= 2;
9280b57cec5SDimitry Andric } while (Mid > 0);
9290b57cec5SDimitry Andric }
9300b57cec5SDimitry Andric
9318bcb0991SDimitry Andric // Handle deferred errors encountered during merging. If the number of errors
9328bcb0991SDimitry Andric // is equal to the number of inputs the merge failed.
9338bcb0991SDimitry Andric unsigned NumErrors = 0;
9340b57cec5SDimitry Andric for (std::unique_ptr<WriterContext> &WC : Contexts) {
9358bcb0991SDimitry Andric for (auto &ErrorPair : WC->Errors) {
9368bcb0991SDimitry Andric ++NumErrors;
9378bcb0991SDimitry Andric warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
9380b57cec5SDimitry Andric }
9398bcb0991SDimitry Andric }
9405f757f3fSDimitry Andric if ((NumErrors == Inputs.size() && FailMode == failIfAllAreInvalid) ||
9418bcb0991SDimitry Andric (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
942fe6060f1SDimitry Andric exitWithError("no profile can be merged");
9430b57cec5SDimitry Andric
9447a6dacacSDimitry Andric filterFunctions(Contexts[0]->Writer.getProfileData());
9457a6dacacSDimitry Andric
9465ffd83dbSDimitry Andric writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
9470b57cec5SDimitry Andric }
9480b57cec5SDimitry Andric
949e8d8bef9SDimitry Andric /// The profile entry for a function in instrumentation profile.
950e8d8bef9SDimitry Andric struct InstrProfileEntry {
951e8d8bef9SDimitry Andric uint64_t MaxCount = 0;
952bdd1243dSDimitry Andric uint64_t NumEdgeCounters = 0;
953e8d8bef9SDimitry Andric float ZeroCounterRatio = 0.0;
954e8d8bef9SDimitry Andric InstrProfRecord *ProfRecord;
955e8d8bef9SDimitry Andric InstrProfileEntry(InstrProfRecord *Record);
956e8d8bef9SDimitry Andric InstrProfileEntry() = default;
957e8d8bef9SDimitry Andric };
958e8d8bef9SDimitry Andric
InstrProfileEntry(InstrProfRecord * Record)959e8d8bef9SDimitry Andric InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
960e8d8bef9SDimitry Andric ProfRecord = Record;
961e8d8bef9SDimitry Andric uint64_t CntNum = Record->Counts.size();
962e8d8bef9SDimitry Andric uint64_t ZeroCntNum = 0;
963e8d8bef9SDimitry Andric for (size_t I = 0; I < CntNum; ++I) {
964e8d8bef9SDimitry Andric MaxCount = std::max(MaxCount, Record->Counts[I]);
965e8d8bef9SDimitry Andric ZeroCntNum += !Record->Counts[I];
966e8d8bef9SDimitry Andric }
967e8d8bef9SDimitry Andric ZeroCounterRatio = (float)ZeroCntNum / CntNum;
968bdd1243dSDimitry Andric NumEdgeCounters = CntNum;
969e8d8bef9SDimitry Andric }
970e8d8bef9SDimitry Andric
971bdd1243dSDimitry Andric /// Either set all the counters in the instr profile entry \p IFE to
972bdd1243dSDimitry Andric /// -1 / -2 /in order to drop the profile or scale up the
973bdd1243dSDimitry Andric /// counters in \p IFP to be above hot / cold threshold. We use
974bdd1243dSDimitry Andric /// the ratio of zero counters in the profile of a function to
975bdd1243dSDimitry Andric /// decide the profile is helpful or harmful for performance,
976bdd1243dSDimitry Andric /// and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,bool SetToHot,uint64_t HotInstrThreshold,uint64_t ColdInstrThreshold,float ZeroCounterThreshold)977bdd1243dSDimitry Andric static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot,
978e8d8bef9SDimitry Andric uint64_t HotInstrThreshold,
979bdd1243dSDimitry Andric uint64_t ColdInstrThreshold,
980e8d8bef9SDimitry Andric float ZeroCounterThreshold) {
981e8d8bef9SDimitry Andric InstrProfRecord *ProfRecord = IFE.ProfRecord;
982e8d8bef9SDimitry Andric if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
983e8d8bef9SDimitry Andric // If all or most of the counters of the function are zero, the
984bdd1243dSDimitry Andric // profile is unaccountable and should be dropped. Reset all the
985bdd1243dSDimitry Andric // counters to be -1 / -2 and PGO profile-use will drop the profile.
986e8d8bef9SDimitry Andric // All counters being -1 also implies that the function is hot so
987e8d8bef9SDimitry Andric // PGO profile-use will also set the entry count metadata to be
988e8d8bef9SDimitry Andric // above hot threshold.
989bdd1243dSDimitry Andric // All counters being -2 implies that the function is warm so
990bdd1243dSDimitry Andric // PGO profile-use will also set the entry count metadata to be
991bdd1243dSDimitry Andric // above cold threshold.
992bdd1243dSDimitry Andric auto Kind =
993bdd1243dSDimitry Andric (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm);
994bdd1243dSDimitry Andric ProfRecord->setPseudoCount(Kind);
995e8d8bef9SDimitry Andric return;
996e8d8bef9SDimitry Andric }
997e8d8bef9SDimitry Andric
998bdd1243dSDimitry Andric // Scale up the MaxCount to be multiple times above hot / cold threshold.
999e8d8bef9SDimitry Andric const unsigned MultiplyFactor = 3;
1000bdd1243dSDimitry Andric uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold);
1001bdd1243dSDimitry Andric uint64_t Numerator = Threshold * MultiplyFactor;
1002bdd1243dSDimitry Andric
1003bdd1243dSDimitry Andric // Make sure Threshold for warm counters is below the HotInstrThreshold.
1004bdd1243dSDimitry Andric if (!SetToHot && Threshold >= HotInstrThreshold) {
1005bdd1243dSDimitry Andric Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2;
1006bdd1243dSDimitry Andric }
1007bdd1243dSDimitry Andric
1008e8d8bef9SDimitry Andric uint64_t Denominator = IFE.MaxCount;
1009bdd1243dSDimitry Andric if (Numerator <= Denominator)
1010bdd1243dSDimitry Andric return;
1011e8d8bef9SDimitry Andric ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
1012e8d8bef9SDimitry Andric warn(toString(make_error<InstrProfError>(E)));
1013e8d8bef9SDimitry Andric });
1014e8d8bef9SDimitry Andric }
1015e8d8bef9SDimitry Andric
1016e8d8bef9SDimitry Andric const uint64_t ColdPercentileIdx = 15;
1017e8d8bef9SDimitry Andric const uint64_t HotPercentileIdx = 11;
1018e8d8bef9SDimitry Andric
1019fe6060f1SDimitry Andric using sampleprof::FSDiscriminatorPass;
1020fe6060f1SDimitry Andric
1021fe6060f1SDimitry Andric // Internal options to set FSDiscriminatorPass. Used in merge and show
1022fe6060f1SDimitry Andric // commands.
1023fe6060f1SDimitry Andric static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
1024fe6060f1SDimitry Andric "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
1025fe6060f1SDimitry Andric cl::desc("Zero out the discriminator bits for the FS discrimiantor "
1026fe6060f1SDimitry Andric "pass beyond this value. The enum values are defined in "
1027fe6060f1SDimitry Andric "Support/Discriminator.h"),
1028fe6060f1SDimitry Andric cl::values(clEnumVal(Base, "Use base discriminators only"),
1029fe6060f1SDimitry Andric clEnumVal(Pass1, "Use base and pass 1 discriminators"),
1030fe6060f1SDimitry Andric clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
1031fe6060f1SDimitry Andric clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
1032fe6060f1SDimitry Andric clEnumVal(PassLast, "Use all discriminator bits (default)")));
1033fe6060f1SDimitry Andric
getDiscriminatorMask()1034fe6060f1SDimitry Andric static unsigned getDiscriminatorMask() {
1035fe6060f1SDimitry Andric return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
1036fe6060f1SDimitry Andric }
1037fe6060f1SDimitry Andric
1038e8d8bef9SDimitry Andric /// Adjust the instr profile in \p WC based on the sample profile in
1039e8d8bef9SDimitry Andric /// \p Reader.
1040e8d8bef9SDimitry Andric static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)1041e8d8bef9SDimitry Andric adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
1042e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
1043e8d8bef9SDimitry Andric unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
1044e8d8bef9SDimitry Andric unsigned InstrProfColdThreshold) {
1045e8d8bef9SDimitry Andric // Function to its entry in instr profile.
1046e8d8bef9SDimitry Andric StringMap<InstrProfileEntry> InstrProfileMap;
1047bdd1243dSDimitry Andric StringMap<StringRef> StaticFuncMap;
1048e8d8bef9SDimitry Andric InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
1049bdd1243dSDimitry Andric
1050bdd1243dSDimitry Andric auto checkSampleProfileHasFUnique = [&Reader]() {
1051bdd1243dSDimitry Andric for (const auto &PD : Reader->getProfiles()) {
10525f757f3fSDimitry Andric auto &FContext = PD.second.getContext();
1053bdd1243dSDimitry Andric if (FContext.toString().find(FunctionSamples::UniqSuffix) !=
1054bdd1243dSDimitry Andric std::string::npos) {
1055bdd1243dSDimitry Andric return true;
1056bdd1243dSDimitry Andric }
1057bdd1243dSDimitry Andric }
1058bdd1243dSDimitry Andric return false;
1059bdd1243dSDimitry Andric };
1060bdd1243dSDimitry Andric
1061bdd1243dSDimitry Andric bool SampleProfileHasFUnique = checkSampleProfileHasFUnique();
1062bdd1243dSDimitry Andric
1063bdd1243dSDimitry Andric auto buildStaticFuncMap = [&StaticFuncMap,
1064bdd1243dSDimitry Andric SampleProfileHasFUnique](const StringRef Name) {
10651db9f3b2SDimitry Andric std::string FilePrefixes[] = {".cpp", "cc", ".c", ".hpp", ".h"};
1066bdd1243dSDimitry Andric size_t PrefixPos = StringRef::npos;
10671db9f3b2SDimitry Andric for (auto &FilePrefix : FilePrefixes) {
10681db9f3b2SDimitry Andric std::string NamePrefix = FilePrefix + kGlobalIdentifierDelimiter;
10691db9f3b2SDimitry Andric PrefixPos = Name.find_insensitive(NamePrefix);
1070bdd1243dSDimitry Andric if (PrefixPos == StringRef::npos)
1071bdd1243dSDimitry Andric continue;
10721db9f3b2SDimitry Andric PrefixPos += NamePrefix.size();
1073bdd1243dSDimitry Andric break;
1074bdd1243dSDimitry Andric }
1075bdd1243dSDimitry Andric
1076bdd1243dSDimitry Andric if (PrefixPos == StringRef::npos) {
1077bdd1243dSDimitry Andric return;
1078bdd1243dSDimitry Andric }
1079bdd1243dSDimitry Andric
1080bdd1243dSDimitry Andric StringRef NewName = Name.drop_front(PrefixPos);
1081bdd1243dSDimitry Andric StringRef FName = Name.substr(0, PrefixPos - 1);
1082bdd1243dSDimitry Andric if (NewName.size() == 0) {
1083bdd1243dSDimitry Andric return;
1084bdd1243dSDimitry Andric }
1085bdd1243dSDimitry Andric
1086bdd1243dSDimitry Andric // This name should have a static linkage.
1087bdd1243dSDimitry Andric size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix);
1088bdd1243dSDimitry Andric bool ProfileHasFUnique = (PostfixPos != StringRef::npos);
1089bdd1243dSDimitry Andric
1090bdd1243dSDimitry Andric // If sample profile and instrumented profile do not agree on symbol
1091bdd1243dSDimitry Andric // uniqification.
1092bdd1243dSDimitry Andric if (SampleProfileHasFUnique != ProfileHasFUnique) {
10935f757f3fSDimitry Andric // If instrumented profile uses -funique-internal-linkage-symbols,
1094bdd1243dSDimitry Andric // we need to trim the name.
1095bdd1243dSDimitry Andric if (ProfileHasFUnique) {
1096bdd1243dSDimitry Andric NewName = NewName.substr(0, PostfixPos);
1097bdd1243dSDimitry Andric } else {
10985f757f3fSDimitry Andric // If sample profile uses -funique-internal-linkage-symbols,
1099bdd1243dSDimitry Andric // we build the map.
1100bdd1243dSDimitry Andric std::string NStr =
1101bdd1243dSDimitry Andric NewName.str() + getUniqueInternalLinkagePostfix(FName);
1102bdd1243dSDimitry Andric NewName = StringRef(NStr);
1103bdd1243dSDimitry Andric StaticFuncMap[NewName] = Name;
1104bdd1243dSDimitry Andric return;
1105bdd1243dSDimitry Andric }
1106bdd1243dSDimitry Andric }
1107bdd1243dSDimitry Andric
110806c3fb27SDimitry Andric if (!StaticFuncMap.contains(NewName)) {
1109bdd1243dSDimitry Andric StaticFuncMap[NewName] = Name;
1110bdd1243dSDimitry Andric } else {
1111bdd1243dSDimitry Andric StaticFuncMap[NewName] = DuplicateNameStr;
1112bdd1243dSDimitry Andric }
1113bdd1243dSDimitry Andric };
1114bdd1243dSDimitry Andric
1115bdd1243dSDimitry Andric // We need to flatten the SampleFDO profile as the InstrFDO
1116bdd1243dSDimitry Andric // profile does not have inlined callsite profiles.
1117bdd1243dSDimitry Andric // One caveat is the pre-inlined function -- their samples
1118bdd1243dSDimitry Andric // should be collapsed into the caller function.
1119bdd1243dSDimitry Andric // Here we do a DFS traversal to get the flatten profile
1120bdd1243dSDimitry Andric // info: the sum of entrycount and the max of maxcount.
1121bdd1243dSDimitry Andric // Here is the algorithm:
1122bdd1243dSDimitry Andric // recursive (FS, root_name) {
1123bdd1243dSDimitry Andric // name = FS->getName();
1124bdd1243dSDimitry Andric // get samples for FS;
1125bdd1243dSDimitry Andric // if (InstrProf.find(name) {
1126bdd1243dSDimitry Andric // root_name = name;
1127bdd1243dSDimitry Andric // } else {
1128bdd1243dSDimitry Andric // if (name is in static_func map) {
1129bdd1243dSDimitry Andric // root_name = static_name;
1130bdd1243dSDimitry Andric // }
1131bdd1243dSDimitry Andric // }
1132bdd1243dSDimitry Andric // update the Map entry for root_name;
1133bdd1243dSDimitry Andric // for (subfs: FS) {
1134bdd1243dSDimitry Andric // recursive(subfs, root_name);
1135bdd1243dSDimitry Andric // }
1136bdd1243dSDimitry Andric // }
1137bdd1243dSDimitry Andric //
1138bdd1243dSDimitry Andric // Here is an example.
1139bdd1243dSDimitry Andric //
1140bdd1243dSDimitry Andric // SampleProfile:
1141bdd1243dSDimitry Andric // foo:12345:1000
1142bdd1243dSDimitry Andric // 1: 1000
1143bdd1243dSDimitry Andric // 2.1: 1000
1144bdd1243dSDimitry Andric // 15: 5000
1145bdd1243dSDimitry Andric // 4: bar:1000
1146bdd1243dSDimitry Andric // 1: 1000
1147bdd1243dSDimitry Andric // 2: goo:3000
1148bdd1243dSDimitry Andric // 1: 3000
1149bdd1243dSDimitry Andric // 8: bar:40000
1150bdd1243dSDimitry Andric // 1: 10000
1151bdd1243dSDimitry Andric // 2: goo:30000
1152bdd1243dSDimitry Andric // 1: 30000
1153bdd1243dSDimitry Andric //
1154bdd1243dSDimitry Andric // InstrProfile has two entries:
1155bdd1243dSDimitry Andric // foo
11561db9f3b2SDimitry Andric // bar.cc;bar
1157bdd1243dSDimitry Andric //
1158bdd1243dSDimitry Andric // After BuildMaxSampleMap, we should have the following in FlattenSampleMap:
1159bdd1243dSDimitry Andric // {"foo", {1000, 5000}}
11601db9f3b2SDimitry Andric // {"bar.cc;bar", {11000, 30000}}
1161bdd1243dSDimitry Andric //
1162bdd1243dSDimitry Andric // foo's has an entry count of 1000, and max body count of 5000.
11631db9f3b2SDimitry Andric // bar.cc;bar has an entry count of 11000 (sum two callsites of 1000 and
1164bdd1243dSDimitry Andric // 10000), and max count of 30000 (from the callsite in line 8).
1165bdd1243dSDimitry Andric //
11661db9f3b2SDimitry Andric // Note that goo's count will remain in bar.cc;bar() as it does not have an
1167bdd1243dSDimitry Andric // entry in InstrProfile.
11685f757f3fSDimitry Andric llvm::StringMap<std::pair<uint64_t, uint64_t>> FlattenSampleMap;
1169bdd1243dSDimitry Andric auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap,
1170bdd1243dSDimitry Andric &InstrProfileMap](const FunctionSamples &FS,
1171bdd1243dSDimitry Andric const StringRef &RootName) {
1172bdd1243dSDimitry Andric auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,
1173bdd1243dSDimitry Andric const StringRef &RootName,
1174bdd1243dSDimitry Andric auto &BuildImpl) -> void {
11755f757f3fSDimitry Andric std::string NameStr = FS.getFunction().str();
11765f757f3fSDimitry Andric const StringRef Name = NameStr;
1177bdd1243dSDimitry Andric const StringRef *NewRootName = &RootName;
1178bdd1243dSDimitry Andric uint64_t EntrySample = FS.getHeadSamplesEstimate();
1179bdd1243dSDimitry Andric uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);
1180bdd1243dSDimitry Andric
1181bdd1243dSDimitry Andric auto It = InstrProfileMap.find(Name);
1182bdd1243dSDimitry Andric if (It != InstrProfileMap.end()) {
1183bdd1243dSDimitry Andric NewRootName = &Name;
1184bdd1243dSDimitry Andric } else {
1185bdd1243dSDimitry Andric auto NewName = StaticFuncMap.find(Name);
1186bdd1243dSDimitry Andric if (NewName != StaticFuncMap.end()) {
1187bdd1243dSDimitry Andric It = InstrProfileMap.find(NewName->second.str());
1188bdd1243dSDimitry Andric if (NewName->second != DuplicateNameStr) {
1189bdd1243dSDimitry Andric NewRootName = &NewName->second;
1190bdd1243dSDimitry Andric }
1191bdd1243dSDimitry Andric } else {
1192bdd1243dSDimitry Andric // Here the EntrySample is of an inlined function, so we should not
1193bdd1243dSDimitry Andric // update the EntrySample in the map.
1194bdd1243dSDimitry Andric EntrySample = 0;
1195bdd1243dSDimitry Andric }
1196bdd1243dSDimitry Andric }
1197bdd1243dSDimitry Andric EntrySample += FlattenSampleMap[*NewRootName].first;
1198bdd1243dSDimitry Andric MaxBodySample =
1199bdd1243dSDimitry Andric std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample);
1200bdd1243dSDimitry Andric FlattenSampleMap[*NewRootName] =
1201bdd1243dSDimitry Andric std::make_pair(EntrySample, MaxBodySample);
1202bdd1243dSDimitry Andric
1203bdd1243dSDimitry Andric for (const auto &C : FS.getCallsiteSamples())
1204bdd1243dSDimitry Andric for (const auto &F : C.second)
1205bdd1243dSDimitry Andric BuildImpl(F.second, *NewRootName, BuildImpl);
1206bdd1243dSDimitry Andric };
1207bdd1243dSDimitry Andric BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl);
1208bdd1243dSDimitry Andric };
1209bdd1243dSDimitry Andric
1210e8d8bef9SDimitry Andric for (auto &PD : WC->Writer.getProfileData()) {
1211e8d8bef9SDimitry Andric // Populate IPBuilder.
1212e8d8bef9SDimitry Andric for (const auto &PDV : PD.getValue()) {
1213e8d8bef9SDimitry Andric InstrProfRecord Record = PDV.second;
1214e8d8bef9SDimitry Andric IPBuilder.addRecord(Record);
1215e8d8bef9SDimitry Andric }
1216e8d8bef9SDimitry Andric
1217e8d8bef9SDimitry Andric // If a function has multiple entries in instr profile, skip it.
1218e8d8bef9SDimitry Andric if (PD.getValue().size() != 1)
1219e8d8bef9SDimitry Andric continue;
1220e8d8bef9SDimitry Andric
1221e8d8bef9SDimitry Andric // Initialize InstrProfileMap.
1222e8d8bef9SDimitry Andric InstrProfRecord *R = &PD.getValue().begin()->second;
1223bdd1243dSDimitry Andric StringRef FullName = PD.getKey();
1224bdd1243dSDimitry Andric InstrProfileMap[FullName] = InstrProfileEntry(R);
1225bdd1243dSDimitry Andric buildStaticFuncMap(FullName);
1226bdd1243dSDimitry Andric }
1227bdd1243dSDimitry Andric
1228bdd1243dSDimitry Andric for (auto &PD : Reader->getProfiles()) {
1229bdd1243dSDimitry Andric sampleprof::FunctionSamples &FS = PD.second;
12305f757f3fSDimitry Andric std::string Name = FS.getFunction().str();
12315f757f3fSDimitry Andric BuildMaxSampleMap(FS, Name);
1232e8d8bef9SDimitry Andric }
1233e8d8bef9SDimitry Andric
1234e8d8bef9SDimitry Andric ProfileSummary InstrPS = *IPBuilder.getSummary();
1235e8d8bef9SDimitry Andric ProfileSummary SamplePS = Reader->getSummary();
1236e8d8bef9SDimitry Andric
1237e8d8bef9SDimitry Andric // Compute cold thresholds for instr profile and sample profile.
1238bdd1243dSDimitry Andric uint64_t HotSampleThreshold =
1239bdd1243dSDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1240bdd1243dSDimitry Andric SamplePS.getDetailedSummary(),
1241bdd1243dSDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1242bdd1243dSDimitry Andric .MinCount;
1243e8d8bef9SDimitry Andric uint64_t ColdSampleThreshold =
1244e8d8bef9SDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1245e8d8bef9SDimitry Andric SamplePS.getDetailedSummary(),
1246e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1247e8d8bef9SDimitry Andric .MinCount;
1248e8d8bef9SDimitry Andric uint64_t HotInstrThreshold =
1249e8d8bef9SDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1250e8d8bef9SDimitry Andric InstrPS.getDetailedSummary(),
1251e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1252e8d8bef9SDimitry Andric .MinCount;
1253e8d8bef9SDimitry Andric uint64_t ColdInstrThreshold =
1254e8d8bef9SDimitry Andric InstrProfColdThreshold
1255e8d8bef9SDimitry Andric ? InstrProfColdThreshold
1256e8d8bef9SDimitry Andric : ProfileSummaryBuilder::getEntryForPercentile(
1257e8d8bef9SDimitry Andric InstrPS.getDetailedSummary(),
1258e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1259e8d8bef9SDimitry Andric .MinCount;
1260e8d8bef9SDimitry Andric
1261e8d8bef9SDimitry Andric // Find hot/warm functions in sample profile which is cold in instr profile
1262e8d8bef9SDimitry Andric // and adjust the profiles of those functions in the instr profile.
1263bdd1243dSDimitry Andric for (const auto &E : FlattenSampleMap) {
1264bdd1243dSDimitry Andric uint64_t SampleMaxCount = std::max(E.second.first, E.second.second);
1265bdd1243dSDimitry Andric if (SampleMaxCount < ColdSampleThreshold)
1266bdd1243dSDimitry Andric continue;
12675f757f3fSDimitry Andric StringRef Name = E.first();
1268bdd1243dSDimitry Andric auto It = InstrProfileMap.find(Name);
1269bdd1243dSDimitry Andric if (It == InstrProfileMap.end()) {
1270bdd1243dSDimitry Andric auto NewName = StaticFuncMap.find(Name);
1271bdd1243dSDimitry Andric if (NewName != StaticFuncMap.end()) {
1272bdd1243dSDimitry Andric It = InstrProfileMap.find(NewName->second.str());
1273bdd1243dSDimitry Andric if (NewName->second == DuplicateNameStr) {
1274bdd1243dSDimitry Andric WithColor::warning()
1275bdd1243dSDimitry Andric << "Static function " << Name
1276bdd1243dSDimitry Andric << " has multiple promoted names, cannot adjust profile.\n";
1277e8d8bef9SDimitry Andric }
1278e8d8bef9SDimitry Andric }
1279e8d8bef9SDimitry Andric }
1280bdd1243dSDimitry Andric if (It == InstrProfileMap.end() ||
1281bdd1243dSDimitry Andric It->second.MaxCount > ColdInstrThreshold ||
1282bdd1243dSDimitry Andric It->second.NumEdgeCounters < SupplMinSizeThreshold)
1283bdd1243dSDimitry Andric continue;
1284bdd1243dSDimitry Andric bool SetToHot = SampleMaxCount >= HotSampleThreshold;
1285bdd1243dSDimitry Andric updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold,
1286bdd1243dSDimitry Andric ColdInstrThreshold, ZeroCounterThreshold);
1287bdd1243dSDimitry Andric }
1288bdd1243dSDimitry Andric }
1289e8d8bef9SDimitry Andric
1290e8d8bef9SDimitry Andric /// The main function to supplement instr profile with sample profile.
1291e8d8bef9SDimitry Andric /// \Inputs contains the instr profile. \p SampleFilename specifies the
1292e8d8bef9SDimitry Andric /// sample profile. \p OutputFilename specifies the output profile name.
1293e8d8bef9SDimitry Andric /// \p OutputFormat specifies the output profile format. \p OutputSparse
1294e8d8bef9SDimitry Andric /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
1295e8d8bef9SDimitry Andric /// specifies the minimal size for the functions whose profile will be
1296e8d8bef9SDimitry Andric /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
1297e8d8bef9SDimitry Andric /// a function contains too many zero counters and whether its profile
1298e8d8bef9SDimitry Andric /// should be dropped. \p InstrProfColdThreshold is the user specified
1299e8d8bef9SDimitry Andric /// cold threshold which will override the cold threshold got from the
1300e8d8bef9SDimitry Andric /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)13015f757f3fSDimitry Andric static void supplementInstrProfile(const WeightedFileVector &Inputs,
13025f757f3fSDimitry Andric StringRef SampleFilename, bool OutputSparse,
13035f757f3fSDimitry Andric unsigned SupplMinSizeThreshold,
13045f757f3fSDimitry Andric float ZeroCounterThreshold,
1305e8d8bef9SDimitry Andric unsigned InstrProfColdThreshold) {
1306e8d8bef9SDimitry Andric if (OutputFilename.compare("-") == 0)
1307fe6060f1SDimitry Andric exitWithError("cannot write indexed profdata format to stdout");
1308e8d8bef9SDimitry Andric if (Inputs.size() != 1)
1309fe6060f1SDimitry Andric exitWithError("expect one input to be an instr profile");
1310e8d8bef9SDimitry Andric if (Inputs[0].Weight != 1)
1311fe6060f1SDimitry Andric exitWithError("expect instr profile doesn't have weight");
1312e8d8bef9SDimitry Andric
1313e8d8bef9SDimitry Andric StringRef InstrFilename = Inputs[0].Filename;
1314e8d8bef9SDimitry Andric
1315e8d8bef9SDimitry Andric // Read sample profile.
1316e8d8bef9SDimitry Andric LLVMContext Context;
131706c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
1318fe6060f1SDimitry Andric auto ReaderOrErr = sampleprof::SampleProfileReader::create(
131906c3fb27SDimitry Andric SampleFilename.str(), Context, *FS, FSDiscriminatorPassOption);
1320e8d8bef9SDimitry Andric if (std::error_code EC = ReaderOrErr.getError())
1321e8d8bef9SDimitry Andric exitWithErrorCode(EC, SampleFilename);
1322e8d8bef9SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
1323e8d8bef9SDimitry Andric if (std::error_code EC = Reader->read())
1324e8d8bef9SDimitry Andric exitWithErrorCode(EC, SampleFilename);
1325e8d8bef9SDimitry Andric
1326e8d8bef9SDimitry Andric // Read instr profile.
1327e8d8bef9SDimitry Andric std::mutex ErrorLock;
1328e8d8bef9SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
1329e8d8bef9SDimitry Andric auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
1330e8d8bef9SDimitry Andric WriterErrorCodes);
133181ad6265SDimitry Andric loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
1332e8d8bef9SDimitry Andric if (WC->Errors.size() > 0)
1333e8d8bef9SDimitry Andric exitWithError(std::move(WC->Errors[0].first), InstrFilename);
1334e8d8bef9SDimitry Andric
1335e8d8bef9SDimitry Andric adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
1336e8d8bef9SDimitry Andric InstrProfColdThreshold);
1337e8d8bef9SDimitry Andric writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
1338e8d8bef9SDimitry Andric }
1339e8d8bef9SDimitry Andric
13400b57cec5SDimitry Andric /// Make a copy of the given function samples with all symbol names remapped
13410b57cec5SDimitry Andric /// by the provided symbol remapper.
13420b57cec5SDimitry Andric static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)13430b57cec5SDimitry Andric remapSamples(const sampleprof::FunctionSamples &Samples,
13440b57cec5SDimitry Andric SymbolRemapper &Remapper, sampleprof_error &Error) {
13450b57cec5SDimitry Andric sampleprof::FunctionSamples Result;
13465f757f3fSDimitry Andric Result.setFunction(Remapper(Samples.getFunction()));
13470b57cec5SDimitry Andric Result.addTotalSamples(Samples.getTotalSamples());
13480b57cec5SDimitry Andric Result.addHeadSamples(Samples.getHeadSamples());
13490b57cec5SDimitry Andric for (const auto &BodySample : Samples.getBodySamples()) {
1350fe6060f1SDimitry Andric uint32_t MaskedDiscriminator =
1351fe6060f1SDimitry Andric BodySample.first.Discriminator & getDiscriminatorMask();
1352fe6060f1SDimitry Andric Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
13530b57cec5SDimitry Andric BodySample.second.getSamples());
13540b57cec5SDimitry Andric for (const auto &Target : BodySample.second.getCallTargets()) {
13550b57cec5SDimitry Andric Result.addCalledTargetSamples(BodySample.first.LineOffset,
1356fe6060f1SDimitry Andric MaskedDiscriminator,
13575f757f3fSDimitry Andric Remapper(Target.first), Target.second);
13580b57cec5SDimitry Andric }
13590b57cec5SDimitry Andric }
13600b57cec5SDimitry Andric for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
13610b57cec5SDimitry Andric sampleprof::FunctionSamplesMap &Target =
13620b57cec5SDimitry Andric Result.functionSamplesAt(CallsiteSamples.first);
13630b57cec5SDimitry Andric for (const auto &Callsite : CallsiteSamples.second) {
13640b57cec5SDimitry Andric sampleprof::FunctionSamples Remapped =
13650b57cec5SDimitry Andric remapSamples(Callsite.second, Remapper, Error);
13665f757f3fSDimitry Andric MergeResult(Error, Target[Remapped.getFunction()].merge(Remapped));
13670b57cec5SDimitry Andric }
13680b57cec5SDimitry Andric }
13690b57cec5SDimitry Andric return Result;
13700b57cec5SDimitry Andric }
13710b57cec5SDimitry Andric
13720b57cec5SDimitry Andric static sampleprof::SampleProfileFormat FormatMap[] = {
13738bcb0991SDimitry Andric sampleprof::SPF_None,
13748bcb0991SDimitry Andric sampleprof::SPF_Text,
137506c3fb27SDimitry Andric sampleprof::SPF_None,
13768bcb0991SDimitry Andric sampleprof::SPF_Ext_Binary,
13778bcb0991SDimitry Andric sampleprof::SPF_GCC,
13788bcb0991SDimitry Andric sampleprof::SPF_Binary};
13798bcb0991SDimitry Andric
13808bcb0991SDimitry Andric static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)13818bcb0991SDimitry Andric getInputFileBuf(const StringRef &InputFile) {
13828bcb0991SDimitry Andric if (InputFile == "")
13838bcb0991SDimitry Andric return {};
13848bcb0991SDimitry Andric
13858bcb0991SDimitry Andric auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
13868bcb0991SDimitry Andric if (!BufOrError)
13878bcb0991SDimitry Andric exitWithErrorCode(BufOrError.getError(), InputFile);
13888bcb0991SDimitry Andric
13898bcb0991SDimitry Andric return std::move(*BufOrError);
13908bcb0991SDimitry Andric }
13918bcb0991SDimitry Andric
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)13928bcb0991SDimitry Andric static void populateProfileSymbolList(MemoryBuffer *Buffer,
13938bcb0991SDimitry Andric sampleprof::ProfileSymbolList &PSL) {
13948bcb0991SDimitry Andric if (!Buffer)
13958bcb0991SDimitry Andric return;
13968bcb0991SDimitry Andric
13978bcb0991SDimitry Andric SmallVector<StringRef, 32> SymbolVec;
13988bcb0991SDimitry Andric StringRef Data = Buffer->getBuffer();
13998bcb0991SDimitry Andric Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
14008bcb0991SDimitry Andric
140104eeddc0SDimitry Andric for (StringRef SymbolStr : SymbolVec)
140204eeddc0SDimitry Andric PSL.add(SymbolStr.trim());
14038bcb0991SDimitry Andric }
14048bcb0991SDimitry Andric
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)14058bcb0991SDimitry Andric static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
14068bcb0991SDimitry Andric ProfileFormat OutputFormat,
14078bcb0991SDimitry Andric MemoryBuffer *Buffer,
14088bcb0991SDimitry Andric sampleprof::ProfileSymbolList &WriterList,
14095ffd83dbSDimitry Andric bool CompressAllSections, bool UseMD5,
14105ffd83dbSDimitry Andric bool GenPartialProfile) {
14118bcb0991SDimitry Andric populateProfileSymbolList(Buffer, WriterList);
14128bcb0991SDimitry Andric if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
14138bcb0991SDimitry Andric warn("Profile Symbol list is not empty but the output format is not "
14148bcb0991SDimitry Andric "ExtBinary format. The list will be lost in the output. ");
14158bcb0991SDimitry Andric
14168bcb0991SDimitry Andric Writer.setProfileSymbolList(&WriterList);
14178bcb0991SDimitry Andric
14188bcb0991SDimitry Andric if (CompressAllSections) {
14195ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14208bcb0991SDimitry Andric warn("-compress-all-section is ignored. Specify -extbinary to enable it");
14215ffd83dbSDimitry Andric else
14225ffd83dbSDimitry Andric Writer.setToCompressAllSections();
14238bcb0991SDimitry Andric }
14245ffd83dbSDimitry Andric if (UseMD5) {
14255ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14265ffd83dbSDimitry Andric warn("-use-md5 is ignored. Specify -extbinary to enable it");
14275ffd83dbSDimitry Andric else
14285ffd83dbSDimitry Andric Writer.setUseMD5();
14295ffd83dbSDimitry Andric }
14305ffd83dbSDimitry Andric if (GenPartialProfile) {
14315ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14325ffd83dbSDimitry Andric warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
14335ffd83dbSDimitry Andric else
14345ffd83dbSDimitry Andric Writer.setPartialProfile();
14358bcb0991SDimitry Andric }
14368bcb0991SDimitry Andric }
14370b57cec5SDimitry Andric
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef ProfileSymbolListFile,size_t OutputSizeLimit)14385f757f3fSDimitry Andric static void mergeSampleProfile(const WeightedFileVector &Inputs,
14395f757f3fSDimitry Andric SymbolRemapper *Remapper,
14405f757f3fSDimitry Andric StringRef ProfileSymbolListFile,
14415f757f3fSDimitry Andric size_t OutputSizeLimit) {
14420b57cec5SDimitry Andric using namespace sampleprof;
1443349cc55cSDimitry Andric SampleProfileMap ProfileMap;
14440b57cec5SDimitry Andric SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
14450b57cec5SDimitry Andric LLVMContext Context;
14468bcb0991SDimitry Andric sampleprof::ProfileSymbolList WriterList;
1447bdd1243dSDimitry Andric std::optional<bool> ProfileIsProbeBased;
1448bdd1243dSDimitry Andric std::optional<bool> ProfileIsCS;
14490b57cec5SDimitry Andric for (const auto &Input : Inputs) {
145006c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
145106c3fb27SDimitry Andric auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, *FS,
1452fe6060f1SDimitry Andric FSDiscriminatorPassOption);
14538bcb0991SDimitry Andric if (std::error_code EC = ReaderOrErr.getError()) {
14548bcb0991SDimitry Andric warnOrExitGivenError(FailMode, EC, Input.Filename);
14558bcb0991SDimitry Andric continue;
14568bcb0991SDimitry Andric }
14570b57cec5SDimitry Andric
14580b57cec5SDimitry Andric // We need to keep the readers around until after all the files are
14590b57cec5SDimitry Andric // read so that we do not lose the function names stored in each
14600b57cec5SDimitry Andric // reader's memory. The function names are needed to write out the
14610b57cec5SDimitry Andric // merged profile map.
14620b57cec5SDimitry Andric Readers.push_back(std::move(ReaderOrErr.get()));
14630b57cec5SDimitry Andric const auto Reader = Readers.back().get();
14648bcb0991SDimitry Andric if (std::error_code EC = Reader->read()) {
14658bcb0991SDimitry Andric warnOrExitGivenError(FailMode, EC, Input.Filename);
14668bcb0991SDimitry Andric Readers.pop_back();
14678bcb0991SDimitry Andric continue;
14688bcb0991SDimitry Andric }
14690b57cec5SDimitry Andric
1470349cc55cSDimitry Andric SampleProfileMap &Profiles = Reader->getProfiles();
147181ad6265SDimitry Andric if (ProfileIsProbeBased &&
1472e8d8bef9SDimitry Andric ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
1473e8d8bef9SDimitry Andric exitWithError(
1474e8d8bef9SDimitry Andric "cannot merge probe-based profile with non-probe-based profile");
1475e8d8bef9SDimitry Andric ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
147681ad6265SDimitry Andric if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
1477fe6060f1SDimitry Andric exitWithError("cannot merge CS profile with non-CS profile");
147881ad6265SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS;
1479349cc55cSDimitry Andric for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
14800b57cec5SDimitry Andric I != E; ++I) {
14810b57cec5SDimitry Andric sampleprof_error Result = sampleprof_error::success;
14820b57cec5SDimitry Andric FunctionSamples Remapped =
14830b57cec5SDimitry Andric Remapper ? remapSamples(I->second, *Remapper, Result)
14840b57cec5SDimitry Andric : FunctionSamples();
14850b57cec5SDimitry Andric FunctionSamples &Samples = Remapper ? Remapped : I->second;
1486349cc55cSDimitry Andric SampleContext FContext = Samples.getContext();
1487349cc55cSDimitry Andric MergeResult(Result, ProfileMap[FContext].merge(Samples, Input.Weight));
14880b57cec5SDimitry Andric if (Result != sampleprof_error::success) {
14890b57cec5SDimitry Andric std::error_code EC = make_error_code(Result);
1490349cc55cSDimitry Andric handleMergeWriterError(errorCodeToError(EC), Input.Filename,
1491349cc55cSDimitry Andric FContext.toString());
14920b57cec5SDimitry Andric }
14930b57cec5SDimitry Andric }
14948bcb0991SDimitry Andric
1495bdd1243dSDimitry Andric if (!DropProfileSymbolList) {
14968bcb0991SDimitry Andric std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
14978bcb0991SDimitry Andric Reader->getProfileSymbolList();
14988bcb0991SDimitry Andric if (ReaderList)
14998bcb0991SDimitry Andric WriterList.merge(*ReaderList);
15000b57cec5SDimitry Andric }
1501bdd1243dSDimitry Andric }
1502fe6060f1SDimitry Andric
150381ad6265SDimitry Andric if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
1504fe6060f1SDimitry Andric // Use threshold calculated from profile summary unless specified.
1505fe6060f1SDimitry Andric SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
1506fe6060f1SDimitry Andric auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
1507fe6060f1SDimitry Andric uint64_t SampleProfColdThreshold =
1508fe6060f1SDimitry Andric ProfileSummaryBuilder::getColdCountThreshold(
1509fe6060f1SDimitry Andric (Summary->getDetailedSummary()));
1510fe6060f1SDimitry Andric
1511fe6060f1SDimitry Andric // Trim and merge cold context profile using cold threshold above;
1512fe6060f1SDimitry Andric SampleContextTrimmer(ProfileMap)
1513fe6060f1SDimitry Andric .trimAndMergeColdContextProfiles(
1514fe6060f1SDimitry Andric SampleProfColdThreshold, SampleTrimColdContext,
1515349cc55cSDimitry Andric SampleMergeColdContext, SampleColdContextFrameDepth, false);
1516fe6060f1SDimitry Andric }
1517fe6060f1SDimitry Andric
151806c3fb27SDimitry Andric if (ProfileLayout == llvm::sampleprof::SPL_Flat) {
151906c3fb27SDimitry Andric ProfileConverter::flattenProfile(ProfileMap, FunctionSamples::ProfileIsCS);
152006c3fb27SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS = false;
152106c3fb27SDimitry Andric } else if (ProfileIsCS && ProfileLayout == llvm::sampleprof::SPL_Nest) {
152206c3fb27SDimitry Andric ProfileConverter CSConverter(ProfileMap);
152306c3fb27SDimitry Andric CSConverter.convertCSProfiles();
152481ad6265SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS = false;
15250eae32dcSDimitry Andric }
15260eae32dcSDimitry Andric
15277a6dacacSDimitry Andric filterFunctions(ProfileMap);
15287a6dacacSDimitry Andric
15290b57cec5SDimitry Andric auto WriterOrErr =
15300b57cec5SDimitry Andric SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
15310b57cec5SDimitry Andric if (std::error_code EC = WriterOrErr.getError())
15320b57cec5SDimitry Andric exitWithErrorCode(EC, OutputFilename);
15330b57cec5SDimitry Andric
15340b57cec5SDimitry Andric auto Writer = std::move(WriterOrErr.get());
15358bcb0991SDimitry Andric // WriterList will have StringRef refering to string in Buffer.
15368bcb0991SDimitry Andric // Make sure Buffer lives as long as WriterList.
15378bcb0991SDimitry Andric auto Buffer = getInputFileBuf(ProfileSymbolListFile);
15388bcb0991SDimitry Andric handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
15395ffd83dbSDimitry Andric CompressAllSections, UseMD5, GenPartialProfile);
154006c3fb27SDimitry Andric
154106c3fb27SDimitry Andric // If OutputSizeLimit is 0 (default), it is the same as write().
154206c3fb27SDimitry Andric if (std::error_code EC =
154306c3fb27SDimitry Andric Writer->writeWithSizeLimit(ProfileMap, OutputSizeLimit))
1544fe6060f1SDimitry Andric exitWithErrorCode(std::move(EC));
15450b57cec5SDimitry Andric }
15460b57cec5SDimitry Andric
parseWeightedFile(const StringRef & WeightedFilename)15470b57cec5SDimitry Andric static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
15480b57cec5SDimitry Andric StringRef WeightStr, FileName;
15490b57cec5SDimitry Andric std::tie(WeightStr, FileName) = WeightedFilename.split(',');
15500b57cec5SDimitry Andric
15510b57cec5SDimitry Andric uint64_t Weight;
15520b57cec5SDimitry Andric if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
1553fe6060f1SDimitry Andric exitWithError("input weight must be a positive integer");
15540b57cec5SDimitry Andric
15555ffd83dbSDimitry Andric return {std::string(FileName), Weight};
15560b57cec5SDimitry Andric }
15570b57cec5SDimitry Andric
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)15580b57cec5SDimitry Andric static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
15590b57cec5SDimitry Andric StringRef Filename = WF.Filename;
15600b57cec5SDimitry Andric uint64_t Weight = WF.Weight;
15610b57cec5SDimitry Andric
15620b57cec5SDimitry Andric // If it's STDIN just pass it on.
15630b57cec5SDimitry Andric if (Filename == "-") {
15645ffd83dbSDimitry Andric WNI.push_back({std::string(Filename), Weight});
15650b57cec5SDimitry Andric return;
15660b57cec5SDimitry Andric }
15670b57cec5SDimitry Andric
15680b57cec5SDimitry Andric llvm::sys::fs::file_status Status;
15690b57cec5SDimitry Andric llvm::sys::fs::status(Filename, Status);
15700b57cec5SDimitry Andric if (!llvm::sys::fs::exists(Status))
15710b57cec5SDimitry Andric exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
15720b57cec5SDimitry Andric Filename);
15730b57cec5SDimitry Andric // If it's a source file, collect it.
15740b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(Status)) {
15755ffd83dbSDimitry Andric WNI.push_back({std::string(Filename), Weight});
15760b57cec5SDimitry Andric return;
15770b57cec5SDimitry Andric }
15780b57cec5SDimitry Andric
15790b57cec5SDimitry Andric if (llvm::sys::fs::is_directory(Status)) {
15800b57cec5SDimitry Andric std::error_code EC;
15810b57cec5SDimitry Andric for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
15820b57cec5SDimitry Andric F != E && !EC; F.increment(EC)) {
15830b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(F->path())) {
15840b57cec5SDimitry Andric addWeightedInput(WNI, {F->path(), Weight});
15850b57cec5SDimitry Andric }
15860b57cec5SDimitry Andric }
15870b57cec5SDimitry Andric if (EC)
15880b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
15890b57cec5SDimitry Andric }
15900b57cec5SDimitry Andric }
15910b57cec5SDimitry Andric
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)15920b57cec5SDimitry Andric static void parseInputFilenamesFile(MemoryBuffer *Buffer,
15930b57cec5SDimitry Andric WeightedFileVector &WFV) {
15940b57cec5SDimitry Andric if (!Buffer)
15950b57cec5SDimitry Andric return;
15960b57cec5SDimitry Andric
15970b57cec5SDimitry Andric SmallVector<StringRef, 8> Entries;
15980b57cec5SDimitry Andric StringRef Data = Buffer->getBuffer();
15990b57cec5SDimitry Andric Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
16000b57cec5SDimitry Andric for (const StringRef &FileWeightEntry : Entries) {
16010b57cec5SDimitry Andric StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
16020b57cec5SDimitry Andric // Skip comments.
16035f757f3fSDimitry Andric if (SanitizedEntry.starts_with("#"))
16040b57cec5SDimitry Andric continue;
16050b57cec5SDimitry Andric // If there's no comma, it's an unweighted profile.
1606349cc55cSDimitry Andric else if (!SanitizedEntry.contains(','))
16075ffd83dbSDimitry Andric addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
16080b57cec5SDimitry Andric else
16090b57cec5SDimitry Andric addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
16100b57cec5SDimitry Andric }
16110b57cec5SDimitry Andric }
16120b57cec5SDimitry Andric
merge_main(int argc,const char * argv[])16130b57cec5SDimitry Andric static int merge_main(int argc, const char *argv[]) {
16140b57cec5SDimitry Andric WeightedFileVector WeightedInputs;
16150b57cec5SDimitry Andric for (StringRef Filename : InputFilenames)
16165ffd83dbSDimitry Andric addWeightedInput(WeightedInputs, {std::string(Filename), 1});
16170b57cec5SDimitry Andric for (StringRef WeightedFilename : WeightedInputFilenames)
16180b57cec5SDimitry Andric addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
16190b57cec5SDimitry Andric
16200b57cec5SDimitry Andric // Make sure that the file buffer stays alive for the duration of the
16210b57cec5SDimitry Andric // weighted input vector's lifetime.
16228bcb0991SDimitry Andric auto Buffer = getInputFileBuf(InputFilenamesFile);
16230b57cec5SDimitry Andric parseInputFilenamesFile(Buffer.get(), WeightedInputs);
16240b57cec5SDimitry Andric
16250b57cec5SDimitry Andric if (WeightedInputs.empty())
1626fe6060f1SDimitry Andric exitWithError("no input files specified. See " +
16275f757f3fSDimitry Andric sys::path::filename(argv[0]) + " " + argv[1] + " -help");
16280b57cec5SDimitry Andric
16290b57cec5SDimitry Andric if (DumpInputFileList) {
16300b57cec5SDimitry Andric for (auto &WF : WeightedInputs)
16310b57cec5SDimitry Andric outs() << WF.Weight << "," << WF.Filename << "\n";
16320b57cec5SDimitry Andric return 0;
16330b57cec5SDimitry Andric }
16340b57cec5SDimitry Andric
16350b57cec5SDimitry Andric std::unique_ptr<SymbolRemapper> Remapper;
16360b57cec5SDimitry Andric if (!RemappingFile.empty())
16370b57cec5SDimitry Andric Remapper = SymbolRemapper::create(RemappingFile);
16380b57cec5SDimitry Andric
1639e8d8bef9SDimitry Andric if (!SupplInstrWithSample.empty()) {
1640e8d8bef9SDimitry Andric if (ProfileKind != instr)
1641e8d8bef9SDimitry Andric exitWithError(
1642e8d8bef9SDimitry Andric "-supplement-instr-with-sample can only work with -instr. ");
1643e8d8bef9SDimitry Andric
16445f757f3fSDimitry Andric supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse,
16455f757f3fSDimitry Andric SupplMinSizeThreshold, ZeroCounterThreshold,
16465f757f3fSDimitry Andric InstrProfColdThreshold);
1647e8d8bef9SDimitry Andric return 0;
1648e8d8bef9SDimitry Andric }
1649e8d8bef9SDimitry Andric
16500b57cec5SDimitry Andric if (ProfileKind == instr)
16515f757f3fSDimitry Andric mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings,
16525f757f3fSDimitry Andric ProfiledBinary);
16530b57cec5SDimitry Andric else
16545f757f3fSDimitry Andric mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile,
16555f757f3fSDimitry Andric OutputSizeLimit);
16560b57cec5SDimitry Andric return 0;
16570b57cec5SDimitry Andric }
16580b57cec5SDimitry Andric
16590b57cec5SDimitry Andric /// Computer the overlap b/w profile BaseFilename and profile TestFilename.
overlapInstrProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,raw_fd_ostream & OS,bool IsCS)16600b57cec5SDimitry Andric static void overlapInstrProfile(const std::string &BaseFilename,
16610b57cec5SDimitry Andric const std::string &TestFilename,
16620b57cec5SDimitry Andric const OverlapFuncFilters &FuncFilter,
16630b57cec5SDimitry Andric raw_fd_ostream &OS, bool IsCS) {
16640b57cec5SDimitry Andric std::mutex ErrorLock;
16650b57cec5SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
16660b57cec5SDimitry Andric WriterContext Context(false, ErrorLock, WriterErrorCodes);
16670b57cec5SDimitry Andric WeightedFile WeightedInput{BaseFilename, 1};
16680b57cec5SDimitry Andric OverlapStats Overlap;
16698bcb0991SDimitry Andric Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
16700b57cec5SDimitry Andric if (E)
1671fe6060f1SDimitry Andric exitWithError(std::move(E), "error in getting profile count sums");
16720b57cec5SDimitry Andric if (Overlap.Base.CountSum < 1.0f) {
16730b57cec5SDimitry Andric OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
16740b57cec5SDimitry Andric exit(0);
16750b57cec5SDimitry Andric }
16760b57cec5SDimitry Andric if (Overlap.Test.CountSum < 1.0f) {
16770b57cec5SDimitry Andric OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
16780b57cec5SDimitry Andric exit(0);
16790b57cec5SDimitry Andric }
168081ad6265SDimitry Andric loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
16810b57cec5SDimitry Andric overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
16820b57cec5SDimitry Andric IsCS);
16830b57cec5SDimitry Andric Overlap.dump(OS);
16840b57cec5SDimitry Andric }
16850b57cec5SDimitry Andric
1686e8d8bef9SDimitry Andric namespace {
1687e8d8bef9SDimitry Andric struct SampleOverlapStats {
1688349cc55cSDimitry Andric SampleContext BaseName;
1689349cc55cSDimitry Andric SampleContext TestName;
1690e8d8bef9SDimitry Andric // Number of overlap units
16915f757f3fSDimitry Andric uint64_t OverlapCount = 0;
1692e8d8bef9SDimitry Andric // Total samples of overlap units
16935f757f3fSDimitry Andric uint64_t OverlapSample = 0;
1694e8d8bef9SDimitry Andric // Number of and total samples of units that only present in base or test
1695e8d8bef9SDimitry Andric // profile
16965f757f3fSDimitry Andric uint64_t BaseUniqueCount = 0;
16975f757f3fSDimitry Andric uint64_t BaseUniqueSample = 0;
16985f757f3fSDimitry Andric uint64_t TestUniqueCount = 0;
16995f757f3fSDimitry Andric uint64_t TestUniqueSample = 0;
1700e8d8bef9SDimitry Andric // Number of units and total samples in base or test profile
17015f757f3fSDimitry Andric uint64_t BaseCount = 0;
17025f757f3fSDimitry Andric uint64_t BaseSample = 0;
17035f757f3fSDimitry Andric uint64_t TestCount = 0;
17045f757f3fSDimitry Andric uint64_t TestSample = 0;
1705e8d8bef9SDimitry Andric // Number of and total samples of units that present in at least one profile
17065f757f3fSDimitry Andric uint64_t UnionCount = 0;
17075f757f3fSDimitry Andric uint64_t UnionSample = 0;
1708e8d8bef9SDimitry Andric // Weighted similarity
17095f757f3fSDimitry Andric double Similarity = 0.0;
1710e8d8bef9SDimitry Andric // For SampleOverlapStats instances representing functions, weights of the
1711e8d8bef9SDimitry Andric // function in base and test profiles
17125f757f3fSDimitry Andric double BaseWeight = 0.0;
17135f757f3fSDimitry Andric double TestWeight = 0.0;
1714e8d8bef9SDimitry Andric
17155f757f3fSDimitry Andric SampleOverlapStats() = default;
1716e8d8bef9SDimitry Andric };
1717e8d8bef9SDimitry Andric } // end anonymous namespace
1718e8d8bef9SDimitry Andric
1719e8d8bef9SDimitry Andric namespace {
1720e8d8bef9SDimitry Andric struct FuncSampleStats {
1721cb14a3feSDimitry Andric uint64_t SampleSum = 0;
1722cb14a3feSDimitry Andric uint64_t MaxSample = 0;
1723cb14a3feSDimitry Andric uint64_t HotBlockCount = 0;
1724cb14a3feSDimitry Andric FuncSampleStats() = default;
FuncSampleStats__anonae06d1131011::FuncSampleStats1725e8d8bef9SDimitry Andric FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1726e8d8bef9SDimitry Andric uint64_t HotBlockCount)
1727e8d8bef9SDimitry Andric : SampleSum(SampleSum), MaxSample(MaxSample),
1728e8d8bef9SDimitry Andric HotBlockCount(HotBlockCount) {}
1729e8d8bef9SDimitry Andric };
1730e8d8bef9SDimitry Andric } // end anonymous namespace
1731e8d8bef9SDimitry Andric
1732e8d8bef9SDimitry Andric namespace {
1733e8d8bef9SDimitry Andric enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1734e8d8bef9SDimitry Andric
1735e8d8bef9SDimitry Andric // Class for updating merging steps for two sorted maps. The class should be
1736e8d8bef9SDimitry Andric // instantiated with a map iterator type.
1737e8d8bef9SDimitry Andric template <class T> class MatchStep {
1738e8d8bef9SDimitry Andric public:
1739e8d8bef9SDimitry Andric MatchStep() = delete;
1740e8d8bef9SDimitry Andric
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1741e8d8bef9SDimitry Andric MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1742e8d8bef9SDimitry Andric : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1743e8d8bef9SDimitry Andric SecondEnd(SecondEnd), Status(MS_None) {}
1744e8d8bef9SDimitry Andric
areBothFinished() const1745e8d8bef9SDimitry Andric bool areBothFinished() const {
1746e8d8bef9SDimitry Andric return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1747e8d8bef9SDimitry Andric }
1748e8d8bef9SDimitry Andric
isFirstFinished() const1749e8d8bef9SDimitry Andric bool isFirstFinished() const { return FirstIter == FirstEnd; }
1750e8d8bef9SDimitry Andric
isSecondFinished() const1751e8d8bef9SDimitry Andric bool isSecondFinished() const { return SecondIter == SecondEnd; }
1752e8d8bef9SDimitry Andric
1753e8d8bef9SDimitry Andric /// Advance one step based on the previous match status unless the previous
1754e8d8bef9SDimitry Andric /// status is MS_None. Then update Status based on the comparison between two
1755e8d8bef9SDimitry Andric /// container iterators at the current step. If the previous status is
1756e8d8bef9SDimitry Andric /// MS_None, it means two iterators are at the beginning and no comparison has
1757e8d8bef9SDimitry Andric /// been made, so we simply update Status without advancing the iterators.
1758e8d8bef9SDimitry Andric void updateOneStep();
1759e8d8bef9SDimitry Andric
getFirstIter() const1760e8d8bef9SDimitry Andric T getFirstIter() const { return FirstIter; }
1761e8d8bef9SDimitry Andric
getSecondIter() const1762e8d8bef9SDimitry Andric T getSecondIter() const { return SecondIter; }
1763e8d8bef9SDimitry Andric
getMatchStatus() const1764e8d8bef9SDimitry Andric MatchStatus getMatchStatus() const { return Status; }
1765e8d8bef9SDimitry Andric
1766e8d8bef9SDimitry Andric private:
1767e8d8bef9SDimitry Andric // Current iterator and end iterator of the first container.
1768e8d8bef9SDimitry Andric T FirstIter;
1769e8d8bef9SDimitry Andric T FirstEnd;
1770e8d8bef9SDimitry Andric // Current iterator and end iterator of the second container.
1771e8d8bef9SDimitry Andric T SecondIter;
1772e8d8bef9SDimitry Andric T SecondEnd;
1773e8d8bef9SDimitry Andric // Match status of the current step.
1774e8d8bef9SDimitry Andric MatchStatus Status;
1775e8d8bef9SDimitry Andric };
1776e8d8bef9SDimitry Andric } // end anonymous namespace
1777e8d8bef9SDimitry Andric
updateOneStep()1778e8d8bef9SDimitry Andric template <class T> void MatchStep<T>::updateOneStep() {
1779e8d8bef9SDimitry Andric switch (Status) {
1780e8d8bef9SDimitry Andric case MS_Match:
1781e8d8bef9SDimitry Andric ++FirstIter;
1782e8d8bef9SDimitry Andric ++SecondIter;
1783e8d8bef9SDimitry Andric break;
1784e8d8bef9SDimitry Andric case MS_FirstUnique:
1785e8d8bef9SDimitry Andric ++FirstIter;
1786e8d8bef9SDimitry Andric break;
1787e8d8bef9SDimitry Andric case MS_SecondUnique:
1788e8d8bef9SDimitry Andric ++SecondIter;
1789e8d8bef9SDimitry Andric break;
1790e8d8bef9SDimitry Andric case MS_None:
1791e8d8bef9SDimitry Andric break;
1792e8d8bef9SDimitry Andric }
1793e8d8bef9SDimitry Andric
1794e8d8bef9SDimitry Andric // Update Status according to iterators at the current step.
1795e8d8bef9SDimitry Andric if (areBothFinished())
1796e8d8bef9SDimitry Andric return;
1797e8d8bef9SDimitry Andric if (FirstIter != FirstEnd &&
1798e8d8bef9SDimitry Andric (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1799e8d8bef9SDimitry Andric Status = MS_FirstUnique;
1800e8d8bef9SDimitry Andric else if (SecondIter != SecondEnd &&
1801e8d8bef9SDimitry Andric (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1802e8d8bef9SDimitry Andric Status = MS_SecondUnique;
1803e8d8bef9SDimitry Andric else
1804e8d8bef9SDimitry Andric Status = MS_Match;
1805e8d8bef9SDimitry Andric }
1806e8d8bef9SDimitry Andric
1807e8d8bef9SDimitry Andric // Return the sum of line/block samples, the max line/block sample, and the
1808e8d8bef9SDimitry Andric // number of line/block samples above the given threshold in a function
1809e8d8bef9SDimitry Andric // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1810e8d8bef9SDimitry Andric static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1811e8d8bef9SDimitry Andric FuncSampleStats &FuncStats,
1812e8d8bef9SDimitry Andric uint64_t HotThreshold) {
1813e8d8bef9SDimitry Andric for (const auto &L : Func.getBodySamples()) {
1814e8d8bef9SDimitry Andric uint64_t Sample = L.second.getSamples();
1815e8d8bef9SDimitry Andric FuncStats.SampleSum += Sample;
1816e8d8bef9SDimitry Andric FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1817e8d8bef9SDimitry Andric if (Sample >= HotThreshold)
1818e8d8bef9SDimitry Andric ++FuncStats.HotBlockCount;
1819e8d8bef9SDimitry Andric }
1820e8d8bef9SDimitry Andric
1821e8d8bef9SDimitry Andric for (const auto &C : Func.getCallsiteSamples()) {
1822e8d8bef9SDimitry Andric for (const auto &F : C.second)
1823e8d8bef9SDimitry Andric getFuncSampleStats(F.second, FuncStats, HotThreshold);
1824e8d8bef9SDimitry Andric }
1825e8d8bef9SDimitry Andric }
1826e8d8bef9SDimitry Andric
1827e8d8bef9SDimitry Andric /// Predicate that determines if a function is hot with a given threshold. We
1828e8d8bef9SDimitry Andric /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1829e8d8bef9SDimitry Andric static bool isFunctionHot(const FuncSampleStats &FuncStats,
1830e8d8bef9SDimitry Andric uint64_t HotThreshold) {
1831e8d8bef9SDimitry Andric // We intentionally compare the maximum sample count in a function with the
1832e8d8bef9SDimitry Andric // HotThreshold to get an approximate determination on hot functions.
1833e8d8bef9SDimitry Andric return (FuncStats.MaxSample >= HotThreshold);
1834e8d8bef9SDimitry Andric }
1835e8d8bef9SDimitry Andric
1836e8d8bef9SDimitry Andric namespace {
1837e8d8bef9SDimitry Andric class SampleOverlapAggregator {
1838e8d8bef9SDimitry Andric public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1839e8d8bef9SDimitry Andric SampleOverlapAggregator(const std::string &BaseFilename,
1840e8d8bef9SDimitry Andric const std::string &TestFilename,
1841e8d8bef9SDimitry Andric double LowSimilarityThreshold, double Epsilon,
1842e8d8bef9SDimitry Andric const OverlapFuncFilters &FuncFilter)
1843e8d8bef9SDimitry Andric : BaseFilename(BaseFilename), TestFilename(TestFilename),
1844e8d8bef9SDimitry Andric LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1845e8d8bef9SDimitry Andric FuncFilter(FuncFilter) {}
1846e8d8bef9SDimitry Andric
1847e8d8bef9SDimitry Andric /// Detect 0-sample input profile and report to output stream. This interface
1848e8d8bef9SDimitry Andric /// should be called after loadProfiles().
1849e8d8bef9SDimitry Andric bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1850e8d8bef9SDimitry Andric
1851e8d8bef9SDimitry Andric /// Write out function-level similarity statistics for functions specified by
1852e8d8bef9SDimitry Andric /// options --function, --value-cutoff, and --similarity-cutoff.
1853e8d8bef9SDimitry Andric void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1854e8d8bef9SDimitry Andric
1855e8d8bef9SDimitry Andric /// Write out program-level similarity and overlap statistics.
1856e8d8bef9SDimitry Andric void dumpProgramSummary(raw_fd_ostream &OS) const;
1857e8d8bef9SDimitry Andric
1858e8d8bef9SDimitry Andric /// Write out hot-function and hot-block statistics for base_profile,
1859e8d8bef9SDimitry Andric /// test_profile, and their overlap. For both cases, the overlap HO is
1860e8d8bef9SDimitry Andric /// calculated as follows:
1861e8d8bef9SDimitry Andric /// Given the number of functions (or blocks) that are hot in both profiles
1862e8d8bef9SDimitry Andric /// HCommon and the number of functions (or blocks) that are hot in at
1863e8d8bef9SDimitry Andric /// least one profile HUnion, HO = HCommon / HUnion.
1864e8d8bef9SDimitry Andric void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1865e8d8bef9SDimitry Andric
1866e8d8bef9SDimitry Andric /// This function tries matching functions in base and test profiles. For each
1867e8d8bef9SDimitry Andric /// pair of matched functions, it aggregates the function-level
1868e8d8bef9SDimitry Andric /// similarity into a profile-level similarity. It also dump function-level
1869e8d8bef9SDimitry Andric /// similarity information of functions specified by --function,
1870e8d8bef9SDimitry Andric /// --value-cutoff, and --similarity-cutoff options. The program-level
1871e8d8bef9SDimitry Andric /// similarity PS is computed as follows:
1872e8d8bef9SDimitry Andric /// Given function-level similarity FS(A) for all function A, the
1873e8d8bef9SDimitry Andric /// weight of function A in base profile WB(A), and the weight of function
1874e8d8bef9SDimitry Andric /// A in test profile WT(A), compute PS(base_profile, test_profile) =
1875e8d8bef9SDimitry Andric /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1876e8d8bef9SDimitry Andric /// meaning no-overlap.
1877e8d8bef9SDimitry Andric void computeSampleProfileOverlap(raw_fd_ostream &OS);
1878e8d8bef9SDimitry Andric
1879e8d8bef9SDimitry Andric /// Initialize ProfOverlap with the sum of samples in base and test
1880e8d8bef9SDimitry Andric /// profiles. This function also computes and keeps the sum of samples and
1881e8d8bef9SDimitry Andric /// max sample counts of each function in BaseStats and TestStats for later
1882e8d8bef9SDimitry Andric /// use to avoid re-computations.
1883e8d8bef9SDimitry Andric void initializeSampleProfileOverlap();
1884e8d8bef9SDimitry Andric
1885e8d8bef9SDimitry Andric /// Load profiles specified by BaseFilename and TestFilename.
1886e8d8bef9SDimitry Andric std::error_code loadProfiles();
1887e8d8bef9SDimitry Andric
1888349cc55cSDimitry Andric using FuncSampleStatsMap =
1889349cc55cSDimitry Andric std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1890349cc55cSDimitry Andric
1891e8d8bef9SDimitry Andric private:
1892e8d8bef9SDimitry Andric SampleOverlapStats ProfOverlap;
1893e8d8bef9SDimitry Andric SampleOverlapStats HotFuncOverlap;
1894e8d8bef9SDimitry Andric SampleOverlapStats HotBlockOverlap;
1895e8d8bef9SDimitry Andric std::string BaseFilename;
1896e8d8bef9SDimitry Andric std::string TestFilename;
1897e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1898e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1899e8d8bef9SDimitry Andric // BaseStats and TestStats hold FuncSampleStats for each function, with
1900e8d8bef9SDimitry Andric // function name as the key.
1901349cc55cSDimitry Andric FuncSampleStatsMap BaseStats;
1902349cc55cSDimitry Andric FuncSampleStatsMap TestStats;
1903e8d8bef9SDimitry Andric // Low similarity threshold in floating point number
1904e8d8bef9SDimitry Andric double LowSimilarityThreshold;
1905e8d8bef9SDimitry Andric // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1906e8d8bef9SDimitry Andric // for tracking hot blocks.
1907e8d8bef9SDimitry Andric uint64_t BaseHotThreshold;
1908e8d8bef9SDimitry Andric uint64_t TestHotThreshold;
1909e8d8bef9SDimitry Andric // A small threshold used to round the results of floating point accumulations
1910e8d8bef9SDimitry Andric // to resolve imprecision.
1911e8d8bef9SDimitry Andric const double Epsilon;
1912e8d8bef9SDimitry Andric std::multimap<double, SampleOverlapStats, std::greater<double>>
1913e8d8bef9SDimitry Andric FuncSimilarityDump;
1914e8d8bef9SDimitry Andric // FuncFilter carries specifications in options --value-cutoff and
1915e8d8bef9SDimitry Andric // --function.
1916e8d8bef9SDimitry Andric OverlapFuncFilters FuncFilter;
1917e8d8bef9SDimitry Andric // Column offsets for printing the function-level details table.
1918e8d8bef9SDimitry Andric static const unsigned int TestWeightCol = 15;
1919e8d8bef9SDimitry Andric static const unsigned int SimilarityCol = 30;
1920e8d8bef9SDimitry Andric static const unsigned int OverlapCol = 43;
1921e8d8bef9SDimitry Andric static const unsigned int BaseUniqueCol = 53;
1922e8d8bef9SDimitry Andric static const unsigned int TestUniqueCol = 67;
1923e8d8bef9SDimitry Andric static const unsigned int BaseSampleCol = 81;
1924e8d8bef9SDimitry Andric static const unsigned int TestSampleCol = 96;
1925e8d8bef9SDimitry Andric static const unsigned int FuncNameCol = 111;
1926e8d8bef9SDimitry Andric
1927e8d8bef9SDimitry Andric /// Return a similarity of two line/block sample counters in the same
1928e8d8bef9SDimitry Andric /// function in base and test profiles. The line/block-similarity BS(i) is
1929e8d8bef9SDimitry Andric /// computed as follows:
1930e8d8bef9SDimitry Andric /// For an offsets i, given the sample count at i in base profile BB(i),
1931e8d8bef9SDimitry Andric /// the sample count at i in test profile BT(i), the sum of sample counts
1932e8d8bef9SDimitry Andric /// in this function in base profile SB, and the sum of sample counts in
1933e8d8bef9SDimitry Andric /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1934e8d8bef9SDimitry Andric /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1935e8d8bef9SDimitry Andric double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1936e8d8bef9SDimitry Andric const SampleOverlapStats &FuncOverlap) const;
1937e8d8bef9SDimitry Andric
1938e8d8bef9SDimitry Andric void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1939e8d8bef9SDimitry Andric uint64_t HotBlockCount);
1940e8d8bef9SDimitry Andric
1941349cc55cSDimitry Andric void getHotFunctions(const FuncSampleStatsMap &ProfStats,
1942349cc55cSDimitry Andric FuncSampleStatsMap &HotFunc,
1943e8d8bef9SDimitry Andric uint64_t HotThreshold) const;
1944e8d8bef9SDimitry Andric
1945e8d8bef9SDimitry Andric void computeHotFuncOverlap();
1946e8d8bef9SDimitry Andric
1947e8d8bef9SDimitry Andric /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1948e8d8bef9SDimitry Andric /// Difference for two sample units in a matched function according to the
1949e8d8bef9SDimitry Andric /// given match status.
1950e8d8bef9SDimitry Andric void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
1951e8d8bef9SDimitry Andric uint64_t HotBlockCount,
1952e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap,
1953e8d8bef9SDimitry Andric double &Difference, MatchStatus Status);
1954e8d8bef9SDimitry Andric
1955e8d8bef9SDimitry Andric /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
1956e8d8bef9SDimitry Andric /// Difference for unmatched callees that only present in one profile in a
1957e8d8bef9SDimitry Andric /// matched caller function.
1958e8d8bef9SDimitry Andric void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
1959e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap,
1960e8d8bef9SDimitry Andric double &Difference, MatchStatus Status);
1961e8d8bef9SDimitry Andric
1962e8d8bef9SDimitry Andric /// This function updates sample overlap statistics of an overlap function in
1963e8d8bef9SDimitry Andric /// base and test profile. It also calculates a function-internal similarity
1964e8d8bef9SDimitry Andric /// FIS as follows:
1965e8d8bef9SDimitry Andric /// For offsets i that have samples in at least one profile in this
1966e8d8bef9SDimitry Andric /// function A, given BS(i) returned by computeBlockSimilarity(), compute
1967e8d8bef9SDimitry Andric /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
1968e8d8bef9SDimitry Andric /// 0.0 meaning no overlap.
1969e8d8bef9SDimitry Andric double computeSampleFunctionInternalOverlap(
1970e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &BaseFunc,
1971e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &TestFunc,
1972e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap);
1973e8d8bef9SDimitry Andric
1974e8d8bef9SDimitry Andric /// Function-level similarity (FS) is a weighted value over function internal
1975e8d8bef9SDimitry Andric /// similarity (FIS). This function computes a function's FS from its FIS by
1976e8d8bef9SDimitry Andric /// applying the weight.
1977e8d8bef9SDimitry Andric double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
1978e8d8bef9SDimitry Andric uint64_t TestFuncSample) const;
1979e8d8bef9SDimitry Andric
1980e8d8bef9SDimitry Andric /// The function-level similarity FS(A) for a function A is computed as
1981e8d8bef9SDimitry Andric /// follows:
1982e8d8bef9SDimitry Andric /// Compute a function-internal similarity FIS(A) by
1983e8d8bef9SDimitry Andric /// computeSampleFunctionInternalOverlap(). Then, with the weight of
1984e8d8bef9SDimitry Andric /// function A in base profile WB(A), and the weight of function A in test
1985e8d8bef9SDimitry Andric /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
1986e8d8bef9SDimitry Andric /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
1987e8d8bef9SDimitry Andric double
1988e8d8bef9SDimitry Andric computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
1989e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *TestFunc,
1990e8d8bef9SDimitry Andric SampleOverlapStats *FuncOverlap,
1991e8d8bef9SDimitry Andric uint64_t BaseFuncSample,
1992e8d8bef9SDimitry Andric uint64_t TestFuncSample);
1993e8d8bef9SDimitry Andric
1994e8d8bef9SDimitry Andric /// Profile-level similarity (PS) is a weighted aggregate over function-level
1995e8d8bef9SDimitry Andric /// similarities (FS). This method weights the FS value by the function
1996e8d8bef9SDimitry Andric /// weights in the base and test profiles for the aggregation.
1997e8d8bef9SDimitry Andric double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
1998e8d8bef9SDimitry Andric uint64_t TestFuncSample) const;
1999e8d8bef9SDimitry Andric };
2000e8d8bef9SDimitry Andric } // end anonymous namespace
2001e8d8bef9SDimitry Andric
detectZeroSampleProfile(raw_fd_ostream & OS) const2002e8d8bef9SDimitry Andric bool SampleOverlapAggregator::detectZeroSampleProfile(
2003e8d8bef9SDimitry Andric raw_fd_ostream &OS) const {
2004e8d8bef9SDimitry Andric bool HaveZeroSample = false;
2005e8d8bef9SDimitry Andric if (ProfOverlap.BaseSample == 0) {
2006e8d8bef9SDimitry Andric OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
2007e8d8bef9SDimitry Andric HaveZeroSample = true;
2008e8d8bef9SDimitry Andric }
2009e8d8bef9SDimitry Andric if (ProfOverlap.TestSample == 0) {
2010e8d8bef9SDimitry Andric OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
2011e8d8bef9SDimitry Andric HaveZeroSample = true;
2012e8d8bef9SDimitry Andric }
2013e8d8bef9SDimitry Andric return HaveZeroSample;
2014e8d8bef9SDimitry Andric }
2015e8d8bef9SDimitry Andric
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const2016e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeBlockSimilarity(
2017e8d8bef9SDimitry Andric uint64_t BaseSample, uint64_t TestSample,
2018e8d8bef9SDimitry Andric const SampleOverlapStats &FuncOverlap) const {
2019e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2020e8d8bef9SDimitry Andric double TestFrac = 0.0;
2021e8d8bef9SDimitry Andric if (FuncOverlap.BaseSample > 0)
2022e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
2023e8d8bef9SDimitry Andric if (FuncOverlap.TestSample > 0)
2024e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
2025e8d8bef9SDimitry Andric return 1.0 - std::fabs(BaseFrac - TestFrac);
2026e8d8bef9SDimitry Andric }
2027e8d8bef9SDimitry Andric
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)2028e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
2029e8d8bef9SDimitry Andric uint64_t TestSample,
2030e8d8bef9SDimitry Andric uint64_t HotBlockCount) {
2031e8d8bef9SDimitry Andric bool IsBaseHot = (BaseSample >= BaseHotThreshold);
2032e8d8bef9SDimitry Andric bool IsTestHot = (TestSample >= TestHotThreshold);
2033e8d8bef9SDimitry Andric if (!IsBaseHot && !IsTestHot)
2034e8d8bef9SDimitry Andric return;
2035e8d8bef9SDimitry Andric
2036e8d8bef9SDimitry Andric HotBlockOverlap.UnionCount += HotBlockCount;
2037e8d8bef9SDimitry Andric if (IsBaseHot)
2038e8d8bef9SDimitry Andric HotBlockOverlap.BaseCount += HotBlockCount;
2039e8d8bef9SDimitry Andric if (IsTestHot)
2040e8d8bef9SDimitry Andric HotBlockOverlap.TestCount += HotBlockCount;
2041e8d8bef9SDimitry Andric if (IsBaseHot && IsTestHot)
2042e8d8bef9SDimitry Andric HotBlockOverlap.OverlapCount += HotBlockCount;
2043e8d8bef9SDimitry Andric }
2044e8d8bef9SDimitry Andric
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const2045e8d8bef9SDimitry Andric void SampleOverlapAggregator::getHotFunctions(
2046349cc55cSDimitry Andric const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
2047349cc55cSDimitry Andric uint64_t HotThreshold) const {
2048e8d8bef9SDimitry Andric for (const auto &F : ProfStats) {
2049e8d8bef9SDimitry Andric if (isFunctionHot(F.second, HotThreshold))
2050349cc55cSDimitry Andric HotFunc.emplace(F.first, F.second);
2051e8d8bef9SDimitry Andric }
2052e8d8bef9SDimitry Andric }
2053e8d8bef9SDimitry Andric
computeHotFuncOverlap()2054e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeHotFuncOverlap() {
2055349cc55cSDimitry Andric FuncSampleStatsMap BaseHotFunc;
2056e8d8bef9SDimitry Andric getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
2057e8d8bef9SDimitry Andric HotFuncOverlap.BaseCount = BaseHotFunc.size();
2058e8d8bef9SDimitry Andric
2059349cc55cSDimitry Andric FuncSampleStatsMap TestHotFunc;
2060e8d8bef9SDimitry Andric getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
2061e8d8bef9SDimitry Andric HotFuncOverlap.TestCount = TestHotFunc.size();
2062e8d8bef9SDimitry Andric HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
2063e8d8bef9SDimitry Andric
2064e8d8bef9SDimitry Andric for (const auto &F : BaseHotFunc) {
2065349cc55cSDimitry Andric if (TestHotFunc.count(F.first))
2066e8d8bef9SDimitry Andric ++HotFuncOverlap.OverlapCount;
2067e8d8bef9SDimitry Andric else
2068e8d8bef9SDimitry Andric ++HotFuncOverlap.UnionCount;
2069e8d8bef9SDimitry Andric }
2070e8d8bef9SDimitry Andric }
2071e8d8bef9SDimitry Andric
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2072e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateOverlapStatsForFunction(
2073e8d8bef9SDimitry Andric uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
2074e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
2075e8d8bef9SDimitry Andric assert(Status != MS_None &&
2076e8d8bef9SDimitry Andric "Match status should be updated before updating overlap statistics");
2077e8d8bef9SDimitry Andric if (Status == MS_FirstUnique) {
2078e8d8bef9SDimitry Andric TestSample = 0;
2079e8d8bef9SDimitry Andric FuncOverlap.BaseUniqueSample += BaseSample;
2080e8d8bef9SDimitry Andric } else if (Status == MS_SecondUnique) {
2081e8d8bef9SDimitry Andric BaseSample = 0;
2082e8d8bef9SDimitry Andric FuncOverlap.TestUniqueSample += TestSample;
2083e8d8bef9SDimitry Andric } else {
2084e8d8bef9SDimitry Andric ++FuncOverlap.OverlapCount;
2085e8d8bef9SDimitry Andric }
2086e8d8bef9SDimitry Andric
2087e8d8bef9SDimitry Andric FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
2088e8d8bef9SDimitry Andric FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
2089e8d8bef9SDimitry Andric Difference +=
2090e8d8bef9SDimitry Andric 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
2091e8d8bef9SDimitry Andric updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
2092e8d8bef9SDimitry Andric }
2093e8d8bef9SDimitry Andric
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2094e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateForUnmatchedCallee(
2095e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
2096e8d8bef9SDimitry Andric double &Difference, MatchStatus Status) {
2097e8d8bef9SDimitry Andric assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
2098e8d8bef9SDimitry Andric "Status must be either of the two unmatched cases");
2099e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2100e8d8bef9SDimitry Andric if (Status == MS_FirstUnique) {
2101e8d8bef9SDimitry Andric getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
2102e8d8bef9SDimitry Andric updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
2103e8d8bef9SDimitry Andric FuncStats.HotBlockCount, FuncOverlap,
2104e8d8bef9SDimitry Andric Difference, Status);
2105e8d8bef9SDimitry Andric } else {
2106e8d8bef9SDimitry Andric getFuncSampleStats(Func, FuncStats, TestHotThreshold);
2107e8d8bef9SDimitry Andric updateOverlapStatsForFunction(0, FuncStats.SampleSum,
2108e8d8bef9SDimitry Andric FuncStats.HotBlockCount, FuncOverlap,
2109e8d8bef9SDimitry Andric Difference, Status);
2110e8d8bef9SDimitry Andric }
2111e8d8bef9SDimitry Andric }
2112e8d8bef9SDimitry Andric
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)2113e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
2114e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &BaseFunc,
2115e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &TestFunc,
2116e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap) {
2117e8d8bef9SDimitry Andric
2118e8d8bef9SDimitry Andric using namespace sampleprof;
2119e8d8bef9SDimitry Andric
2120e8d8bef9SDimitry Andric double Difference = 0;
2121e8d8bef9SDimitry Andric
2122e8d8bef9SDimitry Andric // Accumulate Difference for regular line/block samples in the function.
2123e8d8bef9SDimitry Andric // We match them through sort-merge join algorithm because
2124e8d8bef9SDimitry Andric // FunctionSamples::getBodySamples() returns a map of sample counters ordered
2125e8d8bef9SDimitry Andric // by their offsets.
2126e8d8bef9SDimitry Andric MatchStep<BodySampleMap::const_iterator> BlockIterStep(
2127e8d8bef9SDimitry Andric BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
2128e8d8bef9SDimitry Andric TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
2129e8d8bef9SDimitry Andric BlockIterStep.updateOneStep();
2130e8d8bef9SDimitry Andric while (!BlockIterStep.areBothFinished()) {
2131e8d8bef9SDimitry Andric uint64_t BaseSample =
2132e8d8bef9SDimitry Andric BlockIterStep.isFirstFinished()
2133e8d8bef9SDimitry Andric ? 0
2134e8d8bef9SDimitry Andric : BlockIterStep.getFirstIter()->second.getSamples();
2135e8d8bef9SDimitry Andric uint64_t TestSample =
2136e8d8bef9SDimitry Andric BlockIterStep.isSecondFinished()
2137e8d8bef9SDimitry Andric ? 0
2138e8d8bef9SDimitry Andric : BlockIterStep.getSecondIter()->second.getSamples();
2139e8d8bef9SDimitry Andric updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
2140e8d8bef9SDimitry Andric Difference, BlockIterStep.getMatchStatus());
2141e8d8bef9SDimitry Andric
2142e8d8bef9SDimitry Andric BlockIterStep.updateOneStep();
2143e8d8bef9SDimitry Andric }
2144e8d8bef9SDimitry Andric
2145e8d8bef9SDimitry Andric // Accumulate Difference for callsite lines in the function. We match
2146e8d8bef9SDimitry Andric // them through sort-merge algorithm because
2147e8d8bef9SDimitry Andric // FunctionSamples::getCallsiteSamples() returns a map of callsite records
2148e8d8bef9SDimitry Andric // ordered by their offsets.
2149e8d8bef9SDimitry Andric MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
2150e8d8bef9SDimitry Andric BaseFunc.getCallsiteSamples().cbegin(),
2151e8d8bef9SDimitry Andric BaseFunc.getCallsiteSamples().cend(),
2152e8d8bef9SDimitry Andric TestFunc.getCallsiteSamples().cbegin(),
2153e8d8bef9SDimitry Andric TestFunc.getCallsiteSamples().cend());
2154e8d8bef9SDimitry Andric CallsiteIterStep.updateOneStep();
2155e8d8bef9SDimitry Andric while (!CallsiteIterStep.areBothFinished()) {
2156e8d8bef9SDimitry Andric MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
2157e8d8bef9SDimitry Andric assert(CallsiteStepStatus != MS_None &&
2158e8d8bef9SDimitry Andric "Match status should be updated before entering loop body");
2159e8d8bef9SDimitry Andric
2160e8d8bef9SDimitry Andric if (CallsiteStepStatus != MS_Match) {
2161e8d8bef9SDimitry Andric auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
2162e8d8bef9SDimitry Andric ? CallsiteIterStep.getFirstIter()
2163e8d8bef9SDimitry Andric : CallsiteIterStep.getSecondIter();
2164e8d8bef9SDimitry Andric for (const auto &F : Callsite->second)
2165e8d8bef9SDimitry Andric updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
2166e8d8bef9SDimitry Andric CallsiteStepStatus);
2167e8d8bef9SDimitry Andric } else {
2168e8d8bef9SDimitry Andric // There may be multiple inlinees at the same offset, so we need to try
2169e8d8bef9SDimitry Andric // matching all of them. This match is implemented through sort-merge
2170e8d8bef9SDimitry Andric // algorithm because callsite records at the same offset are ordered by
2171e8d8bef9SDimitry Andric // function names.
2172e8d8bef9SDimitry Andric MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
2173e8d8bef9SDimitry Andric CallsiteIterStep.getFirstIter()->second.cbegin(),
2174e8d8bef9SDimitry Andric CallsiteIterStep.getFirstIter()->second.cend(),
2175e8d8bef9SDimitry Andric CallsiteIterStep.getSecondIter()->second.cbegin(),
2176e8d8bef9SDimitry Andric CallsiteIterStep.getSecondIter()->second.cend());
2177e8d8bef9SDimitry Andric CalleeIterStep.updateOneStep();
2178e8d8bef9SDimitry Andric while (!CalleeIterStep.areBothFinished()) {
2179e8d8bef9SDimitry Andric MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
2180e8d8bef9SDimitry Andric if (CalleeStepStatus != MS_Match) {
2181e8d8bef9SDimitry Andric auto Callee = (CalleeStepStatus == MS_FirstUnique)
2182e8d8bef9SDimitry Andric ? CalleeIterStep.getFirstIter()
2183e8d8bef9SDimitry Andric : CalleeIterStep.getSecondIter();
2184e8d8bef9SDimitry Andric updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
2185e8d8bef9SDimitry Andric CalleeStepStatus);
2186e8d8bef9SDimitry Andric } else {
2187e8d8bef9SDimitry Andric // An inlined function can contain other inlinees inside, so compute
2188e8d8bef9SDimitry Andric // the Difference recursively.
2189e8d8bef9SDimitry Andric Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
2190e8d8bef9SDimitry Andric CalleeIterStep.getFirstIter()->second,
2191e8d8bef9SDimitry Andric CalleeIterStep.getSecondIter()->second,
2192e8d8bef9SDimitry Andric FuncOverlap);
2193e8d8bef9SDimitry Andric }
2194e8d8bef9SDimitry Andric CalleeIterStep.updateOneStep();
2195e8d8bef9SDimitry Andric }
2196e8d8bef9SDimitry Andric }
2197e8d8bef9SDimitry Andric CallsiteIterStep.updateOneStep();
2198e8d8bef9SDimitry Andric }
2199e8d8bef9SDimitry Andric
2200e8d8bef9SDimitry Andric // Difference reflects the total differences of line/block samples in this
2201e8d8bef9SDimitry Andric // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
2202e8d8bef9SDimitry Andric // reflect the similarity between function profiles in [0.0f to 1.0f].
2203e8d8bef9SDimitry Andric return (2.0 - Difference) / 2;
2204e8d8bef9SDimitry Andric }
2205e8d8bef9SDimitry Andric
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2206e8d8bef9SDimitry Andric double SampleOverlapAggregator::weightForFuncSimilarity(
2207e8d8bef9SDimitry Andric double FuncInternalSimilarity, uint64_t BaseFuncSample,
2208e8d8bef9SDimitry Andric uint64_t TestFuncSample) const {
2209e8d8bef9SDimitry Andric // Compute the weight as the distance between the function weights in two
2210e8d8bef9SDimitry Andric // profiles.
2211e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2212e8d8bef9SDimitry Andric double TestFrac = 0.0;
2213e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2214e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2215e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
2216e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2217e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2218e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
2219e8d8bef9SDimitry Andric double WeightDistance = std::fabs(BaseFrac - TestFrac);
2220e8d8bef9SDimitry Andric
2221e8d8bef9SDimitry Andric // Take WeightDistance into the similarity.
2222e8d8bef9SDimitry Andric return FuncInternalSimilarity * (1 - WeightDistance);
2223e8d8bef9SDimitry Andric }
2224e8d8bef9SDimitry Andric
2225e8d8bef9SDimitry Andric double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2226e8d8bef9SDimitry Andric SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
2227e8d8bef9SDimitry Andric uint64_t BaseFuncSample,
2228e8d8bef9SDimitry Andric uint64_t TestFuncSample) const {
2229e8d8bef9SDimitry Andric
2230e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2231e8d8bef9SDimitry Andric double TestFrac = 0.0;
2232e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2233e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2234e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
2235e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2236e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2237e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
2238e8d8bef9SDimitry Andric return FuncSimilarity * (BaseFrac + TestFrac);
2239e8d8bef9SDimitry Andric }
2240e8d8bef9SDimitry Andric
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)2241e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionOverlap(
2242e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *BaseFunc,
2243e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *TestFunc,
2244e8d8bef9SDimitry Andric SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
2245e8d8bef9SDimitry Andric uint64_t TestFuncSample) {
2246e8d8bef9SDimitry Andric // Default function internal similarity before weighted, meaning two functions
2247e8d8bef9SDimitry Andric // has no overlap.
2248e8d8bef9SDimitry Andric const double DefaultFuncInternalSimilarity = 0;
2249e8d8bef9SDimitry Andric double FuncSimilarity;
2250e8d8bef9SDimitry Andric double FuncInternalSimilarity;
2251e8d8bef9SDimitry Andric
2252e8d8bef9SDimitry Andric // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
2253e8d8bef9SDimitry Andric // In this case, we use DefaultFuncInternalSimilarity as the function internal
2254e8d8bef9SDimitry Andric // similarity.
2255e8d8bef9SDimitry Andric if (!BaseFunc || !TestFunc) {
2256e8d8bef9SDimitry Andric FuncInternalSimilarity = DefaultFuncInternalSimilarity;
2257e8d8bef9SDimitry Andric } else {
2258e8d8bef9SDimitry Andric assert(FuncOverlap != nullptr &&
2259e8d8bef9SDimitry Andric "FuncOverlap should be provided in this case");
2260e8d8bef9SDimitry Andric FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
2261e8d8bef9SDimitry Andric *BaseFunc, *TestFunc, *FuncOverlap);
2262e8d8bef9SDimitry Andric // Now, FuncInternalSimilarity may be a little less than 0 due to
2263e8d8bef9SDimitry Andric // imprecision of floating point accumulations. Make it zero if the
2264e8d8bef9SDimitry Andric // difference is below Epsilon.
2265e8d8bef9SDimitry Andric FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
2266e8d8bef9SDimitry Andric ? 0
2267e8d8bef9SDimitry Andric : FuncInternalSimilarity;
2268e8d8bef9SDimitry Andric }
2269e8d8bef9SDimitry Andric FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
2270e8d8bef9SDimitry Andric BaseFuncSample, TestFuncSample);
2271e8d8bef9SDimitry Andric return FuncSimilarity;
2272e8d8bef9SDimitry Andric }
2273e8d8bef9SDimitry Andric
computeSampleProfileOverlap(raw_fd_ostream & OS)2274e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
2275e8d8bef9SDimitry Andric using namespace sampleprof;
2276e8d8bef9SDimitry Andric
2277349cc55cSDimitry Andric std::unordered_map<SampleContext, const FunctionSamples *,
2278349cc55cSDimitry Andric SampleContext::Hash>
2279349cc55cSDimitry Andric BaseFuncProf;
2280e8d8bef9SDimitry Andric const auto &BaseProfiles = BaseReader->getProfiles();
2281e8d8bef9SDimitry Andric for (const auto &BaseFunc : BaseProfiles) {
2282349cc55cSDimitry Andric BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
2283e8d8bef9SDimitry Andric }
2284e8d8bef9SDimitry Andric ProfOverlap.UnionCount = BaseFuncProf.size();
2285e8d8bef9SDimitry Andric
2286e8d8bef9SDimitry Andric const auto &TestProfiles = TestReader->getProfiles();
2287e8d8bef9SDimitry Andric for (const auto &TestFunc : TestProfiles) {
2288e8d8bef9SDimitry Andric SampleOverlapStats FuncOverlap;
2289349cc55cSDimitry Andric FuncOverlap.TestName = TestFunc.second.getContext();
2290e8d8bef9SDimitry Andric assert(TestStats.count(FuncOverlap.TestName) &&
2291e8d8bef9SDimitry Andric "TestStats should have records for all functions in test profile "
2292e8d8bef9SDimitry Andric "except inlinees");
2293e8d8bef9SDimitry Andric FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
2294e8d8bef9SDimitry Andric
2295349cc55cSDimitry Andric bool Matched = false;
2296e8d8bef9SDimitry Andric const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
2297e8d8bef9SDimitry Andric if (Match == BaseFuncProf.end()) {
2298e8d8bef9SDimitry Andric const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
2299e8d8bef9SDimitry Andric ++ProfOverlap.TestUniqueCount;
2300e8d8bef9SDimitry Andric ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
2301e8d8bef9SDimitry Andric FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
2302e8d8bef9SDimitry Andric
2303e8d8bef9SDimitry Andric updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
2304e8d8bef9SDimitry Andric
2305e8d8bef9SDimitry Andric double FuncSimilarity = computeSampleFunctionOverlap(
2306e8d8bef9SDimitry Andric nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
2307e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2308e8d8bef9SDimitry Andric weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
2309e8d8bef9SDimitry Andric
2310e8d8bef9SDimitry Andric ++ProfOverlap.UnionCount;
2311e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncStats.SampleSum;
2312e8d8bef9SDimitry Andric } else {
2313e8d8bef9SDimitry Andric ++ProfOverlap.OverlapCount;
2314e8d8bef9SDimitry Andric
2315e8d8bef9SDimitry Andric // Two functions match with each other. Compute function-level overlap and
2316e8d8bef9SDimitry Andric // aggregate them into profile-level overlap.
2317349cc55cSDimitry Andric FuncOverlap.BaseName = Match->second->getContext();
2318e8d8bef9SDimitry Andric assert(BaseStats.count(FuncOverlap.BaseName) &&
2319e8d8bef9SDimitry Andric "BaseStats should have records for all functions in base profile "
2320e8d8bef9SDimitry Andric "except inlinees");
2321e8d8bef9SDimitry Andric FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
2322e8d8bef9SDimitry Andric
2323e8d8bef9SDimitry Andric FuncOverlap.Similarity = computeSampleFunctionOverlap(
2324e8d8bef9SDimitry Andric Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
2325e8d8bef9SDimitry Andric FuncOverlap.TestSample);
2326e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2327e8d8bef9SDimitry Andric weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
2328e8d8bef9SDimitry Andric FuncOverlap.TestSample);
2329e8d8bef9SDimitry Andric ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
2330e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncOverlap.UnionSample;
2331e8d8bef9SDimitry Andric
2332e8d8bef9SDimitry Andric // Accumulate the percentage of base unique and test unique samples into
2333e8d8bef9SDimitry Andric // ProfOverlap.
2334e8d8bef9SDimitry Andric ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
2335e8d8bef9SDimitry Andric ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
2336e8d8bef9SDimitry Andric
2337e8d8bef9SDimitry Andric // Remove matched base functions for later reporting functions not found
2338e8d8bef9SDimitry Andric // in test profile.
2339e8d8bef9SDimitry Andric BaseFuncProf.erase(Match);
2340349cc55cSDimitry Andric Matched = true;
2341e8d8bef9SDimitry Andric }
2342e8d8bef9SDimitry Andric
2343e8d8bef9SDimitry Andric // Print function-level similarity information if specified by options.
2344e8d8bef9SDimitry Andric assert(TestStats.count(FuncOverlap.TestName) &&
2345e8d8bef9SDimitry Andric "TestStats should have records for all functions in test profile "
2346e8d8bef9SDimitry Andric "except inlinees");
2347e8d8bef9SDimitry Andric if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
2348349cc55cSDimitry Andric (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
2349349cc55cSDimitry Andric (Matched && !FuncFilter.NameFilter.empty() &&
2350349cc55cSDimitry Andric FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
2351349cc55cSDimitry Andric std::string::npos)) {
2352e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2353e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2354e8d8bef9SDimitry Andric FuncOverlap.BaseWeight =
2355e8d8bef9SDimitry Andric static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
2356e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2357e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2358e8d8bef9SDimitry Andric FuncOverlap.TestWeight =
2359e8d8bef9SDimitry Andric static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
2360e8d8bef9SDimitry Andric FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
2361e8d8bef9SDimitry Andric }
2362e8d8bef9SDimitry Andric }
2363e8d8bef9SDimitry Andric
2364e8d8bef9SDimitry Andric // Traverse through functions in base profile but not in test profile.
2365e8d8bef9SDimitry Andric for (const auto &F : BaseFuncProf) {
2366349cc55cSDimitry Andric assert(BaseStats.count(F.second->getContext()) &&
2367e8d8bef9SDimitry Andric "BaseStats should have records for all functions in base profile "
2368e8d8bef9SDimitry Andric "except inlinees");
2369349cc55cSDimitry Andric const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
2370e8d8bef9SDimitry Andric ++ProfOverlap.BaseUniqueCount;
2371e8d8bef9SDimitry Andric ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
2372e8d8bef9SDimitry Andric
2373e8d8bef9SDimitry Andric updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
2374e8d8bef9SDimitry Andric
2375e8d8bef9SDimitry Andric double FuncSimilarity = computeSampleFunctionOverlap(
2376e8d8bef9SDimitry Andric nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
2377e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2378e8d8bef9SDimitry Andric weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
2379e8d8bef9SDimitry Andric
2380e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncStats.SampleSum;
2381e8d8bef9SDimitry Andric }
2382e8d8bef9SDimitry Andric
2383e8d8bef9SDimitry Andric // Now, ProfSimilarity may be a little greater than 1 due to imprecision
2384e8d8bef9SDimitry Andric // of floating point accumulations. Make it 1.0 if the difference is below
2385e8d8bef9SDimitry Andric // Epsilon.
2386e8d8bef9SDimitry Andric ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
2387e8d8bef9SDimitry Andric ? 1
2388e8d8bef9SDimitry Andric : ProfOverlap.Similarity;
2389e8d8bef9SDimitry Andric
2390e8d8bef9SDimitry Andric computeHotFuncOverlap();
2391e8d8bef9SDimitry Andric }
2392e8d8bef9SDimitry Andric
initializeSampleProfileOverlap()2393e8d8bef9SDimitry Andric void SampleOverlapAggregator::initializeSampleProfileOverlap() {
2394e8d8bef9SDimitry Andric const auto &BaseProf = BaseReader->getProfiles();
2395e8d8bef9SDimitry Andric for (const auto &I : BaseProf) {
2396e8d8bef9SDimitry Andric ++ProfOverlap.BaseCount;
2397e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2398e8d8bef9SDimitry Andric getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
2399e8d8bef9SDimitry Andric ProfOverlap.BaseSample += FuncStats.SampleSum;
2400349cc55cSDimitry Andric BaseStats.emplace(I.second.getContext(), FuncStats);
2401e8d8bef9SDimitry Andric }
2402e8d8bef9SDimitry Andric
2403e8d8bef9SDimitry Andric const auto &TestProf = TestReader->getProfiles();
2404e8d8bef9SDimitry Andric for (const auto &I : TestProf) {
2405e8d8bef9SDimitry Andric ++ProfOverlap.TestCount;
2406e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2407e8d8bef9SDimitry Andric getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
2408e8d8bef9SDimitry Andric ProfOverlap.TestSample += FuncStats.SampleSum;
2409349cc55cSDimitry Andric TestStats.emplace(I.second.getContext(), FuncStats);
2410e8d8bef9SDimitry Andric }
2411e8d8bef9SDimitry Andric
2412e8d8bef9SDimitry Andric ProfOverlap.BaseName = StringRef(BaseFilename);
2413e8d8bef9SDimitry Andric ProfOverlap.TestName = StringRef(TestFilename);
2414e8d8bef9SDimitry Andric }
2415e8d8bef9SDimitry Andric
dumpFuncSimilarity(raw_fd_ostream & OS) const2416e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
2417e8d8bef9SDimitry Andric using namespace sampleprof;
2418e8d8bef9SDimitry Andric
2419e8d8bef9SDimitry Andric if (FuncSimilarityDump.empty())
2420e8d8bef9SDimitry Andric return;
2421e8d8bef9SDimitry Andric
2422e8d8bef9SDimitry Andric formatted_raw_ostream FOS(OS);
2423e8d8bef9SDimitry Andric FOS << "Function-level details:\n";
2424e8d8bef9SDimitry Andric FOS << "Base weight";
2425e8d8bef9SDimitry Andric FOS.PadToColumn(TestWeightCol);
2426e8d8bef9SDimitry Andric FOS << "Test weight";
2427e8d8bef9SDimitry Andric FOS.PadToColumn(SimilarityCol);
2428e8d8bef9SDimitry Andric FOS << "Similarity";
2429e8d8bef9SDimitry Andric FOS.PadToColumn(OverlapCol);
2430e8d8bef9SDimitry Andric FOS << "Overlap";
2431e8d8bef9SDimitry Andric FOS.PadToColumn(BaseUniqueCol);
2432e8d8bef9SDimitry Andric FOS << "Base unique";
2433e8d8bef9SDimitry Andric FOS.PadToColumn(TestUniqueCol);
2434e8d8bef9SDimitry Andric FOS << "Test unique";
2435e8d8bef9SDimitry Andric FOS.PadToColumn(BaseSampleCol);
2436e8d8bef9SDimitry Andric FOS << "Base samples";
2437e8d8bef9SDimitry Andric FOS.PadToColumn(TestSampleCol);
2438e8d8bef9SDimitry Andric FOS << "Test samples";
2439e8d8bef9SDimitry Andric FOS.PadToColumn(FuncNameCol);
2440e8d8bef9SDimitry Andric FOS << "Function name\n";
2441e8d8bef9SDimitry Andric for (const auto &F : FuncSimilarityDump) {
2442e8d8bef9SDimitry Andric double OverlapPercent =
2443e8d8bef9SDimitry Andric F.second.UnionSample > 0
2444e8d8bef9SDimitry Andric ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
2445e8d8bef9SDimitry Andric : 0;
2446e8d8bef9SDimitry Andric double BaseUniquePercent =
2447e8d8bef9SDimitry Andric F.second.BaseSample > 0
2448e8d8bef9SDimitry Andric ? static_cast<double>(F.second.BaseUniqueSample) /
2449e8d8bef9SDimitry Andric F.second.BaseSample
2450e8d8bef9SDimitry Andric : 0;
2451e8d8bef9SDimitry Andric double TestUniquePercent =
2452e8d8bef9SDimitry Andric F.second.TestSample > 0
2453e8d8bef9SDimitry Andric ? static_cast<double>(F.second.TestUniqueSample) /
2454e8d8bef9SDimitry Andric F.second.TestSample
2455e8d8bef9SDimitry Andric : 0;
2456e8d8bef9SDimitry Andric
2457e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.BaseWeight * 100);
2458e8d8bef9SDimitry Andric FOS.PadToColumn(TestWeightCol);
2459e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.TestWeight * 100);
2460e8d8bef9SDimitry Andric FOS.PadToColumn(SimilarityCol);
2461e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.Similarity * 100);
2462e8d8bef9SDimitry Andric FOS.PadToColumn(OverlapCol);
2463e8d8bef9SDimitry Andric FOS << format("%.2f%%", OverlapPercent * 100);
2464e8d8bef9SDimitry Andric FOS.PadToColumn(BaseUniqueCol);
2465e8d8bef9SDimitry Andric FOS << format("%.2f%%", BaseUniquePercent * 100);
2466e8d8bef9SDimitry Andric FOS.PadToColumn(TestUniqueCol);
2467e8d8bef9SDimitry Andric FOS << format("%.2f%%", TestUniquePercent * 100);
2468e8d8bef9SDimitry Andric FOS.PadToColumn(BaseSampleCol);
2469e8d8bef9SDimitry Andric FOS << F.second.BaseSample;
2470e8d8bef9SDimitry Andric FOS.PadToColumn(TestSampleCol);
2471e8d8bef9SDimitry Andric FOS << F.second.TestSample;
2472e8d8bef9SDimitry Andric FOS.PadToColumn(FuncNameCol);
2473349cc55cSDimitry Andric FOS << F.second.TestName.toString() << "\n";
2474e8d8bef9SDimitry Andric }
2475e8d8bef9SDimitry Andric }
2476e8d8bef9SDimitry Andric
dumpProgramSummary(raw_fd_ostream & OS) const2477e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
2478349cc55cSDimitry Andric OS << "Profile overlap infomation for base_profile: "
2479349cc55cSDimitry Andric << ProfOverlap.BaseName.toString()
2480349cc55cSDimitry Andric << " and test_profile: " << ProfOverlap.TestName.toString()
2481349cc55cSDimitry Andric << "\nProgram level:\n";
2482e8d8bef9SDimitry Andric
2483e8d8bef9SDimitry Andric OS << " Whole program profile similarity: "
2484e8d8bef9SDimitry Andric << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
2485e8d8bef9SDimitry Andric
2486e8d8bef9SDimitry Andric assert(ProfOverlap.UnionSample > 0 &&
2487e8d8bef9SDimitry Andric "Total samples in two profile should be greater than 0");
2488e8d8bef9SDimitry Andric double OverlapPercent =
2489e8d8bef9SDimitry Andric static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
2490e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2491e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2492e8d8bef9SDimitry Andric double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
2493e8d8bef9SDimitry Andric ProfOverlap.BaseSample;
2494e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2495e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2496e8d8bef9SDimitry Andric double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
2497e8d8bef9SDimitry Andric ProfOverlap.TestSample;
2498e8d8bef9SDimitry Andric
2499e8d8bef9SDimitry Andric OS << " Whole program sample overlap: "
2500e8d8bef9SDimitry Andric << format("%.3f%%", OverlapPercent * 100) << "\n";
2501e8d8bef9SDimitry Andric OS << " percentage of samples unique in base profile: "
2502e8d8bef9SDimitry Andric << format("%.3f%%", BaseUniquePercent * 100) << "\n";
2503e8d8bef9SDimitry Andric OS << " percentage of samples unique in test profile: "
2504e8d8bef9SDimitry Andric << format("%.3f%%", TestUniquePercent * 100) << "\n";
2505e8d8bef9SDimitry Andric OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n"
2506e8d8bef9SDimitry Andric << " total samples in test profile: " << ProfOverlap.TestSample << "\n";
2507e8d8bef9SDimitry Andric
2508e8d8bef9SDimitry Andric assert(ProfOverlap.UnionCount > 0 &&
2509e8d8bef9SDimitry Andric "There should be at least one function in two input profiles");
2510e8d8bef9SDimitry Andric double FuncOverlapPercent =
2511e8d8bef9SDimitry Andric static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
2512e8d8bef9SDimitry Andric OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
2513e8d8bef9SDimitry Andric << "\n";
2514e8d8bef9SDimitry Andric OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n";
2515e8d8bef9SDimitry Andric OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount
2516e8d8bef9SDimitry Andric << "\n";
2517e8d8bef9SDimitry Andric OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount
2518e8d8bef9SDimitry Andric << "\n";
2519e8d8bef9SDimitry Andric }
2520e8d8bef9SDimitry Andric
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const2521e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
2522e8d8bef9SDimitry Andric raw_fd_ostream &OS) const {
2523e8d8bef9SDimitry Andric assert(HotFuncOverlap.UnionCount > 0 &&
2524e8d8bef9SDimitry Andric "There should be at least one hot function in two input profiles");
2525e8d8bef9SDimitry Andric OS << " Hot-function overlap: "
2526e8d8bef9SDimitry Andric << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
2527e8d8bef9SDimitry Andric HotFuncOverlap.UnionCount * 100)
2528e8d8bef9SDimitry Andric << "\n";
2529e8d8bef9SDimitry Andric OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
2530e8d8bef9SDimitry Andric OS << " hot functions unique in base profile: "
2531e8d8bef9SDimitry Andric << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
2532e8d8bef9SDimitry Andric OS << " hot functions unique in test profile: "
2533e8d8bef9SDimitry Andric << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
2534e8d8bef9SDimitry Andric
2535e8d8bef9SDimitry Andric assert(HotBlockOverlap.UnionCount > 0 &&
2536e8d8bef9SDimitry Andric "There should be at least one hot block in two input profiles");
2537e8d8bef9SDimitry Andric OS << " Hot-block overlap: "
2538e8d8bef9SDimitry Andric << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
2539e8d8bef9SDimitry Andric HotBlockOverlap.UnionCount * 100)
2540e8d8bef9SDimitry Andric << "\n";
2541e8d8bef9SDimitry Andric OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
2542e8d8bef9SDimitry Andric OS << " hot blocks unique in base profile: "
2543e8d8bef9SDimitry Andric << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
2544e8d8bef9SDimitry Andric OS << " hot blocks unique in test profile: "
2545e8d8bef9SDimitry Andric << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
2546e8d8bef9SDimitry Andric }
2547e8d8bef9SDimitry Andric
loadProfiles()2548e8d8bef9SDimitry Andric std::error_code SampleOverlapAggregator::loadProfiles() {
2549e8d8bef9SDimitry Andric using namespace sampleprof;
2550e8d8bef9SDimitry Andric
2551e8d8bef9SDimitry Andric LLVMContext Context;
255206c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
255306c3fb27SDimitry Andric auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, *FS,
2554fe6060f1SDimitry Andric FSDiscriminatorPassOption);
2555e8d8bef9SDimitry Andric if (std::error_code EC = BaseReaderOrErr.getError())
2556e8d8bef9SDimitry Andric exitWithErrorCode(EC, BaseFilename);
2557e8d8bef9SDimitry Andric
255806c3fb27SDimitry Andric auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, *FS,
2559fe6060f1SDimitry Andric FSDiscriminatorPassOption);
2560e8d8bef9SDimitry Andric if (std::error_code EC = TestReaderOrErr.getError())
2561e8d8bef9SDimitry Andric exitWithErrorCode(EC, TestFilename);
2562e8d8bef9SDimitry Andric
2563e8d8bef9SDimitry Andric BaseReader = std::move(BaseReaderOrErr.get());
2564e8d8bef9SDimitry Andric TestReader = std::move(TestReaderOrErr.get());
2565e8d8bef9SDimitry Andric
2566e8d8bef9SDimitry Andric if (std::error_code EC = BaseReader->read())
2567e8d8bef9SDimitry Andric exitWithErrorCode(EC, BaseFilename);
2568e8d8bef9SDimitry Andric if (std::error_code EC = TestReader->read())
2569e8d8bef9SDimitry Andric exitWithErrorCode(EC, TestFilename);
2570e8d8bef9SDimitry Andric if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
2571e8d8bef9SDimitry Andric exitWithError(
2572e8d8bef9SDimitry Andric "cannot compare probe-based profile with non-probe-based profile");
257381ad6265SDimitry Andric if (BaseReader->profileIsCS() != TestReader->profileIsCS())
2574fe6060f1SDimitry Andric exitWithError("cannot compare CS profile with non-CS profile");
2575e8d8bef9SDimitry Andric
2576e8d8bef9SDimitry Andric // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
2577e8d8bef9SDimitry Andric // profile summary.
2578e8d8bef9SDimitry Andric ProfileSummary &BasePS = BaseReader->getSummary();
2579e8d8bef9SDimitry Andric ProfileSummary &TestPS = TestReader->getSummary();
2580349cc55cSDimitry Andric BaseHotThreshold =
2581349cc55cSDimitry Andric ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
2582349cc55cSDimitry Andric TestHotThreshold =
2583349cc55cSDimitry Andric ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
2584349cc55cSDimitry Andric
2585e8d8bef9SDimitry Andric return std::error_code();
2586e8d8bef9SDimitry Andric }
2587e8d8bef9SDimitry Andric
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2588e8d8bef9SDimitry Andric void overlapSampleProfile(const std::string &BaseFilename,
2589e8d8bef9SDimitry Andric const std::string &TestFilename,
2590e8d8bef9SDimitry Andric const OverlapFuncFilters &FuncFilter,
2591e8d8bef9SDimitry Andric uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2592e8d8bef9SDimitry Andric using namespace sampleprof;
2593e8d8bef9SDimitry Andric
2594e8d8bef9SDimitry Andric // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2595e8d8bef9SDimitry Andric // report 2--3 places after decimal point in percentage numbers.
2596e8d8bef9SDimitry Andric SampleOverlapAggregator OverlapAggr(
2597e8d8bef9SDimitry Andric BaseFilename, TestFilename,
2598e8d8bef9SDimitry Andric static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2599e8d8bef9SDimitry Andric if (std::error_code EC = OverlapAggr.loadProfiles())
2600e8d8bef9SDimitry Andric exitWithErrorCode(EC);
2601e8d8bef9SDimitry Andric
2602e8d8bef9SDimitry Andric OverlapAggr.initializeSampleProfileOverlap();
2603e8d8bef9SDimitry Andric if (OverlapAggr.detectZeroSampleProfile(OS))
2604e8d8bef9SDimitry Andric return;
2605e8d8bef9SDimitry Andric
2606e8d8bef9SDimitry Andric OverlapAggr.computeSampleProfileOverlap(OS);
2607e8d8bef9SDimitry Andric
2608e8d8bef9SDimitry Andric OverlapAggr.dumpProgramSummary(OS);
2609e8d8bef9SDimitry Andric OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2610e8d8bef9SDimitry Andric OverlapAggr.dumpFuncSimilarity(OS);
2611e8d8bef9SDimitry Andric }
2612e8d8bef9SDimitry Andric
overlap_main(int argc,const char * argv[])26130b57cec5SDimitry Andric static int overlap_main(int argc, const char *argv[]) {
26140b57cec5SDimitry Andric std::error_code EC;
26155f757f3fSDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
26160b57cec5SDimitry Andric if (EC)
26175f757f3fSDimitry Andric exitWithErrorCode(EC, OutputFilename);
26180b57cec5SDimitry Andric
2619e8d8bef9SDimitry Andric if (ProfileKind == instr)
26200b57cec5SDimitry Andric overlapInstrProfile(BaseFilename, TestFilename,
26215f757f3fSDimitry Andric OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
26225f757f3fSDimitry Andric OS, IsCS);
2623e8d8bef9SDimitry Andric else
2624e8d8bef9SDimitry Andric overlapSampleProfile(BaseFilename, TestFilename,
26255f757f3fSDimitry Andric OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
2626e8d8bef9SDimitry Andric SimilarityCutoff, OS);
26270b57cec5SDimitry Andric
26280b57cec5SDimitry Andric return 0;
26290b57cec5SDimitry Andric }
26300b57cec5SDimitry Andric
2631fe6060f1SDimitry Andric namespace {
2632fe6060f1SDimitry Andric struct ValueSitesStats {
2633cb14a3feSDimitry Andric ValueSitesStats() = default;
2634cb14a3feSDimitry Andric uint64_t TotalNumValueSites = 0;
2635cb14a3feSDimitry Andric uint64_t TotalNumValueSitesWithValueProfile = 0;
2636cb14a3feSDimitry Andric uint64_t TotalNumValues = 0;
26370b57cec5SDimitry Andric std::vector<unsigned> ValueSitesHistogram;
2638fe6060f1SDimitry Andric };
2639fe6060f1SDimitry Andric } // namespace
26400b57cec5SDimitry Andric
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)26410b57cec5SDimitry Andric static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
26420b57cec5SDimitry Andric ValueSitesStats &Stats, raw_fd_ostream &OS,
26430b57cec5SDimitry Andric InstrProfSymtab *Symtab) {
26440b57cec5SDimitry Andric uint32_t NS = Func.getNumValueSites(VK);
26450b57cec5SDimitry Andric Stats.TotalNumValueSites += NS;
26460b57cec5SDimitry Andric for (size_t I = 0; I < NS; ++I) {
26470b57cec5SDimitry Andric uint32_t NV = Func.getNumValueDataForSite(VK, I);
26480b57cec5SDimitry Andric std::unique_ptr<InstrProfValueData[]> VD = Func.getValueForSite(VK, I);
26490b57cec5SDimitry Andric Stats.TotalNumValues += NV;
26500b57cec5SDimitry Andric if (NV) {
26510b57cec5SDimitry Andric Stats.TotalNumValueSitesWithValueProfile++;
26520b57cec5SDimitry Andric if (NV > Stats.ValueSitesHistogram.size())
26530b57cec5SDimitry Andric Stats.ValueSitesHistogram.resize(NV, 0);
26540b57cec5SDimitry Andric Stats.ValueSitesHistogram[NV - 1]++;
26550b57cec5SDimitry Andric }
26560b57cec5SDimitry Andric
26570b57cec5SDimitry Andric uint64_t SiteSum = 0;
26580b57cec5SDimitry Andric for (uint32_t V = 0; V < NV; V++)
26590b57cec5SDimitry Andric SiteSum += VD[V].Count;
26600b57cec5SDimitry Andric if (SiteSum == 0)
26610b57cec5SDimitry Andric SiteSum = 1;
26620b57cec5SDimitry Andric
26630b57cec5SDimitry Andric for (uint32_t V = 0; V < NV; V++) {
26640b57cec5SDimitry Andric OS << "\t[ " << format("%2u", I) << ", ";
26650b57cec5SDimitry Andric if (Symtab == nullptr)
26660b57cec5SDimitry Andric OS << format("%4" PRIu64, VD[V].Value);
26670b57cec5SDimitry Andric else
26685f757f3fSDimitry Andric OS << Symtab->getFuncOrVarName(VD[V].Value);
26690b57cec5SDimitry Andric OS << ", " << format("%10" PRId64, VD[V].Count) << " ] ("
26700b57cec5SDimitry Andric << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n";
26710b57cec5SDimitry Andric }
26720b57cec5SDimitry Andric }
26730b57cec5SDimitry Andric }
26740b57cec5SDimitry Andric
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)26750b57cec5SDimitry Andric static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
26760b57cec5SDimitry Andric ValueSitesStats &Stats) {
26770b57cec5SDimitry Andric OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";
26780b57cec5SDimitry Andric OS << " Total number of sites with values: "
26790b57cec5SDimitry Andric << Stats.TotalNumValueSitesWithValueProfile << "\n";
26800b57cec5SDimitry Andric OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";
26810b57cec5SDimitry Andric
26820b57cec5SDimitry Andric OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";
26830b57cec5SDimitry Andric for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
26840b57cec5SDimitry Andric if (Stats.ValueSitesHistogram[I] > 0)
26850b57cec5SDimitry Andric OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
26860b57cec5SDimitry Andric }
26870b57cec5SDimitry Andric }
26880b57cec5SDimitry Andric
showInstrProfile(ShowFormat SFormat,raw_fd_ostream & OS)26895f757f3fSDimitry Andric static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
2690bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
2691bdd1243dSDimitry Andric exitWithError("JSON output is not supported for instr profiles");
2692bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml)
2693bdd1243dSDimitry Andric exitWithError("YAML output is not supported for instr profiles");
269406c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
269506c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
26960b57cec5SDimitry Andric std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
26970b57cec5SDimitry Andric if (ShowDetailedSummary && Cutoffs.empty()) {
269881ad6265SDimitry Andric Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
26990b57cec5SDimitry Andric }
27000b57cec5SDimitry Andric InstrProfSummaryBuilder Builder(std::move(Cutoffs));
27010b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError())
27020b57cec5SDimitry Andric exitWithError(std::move(E), Filename);
27030b57cec5SDimitry Andric
27040b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
27050b57cec5SDimitry Andric bool IsIRInstr = Reader->isIRLevelProfile();
27060b57cec5SDimitry Andric size_t ShownFunctions = 0;
27070b57cec5SDimitry Andric size_t BelowCutoffFunctions = 0;
27080b57cec5SDimitry Andric int NumVPKind = IPVK_Last - IPVK_First + 1;
27090b57cec5SDimitry Andric std::vector<ValueSitesStats> VPStats(NumVPKind);
27100b57cec5SDimitry Andric
27110b57cec5SDimitry Andric auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
27120b57cec5SDimitry Andric const std::pair<std::string, uint64_t> &v2) {
27130b57cec5SDimitry Andric return v1.second > v2.second;
27140b57cec5SDimitry Andric };
27150b57cec5SDimitry Andric
27160b57cec5SDimitry Andric std::priority_queue<std::pair<std::string, uint64_t>,
27170b57cec5SDimitry Andric std::vector<std::pair<std::string, uint64_t>>,
27180b57cec5SDimitry Andric decltype(MinCmp)>
27190b57cec5SDimitry Andric HottestFuncs(MinCmp);
27200b57cec5SDimitry Andric
27210b57cec5SDimitry Andric if (!TextFormat && OnlyListBelow) {
27220b57cec5SDimitry Andric OS << "The list of functions with the maximum counter less than "
27235f757f3fSDimitry Andric << ShowValueCutoff << ":\n";
27240b57cec5SDimitry Andric }
27250b57cec5SDimitry Andric
27260b57cec5SDimitry Andric // Add marker so that IR-level instrumentation round-trips properly.
27270b57cec5SDimitry Andric if (TextFormat && IsIRInstr)
27280b57cec5SDimitry Andric OS << ":ir\n";
27290b57cec5SDimitry Andric
27300b57cec5SDimitry Andric for (const auto &Func : *Reader) {
27310b57cec5SDimitry Andric if (Reader->isIRLevelProfile()) {
27320b57cec5SDimitry Andric bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
27330b57cec5SDimitry Andric if (FuncIsCS != ShowCS)
27340b57cec5SDimitry Andric continue;
27350b57cec5SDimitry Andric }
2736349cc55cSDimitry Andric bool Show = ShowAllFunctions ||
27375f757f3fSDimitry Andric (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter));
27380b57cec5SDimitry Andric
27390b57cec5SDimitry Andric bool doTextFormatDump = (Show && TextFormat);
27400b57cec5SDimitry Andric
27410b57cec5SDimitry Andric if (doTextFormatDump) {
27420b57cec5SDimitry Andric InstrProfSymtab &Symtab = Reader->getSymtab();
27430b57cec5SDimitry Andric InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
27440b57cec5SDimitry Andric OS);
27450b57cec5SDimitry Andric continue;
27460b57cec5SDimitry Andric }
27470b57cec5SDimitry Andric
27480b57cec5SDimitry Andric assert(Func.Counts.size() > 0 && "function missing entry counter");
27490b57cec5SDimitry Andric Builder.addRecord(Func);
27500b57cec5SDimitry Andric
27511fd87a68SDimitry Andric if (ShowCovered) {
2752972a253aSDimitry Andric if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
27531fd87a68SDimitry Andric OS << Func.Name << "\n";
27541fd87a68SDimitry Andric continue;
27551fd87a68SDimitry Andric }
27561fd87a68SDimitry Andric
27570b57cec5SDimitry Andric uint64_t FuncMax = 0;
27580b57cec5SDimitry Andric uint64_t FuncSum = 0;
2759bdd1243dSDimitry Andric
2760bdd1243dSDimitry Andric auto PseudoKind = Func.getCountPseudoKind();
2761bdd1243dSDimitry Andric if (PseudoKind != InstrProfRecord::NotPseudo) {
2762bdd1243dSDimitry Andric if (Show) {
2763bdd1243dSDimitry Andric if (!ShownFunctions)
2764bdd1243dSDimitry Andric OS << "Counters:\n";
2765bdd1243dSDimitry Andric ++ShownFunctions;
2766bdd1243dSDimitry Andric OS << " " << Func.Name << ":\n"
2767bdd1243dSDimitry Andric << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
2768bdd1243dSDimitry Andric << " Counters: " << Func.Counts.size();
2769bdd1243dSDimitry Andric if (PseudoKind == InstrProfRecord::PseudoHot)
2770bdd1243dSDimitry Andric OS << " <PseudoHot>\n";
2771bdd1243dSDimitry Andric else if (PseudoKind == InstrProfRecord::PseudoWarm)
2772bdd1243dSDimitry Andric OS << " <PseudoWarm>\n";
2773bdd1243dSDimitry Andric else
2774bdd1243dSDimitry Andric llvm_unreachable("Unknown PseudoKind");
2775bdd1243dSDimitry Andric }
2776e8d8bef9SDimitry Andric continue;
2777bdd1243dSDimitry Andric }
2778bdd1243dSDimitry Andric
2779bdd1243dSDimitry Andric for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
27800b57cec5SDimitry Andric FuncMax = std::max(FuncMax, Func.Counts[I]);
27810b57cec5SDimitry Andric FuncSum += Func.Counts[I];
27820b57cec5SDimitry Andric }
27830b57cec5SDimitry Andric
27845f757f3fSDimitry Andric if (FuncMax < ShowValueCutoff) {
27850b57cec5SDimitry Andric ++BelowCutoffFunctions;
27860b57cec5SDimitry Andric if (OnlyListBelow) {
27870b57cec5SDimitry Andric OS << " " << Func.Name << ": (Max = " << FuncMax
27880b57cec5SDimitry Andric << " Sum = " << FuncSum << ")\n";
27890b57cec5SDimitry Andric }
27900b57cec5SDimitry Andric continue;
27910b57cec5SDimitry Andric } else if (OnlyListBelow)
27920b57cec5SDimitry Andric continue;
27930b57cec5SDimitry Andric
27945f757f3fSDimitry Andric if (TopNFunctions) {
27955f757f3fSDimitry Andric if (HottestFuncs.size() == TopNFunctions) {
27960b57cec5SDimitry Andric if (HottestFuncs.top().second < FuncMax) {
27970b57cec5SDimitry Andric HottestFuncs.pop();
27980b57cec5SDimitry Andric HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
27990b57cec5SDimitry Andric }
28000b57cec5SDimitry Andric } else
28010b57cec5SDimitry Andric HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28020b57cec5SDimitry Andric }
28030b57cec5SDimitry Andric
28040b57cec5SDimitry Andric if (Show) {
28050b57cec5SDimitry Andric if (!ShownFunctions)
28060b57cec5SDimitry Andric OS << "Counters:\n";
28070b57cec5SDimitry Andric
28080b57cec5SDimitry Andric ++ShownFunctions;
28090b57cec5SDimitry Andric
28100b57cec5SDimitry Andric OS << " " << Func.Name << ":\n"
28110b57cec5SDimitry Andric << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
28120b57cec5SDimitry Andric << " Counters: " << Func.Counts.size() << "\n";
28130b57cec5SDimitry Andric if (!IsIRInstr)
28140b57cec5SDimitry Andric OS << " Function count: " << Func.Counts[0] << "\n";
28150b57cec5SDimitry Andric
28160b57cec5SDimitry Andric if (ShowIndirectCallTargets)
28170b57cec5SDimitry Andric OS << " Indirect Call Site Count: "
28180b57cec5SDimitry Andric << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
28190b57cec5SDimitry Andric
28200b57cec5SDimitry Andric uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
28210b57cec5SDimitry Andric if (ShowMemOPSizes && NumMemOPCalls > 0)
28220b57cec5SDimitry Andric OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls
28230b57cec5SDimitry Andric << "\n";
28240b57cec5SDimitry Andric
28250b57cec5SDimitry Andric if (ShowCounts) {
28260b57cec5SDimitry Andric OS << " Block counts: [";
28270b57cec5SDimitry Andric size_t Start = (IsIRInstr ? 0 : 1);
28280b57cec5SDimitry Andric for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
28290b57cec5SDimitry Andric OS << (I == Start ? "" : ", ") << Func.Counts[I];
28300b57cec5SDimitry Andric }
28310b57cec5SDimitry Andric OS << "]\n";
28320b57cec5SDimitry Andric }
28330b57cec5SDimitry Andric
28340b57cec5SDimitry Andric if (ShowIndirectCallTargets) {
28350b57cec5SDimitry Andric OS << " Indirect Target Results:\n";
28360b57cec5SDimitry Andric traverseAllValueSites(Func, IPVK_IndirectCallTarget,
28370b57cec5SDimitry Andric VPStats[IPVK_IndirectCallTarget], OS,
28380b57cec5SDimitry Andric &(Reader->getSymtab()));
28390b57cec5SDimitry Andric }
28400b57cec5SDimitry Andric
28410b57cec5SDimitry Andric if (ShowMemOPSizes && NumMemOPCalls > 0) {
28420b57cec5SDimitry Andric OS << " Memory Intrinsic Size Results:\n";
28430b57cec5SDimitry Andric traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
28440b57cec5SDimitry Andric nullptr);
28450b57cec5SDimitry Andric }
28460b57cec5SDimitry Andric }
28470b57cec5SDimitry Andric }
28480b57cec5SDimitry Andric if (Reader->hasError())
28490b57cec5SDimitry Andric exitWithError(Reader->getError(), Filename);
28500b57cec5SDimitry Andric
28511fd87a68SDimitry Andric if (TextFormat || ShowCovered)
28520b57cec5SDimitry Andric return 0;
28530b57cec5SDimitry Andric std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
2854e8d8bef9SDimitry Andric bool IsIR = Reader->isIRLevelProfile();
2855e8d8bef9SDimitry Andric OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
2856e8d8bef9SDimitry Andric if (IsIR)
2857e8d8bef9SDimitry Andric OS << " entry_first = " << Reader->instrEntryBBEnabled();
2858e8d8bef9SDimitry Andric OS << "\n";
28595f757f3fSDimitry Andric if (ShowAllFunctions || !FuncNameFilter.empty())
28600b57cec5SDimitry Andric OS << "Functions shown: " << ShownFunctions << "\n";
28610b57cec5SDimitry Andric OS << "Total functions: " << PS->getNumFunctions() << "\n";
28625f757f3fSDimitry Andric if (ShowValueCutoff > 0) {
28635f757f3fSDimitry Andric OS << "Number of functions with maximum count (< " << ShowValueCutoff
28640b57cec5SDimitry Andric << "): " << BelowCutoffFunctions << "\n";
28655f757f3fSDimitry Andric OS << "Number of functions with maximum count (>= " << ShowValueCutoff
28660b57cec5SDimitry Andric << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
28670b57cec5SDimitry Andric }
28680b57cec5SDimitry Andric OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
28690b57cec5SDimitry Andric OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
28700b57cec5SDimitry Andric
28715f757f3fSDimitry Andric if (TopNFunctions) {
28720b57cec5SDimitry Andric std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
28730b57cec5SDimitry Andric while (!HottestFuncs.empty()) {
28740b57cec5SDimitry Andric SortedHottestFuncs.emplace_back(HottestFuncs.top());
28750b57cec5SDimitry Andric HottestFuncs.pop();
28760b57cec5SDimitry Andric }
28775f757f3fSDimitry Andric OS << "Top " << TopNFunctions
28780b57cec5SDimitry Andric << " functions with the largest internal block counts: \n";
28790b57cec5SDimitry Andric for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
28800b57cec5SDimitry Andric OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
28810b57cec5SDimitry Andric }
28820b57cec5SDimitry Andric
28830b57cec5SDimitry Andric if (ShownFunctions && ShowIndirectCallTargets) {
28840b57cec5SDimitry Andric OS << "Statistics for indirect call sites profile:\n";
28850b57cec5SDimitry Andric showValueSitesStats(OS, IPVK_IndirectCallTarget,
28860b57cec5SDimitry Andric VPStats[IPVK_IndirectCallTarget]);
28870b57cec5SDimitry Andric }
28880b57cec5SDimitry Andric
28890b57cec5SDimitry Andric if (ShownFunctions && ShowMemOPSizes) {
28900b57cec5SDimitry Andric OS << "Statistics for memory intrinsic calls sizes profile:\n";
28910b57cec5SDimitry Andric showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
28920b57cec5SDimitry Andric }
28930b57cec5SDimitry Andric
28940b57cec5SDimitry Andric if (ShowDetailedSummary) {
28950b57cec5SDimitry Andric OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
28960b57cec5SDimitry Andric OS << "Total count: " << PS->getTotalCount() << "\n";
28975ffd83dbSDimitry Andric PS->printDetailedSummary(OS);
28980b57cec5SDimitry Andric }
2899fe6060f1SDimitry Andric
2900fe6060f1SDimitry Andric if (ShowBinaryIds)
2901fe6060f1SDimitry Andric if (Error E = Reader->printBinaryIds(OS))
2902fe6060f1SDimitry Andric exitWithError(std::move(E), Filename);
2903fe6060f1SDimitry Andric
2904bdd1243dSDimitry Andric if (ShowProfileVersion)
2905bdd1243dSDimitry Andric OS << "Profile version: " << Reader->getVersion() << "\n";
290606c3fb27SDimitry Andric
290706c3fb27SDimitry Andric if (ShowTemporalProfTraces) {
290806c3fb27SDimitry Andric auto &Traces = Reader->getTemporalProfTraces();
290906c3fb27SDimitry Andric OS << "Temporal Profile Traces (samples=" << Traces.size()
291006c3fb27SDimitry Andric << " seen=" << Reader->getTemporalProfTraceStreamSize() << "):\n";
291106c3fb27SDimitry Andric for (unsigned i = 0; i < Traces.size(); i++) {
291206c3fb27SDimitry Andric OS << " Temporal Profile Trace " << i << " (weight=" << Traces[i].Weight
291306c3fb27SDimitry Andric << " count=" << Traces[i].FunctionNameRefs.size() << "):\n";
291406c3fb27SDimitry Andric for (auto &NameRef : Traces[i].FunctionNameRefs)
29155f757f3fSDimitry Andric OS << " " << Reader->getSymtab().getFuncOrVarName(NameRef) << "\n";
291606c3fb27SDimitry Andric }
291706c3fb27SDimitry Andric }
291806c3fb27SDimitry Andric
29190b57cec5SDimitry Andric return 0;
29200b57cec5SDimitry Andric }
29210b57cec5SDimitry Andric
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)29228bcb0991SDimitry Andric static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
29238bcb0991SDimitry Andric raw_fd_ostream &OS) {
29248bcb0991SDimitry Andric if (!Reader->dumpSectionInfo(OS)) {
29258bcb0991SDimitry Andric WithColor::warning() << "-show-sec-info-only is only supported for "
29268bcb0991SDimitry Andric << "sample profile in extbinary format and is "
29278bcb0991SDimitry Andric << "ignored for other formats.\n";
29288bcb0991SDimitry Andric return;
29298bcb0991SDimitry Andric }
29308bcb0991SDimitry Andric }
29318bcb0991SDimitry Andric
29325ffd83dbSDimitry Andric namespace {
29335ffd83dbSDimitry Andric struct HotFuncInfo {
2934349cc55cSDimitry Andric std::string FuncName;
2935cb14a3feSDimitry Andric uint64_t TotalCount = 0;
2936cb14a3feSDimitry Andric double TotalCountPercent = 0.0f;
2937cb14a3feSDimitry Andric uint64_t MaxCount = 0;
2938cb14a3feSDimitry Andric uint64_t EntryCount = 0;
29395ffd83dbSDimitry Andric
2940cb14a3feSDimitry Andric HotFuncInfo() = default;
29415ffd83dbSDimitry Andric
HotFuncInfo__anonae06d1131611::HotFuncInfo29425ffd83dbSDimitry Andric HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
2943349cc55cSDimitry Andric : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
2944349cc55cSDimitry Andric MaxCount(MS), EntryCount(ES) {}
29455ffd83dbSDimitry Andric };
29465ffd83dbSDimitry Andric } // namespace
29475ffd83dbSDimitry Andric
29485ffd83dbSDimitry Andric // Print out detailed information about hot functions in PrintValues vector.
29495ffd83dbSDimitry Andric // Users specify titles and offset of every columns through ColumnTitle and
29505ffd83dbSDimitry Andric // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
29515ffd83dbSDimitry Andric // and at least 4. Besides, users can optionally give a HotFuncMetric string to
29525ffd83dbSDimitry Andric // print out or let it be an empty string.
dumpHotFunctionList(const std::vector<std::string> & ColumnTitle,const std::vector<int> & ColumnOffset,const std::vector<HotFuncInfo> & PrintValues,uint64_t HotFuncCount,uint64_t TotalFuncCount,uint64_t HotProfCount,uint64_t TotalProfCount,const std::string & HotFuncMetric,uint32_t TopNFunctions,raw_fd_ostream & OS)29535ffd83dbSDimitry Andric static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
29545ffd83dbSDimitry Andric const std::vector<int> &ColumnOffset,
29555ffd83dbSDimitry Andric const std::vector<HotFuncInfo> &PrintValues,
29565ffd83dbSDimitry Andric uint64_t HotFuncCount, uint64_t TotalFuncCount,
29575ffd83dbSDimitry Andric uint64_t HotProfCount, uint64_t TotalProfCount,
29585ffd83dbSDimitry Andric const std::string &HotFuncMetric,
2959349cc55cSDimitry Andric uint32_t TopNFunctions, raw_fd_ostream &OS) {
2960e8d8bef9SDimitry Andric assert(ColumnOffset.size() == ColumnTitle.size() &&
2961e8d8bef9SDimitry Andric "ColumnOffset and ColumnTitle should have the same size");
2962e8d8bef9SDimitry Andric assert(ColumnTitle.size() >= 4 &&
2963e8d8bef9SDimitry Andric "ColumnTitle should have at least 4 elements");
2964e8d8bef9SDimitry Andric assert(TotalFuncCount > 0 &&
2965e8d8bef9SDimitry Andric "There should be at least one function in the profile");
29665ffd83dbSDimitry Andric double TotalProfPercent = 0;
29675ffd83dbSDimitry Andric if (TotalProfCount > 0)
2968e8d8bef9SDimitry Andric TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
29695ffd83dbSDimitry Andric
29705ffd83dbSDimitry Andric formatted_raw_ostream FOS(OS);
29715ffd83dbSDimitry Andric FOS << HotFuncCount << " out of " << TotalFuncCount
29725ffd83dbSDimitry Andric << " functions with profile ("
2973e8d8bef9SDimitry Andric << format("%.2f%%",
2974e8d8bef9SDimitry Andric (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
29755ffd83dbSDimitry Andric << ") are considered hot functions";
29765ffd83dbSDimitry Andric if (!HotFuncMetric.empty())
29775ffd83dbSDimitry Andric FOS << " (" << HotFuncMetric << ")";
29785ffd83dbSDimitry Andric FOS << ".\n";
29795ffd83dbSDimitry Andric FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
29805ffd83dbSDimitry Andric << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
29815ffd83dbSDimitry Andric
29825ffd83dbSDimitry Andric for (size_t I = 0; I < ColumnTitle.size(); ++I) {
29835ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[I]);
29845ffd83dbSDimitry Andric FOS << ColumnTitle[I];
29855ffd83dbSDimitry Andric }
29865ffd83dbSDimitry Andric FOS << "\n";
29875ffd83dbSDimitry Andric
2988349cc55cSDimitry Andric uint32_t Count = 0;
2989349cc55cSDimitry Andric for (const auto &R : PrintValues) {
2990349cc55cSDimitry Andric if (TopNFunctions && (Count++ == TopNFunctions))
2991349cc55cSDimitry Andric break;
29925ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[0]);
29935ffd83dbSDimitry Andric FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
29945ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[1]);
29955ffd83dbSDimitry Andric FOS << R.MaxCount;
29965ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[2]);
29975ffd83dbSDimitry Andric FOS << R.EntryCount;
29985ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[3]);
29995ffd83dbSDimitry Andric FOS << R.FuncName << "\n";
30005ffd83dbSDimitry Andric }
30015ffd83dbSDimitry Andric }
30025ffd83dbSDimitry Andric
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)3003349cc55cSDimitry Andric static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
3004349cc55cSDimitry Andric ProfileSummary &PS, uint32_t TopN,
3005349cc55cSDimitry Andric raw_fd_ostream &OS) {
30065ffd83dbSDimitry Andric using namespace sampleprof;
30075ffd83dbSDimitry Andric
30085ffd83dbSDimitry Andric const uint32_t HotFuncCutoff = 990000;
30095ffd83dbSDimitry Andric auto &SummaryVector = PS.getDetailedSummary();
30105ffd83dbSDimitry Andric uint64_t MinCountThreshold = 0;
30115ffd83dbSDimitry Andric for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
30125ffd83dbSDimitry Andric if (SummaryEntry.Cutoff == HotFuncCutoff) {
30135ffd83dbSDimitry Andric MinCountThreshold = SummaryEntry.MinCount;
30145ffd83dbSDimitry Andric break;
30155ffd83dbSDimitry Andric }
30165ffd83dbSDimitry Andric }
30175ffd83dbSDimitry Andric
30185ffd83dbSDimitry Andric // Traverse all functions in the profile and keep only hot functions.
30195ffd83dbSDimitry Andric // The following loop also calculates the sum of total samples of all
30205ffd83dbSDimitry Andric // functions.
30215ffd83dbSDimitry Andric std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
30225ffd83dbSDimitry Andric std::greater<uint64_t>>
30235ffd83dbSDimitry Andric HotFunc;
30245ffd83dbSDimitry Andric uint64_t ProfileTotalSample = 0;
30255ffd83dbSDimitry Andric uint64_t HotFuncSample = 0;
30265ffd83dbSDimitry Andric uint64_t HotFuncCount = 0;
3027e8d8bef9SDimitry Andric
30285ffd83dbSDimitry Andric for (const auto &I : Profiles) {
3029e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
30305ffd83dbSDimitry Andric const FunctionSamples &FuncProf = I.second;
30315ffd83dbSDimitry Andric ProfileTotalSample += FuncProf.getTotalSamples();
3032e8d8bef9SDimitry Andric getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
30335ffd83dbSDimitry Andric
3034e8d8bef9SDimitry Andric if (isFunctionHot(FuncStats, MinCountThreshold)) {
30355ffd83dbSDimitry Andric HotFunc.emplace(FuncProf.getTotalSamples(),
3036e8d8bef9SDimitry Andric std::make_pair(&(I.second), FuncStats.MaxSample));
30375ffd83dbSDimitry Andric HotFuncSample += FuncProf.getTotalSamples();
30385ffd83dbSDimitry Andric ++HotFuncCount;
30395ffd83dbSDimitry Andric }
30405ffd83dbSDimitry Andric }
30415ffd83dbSDimitry Andric
30425ffd83dbSDimitry Andric std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
30435ffd83dbSDimitry Andric "Entry sample", "Function name"};
30445ffd83dbSDimitry Andric std::vector<int> ColumnOffset{0, 24, 42, 58};
30455ffd83dbSDimitry Andric std::string Metric =
30465ffd83dbSDimitry Andric std::string("max sample >= ") + std::to_string(MinCountThreshold);
30475ffd83dbSDimitry Andric std::vector<HotFuncInfo> PrintValues;
30485ffd83dbSDimitry Andric for (const auto &FuncPair : HotFunc) {
30495ffd83dbSDimitry Andric const FunctionSamples &Func = *FuncPair.second.first;
30505ffd83dbSDimitry Andric double TotalSamplePercent =
30515ffd83dbSDimitry Andric (ProfileTotalSample > 0)
30525ffd83dbSDimitry Andric ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
30535ffd83dbSDimitry Andric : 0;
3054fcaf7f86SDimitry Andric PrintValues.emplace_back(
3055fcaf7f86SDimitry Andric HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
3056fcaf7f86SDimitry Andric TotalSamplePercent, FuncPair.second.second,
3057fcaf7f86SDimitry Andric Func.getHeadSamplesEstimate()));
30585ffd83dbSDimitry Andric }
30595ffd83dbSDimitry Andric dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
30605ffd83dbSDimitry Andric Profiles.size(), HotFuncSample, ProfileTotalSample,
3061349cc55cSDimitry Andric Metric, TopN, OS);
30625ffd83dbSDimitry Andric
30635ffd83dbSDimitry Andric return 0;
30645ffd83dbSDimitry Andric }
30655ffd83dbSDimitry Andric
showSampleProfile(ShowFormat SFormat,raw_fd_ostream & OS)30665f757f3fSDimitry Andric static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3067bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml)
3068bdd1243dSDimitry Andric exitWithError("YAML output is not supported for sample profiles");
30690b57cec5SDimitry Andric using namespace sampleprof;
30700b57cec5SDimitry Andric LLVMContext Context;
307106c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
307206c3fb27SDimitry Andric auto ReaderOrErr = SampleProfileReader::create(Filename, Context, *FS,
307306c3fb27SDimitry Andric FSDiscriminatorPassOption);
30740b57cec5SDimitry Andric if (std::error_code EC = ReaderOrErr.getError())
30750b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
30760b57cec5SDimitry Andric
30770b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
30788bcb0991SDimitry Andric if (ShowSectionInfoOnly) {
30798bcb0991SDimitry Andric showSectionInfo(Reader.get(), OS);
30808bcb0991SDimitry Andric return 0;
30818bcb0991SDimitry Andric }
30828bcb0991SDimitry Andric
30830b57cec5SDimitry Andric if (std::error_code EC = Reader->read())
30840b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
30850b57cec5SDimitry Andric
30865f757f3fSDimitry Andric if (ShowAllFunctions || FuncNameFilter.empty()) {
3087bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3088bdd1243dSDimitry Andric Reader->dumpJson(OS);
30890b57cec5SDimitry Andric else
3090bdd1243dSDimitry Andric Reader->dump(OS);
3091bdd1243dSDimitry Andric } else {
3092bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3093bdd1243dSDimitry Andric exitWithError(
3094bdd1243dSDimitry Andric "the JSON format is supported only when all functions are to "
3095bdd1243dSDimitry Andric "be printed");
3096bdd1243dSDimitry Andric
3097349cc55cSDimitry Andric // TODO: parse context string to support filtering by contexts.
30985f757f3fSDimitry Andric FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter));
30995f757f3fSDimitry Andric Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS);
3100bdd1243dSDimitry Andric }
31010b57cec5SDimitry Andric
31028bcb0991SDimitry Andric if (ShowProfileSymbolList) {
31038bcb0991SDimitry Andric std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
31048bcb0991SDimitry Andric Reader->getProfileSymbolList();
31058bcb0991SDimitry Andric ReaderList->dump(OS);
31068bcb0991SDimitry Andric }
31078bcb0991SDimitry Andric
31085ffd83dbSDimitry Andric if (ShowDetailedSummary) {
31095ffd83dbSDimitry Andric auto &PS = Reader->getSummary();
31105ffd83dbSDimitry Andric PS.printSummary(OS);
31115ffd83dbSDimitry Andric PS.printDetailedSummary(OS);
31125ffd83dbSDimitry Andric }
31135ffd83dbSDimitry Andric
31145f757f3fSDimitry Andric if (ShowHotFuncList || TopNFunctions)
31155f757f3fSDimitry Andric showHotFunctionList(Reader->getProfiles(), Reader->getSummary(),
31165f757f3fSDimitry Andric TopNFunctions, OS);
31175ffd83dbSDimitry Andric
31180b57cec5SDimitry Andric return 0;
31190b57cec5SDimitry Andric }
31200b57cec5SDimitry Andric
showMemProfProfile(ShowFormat SFormat,raw_fd_ostream & OS)31215f757f3fSDimitry Andric static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3122bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3123bdd1243dSDimitry Andric exitWithError("JSON output is not supported for MemProf");
312481ad6265SDimitry Andric auto ReaderOr = llvm::memprof::RawMemProfReader::create(
312581ad6265SDimitry Andric Filename, ProfiledBinary, /*KeepNames=*/true);
31264824e7fdSDimitry Andric if (Error E = ReaderOr.takeError())
312781ad6265SDimitry Andric // Since the error can be related to the profile or the binary we do not
312881ad6265SDimitry Andric // pass whence. Instead additional context is provided where necessary in
312981ad6265SDimitry Andric // the error message.
313081ad6265SDimitry Andric exitWithError(std::move(E), /*Whence*/ "");
31314824e7fdSDimitry Andric
31324824e7fdSDimitry Andric std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
31334824e7fdSDimitry Andric ReaderOr.get().release());
313481ad6265SDimitry Andric
313581ad6265SDimitry Andric Reader->printYAML(OS);
31364824e7fdSDimitry Andric return 0;
31374824e7fdSDimitry Andric }
31384824e7fdSDimitry Andric
showDebugInfoCorrelation(const std::string & Filename,ShowFormat SFormat,raw_fd_ostream & OS)313904eeddc0SDimitry Andric static int showDebugInfoCorrelation(const std::string &Filename,
3140bdd1243dSDimitry Andric ShowFormat SFormat, raw_fd_ostream &OS) {
3141bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3142bdd1243dSDimitry Andric exitWithError("JSON output is not supported for debug info correlation");
314304eeddc0SDimitry Andric std::unique_ptr<InstrProfCorrelator> Correlator;
31445f757f3fSDimitry Andric if (auto Err =
31455f757f3fSDimitry Andric InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)
31465f757f3fSDimitry Andric .moveInto(Correlator))
314704eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
3148bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml) {
31495f757f3fSDimitry Andric if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))
3150bdd1243dSDimitry Andric exitWithError(std::move(Err), Filename);
3151bdd1243dSDimitry Andric return 0;
3152bdd1243dSDimitry Andric }
3153bdd1243dSDimitry Andric
31545f757f3fSDimitry Andric if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
315504eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
315604eeddc0SDimitry Andric
315704eeddc0SDimitry Andric InstrProfSymtab Symtab;
315804eeddc0SDimitry Andric if (auto Err = Symtab.create(
315904eeddc0SDimitry Andric StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
316004eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
316104eeddc0SDimitry Andric
316204eeddc0SDimitry Andric if (ShowProfileSymbolList)
316304eeddc0SDimitry Andric Symtab.dumpNames(OS);
316404eeddc0SDimitry Andric // TODO: Read "Profile Data Type" from debug info to compute and show how many
316504eeddc0SDimitry Andric // counters the section holds.
316604eeddc0SDimitry Andric if (ShowDetailedSummary)
316704eeddc0SDimitry Andric OS << "Counters section size: 0x"
316804eeddc0SDimitry Andric << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
316904eeddc0SDimitry Andric OS << "Found " << Correlator->getDataSize() << " functions\n";
317004eeddc0SDimitry Andric
317104eeddc0SDimitry Andric return 0;
317204eeddc0SDimitry Andric }
317304eeddc0SDimitry Andric
show_main(int argc,const char * argv[])31740b57cec5SDimitry Andric static int show_main(int argc, const char *argv[]) {
317504eeddc0SDimitry Andric if (Filename.empty() && DebugInfoFilename.empty())
317604eeddc0SDimitry Andric exitWithError(
317704eeddc0SDimitry Andric "the positional argument '<profdata-file>' is required unless '--" +
317804eeddc0SDimitry Andric DebugInfoFilename.ArgStr + "' is provided");
317904eeddc0SDimitry Andric
3180e8d8bef9SDimitry Andric if (Filename == OutputFilename) {
31815f757f3fSDimitry Andric errs() << sys::path::filename(argv[0]) << " " << argv[1]
31820b57cec5SDimitry Andric << ": Input file name cannot be the same as the output file name!\n";
31830b57cec5SDimitry Andric return 1;
31840b57cec5SDimitry Andric }
3185bdd1243dSDimitry Andric if (JsonFormat)
3186bdd1243dSDimitry Andric SFormat = ShowFormat::Json;
31870b57cec5SDimitry Andric
31880b57cec5SDimitry Andric std::error_code EC;
3189fe6060f1SDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
31900b57cec5SDimitry Andric if (EC)
31910b57cec5SDimitry Andric exitWithErrorCode(EC, OutputFilename);
31920b57cec5SDimitry Andric
31935f757f3fSDimitry Andric if (ShowAllFunctions && !FuncNameFilter.empty())
31940b57cec5SDimitry Andric WithColor::warning() << "-function argument ignored: showing all functions\n";
31950b57cec5SDimitry Andric
319604eeddc0SDimitry Andric if (!DebugInfoFilename.empty())
31975f757f3fSDimitry Andric return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);
319804eeddc0SDimitry Andric
31995f757f3fSDimitry Andric if (ShowProfileKind == instr)
32005f757f3fSDimitry Andric return showInstrProfile(SFormat, OS);
32015f757f3fSDimitry Andric if (ShowProfileKind == sample)
32025f757f3fSDimitry Andric return showSampleProfile(SFormat, OS);
32035f757f3fSDimitry Andric return showMemProfProfile(SFormat, OS);
32040b57cec5SDimitry Andric }
32050b57cec5SDimitry Andric
order_main(int argc,const char * argv[])320606c3fb27SDimitry Andric static int order_main(int argc, const char *argv[]) {
320706c3fb27SDimitry Andric std::error_code EC;
320806c3fb27SDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
320906c3fb27SDimitry Andric if (EC)
321006c3fb27SDimitry Andric exitWithErrorCode(EC, OutputFilename);
321106c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
321206c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
321306c3fb27SDimitry Andric if (Error E = ReaderOrErr.takeError())
321406c3fb27SDimitry Andric exitWithError(std::move(E), Filename);
321506c3fb27SDimitry Andric
321606c3fb27SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
321706c3fb27SDimitry Andric for (auto &I : *Reader) {
321806c3fb27SDimitry Andric // Read all entries
321906c3fb27SDimitry Andric (void)I;
322006c3fb27SDimitry Andric }
322106c3fb27SDimitry Andric auto &Traces = Reader->getTemporalProfTraces();
322206c3fb27SDimitry Andric auto Nodes = TemporalProfTraceTy::createBPFunctionNodes(Traces);
322306c3fb27SDimitry Andric BalancedPartitioningConfig Config;
322406c3fb27SDimitry Andric BalancedPartitioning BP(Config);
322506c3fb27SDimitry Andric BP.run(Nodes);
322606c3fb27SDimitry Andric
32271db9f3b2SDimitry Andric OS << "# Ordered " << Nodes.size() << " functions\n";
32281db9f3b2SDimitry Andric OS << "# Warning: Mach-O may prefix symbols with \"_\" depending on the "
32291db9f3b2SDimitry Andric "linkage and this output does not take that into account. Some "
32301db9f3b2SDimitry Andric "post-processing may be required before passing to the linker via "
32311db9f3b2SDimitry Andric "-order_file.\n";
323206c3fb27SDimitry Andric for (auto &N : Nodes) {
32335f757f3fSDimitry Andric auto [Filename, ParsedFuncName] =
32345f757f3fSDimitry Andric getParsedIRPGOFuncName(Reader->getSymtab().getFuncOrVarName(N.Id));
32355f757f3fSDimitry Andric if (!Filename.empty())
323606c3fb27SDimitry Andric OS << "# " << Filename << "\n";
32375f757f3fSDimitry Andric OS << ParsedFuncName << "\n";
323806c3fb27SDimitry Andric }
323906c3fb27SDimitry Andric return 0;
324006c3fb27SDimitry Andric }
324106c3fb27SDimitry Andric
llvm_profdata_main(int argc,char ** argvNonConst,const llvm::ToolContext &)324206c3fb27SDimitry Andric int llvm_profdata_main(int argc, char **argvNonConst,
324306c3fb27SDimitry Andric const llvm::ToolContext &) {
3244bdd1243dSDimitry Andric const char **argv = const_cast<const char **>(argvNonConst);
32450b57cec5SDimitry Andric
32460b57cec5SDimitry Andric StringRef ProgName(sys::path::filename(argv[0]));
32470b57cec5SDimitry Andric
32485f757f3fSDimitry Andric if (argc < 2) {
32495f757f3fSDimitry Andric errs() << ProgName
32505f757f3fSDimitry Andric << ": No subcommand specified! Run llvm-profata --help for usage.\n";
32515f757f3fSDimitry Andric return 1;
32520b57cec5SDimitry Andric }
32530b57cec5SDimitry Andric
32545f757f3fSDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");
32550b57cec5SDimitry Andric
32565f757f3fSDimitry Andric if (ShowSubcommand)
32575f757f3fSDimitry Andric return show_main(argc, argv);
325806c3fb27SDimitry Andric
32595f757f3fSDimitry Andric if (OrderSubcommand)
32605f757f3fSDimitry Andric return order_main(argc, argv);
32610b57cec5SDimitry Andric
32625f757f3fSDimitry Andric if (OverlapSubcommand)
32635f757f3fSDimitry Andric return overlap_main(argc, argv);
32640b57cec5SDimitry Andric
32655f757f3fSDimitry Andric if (MergeSubcommand)
32665f757f3fSDimitry Andric return merge_main(argc, argv);
32675f757f3fSDimitry Andric
32685f757f3fSDimitry Andric errs() << ProgName
32695f757f3fSDimitry Andric << ": Unknown command. Run llvm-profdata --help for usage.\n";
32700b57cec5SDimitry Andric return 1;
32710b57cec5SDimitry Andric }
3272