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"
22*0fca6ea1SDimitry Andric #include "llvm/ProfileData/MemProfReader.h"
230b57cec5SDimitry Andric #include "llvm/ProfileData/ProfileCommon.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 };
78bdd1243dSDimitry Andric
790b57cec5SDimitry Andric enum ProfileFormat {
800b57cec5SDimitry Andric PF_None = 0,
810b57cec5SDimitry Andric PF_Text,
8206c3fb27SDimitry Andric PF_Compact_Binary, // Deprecated
838bcb0991SDimitry Andric PF_Ext_Binary,
840b57cec5SDimitry Andric PF_GCC,
850b57cec5SDimitry Andric PF_Binary
860b57cec5SDimitry Andric };
870b57cec5SDimitry Andric
88bdd1243dSDimitry Andric enum class ShowFormat { Text, Json, Yaml };
89*0fca6ea1SDimitry Andric } // namespace
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
294*0fca6ea1SDimitry Andric cl::opt<bool> KeepVTableSymbols(
295*0fca6ea1SDimitry Andric "keep-vtable-symbols", cl::init(false), cl::Hidden,
296*0fca6ea1SDimitry Andric cl::sub(MergeSubcommand),
297*0fca6ea1SDimitry Andric cl::desc("If true, keep the vtable symbols in indexed profiles"));
298*0fca6ea1SDimitry Andric
299*0fca6ea1SDimitry Andric // Temporary support for writing the previous version of the format, to enable
300*0fca6ea1SDimitry Andric // some forward compatibility.
301*0fca6ea1SDimitry Andric // TODO: Consider enabling this with future version changes as well, to ease
302*0fca6ea1SDimitry Andric // deployment of newer versions of llvm-profdata.
303*0fca6ea1SDimitry Andric cl::opt<bool> DoWritePrevVersion(
304*0fca6ea1SDimitry Andric "write-prev-version", cl::init(false), cl::Hidden,
305*0fca6ea1SDimitry Andric cl::desc("Write the previous version of indexed format, to enable "
306*0fca6ea1SDimitry Andric "some forward compatibility."));
307*0fca6ea1SDimitry Andric
308*0fca6ea1SDimitry Andric cl::opt<memprof::IndexedVersion> MemProfVersionRequested(
309*0fca6ea1SDimitry Andric "memprof-version", cl::Hidden, cl::sub(MergeSubcommand),
310*0fca6ea1SDimitry Andric cl::desc("Specify the version of the memprof format to use"),
311*0fca6ea1SDimitry Andric cl::init(memprof::Version0),
312*0fca6ea1SDimitry Andric cl::values(clEnumValN(memprof::Version0, "0", "version 0"),
313*0fca6ea1SDimitry Andric clEnumValN(memprof::Version1, "1", "version 1"),
314*0fca6ea1SDimitry Andric clEnumValN(memprof::Version2, "2", "version 2"),
315*0fca6ea1SDimitry Andric clEnumValN(memprof::Version3, "3", "version 3")));
316*0fca6ea1SDimitry Andric
317*0fca6ea1SDimitry Andric cl::opt<bool> MemProfFullSchema(
318*0fca6ea1SDimitry Andric "memprof-full-schema", cl::Hidden, cl::sub(MergeSubcommand),
319*0fca6ea1SDimitry Andric cl::desc("Use the full schema for serialization"), cl::init(false));
320*0fca6ea1SDimitry Andric
3215f757f3fSDimitry Andric // Options specific to overlap subcommand.
3225f757f3fSDimitry Andric cl::opt<std::string> BaseFilename(cl::Positional, cl::Required,
3235f757f3fSDimitry Andric cl::desc("<base profile file>"),
3245f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3255f757f3fSDimitry Andric cl::opt<std::string> TestFilename(cl::Positional, cl::Required,
3265f757f3fSDimitry Andric cl::desc("<test profile file>"),
3275f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3285f757f3fSDimitry Andric
3295f757f3fSDimitry Andric cl::opt<unsigned long long> SimilarityCutoff(
3305f757f3fSDimitry Andric "similarity-cutoff", cl::init(0),
3315f757f3fSDimitry Andric cl::desc("For sample profiles, list function names (with calling context "
3325f757f3fSDimitry Andric "for csspgo) for overlapped functions "
3335f757f3fSDimitry Andric "with similarities below the cutoff (percentage times 10000)."),
3345f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3355f757f3fSDimitry Andric
3365f757f3fSDimitry Andric cl::opt<bool> IsCS(
3375f757f3fSDimitry Andric "cs", cl::init(false),
3385f757f3fSDimitry Andric cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."),
3395f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3405f757f3fSDimitry Andric
3415f757f3fSDimitry Andric cl::opt<unsigned long long> OverlapValueCutoff(
3425f757f3fSDimitry Andric "value-cutoff", cl::init(-1),
3435f757f3fSDimitry Andric cl::desc(
3445f757f3fSDimitry Andric "Function level overlap information for every function (with calling "
3455f757f3fSDimitry Andric "context for csspgo) in test "
3465f757f3fSDimitry Andric "profile with max count value greater then the parameter value"),
3475f757f3fSDimitry Andric cl::sub(OverlapSubcommand));
3485f757f3fSDimitry Andric
349*0fca6ea1SDimitry Andric // Options specific to show subcommand.
3505f757f3fSDimitry Andric cl::opt<bool> ShowCounts("counts", cl::init(false),
3515f757f3fSDimitry Andric cl::desc("Show counter values for shown functions"),
3525f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3535f757f3fSDimitry Andric cl::opt<ShowFormat>
3545f757f3fSDimitry Andric SFormat("show-format", cl::init(ShowFormat::Text),
3555f757f3fSDimitry Andric cl::desc("Emit output in the selected format if supported"),
3565f757f3fSDimitry Andric cl::sub(ShowSubcommand),
3575f757f3fSDimitry Andric cl::values(clEnumValN(ShowFormat::Text, "text",
3585f757f3fSDimitry Andric "emit normal text output (default)"),
3595f757f3fSDimitry Andric clEnumValN(ShowFormat::Json, "json", "emit JSON"),
3605f757f3fSDimitry Andric clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")));
3615f757f3fSDimitry Andric // TODO: Consider replacing this with `--show-format=text-encoding`.
3625f757f3fSDimitry Andric cl::opt<bool>
3635f757f3fSDimitry Andric TextFormat("text", cl::init(false),
3645f757f3fSDimitry Andric cl::desc("Show instr profile data in text dump format"),
3655f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3665f757f3fSDimitry Andric cl::opt<bool>
3675f757f3fSDimitry Andric JsonFormat("json",
3685f757f3fSDimitry Andric cl::desc("Show sample profile data in the JSON format "
3695f757f3fSDimitry Andric "(deprecated, please use --show-format=json)"),
3705f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3715f757f3fSDimitry Andric cl::opt<bool> ShowIndirectCallTargets(
3725f757f3fSDimitry Andric "ic-targets", cl::init(false),
3735f757f3fSDimitry Andric cl::desc("Show indirect call site target values for shown functions"),
3745f757f3fSDimitry Andric cl::sub(ShowSubcommand));
375*0fca6ea1SDimitry Andric cl::opt<bool> ShowVTables("show-vtables", cl::init(false),
376*0fca6ea1SDimitry Andric cl::desc("Show vtable names for shown functions"),
377*0fca6ea1SDimitry Andric cl::sub(ShowSubcommand));
3785f757f3fSDimitry Andric cl::opt<bool> ShowMemOPSizes(
3795f757f3fSDimitry Andric "memop-sizes", cl::init(false),
3805f757f3fSDimitry Andric cl::desc("Show the profiled sizes of the memory intrinsic calls "
3815f757f3fSDimitry Andric "for shown functions"),
3825f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3835f757f3fSDimitry Andric cl::opt<bool> ShowDetailedSummary("detailed-summary", cl::init(false),
3845f757f3fSDimitry Andric cl::desc("Show detailed profile summary"),
3855f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3865f757f3fSDimitry Andric cl::list<uint32_t> DetailedSummaryCutoffs(
3875f757f3fSDimitry Andric cl::CommaSeparated, "detailed-summary-cutoffs",
3885f757f3fSDimitry Andric cl::desc(
3895f757f3fSDimitry Andric "Cutoff percentages (times 10000) for generating detailed summary"),
3905f757f3fSDimitry Andric cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand));
3915f757f3fSDimitry Andric cl::opt<bool>
3925f757f3fSDimitry Andric ShowHotFuncList("hot-func-list", cl::init(false),
3935f757f3fSDimitry Andric cl::desc("Show profile summary of a list of hot functions"),
3945f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3955f757f3fSDimitry Andric cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
3965f757f3fSDimitry Andric cl::desc("Details for each and every function"),
3975f757f3fSDimitry Andric cl::sub(ShowSubcommand));
3985f757f3fSDimitry Andric cl::opt<bool> ShowCS("showcs", cl::init(false),
3995f757f3fSDimitry Andric cl::desc("Show context sensitive counts"),
4005f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4015f757f3fSDimitry Andric cl::opt<ProfileKinds> ShowProfileKind(
4025f757f3fSDimitry Andric cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand),
4035f757f3fSDimitry Andric cl::init(instr),
4045f757f3fSDimitry Andric cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
4055f757f3fSDimitry Andric clEnumVal(sample, "Sample profile"),
4065f757f3fSDimitry Andric clEnumVal(memory, "MemProf memory access profile")));
4075f757f3fSDimitry Andric cl::opt<uint32_t> TopNFunctions(
4085f757f3fSDimitry Andric "topn", cl::init(0),
4095f757f3fSDimitry Andric cl::desc("Show the list of functions with the largest internal counts"),
4105f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4115f757f3fSDimitry Andric cl::opt<uint32_t> ShowValueCutoff(
4125f757f3fSDimitry Andric "value-cutoff", cl::init(0),
4135f757f3fSDimitry Andric cl::desc("Set the count value cutoff. Functions with the maximum count "
4145f757f3fSDimitry Andric "less than this value will not be printed out. (Default is 0)"),
4155f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4165f757f3fSDimitry Andric cl::opt<bool> OnlyListBelow(
4175f757f3fSDimitry Andric "list-below-cutoff", cl::init(false),
4185f757f3fSDimitry Andric cl::desc("Only output names of functions whose max count values are "
4195f757f3fSDimitry Andric "below the cutoff value"),
4205f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4215f757f3fSDimitry Andric cl::opt<bool> ShowProfileSymbolList(
4225f757f3fSDimitry Andric "show-prof-sym-list", cl::init(false),
4235f757f3fSDimitry Andric cl::desc("Show profile symbol list if it exists in the profile. "),
4245f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4255f757f3fSDimitry Andric cl::opt<bool> ShowSectionInfoOnly(
4265f757f3fSDimitry Andric "show-sec-info-only", cl::init(false),
4275f757f3fSDimitry Andric cl::desc("Show the information of each section in the sample profile. "
4285f757f3fSDimitry Andric "The flag is only usable when the sample profile is in "
4295f757f3fSDimitry Andric "extbinary format"),
4305f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4315f757f3fSDimitry Andric cl::opt<bool> ShowBinaryIds("binary-ids", cl::init(false),
4325f757f3fSDimitry Andric cl::desc("Show binary ids in the profile. "),
4335f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4345f757f3fSDimitry Andric cl::opt<bool> ShowTemporalProfTraces(
4355f757f3fSDimitry Andric "temporal-profile-traces",
4365f757f3fSDimitry Andric cl::desc("Show temporal profile traces in the profile."),
4375f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4385f757f3fSDimitry Andric
4395f757f3fSDimitry Andric cl::opt<bool>
4405f757f3fSDimitry Andric ShowCovered("covered", cl::init(false),
4415f757f3fSDimitry Andric cl::desc("Show only the functions that have been executed."),
4425f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4435f757f3fSDimitry Andric
4445f757f3fSDimitry Andric cl::opt<bool> ShowProfileVersion("profile-version", cl::init(false),
4455f757f3fSDimitry Andric cl::desc("Show profile version. "),
4465f757f3fSDimitry Andric cl::sub(ShowSubcommand));
4475f757f3fSDimitry Andric
448*0fca6ea1SDimitry Andric // Options specific to order subcommand.
449*0fca6ea1SDimitry Andric cl::opt<unsigned>
450*0fca6ea1SDimitry Andric NumTestTraces("num-test-traces", cl::init(0),
451*0fca6ea1SDimitry Andric cl::desc("Keep aside the last <num-test-traces> traces in "
452*0fca6ea1SDimitry Andric "the profile when computing the function order and "
453*0fca6ea1SDimitry Andric "instead use them to evaluate that order"),
454*0fca6ea1SDimitry Andric cl::sub(OrderSubcommand));
455*0fca6ea1SDimitry Andric
4565f757f3fSDimitry Andric // We use this string to indicate that there are
4575f757f3fSDimitry Andric // multiple static functions map to the same name.
4585f757f3fSDimitry Andric const std::string DuplicateNameStr = "----";
4595f757f3fSDimitry Andric
warn(Twine Message,StringRef Whence="",StringRef Hint="")460*0fca6ea1SDimitry Andric static void warn(Twine Message, StringRef Whence = "", StringRef Hint = "") {
4610b57cec5SDimitry Andric WithColor::warning();
4620b57cec5SDimitry Andric if (!Whence.empty())
4630b57cec5SDimitry Andric errs() << Whence << ": ";
4640b57cec5SDimitry Andric errs() << Message << "\n";
4650b57cec5SDimitry Andric if (!Hint.empty())
4660b57cec5SDimitry Andric WithColor::note() << Hint << "\n";
4670b57cec5SDimitry Andric }
4680b57cec5SDimitry Andric
warn(Error E,StringRef Whence="")469fe6060f1SDimitry Andric static void warn(Error E, StringRef Whence = "") {
470fe6060f1SDimitry Andric if (E.isA<InstrProfError>()) {
471fe6060f1SDimitry Andric handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
472*0fca6ea1SDimitry Andric warn(IPE.message(), Whence);
473fe6060f1SDimitry Andric });
474fe6060f1SDimitry Andric }
475fe6060f1SDimitry Andric }
476fe6060f1SDimitry Andric
exitWithError(Twine Message,StringRef Whence="",StringRef Hint="")477*0fca6ea1SDimitry Andric static void exitWithError(Twine Message, StringRef Whence = "",
478*0fca6ea1SDimitry Andric StringRef Hint = "") {
4790b57cec5SDimitry Andric WithColor::error();
4800b57cec5SDimitry Andric if (!Whence.empty())
4810b57cec5SDimitry Andric errs() << Whence << ": ";
4820b57cec5SDimitry Andric errs() << Message << "\n";
4830b57cec5SDimitry Andric if (!Hint.empty())
4840b57cec5SDimitry Andric WithColor::note() << Hint << "\n";
4850b57cec5SDimitry Andric ::exit(1);
4860b57cec5SDimitry Andric }
4870b57cec5SDimitry Andric
exitWithError(Error E,StringRef Whence="")4880b57cec5SDimitry Andric static void exitWithError(Error E, StringRef Whence = "") {
4890b57cec5SDimitry Andric if (E.isA<InstrProfError>()) {
4900b57cec5SDimitry Andric handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
4910b57cec5SDimitry Andric instrprof_error instrError = IPE.get();
4920b57cec5SDimitry Andric StringRef Hint = "";
4930b57cec5SDimitry Andric if (instrError == instrprof_error::unrecognized_format) {
4944824e7fdSDimitry Andric // Hint in case user missed specifying the profile type.
4954824e7fdSDimitry Andric Hint = "Perhaps you forgot to use the --sample or --memory option?";
4960b57cec5SDimitry Andric }
497*0fca6ea1SDimitry Andric exitWithError(IPE.message(), Whence, Hint);
4980b57cec5SDimitry Andric });
49981ad6265SDimitry Andric return;
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric
502*0fca6ea1SDimitry Andric exitWithError(toString(std::move(E)), Whence);
5030b57cec5SDimitry Andric }
5040b57cec5SDimitry Andric
exitWithErrorCode(std::error_code EC,StringRef Whence="")5050b57cec5SDimitry Andric static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") {
506*0fca6ea1SDimitry Andric exitWithError(EC.message(), Whence);
5070b57cec5SDimitry Andric }
5080b57cec5SDimitry Andric
warnOrExitGivenError(FailureMode FailMode,std::error_code EC,StringRef Whence="")5098bcb0991SDimitry Andric static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC,
5108bcb0991SDimitry Andric StringRef Whence = "") {
5118bcb0991SDimitry Andric if (FailMode == failIfAnyAreInvalid)
5128bcb0991SDimitry Andric exitWithErrorCode(EC, Whence);
5138bcb0991SDimitry Andric else
514*0fca6ea1SDimitry Andric warn(EC.message(), Whence);
5150b57cec5SDimitry Andric }
5160b57cec5SDimitry Andric
handleMergeWriterError(Error E,StringRef WhenceFile="",StringRef WhenceFunction="",bool ShowHint=true)5170b57cec5SDimitry Andric static void handleMergeWriterError(Error E, StringRef WhenceFile = "",
5180b57cec5SDimitry Andric StringRef WhenceFunction = "",
5190b57cec5SDimitry Andric bool ShowHint = true) {
5200b57cec5SDimitry Andric if (!WhenceFile.empty())
5210b57cec5SDimitry Andric errs() << WhenceFile << ": ";
5220b57cec5SDimitry Andric if (!WhenceFunction.empty())
5230b57cec5SDimitry Andric errs() << WhenceFunction << ": ";
5240b57cec5SDimitry Andric
5250b57cec5SDimitry Andric auto IPE = instrprof_error::success;
5260b57cec5SDimitry Andric E = handleErrors(std::move(E),
5270b57cec5SDimitry Andric [&IPE](std::unique_ptr<InstrProfError> E) -> Error {
5280b57cec5SDimitry Andric IPE = E->get();
5290b57cec5SDimitry Andric return Error(std::move(E));
5300b57cec5SDimitry Andric });
5310b57cec5SDimitry Andric errs() << toString(std::move(E)) << "\n";
5320b57cec5SDimitry Andric
5330b57cec5SDimitry Andric if (ShowHint) {
5340b57cec5SDimitry Andric StringRef Hint = "";
5350b57cec5SDimitry Andric if (IPE != instrprof_error::success) {
5360b57cec5SDimitry Andric switch (IPE) {
5370b57cec5SDimitry Andric case instrprof_error::hash_mismatch:
5380b57cec5SDimitry Andric case instrprof_error::count_mismatch:
5390b57cec5SDimitry Andric case instrprof_error::value_site_count_mismatch:
5400b57cec5SDimitry Andric Hint = "Make sure that all profile data to be merged is generated "
5410b57cec5SDimitry Andric "from the same binary.";
5420b57cec5SDimitry Andric break;
5430b57cec5SDimitry Andric default:
5440b57cec5SDimitry Andric break;
5450b57cec5SDimitry Andric }
5460b57cec5SDimitry Andric }
5470b57cec5SDimitry Andric
5480b57cec5SDimitry Andric if (!Hint.empty())
5490b57cec5SDimitry Andric errs() << Hint << "\n";
5500b57cec5SDimitry Andric }
5510b57cec5SDimitry Andric }
5520b57cec5SDimitry Andric
5530b57cec5SDimitry Andric namespace {
5540b57cec5SDimitry Andric /// A remapper from original symbol names to new symbol names based on a file
5550b57cec5SDimitry Andric /// containing a list of mappings from old name to new name.
5560b57cec5SDimitry Andric class SymbolRemapper {
5570b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> File;
5580b57cec5SDimitry Andric DenseMap<StringRef, StringRef> RemappingTable;
5590b57cec5SDimitry Andric
5600b57cec5SDimitry Andric public:
5610b57cec5SDimitry Andric /// Build a SymbolRemapper from a file containing a list of old/new symbols.
create(StringRef InputFile)5620b57cec5SDimitry Andric static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) {
5630b57cec5SDimitry Andric auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
5640b57cec5SDimitry Andric if (!BufOrError)
5650b57cec5SDimitry Andric exitWithErrorCode(BufOrError.getError(), InputFile);
5660b57cec5SDimitry Andric
5678bcb0991SDimitry Andric auto Remapper = std::make_unique<SymbolRemapper>();
5680b57cec5SDimitry Andric Remapper->File = std::move(BufOrError.get());
5690b57cec5SDimitry Andric
5700b57cec5SDimitry Andric for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#');
5710b57cec5SDimitry Andric !LineIt.is_at_eof(); ++LineIt) {
5720b57cec5SDimitry Andric std::pair<StringRef, StringRef> Parts = LineIt->split(' ');
5730b57cec5SDimitry Andric if (Parts.first.empty() || Parts.second.empty() ||
5740b57cec5SDimitry Andric Parts.second.count(' ')) {
5750b57cec5SDimitry Andric exitWithError("unexpected line in remapping file",
5760b57cec5SDimitry Andric (InputFile + ":" + Twine(LineIt.line_number())).str(),
5770b57cec5SDimitry Andric "expected 'old_symbol new_symbol'");
5780b57cec5SDimitry Andric }
5790b57cec5SDimitry Andric Remapper->RemappingTable.insert(Parts);
5800b57cec5SDimitry Andric }
5810b57cec5SDimitry Andric return Remapper;
5820b57cec5SDimitry Andric }
5830b57cec5SDimitry Andric
5840b57cec5SDimitry Andric /// Attempt to map the given old symbol into a new symbol.
5850b57cec5SDimitry Andric ///
5860b57cec5SDimitry Andric /// \return The new symbol, or \p Name if no such symbol was found.
operator ()(StringRef Name)5870b57cec5SDimitry Andric StringRef operator()(StringRef Name) {
5880b57cec5SDimitry Andric StringRef New = RemappingTable.lookup(Name);
5890b57cec5SDimitry Andric return New.empty() ? Name : New;
5900b57cec5SDimitry Andric }
5915f757f3fSDimitry Andric
operator ()(FunctionId Name)5925f757f3fSDimitry Andric FunctionId operator()(FunctionId Name) {
5935f757f3fSDimitry Andric // MD5 name cannot be remapped.
5945f757f3fSDimitry Andric if (!Name.isStringRef())
5955f757f3fSDimitry Andric return Name;
5965f757f3fSDimitry Andric StringRef New = RemappingTable.lookup(Name.stringRef());
5975f757f3fSDimitry Andric return New.empty() ? Name : FunctionId(New);
5985f757f3fSDimitry Andric }
5990b57cec5SDimitry Andric };
6000b57cec5SDimitry Andric }
6010b57cec5SDimitry Andric
6020b57cec5SDimitry Andric struct WeightedFile {
6030b57cec5SDimitry Andric std::string Filename;
6040b57cec5SDimitry Andric uint64_t Weight;
6050b57cec5SDimitry Andric };
6060b57cec5SDimitry Andric typedef SmallVector<WeightedFile, 5> WeightedFileVector;
6070b57cec5SDimitry Andric
6080b57cec5SDimitry Andric /// Keep track of merged data and reported errors.
6090b57cec5SDimitry Andric struct WriterContext {
6100b57cec5SDimitry Andric std::mutex Lock;
6110b57cec5SDimitry Andric InstrProfWriter Writer;
6128bcb0991SDimitry Andric std::vector<std::pair<Error, std::string>> Errors;
6130b57cec5SDimitry Andric std::mutex &ErrLock;
6140b57cec5SDimitry Andric SmallSet<instrprof_error, 4> &WriterErrorCodes;
6150b57cec5SDimitry Andric
WriterContextWriterContext6160b57cec5SDimitry Andric WriterContext(bool IsSparse, std::mutex &ErrLock,
61706c3fb27SDimitry Andric SmallSet<instrprof_error, 4> &WriterErrorCodes,
61806c3fb27SDimitry Andric uint64_t ReservoirSize = 0, uint64_t MaxTraceLength = 0)
619*0fca6ea1SDimitry Andric : Writer(IsSparse, ReservoirSize, MaxTraceLength, DoWritePrevVersion,
620*0fca6ea1SDimitry Andric MemProfVersionRequested, MemProfFullSchema),
621*0fca6ea1SDimitry Andric ErrLock(ErrLock), WriterErrorCodes(WriterErrorCodes) {}
6220b57cec5SDimitry Andric };
6230b57cec5SDimitry Andric
6240b57cec5SDimitry Andric /// Computer the overlap b/w profile BaseFilename and TestFileName,
6250b57cec5SDimitry 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)6260b57cec5SDimitry Andric static void overlapInput(const std::string &BaseFilename,
6270b57cec5SDimitry Andric const std::string &TestFilename, WriterContext *WC,
6280b57cec5SDimitry Andric OverlapStats &Overlap,
6290b57cec5SDimitry Andric const OverlapFuncFilters &FuncFilter,
6300b57cec5SDimitry Andric raw_fd_ostream &OS, bool IsCS) {
63106c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
63206c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(TestFilename, *FS);
6330b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError()) {
6340b57cec5SDimitry Andric // Skip the empty profiles by returning sliently.
63506c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
63606c3fb27SDimitry Andric if (ErrorCode != instrprof_error::empty_raw_profile)
63706c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
63806c3fb27SDimitry Andric TestFilename);
6390b57cec5SDimitry Andric return;
6400b57cec5SDimitry Andric }
6410b57cec5SDimitry Andric
6420b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
6430b57cec5SDimitry Andric for (auto &I : *Reader) {
6440b57cec5SDimitry Andric OverlapStats FuncOverlap(OverlapStats::FunctionLevel);
6450b57cec5SDimitry Andric FuncOverlap.setFuncInfo(I.Name, I.Hash);
6460b57cec5SDimitry Andric
6470b57cec5SDimitry Andric WC->Writer.overlapRecord(std::move(I), Overlap, FuncOverlap, FuncFilter);
6480b57cec5SDimitry Andric FuncOverlap.dump(OS);
6490b57cec5SDimitry Andric }
6500b57cec5SDimitry Andric }
6510b57cec5SDimitry Andric
6520b57cec5SDimitry Andric /// Load an input into a writer context.
loadInput(const WeightedFile & Input,SymbolRemapper * Remapper,const InstrProfCorrelator * Correlator,const StringRef ProfiledBinary,WriterContext * WC)6530b57cec5SDimitry Andric static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper,
6540eae32dcSDimitry Andric const InstrProfCorrelator *Correlator,
65581ad6265SDimitry Andric const StringRef ProfiledBinary, WriterContext *WC) {
6560b57cec5SDimitry Andric std::unique_lock<std::mutex> CtxGuard{WC->Lock};
6570b57cec5SDimitry Andric
6580b57cec5SDimitry Andric // Copy the filename, because llvm::ThreadPool copied the input "const
6590b57cec5SDimitry Andric // WeightedFile &" by value, making a reference to the filename within it
6600b57cec5SDimitry Andric // invalid outside of this packaged task.
6618bcb0991SDimitry Andric std::string Filename = Input.Filename;
6620b57cec5SDimitry Andric
66381ad6265SDimitry Andric using ::llvm::memprof::RawMemProfReader;
66481ad6265SDimitry Andric if (RawMemProfReader::hasFormat(Input.Filename)) {
66581ad6265SDimitry Andric auto ReaderOrErr = RawMemProfReader::create(Input.Filename, ProfiledBinary);
66681ad6265SDimitry Andric if (!ReaderOrErr) {
66781ad6265SDimitry Andric exitWithError(ReaderOrErr.takeError(), Input.Filename);
66881ad6265SDimitry Andric }
66981ad6265SDimitry Andric std::unique_ptr<RawMemProfReader> Reader = std::move(ReaderOrErr.get());
67081ad6265SDimitry Andric // Check if the profile types can be merged, e.g. clang frontend profiles
67181ad6265SDimitry Andric // should not be merged with memprof profiles.
67281ad6265SDimitry Andric if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
67381ad6265SDimitry Andric consumeError(std::move(E));
67481ad6265SDimitry Andric WC->Errors.emplace_back(
67581ad6265SDimitry Andric make_error<StringError>(
67681ad6265SDimitry Andric "Cannot merge MemProf profile with Clang generated profile.",
67781ad6265SDimitry Andric std::error_code()),
67881ad6265SDimitry Andric Filename);
67981ad6265SDimitry Andric return;
68081ad6265SDimitry Andric }
68181ad6265SDimitry Andric
68281ad6265SDimitry Andric auto MemProfError = [&](Error E) {
68306c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
68406c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrorCode, Msg),
68506c3fb27SDimitry Andric Filename);
68681ad6265SDimitry Andric };
68781ad6265SDimitry Andric
68881ad6265SDimitry Andric // Add the frame mappings into the writer context.
68981ad6265SDimitry Andric const auto &IdToFrame = Reader->getFrameMapping();
69081ad6265SDimitry Andric for (const auto &I : IdToFrame) {
69181ad6265SDimitry Andric bool Succeeded = WC->Writer.addMemProfFrame(
69281ad6265SDimitry Andric /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
69381ad6265SDimitry Andric // If we weren't able to add the frame mappings then it doesn't make sense
69481ad6265SDimitry Andric // to try to add the records from this profile.
69581ad6265SDimitry Andric if (!Succeeded)
69681ad6265SDimitry Andric return;
69781ad6265SDimitry Andric }
698*0fca6ea1SDimitry Andric
699*0fca6ea1SDimitry Andric // Add the call stacks into the writer context.
700*0fca6ea1SDimitry Andric const auto &CSIdToCallStacks = Reader->getCallStacks();
701*0fca6ea1SDimitry Andric for (const auto &I : CSIdToCallStacks) {
702*0fca6ea1SDimitry Andric bool Succeeded = WC->Writer.addMemProfCallStack(
703*0fca6ea1SDimitry Andric /*Id=*/I.first, /*Frame=*/I.getSecond(), MemProfError);
704*0fca6ea1SDimitry Andric // If we weren't able to add the call stacks then it doesn't make sense
705*0fca6ea1SDimitry Andric // to try to add the records from this profile.
706*0fca6ea1SDimitry Andric if (!Succeeded)
707*0fca6ea1SDimitry Andric return;
708*0fca6ea1SDimitry Andric }
709*0fca6ea1SDimitry Andric
71081ad6265SDimitry Andric const auto &FunctionProfileData = Reader->getProfileData();
71181ad6265SDimitry Andric // Add the memprof records into the writer context.
712*0fca6ea1SDimitry Andric for (const auto &[GUID, Record] : FunctionProfileData) {
713*0fca6ea1SDimitry Andric WC->Writer.addMemProfRecord(GUID, Record);
71481ad6265SDimitry Andric }
71581ad6265SDimitry Andric return;
71681ad6265SDimitry Andric }
71781ad6265SDimitry Andric
71806c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
7195f757f3fSDimitry Andric // TODO: This only saves the first non-fatal error from InstrProfReader, and
7205f757f3fSDimitry Andric // then added to WriterContext::Errors. However, this is not extensible, if
7215f757f3fSDimitry Andric // we have more non-fatal errors from InstrProfReader in the future. How
7225f757f3fSDimitry Andric // should this interact with different -failure-mode?
7235f757f3fSDimitry Andric std::optional<std::pair<Error, std::string>> ReaderWarning;
7245f757f3fSDimitry Andric auto Warn = [&](Error E) {
7255f757f3fSDimitry Andric if (ReaderWarning) {
7265f757f3fSDimitry Andric consumeError(std::move(E));
7275f757f3fSDimitry Andric return;
7285f757f3fSDimitry Andric }
7295f757f3fSDimitry Andric // Only show the first time an error occurs in this file.
7305f757f3fSDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7315f757f3fSDimitry Andric ReaderWarning = {make_error<InstrProfError>(ErrCode, Msg), Filename};
7325f757f3fSDimitry Andric };
7335f757f3fSDimitry Andric auto ReaderOrErr =
7345f757f3fSDimitry Andric InstrProfReader::create(Input.Filename, *FS, Correlator, Warn);
7350b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError()) {
73606c3fb27SDimitry Andric // Skip the empty profiles by returning silently.
73706c3fb27SDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
73806c3fb27SDimitry Andric if (ErrCode != instrprof_error::empty_raw_profile)
73906c3fb27SDimitry Andric WC->Errors.emplace_back(make_error<InstrProfError>(ErrCode, Msg),
74006c3fb27SDimitry Andric Filename);
7410b57cec5SDimitry Andric return;
7420b57cec5SDimitry Andric }
7430b57cec5SDimitry Andric
7440b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
7451fd87a68SDimitry Andric if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) {
746fe6060f1SDimitry Andric consumeError(std::move(E));
7478bcb0991SDimitry Andric WC->Errors.emplace_back(
7488bcb0991SDimitry Andric make_error<StringError>(
7490b57cec5SDimitry Andric "Merge IR generated profile with Clang generated profile.",
7508bcb0991SDimitry Andric std::error_code()),
7518bcb0991SDimitry Andric Filename);
7520b57cec5SDimitry Andric return;
7530b57cec5SDimitry Andric }
7540b57cec5SDimitry Andric
7550b57cec5SDimitry Andric for (auto &I : *Reader) {
7560b57cec5SDimitry Andric if (Remapper)
7570b57cec5SDimitry Andric I.Name = (*Remapper)(I.Name);
7580b57cec5SDimitry Andric const StringRef FuncName = I.Name;
7590b57cec5SDimitry Andric bool Reported = false;
7600b57cec5SDimitry Andric WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
7610b57cec5SDimitry Andric if (Reported) {
7620b57cec5SDimitry Andric consumeError(std::move(E));
7630b57cec5SDimitry Andric return;
7640b57cec5SDimitry Andric }
7650b57cec5SDimitry Andric Reported = true;
7660b57cec5SDimitry Andric // Only show hint the first time an error occurs.
76706c3fb27SDimitry Andric auto [ErrCode, Msg] = InstrProfError::take(std::move(E));
7680b57cec5SDimitry Andric std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
76906c3fb27SDimitry Andric bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second;
77006c3fb27SDimitry Andric handleMergeWriterError(make_error<InstrProfError>(ErrCode, Msg),
77106c3fb27SDimitry Andric Input.Filename, FuncName, firstTime);
7720b57cec5SDimitry Andric });
7730b57cec5SDimitry Andric }
774bdd1243dSDimitry Andric
775*0fca6ea1SDimitry Andric if (KeepVTableSymbols) {
776*0fca6ea1SDimitry Andric const InstrProfSymtab &symtab = Reader->getSymtab();
777*0fca6ea1SDimitry Andric const auto &VTableNames = symtab.getVTableNames();
778*0fca6ea1SDimitry Andric
779*0fca6ea1SDimitry Andric for (const auto &kv : VTableNames)
780*0fca6ea1SDimitry Andric WC->Writer.addVTableName(kv.getKey());
781*0fca6ea1SDimitry Andric }
782*0fca6ea1SDimitry Andric
78306c3fb27SDimitry Andric if (Reader->hasTemporalProfile()) {
78406c3fb27SDimitry Andric auto &Traces = Reader->getTemporalProfTraces(Input.Weight);
78506c3fb27SDimitry Andric if (!Traces.empty())
78606c3fb27SDimitry Andric WC->Writer.addTemporalProfileTraces(
78706c3fb27SDimitry Andric Traces, Reader->getTemporalProfTraceStreamSize());
78806c3fb27SDimitry Andric }
789bdd1243dSDimitry Andric if (Reader->hasError()) {
7905f757f3fSDimitry Andric if (Error E = Reader->getError()) {
7918bcb0991SDimitry Andric WC->Errors.emplace_back(std::move(E), Filename);
7925f757f3fSDimitry Andric return;
7935f757f3fSDimitry Andric }
7940b57cec5SDimitry Andric }
7950b57cec5SDimitry Andric
796bdd1243dSDimitry Andric std::vector<llvm::object::BuildID> BinaryIds;
7975f757f3fSDimitry Andric if (Error E = Reader->readBinaryIds(BinaryIds)) {
798bdd1243dSDimitry Andric WC->Errors.emplace_back(std::move(E), Filename);
7995f757f3fSDimitry Andric return;
8005f757f3fSDimitry Andric }
801bdd1243dSDimitry Andric WC->Writer.addBinaryIds(BinaryIds);
8025f757f3fSDimitry Andric
8035f757f3fSDimitry Andric if (ReaderWarning) {
8045f757f3fSDimitry Andric WC->Errors.emplace_back(std::move(ReaderWarning->first),
8055f757f3fSDimitry Andric ReaderWarning->second);
8065f757f3fSDimitry Andric }
807bdd1243dSDimitry Andric }
808bdd1243dSDimitry Andric
8090b57cec5SDimitry Andric /// Merge the \p Src writer context into \p Dst.
mergeWriterContexts(WriterContext * Dst,WriterContext * Src)8100b57cec5SDimitry Andric static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
8118bcb0991SDimitry Andric for (auto &ErrorPair : Src->Errors)
8128bcb0991SDimitry Andric Dst->Errors.push_back(std::move(ErrorPair));
8138bcb0991SDimitry Andric Src->Errors.clear();
8140b57cec5SDimitry Andric
815bdd1243dSDimitry Andric if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind()))
816bdd1243dSDimitry Andric exitWithError(std::move(E));
817bdd1243dSDimitry Andric
8180b57cec5SDimitry Andric Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
81906c3fb27SDimitry Andric auto [ErrorCode, Msg] = InstrProfError::take(std::move(E));
8208bcb0991SDimitry Andric std::unique_lock<std::mutex> ErrGuard{Dst->ErrLock};
82106c3fb27SDimitry Andric bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second;
8228bcb0991SDimitry Andric if (firstTime)
82306c3fb27SDimitry Andric warn(toString(make_error<InstrProfError>(ErrorCode, Msg)));
8240b57cec5SDimitry Andric });
8250b57cec5SDimitry Andric }
8260b57cec5SDimitry Andric
8277a6dacacSDimitry Andric static StringRef
getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type & Val)8287a6dacacSDimitry Andric getFuncName(const StringMap<InstrProfWriter::ProfilingData>::value_type &Val) {
8297a6dacacSDimitry Andric return Val.first();
8307a6dacacSDimitry Andric }
8317a6dacacSDimitry Andric
8327a6dacacSDimitry Andric static std::string
getFuncName(const SampleProfileMap::value_type & Val)8337a6dacacSDimitry Andric getFuncName(const SampleProfileMap::value_type &Val) {
8347a6dacacSDimitry Andric return Val.second.getContext().toString();
8357a6dacacSDimitry Andric }
8367a6dacacSDimitry Andric
8377a6dacacSDimitry Andric template <typename T>
filterFunctions(T & ProfileMap)8387a6dacacSDimitry Andric static void filterFunctions(T &ProfileMap) {
8397a6dacacSDimitry Andric bool hasFilter = !FuncNameFilter.empty();
8407a6dacacSDimitry Andric bool hasNegativeFilter = !FuncNameNegativeFilter.empty();
8417a6dacacSDimitry Andric if (!hasFilter && !hasNegativeFilter)
8427a6dacacSDimitry Andric return;
8437a6dacacSDimitry Andric
8447a6dacacSDimitry Andric // If filter starts with '?' it is MSVC mangled name, not a regex.
8457a6dacacSDimitry Andric llvm::Regex ProbablyMSVCMangledName("[?@$_0-9A-Za-z]+");
8467a6dacacSDimitry Andric if (hasFilter && FuncNameFilter[0] == '?' &&
8477a6dacacSDimitry Andric ProbablyMSVCMangledName.match(FuncNameFilter))
8487a6dacacSDimitry Andric FuncNameFilter = llvm::Regex::escape(FuncNameFilter);
8497a6dacacSDimitry Andric if (hasNegativeFilter && FuncNameNegativeFilter[0] == '?' &&
8507a6dacacSDimitry Andric ProbablyMSVCMangledName.match(FuncNameNegativeFilter))
8517a6dacacSDimitry Andric FuncNameNegativeFilter = llvm::Regex::escape(FuncNameNegativeFilter);
8527a6dacacSDimitry Andric
8537a6dacacSDimitry Andric size_t Count = ProfileMap.size();
8547a6dacacSDimitry Andric llvm::Regex Pattern(FuncNameFilter);
8557a6dacacSDimitry Andric llvm::Regex NegativePattern(FuncNameNegativeFilter);
8567a6dacacSDimitry Andric std::string Error;
8577a6dacacSDimitry Andric if (hasFilter && !Pattern.isValid(Error))
8587a6dacacSDimitry Andric exitWithError(Error);
8597a6dacacSDimitry Andric if (hasNegativeFilter && !NegativePattern.isValid(Error))
8607a6dacacSDimitry Andric exitWithError(Error);
8617a6dacacSDimitry Andric
8627a6dacacSDimitry Andric // Handle MD5 profile, so it is still able to match using the original name.
8637a6dacacSDimitry Andric std::string MD5Name = std::to_string(llvm::MD5Hash(FuncNameFilter));
8647a6dacacSDimitry Andric std::string NegativeMD5Name =
8657a6dacacSDimitry Andric std::to_string(llvm::MD5Hash(FuncNameNegativeFilter));
8667a6dacacSDimitry Andric
8677a6dacacSDimitry Andric for (auto I = ProfileMap.begin(); I != ProfileMap.end();) {
8687a6dacacSDimitry Andric auto Tmp = I++;
8697a6dacacSDimitry Andric const auto &FuncName = getFuncName(*Tmp);
8707a6dacacSDimitry Andric // Negative filter has higher precedence than positive filter.
8717a6dacacSDimitry Andric if ((hasNegativeFilter &&
8727a6dacacSDimitry Andric (NegativePattern.match(FuncName) ||
8737a6dacacSDimitry Andric (FunctionSamples::UseMD5 && NegativeMD5Name == FuncName))) ||
8747a6dacacSDimitry Andric (hasFilter && !(Pattern.match(FuncName) ||
8757a6dacacSDimitry Andric (FunctionSamples::UseMD5 && MD5Name == FuncName))))
8767a6dacacSDimitry Andric ProfileMap.erase(Tmp);
8777a6dacacSDimitry Andric }
8787a6dacacSDimitry Andric
8797a6dacacSDimitry Andric llvm::dbgs() << Count - ProfileMap.size() << " of " << Count << " functions "
8807a6dacacSDimitry Andric << "in the original profile are filtered.\n";
8817a6dacacSDimitry Andric }
8827a6dacacSDimitry Andric
writeInstrProfile(StringRef OutputFilename,ProfileFormat OutputFormat,InstrProfWriter & Writer)8835ffd83dbSDimitry Andric static void writeInstrProfile(StringRef OutputFilename,
8845ffd83dbSDimitry Andric ProfileFormat OutputFormat,
8855ffd83dbSDimitry Andric InstrProfWriter &Writer) {
8865ffd83dbSDimitry Andric std::error_code EC;
887e8d8bef9SDimitry Andric raw_fd_ostream Output(OutputFilename.data(), EC,
888fe6060f1SDimitry Andric OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF
889e8d8bef9SDimitry Andric : sys::fs::OF_None);
8905ffd83dbSDimitry Andric if (EC)
8915ffd83dbSDimitry Andric exitWithErrorCode(EC, OutputFilename);
8925ffd83dbSDimitry Andric
8935ffd83dbSDimitry Andric if (OutputFormat == PF_Text) {
8945ffd83dbSDimitry Andric if (Error E = Writer.writeText(Output))
895fe6060f1SDimitry Andric warn(std::move(E));
8965ffd83dbSDimitry Andric } else {
897fe6060f1SDimitry Andric if (Output.is_displayed())
898fe6060f1SDimitry Andric exitWithError("cannot write a non-text format profile to the terminal");
899fe6060f1SDimitry Andric if (Error E = Writer.write(Output))
900fe6060f1SDimitry Andric warn(std::move(E));
9015ffd83dbSDimitry Andric }
9025ffd83dbSDimitry Andric }
9035ffd83dbSDimitry Andric
mergeInstrProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,int MaxDbgCorrelationWarnings,const StringRef ProfiledBinary)9045f757f3fSDimitry Andric static void mergeInstrProfile(const WeightedFileVector &Inputs,
9055f757f3fSDimitry Andric SymbolRemapper *Remapper,
9065f757f3fSDimitry Andric int MaxDbgCorrelationWarnings,
90781ad6265SDimitry Andric const StringRef ProfiledBinary) {
9085f757f3fSDimitry Andric const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue();
9095f757f3fSDimitry Andric const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue();
91006c3fb27SDimitry Andric if (OutputFormat == PF_Compact_Binary)
91106c3fb27SDimitry Andric exitWithError("Compact Binary is deprecated");
91206c3fb27SDimitry Andric if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary &&
91306c3fb27SDimitry Andric OutputFormat != PF_Text)
914fe6060f1SDimitry Andric exitWithError("unknown format is specified");
9150b57cec5SDimitry Andric
9165f757f3fSDimitry Andric // TODO: Maybe we should support correlation with mixture of different
9175f757f3fSDimitry Andric // correlation modes(w/wo debug-info/object correlation).
9185f757f3fSDimitry Andric if (!DebugInfoFilename.empty() && !BinaryFilename.empty())
9195f757f3fSDimitry Andric exitWithError("Expected only one of -debug-info, -binary-file");
9205f757f3fSDimitry Andric std::string CorrelateFilename;
9215f757f3fSDimitry Andric ProfCorrelatorKind CorrelateKind = ProfCorrelatorKind::NONE;
9220eae32dcSDimitry Andric if (!DebugInfoFilename.empty()) {
9235f757f3fSDimitry Andric CorrelateFilename = DebugInfoFilename;
9245f757f3fSDimitry Andric CorrelateKind = ProfCorrelatorKind::DEBUG_INFO;
9255f757f3fSDimitry Andric } else if (!BinaryFilename.empty()) {
9265f757f3fSDimitry Andric CorrelateFilename = BinaryFilename;
9275f757f3fSDimitry Andric CorrelateKind = ProfCorrelatorKind::BINARY;
9285f757f3fSDimitry Andric }
9295f757f3fSDimitry Andric
9305f757f3fSDimitry Andric std::unique_ptr<InstrProfCorrelator> Correlator;
9315f757f3fSDimitry Andric if (CorrelateKind != InstrProfCorrelator::NONE) {
9325f757f3fSDimitry Andric if (auto Err = InstrProfCorrelator::get(CorrelateFilename, CorrelateKind)
9335f757f3fSDimitry Andric .moveInto(Correlator))
9345f757f3fSDimitry Andric exitWithError(std::move(Err), CorrelateFilename);
9355f757f3fSDimitry Andric if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
9365f757f3fSDimitry Andric exitWithError(std::move(Err), CorrelateFilename);
9370eae32dcSDimitry Andric }
9380eae32dcSDimitry Andric
9390b57cec5SDimitry Andric std::mutex ErrorLock;
9400b57cec5SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
9410b57cec5SDimitry Andric
9420b57cec5SDimitry Andric // If NumThreads is not specified, auto-detect a good default.
9430b57cec5SDimitry Andric if (NumThreads == 0)
9445ffd83dbSDimitry Andric NumThreads = std::min(hardware_concurrency().compute_thread_count(),
9455ffd83dbSDimitry Andric unsigned((Inputs.size() + 1) / 2));
9460b57cec5SDimitry Andric
9470b57cec5SDimitry Andric // Initialize the writer contexts.
9480b57cec5SDimitry Andric SmallVector<std::unique_ptr<WriterContext>, 4> Contexts;
9490b57cec5SDimitry Andric for (unsigned I = 0; I < NumThreads; ++I)
9508bcb0991SDimitry Andric Contexts.emplace_back(std::make_unique<WriterContext>(
95106c3fb27SDimitry Andric OutputSparse, ErrorLock, WriterErrorCodes, TraceReservoirSize,
95206c3fb27SDimitry Andric MaxTraceLength));
9530b57cec5SDimitry Andric
9540b57cec5SDimitry Andric if (NumThreads == 1) {
9550b57cec5SDimitry Andric for (const auto &Input : Inputs)
95681ad6265SDimitry Andric loadInput(Input, Remapper, Correlator.get(), ProfiledBinary,
95781ad6265SDimitry Andric Contexts[0].get());
9580b57cec5SDimitry Andric } else {
959*0fca6ea1SDimitry Andric DefaultThreadPool Pool(hardware_concurrency(NumThreads));
9600b57cec5SDimitry Andric
9610b57cec5SDimitry Andric // Load the inputs in parallel (N/NumThreads serial steps).
9620b57cec5SDimitry Andric unsigned Ctx = 0;
9630b57cec5SDimitry Andric for (const auto &Input : Inputs) {
96481ad6265SDimitry Andric Pool.async(loadInput, Input, Remapper, Correlator.get(), ProfiledBinary,
9650eae32dcSDimitry Andric Contexts[Ctx].get());
9660b57cec5SDimitry Andric Ctx = (Ctx + 1) % NumThreads;
9670b57cec5SDimitry Andric }
9680b57cec5SDimitry Andric Pool.wait();
9690b57cec5SDimitry Andric
9700b57cec5SDimitry Andric // Merge the writer contexts together (~ lg(NumThreads) serial steps).
9710b57cec5SDimitry Andric unsigned Mid = Contexts.size() / 2;
9720b57cec5SDimitry Andric unsigned End = Contexts.size();
9730b57cec5SDimitry Andric assert(Mid > 0 && "Expected more than one context");
9740b57cec5SDimitry Andric do {
9750b57cec5SDimitry Andric for (unsigned I = 0; I < Mid; ++I)
9760b57cec5SDimitry Andric Pool.async(mergeWriterContexts, Contexts[I].get(),
9770b57cec5SDimitry Andric Contexts[I + Mid].get());
9780b57cec5SDimitry Andric Pool.wait();
9790b57cec5SDimitry Andric if (End & 1) {
9800b57cec5SDimitry Andric Pool.async(mergeWriterContexts, Contexts[0].get(),
9810b57cec5SDimitry Andric Contexts[End - 1].get());
9820b57cec5SDimitry Andric Pool.wait();
9830b57cec5SDimitry Andric }
9840b57cec5SDimitry Andric End = Mid;
9850b57cec5SDimitry Andric Mid /= 2;
9860b57cec5SDimitry Andric } while (Mid > 0);
9870b57cec5SDimitry Andric }
9880b57cec5SDimitry Andric
9898bcb0991SDimitry Andric // Handle deferred errors encountered during merging. If the number of errors
9908bcb0991SDimitry Andric // is equal to the number of inputs the merge failed.
9918bcb0991SDimitry Andric unsigned NumErrors = 0;
9920b57cec5SDimitry Andric for (std::unique_ptr<WriterContext> &WC : Contexts) {
9938bcb0991SDimitry Andric for (auto &ErrorPair : WC->Errors) {
9948bcb0991SDimitry Andric ++NumErrors;
9958bcb0991SDimitry Andric warn(toString(std::move(ErrorPair.first)), ErrorPair.second);
9960b57cec5SDimitry Andric }
9978bcb0991SDimitry Andric }
9985f757f3fSDimitry Andric if ((NumErrors == Inputs.size() && FailMode == failIfAllAreInvalid) ||
9998bcb0991SDimitry Andric (NumErrors > 0 && FailMode == failIfAnyAreInvalid))
1000fe6060f1SDimitry Andric exitWithError("no profile can be merged");
10010b57cec5SDimitry Andric
10027a6dacacSDimitry Andric filterFunctions(Contexts[0]->Writer.getProfileData());
10037a6dacacSDimitry Andric
10045ffd83dbSDimitry Andric writeInstrProfile(OutputFilename, OutputFormat, Contexts[0]->Writer);
10050b57cec5SDimitry Andric }
10060b57cec5SDimitry Andric
1007e8d8bef9SDimitry Andric /// The profile entry for a function in instrumentation profile.
1008e8d8bef9SDimitry Andric struct InstrProfileEntry {
1009e8d8bef9SDimitry Andric uint64_t MaxCount = 0;
1010bdd1243dSDimitry Andric uint64_t NumEdgeCounters = 0;
1011e8d8bef9SDimitry Andric float ZeroCounterRatio = 0.0;
1012e8d8bef9SDimitry Andric InstrProfRecord *ProfRecord;
1013e8d8bef9SDimitry Andric InstrProfileEntry(InstrProfRecord *Record);
1014e8d8bef9SDimitry Andric InstrProfileEntry() = default;
1015e8d8bef9SDimitry Andric };
1016e8d8bef9SDimitry Andric
InstrProfileEntry(InstrProfRecord * Record)1017e8d8bef9SDimitry Andric InstrProfileEntry::InstrProfileEntry(InstrProfRecord *Record) {
1018e8d8bef9SDimitry Andric ProfRecord = Record;
1019e8d8bef9SDimitry Andric uint64_t CntNum = Record->Counts.size();
1020e8d8bef9SDimitry Andric uint64_t ZeroCntNum = 0;
1021e8d8bef9SDimitry Andric for (size_t I = 0; I < CntNum; ++I) {
1022e8d8bef9SDimitry Andric MaxCount = std::max(MaxCount, Record->Counts[I]);
1023e8d8bef9SDimitry Andric ZeroCntNum += !Record->Counts[I];
1024e8d8bef9SDimitry Andric }
1025e8d8bef9SDimitry Andric ZeroCounterRatio = (float)ZeroCntNum / CntNum;
1026bdd1243dSDimitry Andric NumEdgeCounters = CntNum;
1027e8d8bef9SDimitry Andric }
1028e8d8bef9SDimitry Andric
1029bdd1243dSDimitry Andric /// Either set all the counters in the instr profile entry \p IFE to
1030bdd1243dSDimitry Andric /// -1 / -2 /in order to drop the profile or scale up the
1031bdd1243dSDimitry Andric /// counters in \p IFP to be above hot / cold threshold. We use
1032bdd1243dSDimitry Andric /// the ratio of zero counters in the profile of a function to
1033bdd1243dSDimitry Andric /// decide the profile is helpful or harmful for performance,
1034bdd1243dSDimitry Andric /// and to choose whether to scale up or drop it.
updateInstrProfileEntry(InstrProfileEntry & IFE,bool SetToHot,uint64_t HotInstrThreshold,uint64_t ColdInstrThreshold,float ZeroCounterThreshold)1035bdd1243dSDimitry Andric static void updateInstrProfileEntry(InstrProfileEntry &IFE, bool SetToHot,
1036e8d8bef9SDimitry Andric uint64_t HotInstrThreshold,
1037bdd1243dSDimitry Andric uint64_t ColdInstrThreshold,
1038e8d8bef9SDimitry Andric float ZeroCounterThreshold) {
1039e8d8bef9SDimitry Andric InstrProfRecord *ProfRecord = IFE.ProfRecord;
1040e8d8bef9SDimitry Andric if (!IFE.MaxCount || IFE.ZeroCounterRatio > ZeroCounterThreshold) {
1041e8d8bef9SDimitry Andric // If all or most of the counters of the function are zero, the
1042bdd1243dSDimitry Andric // profile is unaccountable and should be dropped. Reset all the
1043bdd1243dSDimitry Andric // counters to be -1 / -2 and PGO profile-use will drop the profile.
1044e8d8bef9SDimitry Andric // All counters being -1 also implies that the function is hot so
1045e8d8bef9SDimitry Andric // PGO profile-use will also set the entry count metadata to be
1046e8d8bef9SDimitry Andric // above hot threshold.
1047bdd1243dSDimitry Andric // All counters being -2 implies that the function is warm so
1048bdd1243dSDimitry Andric // PGO profile-use will also set the entry count metadata to be
1049bdd1243dSDimitry Andric // above cold threshold.
1050bdd1243dSDimitry Andric auto Kind =
1051bdd1243dSDimitry Andric (SetToHot ? InstrProfRecord::PseudoHot : InstrProfRecord::PseudoWarm);
1052bdd1243dSDimitry Andric ProfRecord->setPseudoCount(Kind);
1053e8d8bef9SDimitry Andric return;
1054e8d8bef9SDimitry Andric }
1055e8d8bef9SDimitry Andric
1056bdd1243dSDimitry Andric // Scale up the MaxCount to be multiple times above hot / cold threshold.
1057e8d8bef9SDimitry Andric const unsigned MultiplyFactor = 3;
1058bdd1243dSDimitry Andric uint64_t Threshold = (SetToHot ? HotInstrThreshold : ColdInstrThreshold);
1059bdd1243dSDimitry Andric uint64_t Numerator = Threshold * MultiplyFactor;
1060bdd1243dSDimitry Andric
1061bdd1243dSDimitry Andric // Make sure Threshold for warm counters is below the HotInstrThreshold.
1062bdd1243dSDimitry Andric if (!SetToHot && Threshold >= HotInstrThreshold) {
1063bdd1243dSDimitry Andric Threshold = (HotInstrThreshold + ColdInstrThreshold) / 2;
1064bdd1243dSDimitry Andric }
1065bdd1243dSDimitry Andric
1066e8d8bef9SDimitry Andric uint64_t Denominator = IFE.MaxCount;
1067bdd1243dSDimitry Andric if (Numerator <= Denominator)
1068bdd1243dSDimitry Andric return;
1069e8d8bef9SDimitry Andric ProfRecord->scale(Numerator, Denominator, [&](instrprof_error E) {
1070e8d8bef9SDimitry Andric warn(toString(make_error<InstrProfError>(E)));
1071e8d8bef9SDimitry Andric });
1072e8d8bef9SDimitry Andric }
1073e8d8bef9SDimitry Andric
1074e8d8bef9SDimitry Andric const uint64_t ColdPercentileIdx = 15;
1075e8d8bef9SDimitry Andric const uint64_t HotPercentileIdx = 11;
1076e8d8bef9SDimitry Andric
1077fe6060f1SDimitry Andric using sampleprof::FSDiscriminatorPass;
1078fe6060f1SDimitry Andric
1079fe6060f1SDimitry Andric // Internal options to set FSDiscriminatorPass. Used in merge and show
1080fe6060f1SDimitry Andric // commands.
1081fe6060f1SDimitry Andric static cl::opt<FSDiscriminatorPass> FSDiscriminatorPassOption(
1082fe6060f1SDimitry Andric "fs-discriminator-pass", cl::init(PassLast), cl::Hidden,
1083fe6060f1SDimitry Andric cl::desc("Zero out the discriminator bits for the FS discrimiantor "
1084fe6060f1SDimitry Andric "pass beyond this value. The enum values are defined in "
1085fe6060f1SDimitry Andric "Support/Discriminator.h"),
1086fe6060f1SDimitry Andric cl::values(clEnumVal(Base, "Use base discriminators only"),
1087fe6060f1SDimitry Andric clEnumVal(Pass1, "Use base and pass 1 discriminators"),
1088fe6060f1SDimitry Andric clEnumVal(Pass2, "Use base and pass 1-2 discriminators"),
1089fe6060f1SDimitry Andric clEnumVal(Pass3, "Use base and pass 1-3 discriminators"),
1090fe6060f1SDimitry Andric clEnumVal(PassLast, "Use all discriminator bits (default)")));
1091fe6060f1SDimitry Andric
getDiscriminatorMask()1092fe6060f1SDimitry Andric static unsigned getDiscriminatorMask() {
1093fe6060f1SDimitry Andric return getN1Bits(getFSPassBitEnd(FSDiscriminatorPassOption.getValue()));
1094fe6060f1SDimitry Andric }
1095fe6060f1SDimitry Andric
1096e8d8bef9SDimitry Andric /// Adjust the instr profile in \p WC based on the sample profile in
1097e8d8bef9SDimitry Andric /// \p Reader.
1098e8d8bef9SDimitry Andric static void
adjustInstrProfile(std::unique_ptr<WriterContext> & WC,std::unique_ptr<sampleprof::SampleProfileReader> & Reader,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)1099e8d8bef9SDimitry Andric adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
1100e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> &Reader,
1101e8d8bef9SDimitry Andric unsigned SupplMinSizeThreshold, float ZeroCounterThreshold,
1102e8d8bef9SDimitry Andric unsigned InstrProfColdThreshold) {
1103e8d8bef9SDimitry Andric // Function to its entry in instr profile.
1104e8d8bef9SDimitry Andric StringMap<InstrProfileEntry> InstrProfileMap;
1105bdd1243dSDimitry Andric StringMap<StringRef> StaticFuncMap;
1106e8d8bef9SDimitry Andric InstrProfSummaryBuilder IPBuilder(ProfileSummaryBuilder::DefaultCutoffs);
1107bdd1243dSDimitry Andric
1108bdd1243dSDimitry Andric auto checkSampleProfileHasFUnique = [&Reader]() {
1109bdd1243dSDimitry Andric for (const auto &PD : Reader->getProfiles()) {
11105f757f3fSDimitry Andric auto &FContext = PD.second.getContext();
1111bdd1243dSDimitry Andric if (FContext.toString().find(FunctionSamples::UniqSuffix) !=
1112bdd1243dSDimitry Andric std::string::npos) {
1113bdd1243dSDimitry Andric return true;
1114bdd1243dSDimitry Andric }
1115bdd1243dSDimitry Andric }
1116bdd1243dSDimitry Andric return false;
1117bdd1243dSDimitry Andric };
1118bdd1243dSDimitry Andric
1119bdd1243dSDimitry Andric bool SampleProfileHasFUnique = checkSampleProfileHasFUnique();
1120bdd1243dSDimitry Andric
1121bdd1243dSDimitry Andric auto buildStaticFuncMap = [&StaticFuncMap,
1122bdd1243dSDimitry Andric SampleProfileHasFUnique](const StringRef Name) {
11231db9f3b2SDimitry Andric std::string FilePrefixes[] = {".cpp", "cc", ".c", ".hpp", ".h"};
1124bdd1243dSDimitry Andric size_t PrefixPos = StringRef::npos;
11251db9f3b2SDimitry Andric for (auto &FilePrefix : FilePrefixes) {
1126*0fca6ea1SDimitry Andric std::string NamePrefix = FilePrefix + GlobalIdentifierDelimiter;
11271db9f3b2SDimitry Andric PrefixPos = Name.find_insensitive(NamePrefix);
1128bdd1243dSDimitry Andric if (PrefixPos == StringRef::npos)
1129bdd1243dSDimitry Andric continue;
11301db9f3b2SDimitry Andric PrefixPos += NamePrefix.size();
1131bdd1243dSDimitry Andric break;
1132bdd1243dSDimitry Andric }
1133bdd1243dSDimitry Andric
1134bdd1243dSDimitry Andric if (PrefixPos == StringRef::npos) {
1135bdd1243dSDimitry Andric return;
1136bdd1243dSDimitry Andric }
1137bdd1243dSDimitry Andric
1138bdd1243dSDimitry Andric StringRef NewName = Name.drop_front(PrefixPos);
1139bdd1243dSDimitry Andric StringRef FName = Name.substr(0, PrefixPos - 1);
1140bdd1243dSDimitry Andric if (NewName.size() == 0) {
1141bdd1243dSDimitry Andric return;
1142bdd1243dSDimitry Andric }
1143bdd1243dSDimitry Andric
1144bdd1243dSDimitry Andric // This name should have a static linkage.
1145bdd1243dSDimitry Andric size_t PostfixPos = NewName.find(FunctionSamples::UniqSuffix);
1146bdd1243dSDimitry Andric bool ProfileHasFUnique = (PostfixPos != StringRef::npos);
1147bdd1243dSDimitry Andric
1148bdd1243dSDimitry Andric // If sample profile and instrumented profile do not agree on symbol
1149bdd1243dSDimitry Andric // uniqification.
1150bdd1243dSDimitry Andric if (SampleProfileHasFUnique != ProfileHasFUnique) {
11515f757f3fSDimitry Andric // If instrumented profile uses -funique-internal-linkage-symbols,
1152bdd1243dSDimitry Andric // we need to trim the name.
1153bdd1243dSDimitry Andric if (ProfileHasFUnique) {
1154bdd1243dSDimitry Andric NewName = NewName.substr(0, PostfixPos);
1155bdd1243dSDimitry Andric } else {
11565f757f3fSDimitry Andric // If sample profile uses -funique-internal-linkage-symbols,
1157bdd1243dSDimitry Andric // we build the map.
1158bdd1243dSDimitry Andric std::string NStr =
1159bdd1243dSDimitry Andric NewName.str() + getUniqueInternalLinkagePostfix(FName);
1160bdd1243dSDimitry Andric NewName = StringRef(NStr);
1161bdd1243dSDimitry Andric StaticFuncMap[NewName] = Name;
1162bdd1243dSDimitry Andric return;
1163bdd1243dSDimitry Andric }
1164bdd1243dSDimitry Andric }
1165bdd1243dSDimitry Andric
116606c3fb27SDimitry Andric if (!StaticFuncMap.contains(NewName)) {
1167bdd1243dSDimitry Andric StaticFuncMap[NewName] = Name;
1168bdd1243dSDimitry Andric } else {
1169bdd1243dSDimitry Andric StaticFuncMap[NewName] = DuplicateNameStr;
1170bdd1243dSDimitry Andric }
1171bdd1243dSDimitry Andric };
1172bdd1243dSDimitry Andric
1173bdd1243dSDimitry Andric // We need to flatten the SampleFDO profile as the InstrFDO
1174bdd1243dSDimitry Andric // profile does not have inlined callsite profiles.
1175bdd1243dSDimitry Andric // One caveat is the pre-inlined function -- their samples
1176bdd1243dSDimitry Andric // should be collapsed into the caller function.
1177bdd1243dSDimitry Andric // Here we do a DFS traversal to get the flatten profile
1178bdd1243dSDimitry Andric // info: the sum of entrycount and the max of maxcount.
1179bdd1243dSDimitry Andric // Here is the algorithm:
1180bdd1243dSDimitry Andric // recursive (FS, root_name) {
1181bdd1243dSDimitry Andric // name = FS->getName();
1182bdd1243dSDimitry Andric // get samples for FS;
1183bdd1243dSDimitry Andric // if (InstrProf.find(name) {
1184bdd1243dSDimitry Andric // root_name = name;
1185bdd1243dSDimitry Andric // } else {
1186bdd1243dSDimitry Andric // if (name is in static_func map) {
1187bdd1243dSDimitry Andric // root_name = static_name;
1188bdd1243dSDimitry Andric // }
1189bdd1243dSDimitry Andric // }
1190bdd1243dSDimitry Andric // update the Map entry for root_name;
1191bdd1243dSDimitry Andric // for (subfs: FS) {
1192bdd1243dSDimitry Andric // recursive(subfs, root_name);
1193bdd1243dSDimitry Andric // }
1194bdd1243dSDimitry Andric // }
1195bdd1243dSDimitry Andric //
1196bdd1243dSDimitry Andric // Here is an example.
1197bdd1243dSDimitry Andric //
1198bdd1243dSDimitry Andric // SampleProfile:
1199bdd1243dSDimitry Andric // foo:12345:1000
1200bdd1243dSDimitry Andric // 1: 1000
1201bdd1243dSDimitry Andric // 2.1: 1000
1202bdd1243dSDimitry Andric // 15: 5000
1203bdd1243dSDimitry Andric // 4: bar:1000
1204bdd1243dSDimitry Andric // 1: 1000
1205bdd1243dSDimitry Andric // 2: goo:3000
1206bdd1243dSDimitry Andric // 1: 3000
1207bdd1243dSDimitry Andric // 8: bar:40000
1208bdd1243dSDimitry Andric // 1: 10000
1209bdd1243dSDimitry Andric // 2: goo:30000
1210bdd1243dSDimitry Andric // 1: 30000
1211bdd1243dSDimitry Andric //
1212bdd1243dSDimitry Andric // InstrProfile has two entries:
1213bdd1243dSDimitry Andric // foo
12141db9f3b2SDimitry Andric // bar.cc;bar
1215bdd1243dSDimitry Andric //
1216bdd1243dSDimitry Andric // After BuildMaxSampleMap, we should have the following in FlattenSampleMap:
1217bdd1243dSDimitry Andric // {"foo", {1000, 5000}}
12181db9f3b2SDimitry Andric // {"bar.cc;bar", {11000, 30000}}
1219bdd1243dSDimitry Andric //
1220bdd1243dSDimitry Andric // foo's has an entry count of 1000, and max body count of 5000.
12211db9f3b2SDimitry Andric // bar.cc;bar has an entry count of 11000 (sum two callsites of 1000 and
1222bdd1243dSDimitry Andric // 10000), and max count of 30000 (from the callsite in line 8).
1223bdd1243dSDimitry Andric //
12241db9f3b2SDimitry Andric // Note that goo's count will remain in bar.cc;bar() as it does not have an
1225bdd1243dSDimitry Andric // entry in InstrProfile.
12265f757f3fSDimitry Andric llvm::StringMap<std::pair<uint64_t, uint64_t>> FlattenSampleMap;
1227bdd1243dSDimitry Andric auto BuildMaxSampleMap = [&FlattenSampleMap, &StaticFuncMap,
1228bdd1243dSDimitry Andric &InstrProfileMap](const FunctionSamples &FS,
1229bdd1243dSDimitry Andric const StringRef &RootName) {
1230bdd1243dSDimitry Andric auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,
1231bdd1243dSDimitry Andric const StringRef &RootName,
1232bdd1243dSDimitry Andric auto &BuildImpl) -> void {
12335f757f3fSDimitry Andric std::string NameStr = FS.getFunction().str();
12345f757f3fSDimitry Andric const StringRef Name = NameStr;
1235bdd1243dSDimitry Andric const StringRef *NewRootName = &RootName;
1236bdd1243dSDimitry Andric uint64_t EntrySample = FS.getHeadSamplesEstimate();
1237bdd1243dSDimitry Andric uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);
1238bdd1243dSDimitry Andric
1239bdd1243dSDimitry Andric auto It = InstrProfileMap.find(Name);
1240bdd1243dSDimitry Andric if (It != InstrProfileMap.end()) {
1241bdd1243dSDimitry Andric NewRootName = &Name;
1242bdd1243dSDimitry Andric } else {
1243bdd1243dSDimitry Andric auto NewName = StaticFuncMap.find(Name);
1244bdd1243dSDimitry Andric if (NewName != StaticFuncMap.end()) {
1245bdd1243dSDimitry Andric It = InstrProfileMap.find(NewName->second.str());
1246bdd1243dSDimitry Andric if (NewName->second != DuplicateNameStr) {
1247bdd1243dSDimitry Andric NewRootName = &NewName->second;
1248bdd1243dSDimitry Andric }
1249bdd1243dSDimitry Andric } else {
1250bdd1243dSDimitry Andric // Here the EntrySample is of an inlined function, so we should not
1251bdd1243dSDimitry Andric // update the EntrySample in the map.
1252bdd1243dSDimitry Andric EntrySample = 0;
1253bdd1243dSDimitry Andric }
1254bdd1243dSDimitry Andric }
1255bdd1243dSDimitry Andric EntrySample += FlattenSampleMap[*NewRootName].first;
1256bdd1243dSDimitry Andric MaxBodySample =
1257bdd1243dSDimitry Andric std::max(FlattenSampleMap[*NewRootName].second, MaxBodySample);
1258bdd1243dSDimitry Andric FlattenSampleMap[*NewRootName] =
1259bdd1243dSDimitry Andric std::make_pair(EntrySample, MaxBodySample);
1260bdd1243dSDimitry Andric
1261bdd1243dSDimitry Andric for (const auto &C : FS.getCallsiteSamples())
1262bdd1243dSDimitry Andric for (const auto &F : C.second)
1263bdd1243dSDimitry Andric BuildImpl(F.second, *NewRootName, BuildImpl);
1264bdd1243dSDimitry Andric };
1265bdd1243dSDimitry Andric BuildMaxSampleMapImpl(FS, RootName, BuildMaxSampleMapImpl);
1266bdd1243dSDimitry Andric };
1267bdd1243dSDimitry Andric
1268e8d8bef9SDimitry Andric for (auto &PD : WC->Writer.getProfileData()) {
1269e8d8bef9SDimitry Andric // Populate IPBuilder.
1270e8d8bef9SDimitry Andric for (const auto &PDV : PD.getValue()) {
1271e8d8bef9SDimitry Andric InstrProfRecord Record = PDV.second;
1272e8d8bef9SDimitry Andric IPBuilder.addRecord(Record);
1273e8d8bef9SDimitry Andric }
1274e8d8bef9SDimitry Andric
1275e8d8bef9SDimitry Andric // If a function has multiple entries in instr profile, skip it.
1276e8d8bef9SDimitry Andric if (PD.getValue().size() != 1)
1277e8d8bef9SDimitry Andric continue;
1278e8d8bef9SDimitry Andric
1279e8d8bef9SDimitry Andric // Initialize InstrProfileMap.
1280e8d8bef9SDimitry Andric InstrProfRecord *R = &PD.getValue().begin()->second;
1281bdd1243dSDimitry Andric StringRef FullName = PD.getKey();
1282bdd1243dSDimitry Andric InstrProfileMap[FullName] = InstrProfileEntry(R);
1283bdd1243dSDimitry Andric buildStaticFuncMap(FullName);
1284bdd1243dSDimitry Andric }
1285bdd1243dSDimitry Andric
1286bdd1243dSDimitry Andric for (auto &PD : Reader->getProfiles()) {
1287bdd1243dSDimitry Andric sampleprof::FunctionSamples &FS = PD.second;
12885f757f3fSDimitry Andric std::string Name = FS.getFunction().str();
12895f757f3fSDimitry Andric BuildMaxSampleMap(FS, Name);
1290e8d8bef9SDimitry Andric }
1291e8d8bef9SDimitry Andric
1292e8d8bef9SDimitry Andric ProfileSummary InstrPS = *IPBuilder.getSummary();
1293e8d8bef9SDimitry Andric ProfileSummary SamplePS = Reader->getSummary();
1294e8d8bef9SDimitry Andric
1295e8d8bef9SDimitry Andric // Compute cold thresholds for instr profile and sample profile.
1296bdd1243dSDimitry Andric uint64_t HotSampleThreshold =
1297bdd1243dSDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1298bdd1243dSDimitry Andric SamplePS.getDetailedSummary(),
1299bdd1243dSDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1300bdd1243dSDimitry Andric .MinCount;
1301e8d8bef9SDimitry Andric uint64_t ColdSampleThreshold =
1302e8d8bef9SDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1303e8d8bef9SDimitry Andric SamplePS.getDetailedSummary(),
1304e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1305e8d8bef9SDimitry Andric .MinCount;
1306e8d8bef9SDimitry Andric uint64_t HotInstrThreshold =
1307e8d8bef9SDimitry Andric ProfileSummaryBuilder::getEntryForPercentile(
1308e8d8bef9SDimitry Andric InstrPS.getDetailedSummary(),
1309e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[HotPercentileIdx])
1310e8d8bef9SDimitry Andric .MinCount;
1311e8d8bef9SDimitry Andric uint64_t ColdInstrThreshold =
1312e8d8bef9SDimitry Andric InstrProfColdThreshold
1313e8d8bef9SDimitry Andric ? InstrProfColdThreshold
1314e8d8bef9SDimitry Andric : ProfileSummaryBuilder::getEntryForPercentile(
1315e8d8bef9SDimitry Andric InstrPS.getDetailedSummary(),
1316e8d8bef9SDimitry Andric ProfileSummaryBuilder::DefaultCutoffs[ColdPercentileIdx])
1317e8d8bef9SDimitry Andric .MinCount;
1318e8d8bef9SDimitry Andric
1319e8d8bef9SDimitry Andric // Find hot/warm functions in sample profile which is cold in instr profile
1320e8d8bef9SDimitry Andric // and adjust the profiles of those functions in the instr profile.
1321bdd1243dSDimitry Andric for (const auto &E : FlattenSampleMap) {
1322bdd1243dSDimitry Andric uint64_t SampleMaxCount = std::max(E.second.first, E.second.second);
1323bdd1243dSDimitry Andric if (SampleMaxCount < ColdSampleThreshold)
1324bdd1243dSDimitry Andric continue;
13255f757f3fSDimitry Andric StringRef Name = E.first();
1326bdd1243dSDimitry Andric auto It = InstrProfileMap.find(Name);
1327bdd1243dSDimitry Andric if (It == InstrProfileMap.end()) {
1328bdd1243dSDimitry Andric auto NewName = StaticFuncMap.find(Name);
1329bdd1243dSDimitry Andric if (NewName != StaticFuncMap.end()) {
1330bdd1243dSDimitry Andric It = InstrProfileMap.find(NewName->second.str());
1331bdd1243dSDimitry Andric if (NewName->second == DuplicateNameStr) {
1332bdd1243dSDimitry Andric WithColor::warning()
1333bdd1243dSDimitry Andric << "Static function " << Name
1334bdd1243dSDimitry Andric << " has multiple promoted names, cannot adjust profile.\n";
1335e8d8bef9SDimitry Andric }
1336e8d8bef9SDimitry Andric }
1337e8d8bef9SDimitry Andric }
1338bdd1243dSDimitry Andric if (It == InstrProfileMap.end() ||
1339bdd1243dSDimitry Andric It->second.MaxCount > ColdInstrThreshold ||
1340bdd1243dSDimitry Andric It->second.NumEdgeCounters < SupplMinSizeThreshold)
1341bdd1243dSDimitry Andric continue;
1342bdd1243dSDimitry Andric bool SetToHot = SampleMaxCount >= HotSampleThreshold;
1343bdd1243dSDimitry Andric updateInstrProfileEntry(It->second, SetToHot, HotInstrThreshold,
1344bdd1243dSDimitry Andric ColdInstrThreshold, ZeroCounterThreshold);
1345bdd1243dSDimitry Andric }
1346bdd1243dSDimitry Andric }
1347e8d8bef9SDimitry Andric
1348e8d8bef9SDimitry Andric /// The main function to supplement instr profile with sample profile.
1349e8d8bef9SDimitry Andric /// \Inputs contains the instr profile. \p SampleFilename specifies the
1350e8d8bef9SDimitry Andric /// sample profile. \p OutputFilename specifies the output profile name.
1351e8d8bef9SDimitry Andric /// \p OutputFormat specifies the output profile format. \p OutputSparse
1352e8d8bef9SDimitry Andric /// specifies whether to generate sparse profile. \p SupplMinSizeThreshold
1353e8d8bef9SDimitry Andric /// specifies the minimal size for the functions whose profile will be
1354e8d8bef9SDimitry Andric /// adjusted. \p ZeroCounterThreshold is the threshold to check whether
1355e8d8bef9SDimitry Andric /// a function contains too many zero counters and whether its profile
1356e8d8bef9SDimitry Andric /// should be dropped. \p InstrProfColdThreshold is the user specified
1357e8d8bef9SDimitry Andric /// cold threshold which will override the cold threshold got from the
1358e8d8bef9SDimitry Andric /// instr profile summary.
supplementInstrProfile(const WeightedFileVector & Inputs,StringRef SampleFilename,bool OutputSparse,unsigned SupplMinSizeThreshold,float ZeroCounterThreshold,unsigned InstrProfColdThreshold)13595f757f3fSDimitry Andric static void supplementInstrProfile(const WeightedFileVector &Inputs,
13605f757f3fSDimitry Andric StringRef SampleFilename, bool OutputSparse,
13615f757f3fSDimitry Andric unsigned SupplMinSizeThreshold,
13625f757f3fSDimitry Andric float ZeroCounterThreshold,
1363e8d8bef9SDimitry Andric unsigned InstrProfColdThreshold) {
1364*0fca6ea1SDimitry Andric if (OutputFilename == "-")
1365fe6060f1SDimitry Andric exitWithError("cannot write indexed profdata format to stdout");
1366e8d8bef9SDimitry Andric if (Inputs.size() != 1)
1367fe6060f1SDimitry Andric exitWithError("expect one input to be an instr profile");
1368e8d8bef9SDimitry Andric if (Inputs[0].Weight != 1)
1369fe6060f1SDimitry Andric exitWithError("expect instr profile doesn't have weight");
1370e8d8bef9SDimitry Andric
1371e8d8bef9SDimitry Andric StringRef InstrFilename = Inputs[0].Filename;
1372e8d8bef9SDimitry Andric
1373e8d8bef9SDimitry Andric // Read sample profile.
1374e8d8bef9SDimitry Andric LLVMContext Context;
137506c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
1376fe6060f1SDimitry Andric auto ReaderOrErr = sampleprof::SampleProfileReader::create(
137706c3fb27SDimitry Andric SampleFilename.str(), Context, *FS, FSDiscriminatorPassOption);
1378e8d8bef9SDimitry Andric if (std::error_code EC = ReaderOrErr.getError())
1379e8d8bef9SDimitry Andric exitWithErrorCode(EC, SampleFilename);
1380e8d8bef9SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
1381e8d8bef9SDimitry Andric if (std::error_code EC = Reader->read())
1382e8d8bef9SDimitry Andric exitWithErrorCode(EC, SampleFilename);
1383e8d8bef9SDimitry Andric
1384e8d8bef9SDimitry Andric // Read instr profile.
1385e8d8bef9SDimitry Andric std::mutex ErrorLock;
1386e8d8bef9SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
1387e8d8bef9SDimitry Andric auto WC = std::make_unique<WriterContext>(OutputSparse, ErrorLock,
1388e8d8bef9SDimitry Andric WriterErrorCodes);
138981ad6265SDimitry Andric loadInput(Inputs[0], nullptr, nullptr, /*ProfiledBinary=*/"", WC.get());
1390e8d8bef9SDimitry Andric if (WC->Errors.size() > 0)
1391e8d8bef9SDimitry Andric exitWithError(std::move(WC->Errors[0].first), InstrFilename);
1392e8d8bef9SDimitry Andric
1393e8d8bef9SDimitry Andric adjustInstrProfile(WC, Reader, SupplMinSizeThreshold, ZeroCounterThreshold,
1394e8d8bef9SDimitry Andric InstrProfColdThreshold);
1395e8d8bef9SDimitry Andric writeInstrProfile(OutputFilename, OutputFormat, WC->Writer);
1396e8d8bef9SDimitry Andric }
1397e8d8bef9SDimitry Andric
13980b57cec5SDimitry Andric /// Make a copy of the given function samples with all symbol names remapped
13990b57cec5SDimitry Andric /// by the provided symbol remapper.
14000b57cec5SDimitry Andric static sampleprof::FunctionSamples
remapSamples(const sampleprof::FunctionSamples & Samples,SymbolRemapper & Remapper,sampleprof_error & Error)14010b57cec5SDimitry Andric remapSamples(const sampleprof::FunctionSamples &Samples,
14020b57cec5SDimitry Andric SymbolRemapper &Remapper, sampleprof_error &Error) {
14030b57cec5SDimitry Andric sampleprof::FunctionSamples Result;
14045f757f3fSDimitry Andric Result.setFunction(Remapper(Samples.getFunction()));
14050b57cec5SDimitry Andric Result.addTotalSamples(Samples.getTotalSamples());
14060b57cec5SDimitry Andric Result.addHeadSamples(Samples.getHeadSamples());
14070b57cec5SDimitry Andric for (const auto &BodySample : Samples.getBodySamples()) {
1408fe6060f1SDimitry Andric uint32_t MaskedDiscriminator =
1409fe6060f1SDimitry Andric BodySample.first.Discriminator & getDiscriminatorMask();
1410fe6060f1SDimitry Andric Result.addBodySamples(BodySample.first.LineOffset, MaskedDiscriminator,
14110b57cec5SDimitry Andric BodySample.second.getSamples());
14120b57cec5SDimitry Andric for (const auto &Target : BodySample.second.getCallTargets()) {
14130b57cec5SDimitry Andric Result.addCalledTargetSamples(BodySample.first.LineOffset,
1414fe6060f1SDimitry Andric MaskedDiscriminator,
14155f757f3fSDimitry Andric Remapper(Target.first), Target.second);
14160b57cec5SDimitry Andric }
14170b57cec5SDimitry Andric }
14180b57cec5SDimitry Andric for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
14190b57cec5SDimitry Andric sampleprof::FunctionSamplesMap &Target =
14200b57cec5SDimitry Andric Result.functionSamplesAt(CallsiteSamples.first);
14210b57cec5SDimitry Andric for (const auto &Callsite : CallsiteSamples.second) {
14220b57cec5SDimitry Andric sampleprof::FunctionSamples Remapped =
14230b57cec5SDimitry Andric remapSamples(Callsite.second, Remapper, Error);
1424*0fca6ea1SDimitry Andric mergeSampleProfErrors(Error,
1425*0fca6ea1SDimitry Andric Target[Remapped.getFunction()].merge(Remapped));
14260b57cec5SDimitry Andric }
14270b57cec5SDimitry Andric }
14280b57cec5SDimitry Andric return Result;
14290b57cec5SDimitry Andric }
14300b57cec5SDimitry Andric
14310b57cec5SDimitry Andric static sampleprof::SampleProfileFormat FormatMap[] = {
14328bcb0991SDimitry Andric sampleprof::SPF_None,
14338bcb0991SDimitry Andric sampleprof::SPF_Text,
143406c3fb27SDimitry Andric sampleprof::SPF_None,
14358bcb0991SDimitry Andric sampleprof::SPF_Ext_Binary,
14368bcb0991SDimitry Andric sampleprof::SPF_GCC,
14378bcb0991SDimitry Andric sampleprof::SPF_Binary};
14388bcb0991SDimitry Andric
14398bcb0991SDimitry Andric static std::unique_ptr<MemoryBuffer>
getInputFileBuf(const StringRef & InputFile)14408bcb0991SDimitry Andric getInputFileBuf(const StringRef &InputFile) {
14418bcb0991SDimitry Andric if (InputFile == "")
14428bcb0991SDimitry Andric return {};
14438bcb0991SDimitry Andric
14448bcb0991SDimitry Andric auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile);
14458bcb0991SDimitry Andric if (!BufOrError)
14468bcb0991SDimitry Andric exitWithErrorCode(BufOrError.getError(), InputFile);
14478bcb0991SDimitry Andric
14488bcb0991SDimitry Andric return std::move(*BufOrError);
14498bcb0991SDimitry Andric }
14508bcb0991SDimitry Andric
populateProfileSymbolList(MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & PSL)14518bcb0991SDimitry Andric static void populateProfileSymbolList(MemoryBuffer *Buffer,
14528bcb0991SDimitry Andric sampleprof::ProfileSymbolList &PSL) {
14538bcb0991SDimitry Andric if (!Buffer)
14548bcb0991SDimitry Andric return;
14558bcb0991SDimitry Andric
14568bcb0991SDimitry Andric SmallVector<StringRef, 32> SymbolVec;
14578bcb0991SDimitry Andric StringRef Data = Buffer->getBuffer();
14588bcb0991SDimitry Andric Data.split(SymbolVec, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
14598bcb0991SDimitry Andric
146004eeddc0SDimitry Andric for (StringRef SymbolStr : SymbolVec)
146104eeddc0SDimitry Andric PSL.add(SymbolStr.trim());
14628bcb0991SDimitry Andric }
14638bcb0991SDimitry Andric
handleExtBinaryWriter(sampleprof::SampleProfileWriter & Writer,ProfileFormat OutputFormat,MemoryBuffer * Buffer,sampleprof::ProfileSymbolList & WriterList,bool CompressAllSections,bool UseMD5,bool GenPartialProfile)14648bcb0991SDimitry Andric static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer,
14658bcb0991SDimitry Andric ProfileFormat OutputFormat,
14668bcb0991SDimitry Andric MemoryBuffer *Buffer,
14678bcb0991SDimitry Andric sampleprof::ProfileSymbolList &WriterList,
14685ffd83dbSDimitry Andric bool CompressAllSections, bool UseMD5,
14695ffd83dbSDimitry Andric bool GenPartialProfile) {
14708bcb0991SDimitry Andric populateProfileSymbolList(Buffer, WriterList);
14718bcb0991SDimitry Andric if (WriterList.size() > 0 && OutputFormat != PF_Ext_Binary)
14728bcb0991SDimitry Andric warn("Profile Symbol list is not empty but the output format is not "
14738bcb0991SDimitry Andric "ExtBinary format. The list will be lost in the output. ");
14748bcb0991SDimitry Andric
14758bcb0991SDimitry Andric Writer.setProfileSymbolList(&WriterList);
14768bcb0991SDimitry Andric
14778bcb0991SDimitry Andric if (CompressAllSections) {
14785ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14798bcb0991SDimitry Andric warn("-compress-all-section is ignored. Specify -extbinary to enable it");
14805ffd83dbSDimitry Andric else
14815ffd83dbSDimitry Andric Writer.setToCompressAllSections();
14828bcb0991SDimitry Andric }
14835ffd83dbSDimitry Andric if (UseMD5) {
14845ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14855ffd83dbSDimitry Andric warn("-use-md5 is ignored. Specify -extbinary to enable it");
14865ffd83dbSDimitry Andric else
14875ffd83dbSDimitry Andric Writer.setUseMD5();
14885ffd83dbSDimitry Andric }
14895ffd83dbSDimitry Andric if (GenPartialProfile) {
14905ffd83dbSDimitry Andric if (OutputFormat != PF_Ext_Binary)
14915ffd83dbSDimitry Andric warn("-gen-partial-profile is ignored. Specify -extbinary to enable it");
14925ffd83dbSDimitry Andric else
14935ffd83dbSDimitry Andric Writer.setPartialProfile();
14948bcb0991SDimitry Andric }
14958bcb0991SDimitry Andric }
14960b57cec5SDimitry Andric
mergeSampleProfile(const WeightedFileVector & Inputs,SymbolRemapper * Remapper,StringRef ProfileSymbolListFile,size_t OutputSizeLimit)14975f757f3fSDimitry Andric static void mergeSampleProfile(const WeightedFileVector &Inputs,
14985f757f3fSDimitry Andric SymbolRemapper *Remapper,
14995f757f3fSDimitry Andric StringRef ProfileSymbolListFile,
15005f757f3fSDimitry Andric size_t OutputSizeLimit) {
15010b57cec5SDimitry Andric using namespace sampleprof;
1502349cc55cSDimitry Andric SampleProfileMap ProfileMap;
15030b57cec5SDimitry Andric SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers;
15040b57cec5SDimitry Andric LLVMContext Context;
15058bcb0991SDimitry Andric sampleprof::ProfileSymbolList WriterList;
1506bdd1243dSDimitry Andric std::optional<bool> ProfileIsProbeBased;
1507bdd1243dSDimitry Andric std::optional<bool> ProfileIsCS;
15080b57cec5SDimitry Andric for (const auto &Input : Inputs) {
150906c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
151006c3fb27SDimitry Andric auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context, *FS,
1511fe6060f1SDimitry Andric FSDiscriminatorPassOption);
15128bcb0991SDimitry Andric if (std::error_code EC = ReaderOrErr.getError()) {
15138bcb0991SDimitry Andric warnOrExitGivenError(FailMode, EC, Input.Filename);
15148bcb0991SDimitry Andric continue;
15158bcb0991SDimitry Andric }
15160b57cec5SDimitry Andric
15170b57cec5SDimitry Andric // We need to keep the readers around until after all the files are
15180b57cec5SDimitry Andric // read so that we do not lose the function names stored in each
15190b57cec5SDimitry Andric // reader's memory. The function names are needed to write out the
15200b57cec5SDimitry Andric // merged profile map.
15210b57cec5SDimitry Andric Readers.push_back(std::move(ReaderOrErr.get()));
15220b57cec5SDimitry Andric const auto Reader = Readers.back().get();
15238bcb0991SDimitry Andric if (std::error_code EC = Reader->read()) {
15248bcb0991SDimitry Andric warnOrExitGivenError(FailMode, EC, Input.Filename);
15258bcb0991SDimitry Andric Readers.pop_back();
15268bcb0991SDimitry Andric continue;
15278bcb0991SDimitry Andric }
15280b57cec5SDimitry Andric
1529349cc55cSDimitry Andric SampleProfileMap &Profiles = Reader->getProfiles();
153081ad6265SDimitry Andric if (ProfileIsProbeBased &&
1531e8d8bef9SDimitry Andric ProfileIsProbeBased != FunctionSamples::ProfileIsProbeBased)
1532e8d8bef9SDimitry Andric exitWithError(
1533e8d8bef9SDimitry Andric "cannot merge probe-based profile with non-probe-based profile");
1534e8d8bef9SDimitry Andric ProfileIsProbeBased = FunctionSamples::ProfileIsProbeBased;
153581ad6265SDimitry Andric if (ProfileIsCS && ProfileIsCS != FunctionSamples::ProfileIsCS)
1536fe6060f1SDimitry Andric exitWithError("cannot merge CS profile with non-CS profile");
153781ad6265SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS;
1538349cc55cSDimitry Andric for (SampleProfileMap::iterator I = Profiles.begin(), E = Profiles.end();
15390b57cec5SDimitry Andric I != E; ++I) {
15400b57cec5SDimitry Andric sampleprof_error Result = sampleprof_error::success;
15410b57cec5SDimitry Andric FunctionSamples Remapped =
15420b57cec5SDimitry Andric Remapper ? remapSamples(I->second, *Remapper, Result)
15430b57cec5SDimitry Andric : FunctionSamples();
15440b57cec5SDimitry Andric FunctionSamples &Samples = Remapper ? Remapped : I->second;
1545349cc55cSDimitry Andric SampleContext FContext = Samples.getContext();
1546*0fca6ea1SDimitry Andric mergeSampleProfErrors(Result,
1547*0fca6ea1SDimitry Andric ProfileMap[FContext].merge(Samples, Input.Weight));
15480b57cec5SDimitry Andric if (Result != sampleprof_error::success) {
15490b57cec5SDimitry Andric std::error_code EC = make_error_code(Result);
1550349cc55cSDimitry Andric handleMergeWriterError(errorCodeToError(EC), Input.Filename,
1551349cc55cSDimitry Andric FContext.toString());
15520b57cec5SDimitry Andric }
15530b57cec5SDimitry Andric }
15548bcb0991SDimitry Andric
1555bdd1243dSDimitry Andric if (!DropProfileSymbolList) {
15568bcb0991SDimitry Andric std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
15578bcb0991SDimitry Andric Reader->getProfileSymbolList();
15588bcb0991SDimitry Andric if (ReaderList)
15598bcb0991SDimitry Andric WriterList.merge(*ReaderList);
15600b57cec5SDimitry Andric }
1561bdd1243dSDimitry Andric }
1562fe6060f1SDimitry Andric
156381ad6265SDimitry Andric if (ProfileIsCS && (SampleMergeColdContext || SampleTrimColdContext)) {
1564fe6060f1SDimitry Andric // Use threshold calculated from profile summary unless specified.
1565fe6060f1SDimitry Andric SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
1566fe6060f1SDimitry Andric auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
1567fe6060f1SDimitry Andric uint64_t SampleProfColdThreshold =
1568fe6060f1SDimitry Andric ProfileSummaryBuilder::getColdCountThreshold(
1569fe6060f1SDimitry Andric (Summary->getDetailedSummary()));
1570fe6060f1SDimitry Andric
1571fe6060f1SDimitry Andric // Trim and merge cold context profile using cold threshold above;
1572fe6060f1SDimitry Andric SampleContextTrimmer(ProfileMap)
1573fe6060f1SDimitry Andric .trimAndMergeColdContextProfiles(
1574fe6060f1SDimitry Andric SampleProfColdThreshold, SampleTrimColdContext,
1575349cc55cSDimitry Andric SampleMergeColdContext, SampleColdContextFrameDepth, false);
1576fe6060f1SDimitry Andric }
1577fe6060f1SDimitry Andric
157806c3fb27SDimitry Andric if (ProfileLayout == llvm::sampleprof::SPL_Flat) {
157906c3fb27SDimitry Andric ProfileConverter::flattenProfile(ProfileMap, FunctionSamples::ProfileIsCS);
158006c3fb27SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS = false;
158106c3fb27SDimitry Andric } else if (ProfileIsCS && ProfileLayout == llvm::sampleprof::SPL_Nest) {
158206c3fb27SDimitry Andric ProfileConverter CSConverter(ProfileMap);
158306c3fb27SDimitry Andric CSConverter.convertCSProfiles();
158481ad6265SDimitry Andric ProfileIsCS = FunctionSamples::ProfileIsCS = false;
15850eae32dcSDimitry Andric }
15860eae32dcSDimitry Andric
15877a6dacacSDimitry Andric filterFunctions(ProfileMap);
15887a6dacacSDimitry Andric
15890b57cec5SDimitry Andric auto WriterOrErr =
15900b57cec5SDimitry Andric SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]);
15910b57cec5SDimitry Andric if (std::error_code EC = WriterOrErr.getError())
15920b57cec5SDimitry Andric exitWithErrorCode(EC, OutputFilename);
15930b57cec5SDimitry Andric
15940b57cec5SDimitry Andric auto Writer = std::move(WriterOrErr.get());
15958bcb0991SDimitry Andric // WriterList will have StringRef refering to string in Buffer.
15968bcb0991SDimitry Andric // Make sure Buffer lives as long as WriterList.
15978bcb0991SDimitry Andric auto Buffer = getInputFileBuf(ProfileSymbolListFile);
15988bcb0991SDimitry Andric handleExtBinaryWriter(*Writer, OutputFormat, Buffer.get(), WriterList,
15995ffd83dbSDimitry Andric CompressAllSections, UseMD5, GenPartialProfile);
160006c3fb27SDimitry Andric
160106c3fb27SDimitry Andric // If OutputSizeLimit is 0 (default), it is the same as write().
160206c3fb27SDimitry Andric if (std::error_code EC =
160306c3fb27SDimitry Andric Writer->writeWithSizeLimit(ProfileMap, OutputSizeLimit))
1604*0fca6ea1SDimitry Andric exitWithErrorCode(EC);
16050b57cec5SDimitry Andric }
16060b57cec5SDimitry Andric
parseWeightedFile(const StringRef & WeightedFilename)16070b57cec5SDimitry Andric static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) {
16080b57cec5SDimitry Andric StringRef WeightStr, FileName;
16090b57cec5SDimitry Andric std::tie(WeightStr, FileName) = WeightedFilename.split(',');
16100b57cec5SDimitry Andric
16110b57cec5SDimitry Andric uint64_t Weight;
16120b57cec5SDimitry Andric if (WeightStr.getAsInteger(10, Weight) || Weight < 1)
1613fe6060f1SDimitry Andric exitWithError("input weight must be a positive integer");
16140b57cec5SDimitry Andric
16155ffd83dbSDimitry Andric return {std::string(FileName), Weight};
16160b57cec5SDimitry Andric }
16170b57cec5SDimitry Andric
addWeightedInput(WeightedFileVector & WNI,const WeightedFile & WF)16180b57cec5SDimitry Andric static void addWeightedInput(WeightedFileVector &WNI, const WeightedFile &WF) {
16190b57cec5SDimitry Andric StringRef Filename = WF.Filename;
16200b57cec5SDimitry Andric uint64_t Weight = WF.Weight;
16210b57cec5SDimitry Andric
16220b57cec5SDimitry Andric // If it's STDIN just pass it on.
16230b57cec5SDimitry Andric if (Filename == "-") {
16245ffd83dbSDimitry Andric WNI.push_back({std::string(Filename), Weight});
16250b57cec5SDimitry Andric return;
16260b57cec5SDimitry Andric }
16270b57cec5SDimitry Andric
16280b57cec5SDimitry Andric llvm::sys::fs::file_status Status;
16290b57cec5SDimitry Andric llvm::sys::fs::status(Filename, Status);
16300b57cec5SDimitry Andric if (!llvm::sys::fs::exists(Status))
16310b57cec5SDimitry Andric exitWithErrorCode(make_error_code(errc::no_such_file_or_directory),
16320b57cec5SDimitry Andric Filename);
16330b57cec5SDimitry Andric // If it's a source file, collect it.
16340b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(Status)) {
16355ffd83dbSDimitry Andric WNI.push_back({std::string(Filename), Weight});
16360b57cec5SDimitry Andric return;
16370b57cec5SDimitry Andric }
16380b57cec5SDimitry Andric
16390b57cec5SDimitry Andric if (llvm::sys::fs::is_directory(Status)) {
16400b57cec5SDimitry Andric std::error_code EC;
16410b57cec5SDimitry Andric for (llvm::sys::fs::recursive_directory_iterator F(Filename, EC), E;
16420b57cec5SDimitry Andric F != E && !EC; F.increment(EC)) {
16430b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(F->path())) {
16440b57cec5SDimitry Andric addWeightedInput(WNI, {F->path(), Weight});
16450b57cec5SDimitry Andric }
16460b57cec5SDimitry Andric }
16470b57cec5SDimitry Andric if (EC)
16480b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
16490b57cec5SDimitry Andric }
16500b57cec5SDimitry Andric }
16510b57cec5SDimitry Andric
parseInputFilenamesFile(MemoryBuffer * Buffer,WeightedFileVector & WFV)16520b57cec5SDimitry Andric static void parseInputFilenamesFile(MemoryBuffer *Buffer,
16530b57cec5SDimitry Andric WeightedFileVector &WFV) {
16540b57cec5SDimitry Andric if (!Buffer)
16550b57cec5SDimitry Andric return;
16560b57cec5SDimitry Andric
16570b57cec5SDimitry Andric SmallVector<StringRef, 8> Entries;
16580b57cec5SDimitry Andric StringRef Data = Buffer->getBuffer();
16590b57cec5SDimitry Andric Data.split(Entries, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
16600b57cec5SDimitry Andric for (const StringRef &FileWeightEntry : Entries) {
16610b57cec5SDimitry Andric StringRef SanitizedEntry = FileWeightEntry.trim(" \t\v\f\r");
16620b57cec5SDimitry Andric // Skip comments.
16635f757f3fSDimitry Andric if (SanitizedEntry.starts_with("#"))
16640b57cec5SDimitry Andric continue;
16650b57cec5SDimitry Andric // If there's no comma, it's an unweighted profile.
1666349cc55cSDimitry Andric else if (!SanitizedEntry.contains(','))
16675ffd83dbSDimitry Andric addWeightedInput(WFV, {std::string(SanitizedEntry), 1});
16680b57cec5SDimitry Andric else
16690b57cec5SDimitry Andric addWeightedInput(WFV, parseWeightedFile(SanitizedEntry));
16700b57cec5SDimitry Andric }
16710b57cec5SDimitry Andric }
16720b57cec5SDimitry Andric
merge_main(StringRef ProgName)1673*0fca6ea1SDimitry Andric static int merge_main(StringRef ProgName) {
16740b57cec5SDimitry Andric WeightedFileVector WeightedInputs;
16750b57cec5SDimitry Andric for (StringRef Filename : InputFilenames)
16765ffd83dbSDimitry Andric addWeightedInput(WeightedInputs, {std::string(Filename), 1});
16770b57cec5SDimitry Andric for (StringRef WeightedFilename : WeightedInputFilenames)
16780b57cec5SDimitry Andric addWeightedInput(WeightedInputs, parseWeightedFile(WeightedFilename));
16790b57cec5SDimitry Andric
16800b57cec5SDimitry Andric // Make sure that the file buffer stays alive for the duration of the
16810b57cec5SDimitry Andric // weighted input vector's lifetime.
16828bcb0991SDimitry Andric auto Buffer = getInputFileBuf(InputFilenamesFile);
16830b57cec5SDimitry Andric parseInputFilenamesFile(Buffer.get(), WeightedInputs);
16840b57cec5SDimitry Andric
16850b57cec5SDimitry Andric if (WeightedInputs.empty())
1686*0fca6ea1SDimitry Andric exitWithError("no input files specified. See " + ProgName + " merge -help");
16870b57cec5SDimitry Andric
16880b57cec5SDimitry Andric if (DumpInputFileList) {
16890b57cec5SDimitry Andric for (auto &WF : WeightedInputs)
16900b57cec5SDimitry Andric outs() << WF.Weight << "," << WF.Filename << "\n";
16910b57cec5SDimitry Andric return 0;
16920b57cec5SDimitry Andric }
16930b57cec5SDimitry Andric
16940b57cec5SDimitry Andric std::unique_ptr<SymbolRemapper> Remapper;
16950b57cec5SDimitry Andric if (!RemappingFile.empty())
16960b57cec5SDimitry Andric Remapper = SymbolRemapper::create(RemappingFile);
16970b57cec5SDimitry Andric
1698e8d8bef9SDimitry Andric if (!SupplInstrWithSample.empty()) {
1699e8d8bef9SDimitry Andric if (ProfileKind != instr)
1700e8d8bef9SDimitry Andric exitWithError(
1701e8d8bef9SDimitry Andric "-supplement-instr-with-sample can only work with -instr. ");
1702e8d8bef9SDimitry Andric
17035f757f3fSDimitry Andric supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse,
17045f757f3fSDimitry Andric SupplMinSizeThreshold, ZeroCounterThreshold,
17055f757f3fSDimitry Andric InstrProfColdThreshold);
1706e8d8bef9SDimitry Andric return 0;
1707e8d8bef9SDimitry Andric }
1708e8d8bef9SDimitry Andric
17090b57cec5SDimitry Andric if (ProfileKind == instr)
17105f757f3fSDimitry Andric mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings,
17115f757f3fSDimitry Andric ProfiledBinary);
17120b57cec5SDimitry Andric else
17135f757f3fSDimitry Andric mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile,
17145f757f3fSDimitry Andric OutputSizeLimit);
17150b57cec5SDimitry Andric return 0;
17160b57cec5SDimitry Andric }
17170b57cec5SDimitry Andric
17180b57cec5SDimitry 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)17190b57cec5SDimitry Andric static void overlapInstrProfile(const std::string &BaseFilename,
17200b57cec5SDimitry Andric const std::string &TestFilename,
17210b57cec5SDimitry Andric const OverlapFuncFilters &FuncFilter,
17220b57cec5SDimitry Andric raw_fd_ostream &OS, bool IsCS) {
17230b57cec5SDimitry Andric std::mutex ErrorLock;
17240b57cec5SDimitry Andric SmallSet<instrprof_error, 4> WriterErrorCodes;
17250b57cec5SDimitry Andric WriterContext Context(false, ErrorLock, WriterErrorCodes);
17260b57cec5SDimitry Andric WeightedFile WeightedInput{BaseFilename, 1};
17270b57cec5SDimitry Andric OverlapStats Overlap;
17288bcb0991SDimitry Andric Error E = Overlap.accumulateCounts(BaseFilename, TestFilename, IsCS);
17290b57cec5SDimitry Andric if (E)
1730fe6060f1SDimitry Andric exitWithError(std::move(E), "error in getting profile count sums");
17310b57cec5SDimitry Andric if (Overlap.Base.CountSum < 1.0f) {
17320b57cec5SDimitry Andric OS << "Sum of edge counts for profile " << BaseFilename << " is 0.\n";
17330b57cec5SDimitry Andric exit(0);
17340b57cec5SDimitry Andric }
17350b57cec5SDimitry Andric if (Overlap.Test.CountSum < 1.0f) {
17360b57cec5SDimitry Andric OS << "Sum of edge counts for profile " << TestFilename << " is 0.\n";
17370b57cec5SDimitry Andric exit(0);
17380b57cec5SDimitry Andric }
173981ad6265SDimitry Andric loadInput(WeightedInput, nullptr, nullptr, /*ProfiledBinary=*/"", &Context);
17400b57cec5SDimitry Andric overlapInput(BaseFilename, TestFilename, &Context, Overlap, FuncFilter, OS,
17410b57cec5SDimitry Andric IsCS);
17420b57cec5SDimitry Andric Overlap.dump(OS);
17430b57cec5SDimitry Andric }
17440b57cec5SDimitry Andric
1745e8d8bef9SDimitry Andric namespace {
1746e8d8bef9SDimitry Andric struct SampleOverlapStats {
1747349cc55cSDimitry Andric SampleContext BaseName;
1748349cc55cSDimitry Andric SampleContext TestName;
1749e8d8bef9SDimitry Andric // Number of overlap units
17505f757f3fSDimitry Andric uint64_t OverlapCount = 0;
1751e8d8bef9SDimitry Andric // Total samples of overlap units
17525f757f3fSDimitry Andric uint64_t OverlapSample = 0;
1753e8d8bef9SDimitry Andric // Number of and total samples of units that only present in base or test
1754e8d8bef9SDimitry Andric // profile
17555f757f3fSDimitry Andric uint64_t BaseUniqueCount = 0;
17565f757f3fSDimitry Andric uint64_t BaseUniqueSample = 0;
17575f757f3fSDimitry Andric uint64_t TestUniqueCount = 0;
17585f757f3fSDimitry Andric uint64_t TestUniqueSample = 0;
1759e8d8bef9SDimitry Andric // Number of units and total samples in base or test profile
17605f757f3fSDimitry Andric uint64_t BaseCount = 0;
17615f757f3fSDimitry Andric uint64_t BaseSample = 0;
17625f757f3fSDimitry Andric uint64_t TestCount = 0;
17635f757f3fSDimitry Andric uint64_t TestSample = 0;
1764e8d8bef9SDimitry Andric // Number of and total samples of units that present in at least one profile
17655f757f3fSDimitry Andric uint64_t UnionCount = 0;
17665f757f3fSDimitry Andric uint64_t UnionSample = 0;
1767e8d8bef9SDimitry Andric // Weighted similarity
17685f757f3fSDimitry Andric double Similarity = 0.0;
1769e8d8bef9SDimitry Andric // For SampleOverlapStats instances representing functions, weights of the
1770e8d8bef9SDimitry Andric // function in base and test profiles
17715f757f3fSDimitry Andric double BaseWeight = 0.0;
17725f757f3fSDimitry Andric double TestWeight = 0.0;
1773e8d8bef9SDimitry Andric
17745f757f3fSDimitry Andric SampleOverlapStats() = default;
1775e8d8bef9SDimitry Andric };
1776e8d8bef9SDimitry Andric } // end anonymous namespace
1777e8d8bef9SDimitry Andric
1778e8d8bef9SDimitry Andric namespace {
1779e8d8bef9SDimitry Andric struct FuncSampleStats {
1780cb14a3feSDimitry Andric uint64_t SampleSum = 0;
1781cb14a3feSDimitry Andric uint64_t MaxSample = 0;
1782cb14a3feSDimitry Andric uint64_t HotBlockCount = 0;
1783cb14a3feSDimitry Andric FuncSampleStats() = default;
FuncSampleStats__anon209d874b1011::FuncSampleStats1784e8d8bef9SDimitry Andric FuncSampleStats(uint64_t SampleSum, uint64_t MaxSample,
1785e8d8bef9SDimitry Andric uint64_t HotBlockCount)
1786e8d8bef9SDimitry Andric : SampleSum(SampleSum), MaxSample(MaxSample),
1787e8d8bef9SDimitry Andric HotBlockCount(HotBlockCount) {}
1788e8d8bef9SDimitry Andric };
1789e8d8bef9SDimitry Andric } // end anonymous namespace
1790e8d8bef9SDimitry Andric
1791e8d8bef9SDimitry Andric namespace {
1792e8d8bef9SDimitry Andric enum MatchStatus { MS_Match, MS_FirstUnique, MS_SecondUnique, MS_None };
1793e8d8bef9SDimitry Andric
1794e8d8bef9SDimitry Andric // Class for updating merging steps for two sorted maps. The class should be
1795e8d8bef9SDimitry Andric // instantiated with a map iterator type.
1796e8d8bef9SDimitry Andric template <class T> class MatchStep {
1797e8d8bef9SDimitry Andric public:
1798e8d8bef9SDimitry Andric MatchStep() = delete;
1799e8d8bef9SDimitry Andric
MatchStep(T FirstIter,T FirstEnd,T SecondIter,T SecondEnd)1800e8d8bef9SDimitry Andric MatchStep(T FirstIter, T FirstEnd, T SecondIter, T SecondEnd)
1801e8d8bef9SDimitry Andric : FirstIter(FirstIter), FirstEnd(FirstEnd), SecondIter(SecondIter),
1802e8d8bef9SDimitry Andric SecondEnd(SecondEnd), Status(MS_None) {}
1803e8d8bef9SDimitry Andric
areBothFinished() const1804e8d8bef9SDimitry Andric bool areBothFinished() const {
1805e8d8bef9SDimitry Andric return (FirstIter == FirstEnd && SecondIter == SecondEnd);
1806e8d8bef9SDimitry Andric }
1807e8d8bef9SDimitry Andric
isFirstFinished() const1808e8d8bef9SDimitry Andric bool isFirstFinished() const { return FirstIter == FirstEnd; }
1809e8d8bef9SDimitry Andric
isSecondFinished() const1810e8d8bef9SDimitry Andric bool isSecondFinished() const { return SecondIter == SecondEnd; }
1811e8d8bef9SDimitry Andric
1812e8d8bef9SDimitry Andric /// Advance one step based on the previous match status unless the previous
1813e8d8bef9SDimitry Andric /// status is MS_None. Then update Status based on the comparison between two
1814e8d8bef9SDimitry Andric /// container iterators at the current step. If the previous status is
1815e8d8bef9SDimitry Andric /// MS_None, it means two iterators are at the beginning and no comparison has
1816e8d8bef9SDimitry Andric /// been made, so we simply update Status without advancing the iterators.
1817e8d8bef9SDimitry Andric void updateOneStep();
1818e8d8bef9SDimitry Andric
getFirstIter() const1819e8d8bef9SDimitry Andric T getFirstIter() const { return FirstIter; }
1820e8d8bef9SDimitry Andric
getSecondIter() const1821e8d8bef9SDimitry Andric T getSecondIter() const { return SecondIter; }
1822e8d8bef9SDimitry Andric
getMatchStatus() const1823e8d8bef9SDimitry Andric MatchStatus getMatchStatus() const { return Status; }
1824e8d8bef9SDimitry Andric
1825e8d8bef9SDimitry Andric private:
1826e8d8bef9SDimitry Andric // Current iterator and end iterator of the first container.
1827e8d8bef9SDimitry Andric T FirstIter;
1828e8d8bef9SDimitry Andric T FirstEnd;
1829e8d8bef9SDimitry Andric // Current iterator and end iterator of the second container.
1830e8d8bef9SDimitry Andric T SecondIter;
1831e8d8bef9SDimitry Andric T SecondEnd;
1832e8d8bef9SDimitry Andric // Match status of the current step.
1833e8d8bef9SDimitry Andric MatchStatus Status;
1834e8d8bef9SDimitry Andric };
1835e8d8bef9SDimitry Andric } // end anonymous namespace
1836e8d8bef9SDimitry Andric
updateOneStep()1837e8d8bef9SDimitry Andric template <class T> void MatchStep<T>::updateOneStep() {
1838e8d8bef9SDimitry Andric switch (Status) {
1839e8d8bef9SDimitry Andric case MS_Match:
1840e8d8bef9SDimitry Andric ++FirstIter;
1841e8d8bef9SDimitry Andric ++SecondIter;
1842e8d8bef9SDimitry Andric break;
1843e8d8bef9SDimitry Andric case MS_FirstUnique:
1844e8d8bef9SDimitry Andric ++FirstIter;
1845e8d8bef9SDimitry Andric break;
1846e8d8bef9SDimitry Andric case MS_SecondUnique:
1847e8d8bef9SDimitry Andric ++SecondIter;
1848e8d8bef9SDimitry Andric break;
1849e8d8bef9SDimitry Andric case MS_None:
1850e8d8bef9SDimitry Andric break;
1851e8d8bef9SDimitry Andric }
1852e8d8bef9SDimitry Andric
1853e8d8bef9SDimitry Andric // Update Status according to iterators at the current step.
1854e8d8bef9SDimitry Andric if (areBothFinished())
1855e8d8bef9SDimitry Andric return;
1856e8d8bef9SDimitry Andric if (FirstIter != FirstEnd &&
1857e8d8bef9SDimitry Andric (SecondIter == SecondEnd || FirstIter->first < SecondIter->first))
1858e8d8bef9SDimitry Andric Status = MS_FirstUnique;
1859e8d8bef9SDimitry Andric else if (SecondIter != SecondEnd &&
1860e8d8bef9SDimitry Andric (FirstIter == FirstEnd || SecondIter->first < FirstIter->first))
1861e8d8bef9SDimitry Andric Status = MS_SecondUnique;
1862e8d8bef9SDimitry Andric else
1863e8d8bef9SDimitry Andric Status = MS_Match;
1864e8d8bef9SDimitry Andric }
1865e8d8bef9SDimitry Andric
1866e8d8bef9SDimitry Andric // Return the sum of line/block samples, the max line/block sample, and the
1867e8d8bef9SDimitry Andric // number of line/block samples above the given threshold in a function
1868e8d8bef9SDimitry Andric // including its inlinees.
getFuncSampleStats(const sampleprof::FunctionSamples & Func,FuncSampleStats & FuncStats,uint64_t HotThreshold)1869e8d8bef9SDimitry Andric static void getFuncSampleStats(const sampleprof::FunctionSamples &Func,
1870e8d8bef9SDimitry Andric FuncSampleStats &FuncStats,
1871e8d8bef9SDimitry Andric uint64_t HotThreshold) {
1872e8d8bef9SDimitry Andric for (const auto &L : Func.getBodySamples()) {
1873e8d8bef9SDimitry Andric uint64_t Sample = L.second.getSamples();
1874e8d8bef9SDimitry Andric FuncStats.SampleSum += Sample;
1875e8d8bef9SDimitry Andric FuncStats.MaxSample = std::max(FuncStats.MaxSample, Sample);
1876e8d8bef9SDimitry Andric if (Sample >= HotThreshold)
1877e8d8bef9SDimitry Andric ++FuncStats.HotBlockCount;
1878e8d8bef9SDimitry Andric }
1879e8d8bef9SDimitry Andric
1880e8d8bef9SDimitry Andric for (const auto &C : Func.getCallsiteSamples()) {
1881e8d8bef9SDimitry Andric for (const auto &F : C.second)
1882e8d8bef9SDimitry Andric getFuncSampleStats(F.second, FuncStats, HotThreshold);
1883e8d8bef9SDimitry Andric }
1884e8d8bef9SDimitry Andric }
1885e8d8bef9SDimitry Andric
1886e8d8bef9SDimitry Andric /// Predicate that determines if a function is hot with a given threshold. We
1887e8d8bef9SDimitry Andric /// keep it separate from its callsites for possible extension in the future.
isFunctionHot(const FuncSampleStats & FuncStats,uint64_t HotThreshold)1888e8d8bef9SDimitry Andric static bool isFunctionHot(const FuncSampleStats &FuncStats,
1889e8d8bef9SDimitry Andric uint64_t HotThreshold) {
1890e8d8bef9SDimitry Andric // We intentionally compare the maximum sample count in a function with the
1891e8d8bef9SDimitry Andric // HotThreshold to get an approximate determination on hot functions.
1892e8d8bef9SDimitry Andric return (FuncStats.MaxSample >= HotThreshold);
1893e8d8bef9SDimitry Andric }
1894e8d8bef9SDimitry Andric
1895e8d8bef9SDimitry Andric namespace {
1896e8d8bef9SDimitry Andric class SampleOverlapAggregator {
1897e8d8bef9SDimitry Andric public:
SampleOverlapAggregator(const std::string & BaseFilename,const std::string & TestFilename,double LowSimilarityThreshold,double Epsilon,const OverlapFuncFilters & FuncFilter)1898e8d8bef9SDimitry Andric SampleOverlapAggregator(const std::string &BaseFilename,
1899e8d8bef9SDimitry Andric const std::string &TestFilename,
1900e8d8bef9SDimitry Andric double LowSimilarityThreshold, double Epsilon,
1901e8d8bef9SDimitry Andric const OverlapFuncFilters &FuncFilter)
1902e8d8bef9SDimitry Andric : BaseFilename(BaseFilename), TestFilename(TestFilename),
1903e8d8bef9SDimitry Andric LowSimilarityThreshold(LowSimilarityThreshold), Epsilon(Epsilon),
1904e8d8bef9SDimitry Andric FuncFilter(FuncFilter) {}
1905e8d8bef9SDimitry Andric
1906e8d8bef9SDimitry Andric /// Detect 0-sample input profile and report to output stream. This interface
1907e8d8bef9SDimitry Andric /// should be called after loadProfiles().
1908e8d8bef9SDimitry Andric bool detectZeroSampleProfile(raw_fd_ostream &OS) const;
1909e8d8bef9SDimitry Andric
1910e8d8bef9SDimitry Andric /// Write out function-level similarity statistics for functions specified by
1911e8d8bef9SDimitry Andric /// options --function, --value-cutoff, and --similarity-cutoff.
1912e8d8bef9SDimitry Andric void dumpFuncSimilarity(raw_fd_ostream &OS) const;
1913e8d8bef9SDimitry Andric
1914e8d8bef9SDimitry Andric /// Write out program-level similarity and overlap statistics.
1915e8d8bef9SDimitry Andric void dumpProgramSummary(raw_fd_ostream &OS) const;
1916e8d8bef9SDimitry Andric
1917e8d8bef9SDimitry Andric /// Write out hot-function and hot-block statistics for base_profile,
1918e8d8bef9SDimitry Andric /// test_profile, and their overlap. For both cases, the overlap HO is
1919e8d8bef9SDimitry Andric /// calculated as follows:
1920e8d8bef9SDimitry Andric /// Given the number of functions (or blocks) that are hot in both profiles
1921e8d8bef9SDimitry Andric /// HCommon and the number of functions (or blocks) that are hot in at
1922e8d8bef9SDimitry Andric /// least one profile HUnion, HO = HCommon / HUnion.
1923e8d8bef9SDimitry Andric void dumpHotFuncAndBlockOverlap(raw_fd_ostream &OS) const;
1924e8d8bef9SDimitry Andric
1925e8d8bef9SDimitry Andric /// This function tries matching functions in base and test profiles. For each
1926e8d8bef9SDimitry Andric /// pair of matched functions, it aggregates the function-level
1927e8d8bef9SDimitry Andric /// similarity into a profile-level similarity. It also dump function-level
1928e8d8bef9SDimitry Andric /// similarity information of functions specified by --function,
1929e8d8bef9SDimitry Andric /// --value-cutoff, and --similarity-cutoff options. The program-level
1930e8d8bef9SDimitry Andric /// similarity PS is computed as follows:
1931e8d8bef9SDimitry Andric /// Given function-level similarity FS(A) for all function A, the
1932e8d8bef9SDimitry Andric /// weight of function A in base profile WB(A), and the weight of function
1933e8d8bef9SDimitry Andric /// A in test profile WT(A), compute PS(base_profile, test_profile) =
1934e8d8bef9SDimitry Andric /// sum_A(FS(A) * avg(WB(A), WT(A))) ranging in [0.0f to 1.0f] with 0.0
1935e8d8bef9SDimitry Andric /// meaning no-overlap.
1936e8d8bef9SDimitry Andric void computeSampleProfileOverlap(raw_fd_ostream &OS);
1937e8d8bef9SDimitry Andric
1938e8d8bef9SDimitry Andric /// Initialize ProfOverlap with the sum of samples in base and test
1939e8d8bef9SDimitry Andric /// profiles. This function also computes and keeps the sum of samples and
1940e8d8bef9SDimitry Andric /// max sample counts of each function in BaseStats and TestStats for later
1941e8d8bef9SDimitry Andric /// use to avoid re-computations.
1942e8d8bef9SDimitry Andric void initializeSampleProfileOverlap();
1943e8d8bef9SDimitry Andric
1944e8d8bef9SDimitry Andric /// Load profiles specified by BaseFilename and TestFilename.
1945e8d8bef9SDimitry Andric std::error_code loadProfiles();
1946e8d8bef9SDimitry Andric
1947349cc55cSDimitry Andric using FuncSampleStatsMap =
1948349cc55cSDimitry Andric std::unordered_map<SampleContext, FuncSampleStats, SampleContext::Hash>;
1949349cc55cSDimitry Andric
1950e8d8bef9SDimitry Andric private:
1951e8d8bef9SDimitry Andric SampleOverlapStats ProfOverlap;
1952e8d8bef9SDimitry Andric SampleOverlapStats HotFuncOverlap;
1953e8d8bef9SDimitry Andric SampleOverlapStats HotBlockOverlap;
1954e8d8bef9SDimitry Andric std::string BaseFilename;
1955e8d8bef9SDimitry Andric std::string TestFilename;
1956e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> BaseReader;
1957e8d8bef9SDimitry Andric std::unique_ptr<sampleprof::SampleProfileReader> TestReader;
1958e8d8bef9SDimitry Andric // BaseStats and TestStats hold FuncSampleStats for each function, with
1959e8d8bef9SDimitry Andric // function name as the key.
1960349cc55cSDimitry Andric FuncSampleStatsMap BaseStats;
1961349cc55cSDimitry Andric FuncSampleStatsMap TestStats;
1962e8d8bef9SDimitry Andric // Low similarity threshold in floating point number
1963e8d8bef9SDimitry Andric double LowSimilarityThreshold;
1964e8d8bef9SDimitry Andric // Block samples above BaseHotThreshold or TestHotThreshold are considered hot
1965e8d8bef9SDimitry Andric // for tracking hot blocks.
1966e8d8bef9SDimitry Andric uint64_t BaseHotThreshold;
1967e8d8bef9SDimitry Andric uint64_t TestHotThreshold;
1968e8d8bef9SDimitry Andric // A small threshold used to round the results of floating point accumulations
1969e8d8bef9SDimitry Andric // to resolve imprecision.
1970e8d8bef9SDimitry Andric const double Epsilon;
1971e8d8bef9SDimitry Andric std::multimap<double, SampleOverlapStats, std::greater<double>>
1972e8d8bef9SDimitry Andric FuncSimilarityDump;
1973e8d8bef9SDimitry Andric // FuncFilter carries specifications in options --value-cutoff and
1974e8d8bef9SDimitry Andric // --function.
1975e8d8bef9SDimitry Andric OverlapFuncFilters FuncFilter;
1976e8d8bef9SDimitry Andric // Column offsets for printing the function-level details table.
1977e8d8bef9SDimitry Andric static const unsigned int TestWeightCol = 15;
1978e8d8bef9SDimitry Andric static const unsigned int SimilarityCol = 30;
1979e8d8bef9SDimitry Andric static const unsigned int OverlapCol = 43;
1980e8d8bef9SDimitry Andric static const unsigned int BaseUniqueCol = 53;
1981e8d8bef9SDimitry Andric static const unsigned int TestUniqueCol = 67;
1982e8d8bef9SDimitry Andric static const unsigned int BaseSampleCol = 81;
1983e8d8bef9SDimitry Andric static const unsigned int TestSampleCol = 96;
1984e8d8bef9SDimitry Andric static const unsigned int FuncNameCol = 111;
1985e8d8bef9SDimitry Andric
1986e8d8bef9SDimitry Andric /// Return a similarity of two line/block sample counters in the same
1987e8d8bef9SDimitry Andric /// function in base and test profiles. The line/block-similarity BS(i) is
1988e8d8bef9SDimitry Andric /// computed as follows:
1989e8d8bef9SDimitry Andric /// For an offsets i, given the sample count at i in base profile BB(i),
1990e8d8bef9SDimitry Andric /// the sample count at i in test profile BT(i), the sum of sample counts
1991e8d8bef9SDimitry Andric /// in this function in base profile SB, and the sum of sample counts in
1992e8d8bef9SDimitry Andric /// this function in test profile ST, compute BS(i) = 1.0 - fabs(BB(i)/SB -
1993e8d8bef9SDimitry Andric /// BT(i)/ST), ranging in [0.0f to 1.0f] with 0.0 meaning no-overlap.
1994e8d8bef9SDimitry Andric double computeBlockSimilarity(uint64_t BaseSample, uint64_t TestSample,
1995e8d8bef9SDimitry Andric const SampleOverlapStats &FuncOverlap) const;
1996e8d8bef9SDimitry Andric
1997e8d8bef9SDimitry Andric void updateHotBlockOverlap(uint64_t BaseSample, uint64_t TestSample,
1998e8d8bef9SDimitry Andric uint64_t HotBlockCount);
1999e8d8bef9SDimitry Andric
2000349cc55cSDimitry Andric void getHotFunctions(const FuncSampleStatsMap &ProfStats,
2001349cc55cSDimitry Andric FuncSampleStatsMap &HotFunc,
2002e8d8bef9SDimitry Andric uint64_t HotThreshold) const;
2003e8d8bef9SDimitry Andric
2004e8d8bef9SDimitry Andric void computeHotFuncOverlap();
2005e8d8bef9SDimitry Andric
2006e8d8bef9SDimitry Andric /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
2007e8d8bef9SDimitry Andric /// Difference for two sample units in a matched function according to the
2008e8d8bef9SDimitry Andric /// given match status.
2009e8d8bef9SDimitry Andric void updateOverlapStatsForFunction(uint64_t BaseSample, uint64_t TestSample,
2010e8d8bef9SDimitry Andric uint64_t HotBlockCount,
2011e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap,
2012e8d8bef9SDimitry Andric double &Difference, MatchStatus Status);
2013e8d8bef9SDimitry Andric
2014e8d8bef9SDimitry Andric /// This function updates statistics in FuncOverlap, HotBlockOverlap, and
2015e8d8bef9SDimitry Andric /// Difference for unmatched callees that only present in one profile in a
2016e8d8bef9SDimitry Andric /// matched caller function.
2017e8d8bef9SDimitry Andric void updateForUnmatchedCallee(const sampleprof::FunctionSamples &Func,
2018e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap,
2019e8d8bef9SDimitry Andric double &Difference, MatchStatus Status);
2020e8d8bef9SDimitry Andric
2021e8d8bef9SDimitry Andric /// This function updates sample overlap statistics of an overlap function in
2022e8d8bef9SDimitry Andric /// base and test profile. It also calculates a function-internal similarity
2023e8d8bef9SDimitry Andric /// FIS as follows:
2024e8d8bef9SDimitry Andric /// For offsets i that have samples in at least one profile in this
2025e8d8bef9SDimitry Andric /// function A, given BS(i) returned by computeBlockSimilarity(), compute
2026e8d8bef9SDimitry Andric /// FIS(A) = (2.0 - sum_i(1.0 - BS(i))) / 2, ranging in [0.0f to 1.0f] with
2027e8d8bef9SDimitry Andric /// 0.0 meaning no overlap.
2028e8d8bef9SDimitry Andric double computeSampleFunctionInternalOverlap(
2029e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &BaseFunc,
2030e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &TestFunc,
2031e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap);
2032e8d8bef9SDimitry Andric
2033e8d8bef9SDimitry Andric /// Function-level similarity (FS) is a weighted value over function internal
2034e8d8bef9SDimitry Andric /// similarity (FIS). This function computes a function's FS from its FIS by
2035e8d8bef9SDimitry Andric /// applying the weight.
2036e8d8bef9SDimitry Andric double weightForFuncSimilarity(double FuncSimilarity, uint64_t BaseFuncSample,
2037e8d8bef9SDimitry Andric uint64_t TestFuncSample) const;
2038e8d8bef9SDimitry Andric
2039e8d8bef9SDimitry Andric /// The function-level similarity FS(A) for a function A is computed as
2040e8d8bef9SDimitry Andric /// follows:
2041e8d8bef9SDimitry Andric /// Compute a function-internal similarity FIS(A) by
2042e8d8bef9SDimitry Andric /// computeSampleFunctionInternalOverlap(). Then, with the weight of
2043e8d8bef9SDimitry Andric /// function A in base profile WB(A), and the weight of function A in test
2044e8d8bef9SDimitry Andric /// profile WT(A), compute FS(A) = FIS(A) * (1.0 - fabs(WB(A) - WT(A)))
2045e8d8bef9SDimitry Andric /// ranging in [0.0f to 1.0f] with 0.0 meaning no overlap.
2046e8d8bef9SDimitry Andric double
2047e8d8bef9SDimitry Andric computeSampleFunctionOverlap(const sampleprof::FunctionSamples *BaseFunc,
2048e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *TestFunc,
2049e8d8bef9SDimitry Andric SampleOverlapStats *FuncOverlap,
2050e8d8bef9SDimitry Andric uint64_t BaseFuncSample,
2051e8d8bef9SDimitry Andric uint64_t TestFuncSample);
2052e8d8bef9SDimitry Andric
2053e8d8bef9SDimitry Andric /// Profile-level similarity (PS) is a weighted aggregate over function-level
2054e8d8bef9SDimitry Andric /// similarities (FS). This method weights the FS value by the function
2055e8d8bef9SDimitry Andric /// weights in the base and test profiles for the aggregation.
2056e8d8bef9SDimitry Andric double weightByImportance(double FuncSimilarity, uint64_t BaseFuncSample,
2057e8d8bef9SDimitry Andric uint64_t TestFuncSample) const;
2058e8d8bef9SDimitry Andric };
2059e8d8bef9SDimitry Andric } // end anonymous namespace
2060e8d8bef9SDimitry Andric
detectZeroSampleProfile(raw_fd_ostream & OS) const2061e8d8bef9SDimitry Andric bool SampleOverlapAggregator::detectZeroSampleProfile(
2062e8d8bef9SDimitry Andric raw_fd_ostream &OS) const {
2063e8d8bef9SDimitry Andric bool HaveZeroSample = false;
2064e8d8bef9SDimitry Andric if (ProfOverlap.BaseSample == 0) {
2065e8d8bef9SDimitry Andric OS << "Sum of sample counts for profile " << BaseFilename << " is 0.\n";
2066e8d8bef9SDimitry Andric HaveZeroSample = true;
2067e8d8bef9SDimitry Andric }
2068e8d8bef9SDimitry Andric if (ProfOverlap.TestSample == 0) {
2069e8d8bef9SDimitry Andric OS << "Sum of sample counts for profile " << TestFilename << " is 0.\n";
2070e8d8bef9SDimitry Andric HaveZeroSample = true;
2071e8d8bef9SDimitry Andric }
2072e8d8bef9SDimitry Andric return HaveZeroSample;
2073e8d8bef9SDimitry Andric }
2074e8d8bef9SDimitry Andric
computeBlockSimilarity(uint64_t BaseSample,uint64_t TestSample,const SampleOverlapStats & FuncOverlap) const2075e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeBlockSimilarity(
2076e8d8bef9SDimitry Andric uint64_t BaseSample, uint64_t TestSample,
2077e8d8bef9SDimitry Andric const SampleOverlapStats &FuncOverlap) const {
2078e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2079e8d8bef9SDimitry Andric double TestFrac = 0.0;
2080e8d8bef9SDimitry Andric if (FuncOverlap.BaseSample > 0)
2081e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseSample) / FuncOverlap.BaseSample;
2082e8d8bef9SDimitry Andric if (FuncOverlap.TestSample > 0)
2083e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestSample) / FuncOverlap.TestSample;
2084e8d8bef9SDimitry Andric return 1.0 - std::fabs(BaseFrac - TestFrac);
2085e8d8bef9SDimitry Andric }
2086e8d8bef9SDimitry Andric
updateHotBlockOverlap(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount)2087e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateHotBlockOverlap(uint64_t BaseSample,
2088e8d8bef9SDimitry Andric uint64_t TestSample,
2089e8d8bef9SDimitry Andric uint64_t HotBlockCount) {
2090e8d8bef9SDimitry Andric bool IsBaseHot = (BaseSample >= BaseHotThreshold);
2091e8d8bef9SDimitry Andric bool IsTestHot = (TestSample >= TestHotThreshold);
2092e8d8bef9SDimitry Andric if (!IsBaseHot && !IsTestHot)
2093e8d8bef9SDimitry Andric return;
2094e8d8bef9SDimitry Andric
2095e8d8bef9SDimitry Andric HotBlockOverlap.UnionCount += HotBlockCount;
2096e8d8bef9SDimitry Andric if (IsBaseHot)
2097e8d8bef9SDimitry Andric HotBlockOverlap.BaseCount += HotBlockCount;
2098e8d8bef9SDimitry Andric if (IsTestHot)
2099e8d8bef9SDimitry Andric HotBlockOverlap.TestCount += HotBlockCount;
2100e8d8bef9SDimitry Andric if (IsBaseHot && IsTestHot)
2101e8d8bef9SDimitry Andric HotBlockOverlap.OverlapCount += HotBlockCount;
2102e8d8bef9SDimitry Andric }
2103e8d8bef9SDimitry Andric
getHotFunctions(const FuncSampleStatsMap & ProfStats,FuncSampleStatsMap & HotFunc,uint64_t HotThreshold) const2104e8d8bef9SDimitry Andric void SampleOverlapAggregator::getHotFunctions(
2105349cc55cSDimitry Andric const FuncSampleStatsMap &ProfStats, FuncSampleStatsMap &HotFunc,
2106349cc55cSDimitry Andric uint64_t HotThreshold) const {
2107e8d8bef9SDimitry Andric for (const auto &F : ProfStats) {
2108e8d8bef9SDimitry Andric if (isFunctionHot(F.second, HotThreshold))
2109349cc55cSDimitry Andric HotFunc.emplace(F.first, F.second);
2110e8d8bef9SDimitry Andric }
2111e8d8bef9SDimitry Andric }
2112e8d8bef9SDimitry Andric
computeHotFuncOverlap()2113e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeHotFuncOverlap() {
2114349cc55cSDimitry Andric FuncSampleStatsMap BaseHotFunc;
2115e8d8bef9SDimitry Andric getHotFunctions(BaseStats, BaseHotFunc, BaseHotThreshold);
2116e8d8bef9SDimitry Andric HotFuncOverlap.BaseCount = BaseHotFunc.size();
2117e8d8bef9SDimitry Andric
2118349cc55cSDimitry Andric FuncSampleStatsMap TestHotFunc;
2119e8d8bef9SDimitry Andric getHotFunctions(TestStats, TestHotFunc, TestHotThreshold);
2120e8d8bef9SDimitry Andric HotFuncOverlap.TestCount = TestHotFunc.size();
2121e8d8bef9SDimitry Andric HotFuncOverlap.UnionCount = HotFuncOverlap.TestCount;
2122e8d8bef9SDimitry Andric
2123e8d8bef9SDimitry Andric for (const auto &F : BaseHotFunc) {
2124349cc55cSDimitry Andric if (TestHotFunc.count(F.first))
2125e8d8bef9SDimitry Andric ++HotFuncOverlap.OverlapCount;
2126e8d8bef9SDimitry Andric else
2127e8d8bef9SDimitry Andric ++HotFuncOverlap.UnionCount;
2128e8d8bef9SDimitry Andric }
2129e8d8bef9SDimitry Andric }
2130e8d8bef9SDimitry Andric
updateOverlapStatsForFunction(uint64_t BaseSample,uint64_t TestSample,uint64_t HotBlockCount,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2131e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateOverlapStatsForFunction(
2132e8d8bef9SDimitry Andric uint64_t BaseSample, uint64_t TestSample, uint64_t HotBlockCount,
2133e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap, double &Difference, MatchStatus Status) {
2134e8d8bef9SDimitry Andric assert(Status != MS_None &&
2135e8d8bef9SDimitry Andric "Match status should be updated before updating overlap statistics");
2136e8d8bef9SDimitry Andric if (Status == MS_FirstUnique) {
2137e8d8bef9SDimitry Andric TestSample = 0;
2138e8d8bef9SDimitry Andric FuncOverlap.BaseUniqueSample += BaseSample;
2139e8d8bef9SDimitry Andric } else if (Status == MS_SecondUnique) {
2140e8d8bef9SDimitry Andric BaseSample = 0;
2141e8d8bef9SDimitry Andric FuncOverlap.TestUniqueSample += TestSample;
2142e8d8bef9SDimitry Andric } else {
2143e8d8bef9SDimitry Andric ++FuncOverlap.OverlapCount;
2144e8d8bef9SDimitry Andric }
2145e8d8bef9SDimitry Andric
2146e8d8bef9SDimitry Andric FuncOverlap.UnionSample += std::max(BaseSample, TestSample);
2147e8d8bef9SDimitry Andric FuncOverlap.OverlapSample += std::min(BaseSample, TestSample);
2148e8d8bef9SDimitry Andric Difference +=
2149e8d8bef9SDimitry Andric 1.0 - computeBlockSimilarity(BaseSample, TestSample, FuncOverlap);
2150e8d8bef9SDimitry Andric updateHotBlockOverlap(BaseSample, TestSample, HotBlockCount);
2151e8d8bef9SDimitry Andric }
2152e8d8bef9SDimitry Andric
updateForUnmatchedCallee(const sampleprof::FunctionSamples & Func,SampleOverlapStats & FuncOverlap,double & Difference,MatchStatus Status)2153e8d8bef9SDimitry Andric void SampleOverlapAggregator::updateForUnmatchedCallee(
2154e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &Func, SampleOverlapStats &FuncOverlap,
2155e8d8bef9SDimitry Andric double &Difference, MatchStatus Status) {
2156e8d8bef9SDimitry Andric assert((Status == MS_FirstUnique || Status == MS_SecondUnique) &&
2157e8d8bef9SDimitry Andric "Status must be either of the two unmatched cases");
2158e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2159e8d8bef9SDimitry Andric if (Status == MS_FirstUnique) {
2160e8d8bef9SDimitry Andric getFuncSampleStats(Func, FuncStats, BaseHotThreshold);
2161e8d8bef9SDimitry Andric updateOverlapStatsForFunction(FuncStats.SampleSum, 0,
2162e8d8bef9SDimitry Andric FuncStats.HotBlockCount, FuncOverlap,
2163e8d8bef9SDimitry Andric Difference, Status);
2164e8d8bef9SDimitry Andric } else {
2165e8d8bef9SDimitry Andric getFuncSampleStats(Func, FuncStats, TestHotThreshold);
2166e8d8bef9SDimitry Andric updateOverlapStatsForFunction(0, FuncStats.SampleSum,
2167e8d8bef9SDimitry Andric FuncStats.HotBlockCount, FuncOverlap,
2168e8d8bef9SDimitry Andric Difference, Status);
2169e8d8bef9SDimitry Andric }
2170e8d8bef9SDimitry Andric }
2171e8d8bef9SDimitry Andric
computeSampleFunctionInternalOverlap(const sampleprof::FunctionSamples & BaseFunc,const sampleprof::FunctionSamples & TestFunc,SampleOverlapStats & FuncOverlap)2172e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionInternalOverlap(
2173e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &BaseFunc,
2174e8d8bef9SDimitry Andric const sampleprof::FunctionSamples &TestFunc,
2175e8d8bef9SDimitry Andric SampleOverlapStats &FuncOverlap) {
2176e8d8bef9SDimitry Andric
2177e8d8bef9SDimitry Andric using namespace sampleprof;
2178e8d8bef9SDimitry Andric
2179e8d8bef9SDimitry Andric double Difference = 0;
2180e8d8bef9SDimitry Andric
2181e8d8bef9SDimitry Andric // Accumulate Difference for regular line/block samples in the function.
2182e8d8bef9SDimitry Andric // We match them through sort-merge join algorithm because
2183e8d8bef9SDimitry Andric // FunctionSamples::getBodySamples() returns a map of sample counters ordered
2184e8d8bef9SDimitry Andric // by their offsets.
2185e8d8bef9SDimitry Andric MatchStep<BodySampleMap::const_iterator> BlockIterStep(
2186e8d8bef9SDimitry Andric BaseFunc.getBodySamples().cbegin(), BaseFunc.getBodySamples().cend(),
2187e8d8bef9SDimitry Andric TestFunc.getBodySamples().cbegin(), TestFunc.getBodySamples().cend());
2188e8d8bef9SDimitry Andric BlockIterStep.updateOneStep();
2189e8d8bef9SDimitry Andric while (!BlockIterStep.areBothFinished()) {
2190e8d8bef9SDimitry Andric uint64_t BaseSample =
2191e8d8bef9SDimitry Andric BlockIterStep.isFirstFinished()
2192e8d8bef9SDimitry Andric ? 0
2193e8d8bef9SDimitry Andric : BlockIterStep.getFirstIter()->second.getSamples();
2194e8d8bef9SDimitry Andric uint64_t TestSample =
2195e8d8bef9SDimitry Andric BlockIterStep.isSecondFinished()
2196e8d8bef9SDimitry Andric ? 0
2197e8d8bef9SDimitry Andric : BlockIterStep.getSecondIter()->second.getSamples();
2198e8d8bef9SDimitry Andric updateOverlapStatsForFunction(BaseSample, TestSample, 1, FuncOverlap,
2199e8d8bef9SDimitry Andric Difference, BlockIterStep.getMatchStatus());
2200e8d8bef9SDimitry Andric
2201e8d8bef9SDimitry Andric BlockIterStep.updateOneStep();
2202e8d8bef9SDimitry Andric }
2203e8d8bef9SDimitry Andric
2204e8d8bef9SDimitry Andric // Accumulate Difference for callsite lines in the function. We match
2205e8d8bef9SDimitry Andric // them through sort-merge algorithm because
2206e8d8bef9SDimitry Andric // FunctionSamples::getCallsiteSamples() returns a map of callsite records
2207e8d8bef9SDimitry Andric // ordered by their offsets.
2208e8d8bef9SDimitry Andric MatchStep<CallsiteSampleMap::const_iterator> CallsiteIterStep(
2209e8d8bef9SDimitry Andric BaseFunc.getCallsiteSamples().cbegin(),
2210e8d8bef9SDimitry Andric BaseFunc.getCallsiteSamples().cend(),
2211e8d8bef9SDimitry Andric TestFunc.getCallsiteSamples().cbegin(),
2212e8d8bef9SDimitry Andric TestFunc.getCallsiteSamples().cend());
2213e8d8bef9SDimitry Andric CallsiteIterStep.updateOneStep();
2214e8d8bef9SDimitry Andric while (!CallsiteIterStep.areBothFinished()) {
2215e8d8bef9SDimitry Andric MatchStatus CallsiteStepStatus = CallsiteIterStep.getMatchStatus();
2216e8d8bef9SDimitry Andric assert(CallsiteStepStatus != MS_None &&
2217e8d8bef9SDimitry Andric "Match status should be updated before entering loop body");
2218e8d8bef9SDimitry Andric
2219e8d8bef9SDimitry Andric if (CallsiteStepStatus != MS_Match) {
2220e8d8bef9SDimitry Andric auto Callsite = (CallsiteStepStatus == MS_FirstUnique)
2221e8d8bef9SDimitry Andric ? CallsiteIterStep.getFirstIter()
2222e8d8bef9SDimitry Andric : CallsiteIterStep.getSecondIter();
2223e8d8bef9SDimitry Andric for (const auto &F : Callsite->second)
2224e8d8bef9SDimitry Andric updateForUnmatchedCallee(F.second, FuncOverlap, Difference,
2225e8d8bef9SDimitry Andric CallsiteStepStatus);
2226e8d8bef9SDimitry Andric } else {
2227e8d8bef9SDimitry Andric // There may be multiple inlinees at the same offset, so we need to try
2228e8d8bef9SDimitry Andric // matching all of them. This match is implemented through sort-merge
2229e8d8bef9SDimitry Andric // algorithm because callsite records at the same offset are ordered by
2230e8d8bef9SDimitry Andric // function names.
2231e8d8bef9SDimitry Andric MatchStep<FunctionSamplesMap::const_iterator> CalleeIterStep(
2232e8d8bef9SDimitry Andric CallsiteIterStep.getFirstIter()->second.cbegin(),
2233e8d8bef9SDimitry Andric CallsiteIterStep.getFirstIter()->second.cend(),
2234e8d8bef9SDimitry Andric CallsiteIterStep.getSecondIter()->second.cbegin(),
2235e8d8bef9SDimitry Andric CallsiteIterStep.getSecondIter()->second.cend());
2236e8d8bef9SDimitry Andric CalleeIterStep.updateOneStep();
2237e8d8bef9SDimitry Andric while (!CalleeIterStep.areBothFinished()) {
2238e8d8bef9SDimitry Andric MatchStatus CalleeStepStatus = CalleeIterStep.getMatchStatus();
2239e8d8bef9SDimitry Andric if (CalleeStepStatus != MS_Match) {
2240e8d8bef9SDimitry Andric auto Callee = (CalleeStepStatus == MS_FirstUnique)
2241e8d8bef9SDimitry Andric ? CalleeIterStep.getFirstIter()
2242e8d8bef9SDimitry Andric : CalleeIterStep.getSecondIter();
2243e8d8bef9SDimitry Andric updateForUnmatchedCallee(Callee->second, FuncOverlap, Difference,
2244e8d8bef9SDimitry Andric CalleeStepStatus);
2245e8d8bef9SDimitry Andric } else {
2246e8d8bef9SDimitry Andric // An inlined function can contain other inlinees inside, so compute
2247e8d8bef9SDimitry Andric // the Difference recursively.
2248e8d8bef9SDimitry Andric Difference += 2.0 - 2 * computeSampleFunctionInternalOverlap(
2249e8d8bef9SDimitry Andric CalleeIterStep.getFirstIter()->second,
2250e8d8bef9SDimitry Andric CalleeIterStep.getSecondIter()->second,
2251e8d8bef9SDimitry Andric FuncOverlap);
2252e8d8bef9SDimitry Andric }
2253e8d8bef9SDimitry Andric CalleeIterStep.updateOneStep();
2254e8d8bef9SDimitry Andric }
2255e8d8bef9SDimitry Andric }
2256e8d8bef9SDimitry Andric CallsiteIterStep.updateOneStep();
2257e8d8bef9SDimitry Andric }
2258e8d8bef9SDimitry Andric
2259e8d8bef9SDimitry Andric // Difference reflects the total differences of line/block samples in this
2260e8d8bef9SDimitry Andric // function and ranges in [0.0f to 2.0f]. Take (2.0 - Difference) / 2 to
2261e8d8bef9SDimitry Andric // reflect the similarity between function profiles in [0.0f to 1.0f].
2262e8d8bef9SDimitry Andric return (2.0 - Difference) / 2;
2263e8d8bef9SDimitry Andric }
2264e8d8bef9SDimitry Andric
weightForFuncSimilarity(double FuncInternalSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2265e8d8bef9SDimitry Andric double SampleOverlapAggregator::weightForFuncSimilarity(
2266e8d8bef9SDimitry Andric double FuncInternalSimilarity, uint64_t BaseFuncSample,
2267e8d8bef9SDimitry Andric uint64_t TestFuncSample) const {
2268e8d8bef9SDimitry Andric // Compute the weight as the distance between the function weights in two
2269e8d8bef9SDimitry Andric // profiles.
2270e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2271e8d8bef9SDimitry Andric double TestFrac = 0.0;
2272e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2273e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2274e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample;
2275e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2276e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2277e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample;
2278e8d8bef9SDimitry Andric double WeightDistance = std::fabs(BaseFrac - TestFrac);
2279e8d8bef9SDimitry Andric
2280e8d8bef9SDimitry Andric // Take WeightDistance into the similarity.
2281e8d8bef9SDimitry Andric return FuncInternalSimilarity * (1 - WeightDistance);
2282e8d8bef9SDimitry Andric }
2283e8d8bef9SDimitry Andric
2284e8d8bef9SDimitry Andric double
weightByImportance(double FuncSimilarity,uint64_t BaseFuncSample,uint64_t TestFuncSample) const2285e8d8bef9SDimitry Andric SampleOverlapAggregator::weightByImportance(double FuncSimilarity,
2286e8d8bef9SDimitry Andric uint64_t BaseFuncSample,
2287e8d8bef9SDimitry Andric uint64_t TestFuncSample) const {
2288e8d8bef9SDimitry Andric
2289e8d8bef9SDimitry Andric double BaseFrac = 0.0;
2290e8d8bef9SDimitry Andric double TestFrac = 0.0;
2291e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2292e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2293e8d8bef9SDimitry Andric BaseFrac = static_cast<double>(BaseFuncSample) / ProfOverlap.BaseSample / 2.0;
2294e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2295e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2296e8d8bef9SDimitry Andric TestFrac = static_cast<double>(TestFuncSample) / ProfOverlap.TestSample / 2.0;
2297e8d8bef9SDimitry Andric return FuncSimilarity * (BaseFrac + TestFrac);
2298e8d8bef9SDimitry Andric }
2299e8d8bef9SDimitry Andric
computeSampleFunctionOverlap(const sampleprof::FunctionSamples * BaseFunc,const sampleprof::FunctionSamples * TestFunc,SampleOverlapStats * FuncOverlap,uint64_t BaseFuncSample,uint64_t TestFuncSample)2300e8d8bef9SDimitry Andric double SampleOverlapAggregator::computeSampleFunctionOverlap(
2301e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *BaseFunc,
2302e8d8bef9SDimitry Andric const sampleprof::FunctionSamples *TestFunc,
2303e8d8bef9SDimitry Andric SampleOverlapStats *FuncOverlap, uint64_t BaseFuncSample,
2304e8d8bef9SDimitry Andric uint64_t TestFuncSample) {
2305e8d8bef9SDimitry Andric // Default function internal similarity before weighted, meaning two functions
2306e8d8bef9SDimitry Andric // has no overlap.
2307e8d8bef9SDimitry Andric const double DefaultFuncInternalSimilarity = 0;
2308e8d8bef9SDimitry Andric double FuncSimilarity;
2309e8d8bef9SDimitry Andric double FuncInternalSimilarity;
2310e8d8bef9SDimitry Andric
2311e8d8bef9SDimitry Andric // If BaseFunc or TestFunc is nullptr, it means the functions do not overlap.
2312e8d8bef9SDimitry Andric // In this case, we use DefaultFuncInternalSimilarity as the function internal
2313e8d8bef9SDimitry Andric // similarity.
2314e8d8bef9SDimitry Andric if (!BaseFunc || !TestFunc) {
2315e8d8bef9SDimitry Andric FuncInternalSimilarity = DefaultFuncInternalSimilarity;
2316e8d8bef9SDimitry Andric } else {
2317e8d8bef9SDimitry Andric assert(FuncOverlap != nullptr &&
2318e8d8bef9SDimitry Andric "FuncOverlap should be provided in this case");
2319e8d8bef9SDimitry Andric FuncInternalSimilarity = computeSampleFunctionInternalOverlap(
2320e8d8bef9SDimitry Andric *BaseFunc, *TestFunc, *FuncOverlap);
2321e8d8bef9SDimitry Andric // Now, FuncInternalSimilarity may be a little less than 0 due to
2322e8d8bef9SDimitry Andric // imprecision of floating point accumulations. Make it zero if the
2323e8d8bef9SDimitry Andric // difference is below Epsilon.
2324e8d8bef9SDimitry Andric FuncInternalSimilarity = (std::fabs(FuncInternalSimilarity - 0) < Epsilon)
2325e8d8bef9SDimitry Andric ? 0
2326e8d8bef9SDimitry Andric : FuncInternalSimilarity;
2327e8d8bef9SDimitry Andric }
2328e8d8bef9SDimitry Andric FuncSimilarity = weightForFuncSimilarity(FuncInternalSimilarity,
2329e8d8bef9SDimitry Andric BaseFuncSample, TestFuncSample);
2330e8d8bef9SDimitry Andric return FuncSimilarity;
2331e8d8bef9SDimitry Andric }
2332e8d8bef9SDimitry Andric
computeSampleProfileOverlap(raw_fd_ostream & OS)2333e8d8bef9SDimitry Andric void SampleOverlapAggregator::computeSampleProfileOverlap(raw_fd_ostream &OS) {
2334e8d8bef9SDimitry Andric using namespace sampleprof;
2335e8d8bef9SDimitry Andric
2336349cc55cSDimitry Andric std::unordered_map<SampleContext, const FunctionSamples *,
2337349cc55cSDimitry Andric SampleContext::Hash>
2338349cc55cSDimitry Andric BaseFuncProf;
2339e8d8bef9SDimitry Andric const auto &BaseProfiles = BaseReader->getProfiles();
2340e8d8bef9SDimitry Andric for (const auto &BaseFunc : BaseProfiles) {
2341349cc55cSDimitry Andric BaseFuncProf.emplace(BaseFunc.second.getContext(), &(BaseFunc.second));
2342e8d8bef9SDimitry Andric }
2343e8d8bef9SDimitry Andric ProfOverlap.UnionCount = BaseFuncProf.size();
2344e8d8bef9SDimitry Andric
2345e8d8bef9SDimitry Andric const auto &TestProfiles = TestReader->getProfiles();
2346e8d8bef9SDimitry Andric for (const auto &TestFunc : TestProfiles) {
2347e8d8bef9SDimitry Andric SampleOverlapStats FuncOverlap;
2348349cc55cSDimitry Andric FuncOverlap.TestName = TestFunc.second.getContext();
2349e8d8bef9SDimitry Andric assert(TestStats.count(FuncOverlap.TestName) &&
2350e8d8bef9SDimitry Andric "TestStats should have records for all functions in test profile "
2351e8d8bef9SDimitry Andric "except inlinees");
2352e8d8bef9SDimitry Andric FuncOverlap.TestSample = TestStats[FuncOverlap.TestName].SampleSum;
2353e8d8bef9SDimitry Andric
2354349cc55cSDimitry Andric bool Matched = false;
2355e8d8bef9SDimitry Andric const auto Match = BaseFuncProf.find(FuncOverlap.TestName);
2356e8d8bef9SDimitry Andric if (Match == BaseFuncProf.end()) {
2357e8d8bef9SDimitry Andric const FuncSampleStats &FuncStats = TestStats[FuncOverlap.TestName];
2358e8d8bef9SDimitry Andric ++ProfOverlap.TestUniqueCount;
2359e8d8bef9SDimitry Andric ProfOverlap.TestUniqueSample += FuncStats.SampleSum;
2360e8d8bef9SDimitry Andric FuncOverlap.TestUniqueSample = FuncStats.SampleSum;
2361e8d8bef9SDimitry Andric
2362e8d8bef9SDimitry Andric updateHotBlockOverlap(0, FuncStats.SampleSum, FuncStats.HotBlockCount);
2363e8d8bef9SDimitry Andric
2364e8d8bef9SDimitry Andric double FuncSimilarity = computeSampleFunctionOverlap(
2365e8d8bef9SDimitry Andric nullptr, nullptr, nullptr, 0, FuncStats.SampleSum);
2366e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2367e8d8bef9SDimitry Andric weightByImportance(FuncSimilarity, 0, FuncStats.SampleSum);
2368e8d8bef9SDimitry Andric
2369e8d8bef9SDimitry Andric ++ProfOverlap.UnionCount;
2370e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncStats.SampleSum;
2371e8d8bef9SDimitry Andric } else {
2372e8d8bef9SDimitry Andric ++ProfOverlap.OverlapCount;
2373e8d8bef9SDimitry Andric
2374e8d8bef9SDimitry Andric // Two functions match with each other. Compute function-level overlap and
2375e8d8bef9SDimitry Andric // aggregate them into profile-level overlap.
2376349cc55cSDimitry Andric FuncOverlap.BaseName = Match->second->getContext();
2377e8d8bef9SDimitry Andric assert(BaseStats.count(FuncOverlap.BaseName) &&
2378e8d8bef9SDimitry Andric "BaseStats should have records for all functions in base profile "
2379e8d8bef9SDimitry Andric "except inlinees");
2380e8d8bef9SDimitry Andric FuncOverlap.BaseSample = BaseStats[FuncOverlap.BaseName].SampleSum;
2381e8d8bef9SDimitry Andric
2382e8d8bef9SDimitry Andric FuncOverlap.Similarity = computeSampleFunctionOverlap(
2383e8d8bef9SDimitry Andric Match->second, &TestFunc.second, &FuncOverlap, FuncOverlap.BaseSample,
2384e8d8bef9SDimitry Andric FuncOverlap.TestSample);
2385e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2386e8d8bef9SDimitry Andric weightByImportance(FuncOverlap.Similarity, FuncOverlap.BaseSample,
2387e8d8bef9SDimitry Andric FuncOverlap.TestSample);
2388e8d8bef9SDimitry Andric ProfOverlap.OverlapSample += FuncOverlap.OverlapSample;
2389e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncOverlap.UnionSample;
2390e8d8bef9SDimitry Andric
2391e8d8bef9SDimitry Andric // Accumulate the percentage of base unique and test unique samples into
2392e8d8bef9SDimitry Andric // ProfOverlap.
2393e8d8bef9SDimitry Andric ProfOverlap.BaseUniqueSample += FuncOverlap.BaseUniqueSample;
2394e8d8bef9SDimitry Andric ProfOverlap.TestUniqueSample += FuncOverlap.TestUniqueSample;
2395e8d8bef9SDimitry Andric
2396e8d8bef9SDimitry Andric // Remove matched base functions for later reporting functions not found
2397e8d8bef9SDimitry Andric // in test profile.
2398e8d8bef9SDimitry Andric BaseFuncProf.erase(Match);
2399349cc55cSDimitry Andric Matched = true;
2400e8d8bef9SDimitry Andric }
2401e8d8bef9SDimitry Andric
2402e8d8bef9SDimitry Andric // Print function-level similarity information if specified by options.
2403e8d8bef9SDimitry Andric assert(TestStats.count(FuncOverlap.TestName) &&
2404e8d8bef9SDimitry Andric "TestStats should have records for all functions in test profile "
2405e8d8bef9SDimitry Andric "except inlinees");
2406e8d8bef9SDimitry Andric if (TestStats[FuncOverlap.TestName].MaxSample >= FuncFilter.ValueCutoff ||
2407349cc55cSDimitry Andric (Matched && FuncOverlap.Similarity < LowSimilarityThreshold) ||
2408349cc55cSDimitry Andric (Matched && !FuncFilter.NameFilter.empty() &&
2409349cc55cSDimitry Andric FuncOverlap.BaseName.toString().find(FuncFilter.NameFilter) !=
2410349cc55cSDimitry Andric std::string::npos)) {
2411e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2412e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2413e8d8bef9SDimitry Andric FuncOverlap.BaseWeight =
2414e8d8bef9SDimitry Andric static_cast<double>(FuncOverlap.BaseSample) / ProfOverlap.BaseSample;
2415e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2416e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2417e8d8bef9SDimitry Andric FuncOverlap.TestWeight =
2418e8d8bef9SDimitry Andric static_cast<double>(FuncOverlap.TestSample) / ProfOverlap.TestSample;
2419e8d8bef9SDimitry Andric FuncSimilarityDump.emplace(FuncOverlap.BaseWeight, FuncOverlap);
2420e8d8bef9SDimitry Andric }
2421e8d8bef9SDimitry Andric }
2422e8d8bef9SDimitry Andric
2423e8d8bef9SDimitry Andric // Traverse through functions in base profile but not in test profile.
2424e8d8bef9SDimitry Andric for (const auto &F : BaseFuncProf) {
2425349cc55cSDimitry Andric assert(BaseStats.count(F.second->getContext()) &&
2426e8d8bef9SDimitry Andric "BaseStats should have records for all functions in base profile "
2427e8d8bef9SDimitry Andric "except inlinees");
2428349cc55cSDimitry Andric const FuncSampleStats &FuncStats = BaseStats[F.second->getContext()];
2429e8d8bef9SDimitry Andric ++ProfOverlap.BaseUniqueCount;
2430e8d8bef9SDimitry Andric ProfOverlap.BaseUniqueSample += FuncStats.SampleSum;
2431e8d8bef9SDimitry Andric
2432e8d8bef9SDimitry Andric updateHotBlockOverlap(FuncStats.SampleSum, 0, FuncStats.HotBlockCount);
2433e8d8bef9SDimitry Andric
2434e8d8bef9SDimitry Andric double FuncSimilarity = computeSampleFunctionOverlap(
2435e8d8bef9SDimitry Andric nullptr, nullptr, nullptr, FuncStats.SampleSum, 0);
2436e8d8bef9SDimitry Andric ProfOverlap.Similarity +=
2437e8d8bef9SDimitry Andric weightByImportance(FuncSimilarity, FuncStats.SampleSum, 0);
2438e8d8bef9SDimitry Andric
2439e8d8bef9SDimitry Andric ProfOverlap.UnionSample += FuncStats.SampleSum;
2440e8d8bef9SDimitry Andric }
2441e8d8bef9SDimitry Andric
2442e8d8bef9SDimitry Andric // Now, ProfSimilarity may be a little greater than 1 due to imprecision
2443e8d8bef9SDimitry Andric // of floating point accumulations. Make it 1.0 if the difference is below
2444e8d8bef9SDimitry Andric // Epsilon.
2445e8d8bef9SDimitry Andric ProfOverlap.Similarity = (std::fabs(ProfOverlap.Similarity - 1) < Epsilon)
2446e8d8bef9SDimitry Andric ? 1
2447e8d8bef9SDimitry Andric : ProfOverlap.Similarity;
2448e8d8bef9SDimitry Andric
2449e8d8bef9SDimitry Andric computeHotFuncOverlap();
2450e8d8bef9SDimitry Andric }
2451e8d8bef9SDimitry Andric
initializeSampleProfileOverlap()2452e8d8bef9SDimitry Andric void SampleOverlapAggregator::initializeSampleProfileOverlap() {
2453e8d8bef9SDimitry Andric const auto &BaseProf = BaseReader->getProfiles();
2454e8d8bef9SDimitry Andric for (const auto &I : BaseProf) {
2455e8d8bef9SDimitry Andric ++ProfOverlap.BaseCount;
2456e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2457e8d8bef9SDimitry Andric getFuncSampleStats(I.second, FuncStats, BaseHotThreshold);
2458e8d8bef9SDimitry Andric ProfOverlap.BaseSample += FuncStats.SampleSum;
2459349cc55cSDimitry Andric BaseStats.emplace(I.second.getContext(), FuncStats);
2460e8d8bef9SDimitry Andric }
2461e8d8bef9SDimitry Andric
2462e8d8bef9SDimitry Andric const auto &TestProf = TestReader->getProfiles();
2463e8d8bef9SDimitry Andric for (const auto &I : TestProf) {
2464e8d8bef9SDimitry Andric ++ProfOverlap.TestCount;
2465e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
2466e8d8bef9SDimitry Andric getFuncSampleStats(I.second, FuncStats, TestHotThreshold);
2467e8d8bef9SDimitry Andric ProfOverlap.TestSample += FuncStats.SampleSum;
2468349cc55cSDimitry Andric TestStats.emplace(I.second.getContext(), FuncStats);
2469e8d8bef9SDimitry Andric }
2470e8d8bef9SDimitry Andric
2471e8d8bef9SDimitry Andric ProfOverlap.BaseName = StringRef(BaseFilename);
2472e8d8bef9SDimitry Andric ProfOverlap.TestName = StringRef(TestFilename);
2473e8d8bef9SDimitry Andric }
2474e8d8bef9SDimitry Andric
dumpFuncSimilarity(raw_fd_ostream & OS) const2475e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpFuncSimilarity(raw_fd_ostream &OS) const {
2476e8d8bef9SDimitry Andric using namespace sampleprof;
2477e8d8bef9SDimitry Andric
2478e8d8bef9SDimitry Andric if (FuncSimilarityDump.empty())
2479e8d8bef9SDimitry Andric return;
2480e8d8bef9SDimitry Andric
2481e8d8bef9SDimitry Andric formatted_raw_ostream FOS(OS);
2482e8d8bef9SDimitry Andric FOS << "Function-level details:\n";
2483e8d8bef9SDimitry Andric FOS << "Base weight";
2484e8d8bef9SDimitry Andric FOS.PadToColumn(TestWeightCol);
2485e8d8bef9SDimitry Andric FOS << "Test weight";
2486e8d8bef9SDimitry Andric FOS.PadToColumn(SimilarityCol);
2487e8d8bef9SDimitry Andric FOS << "Similarity";
2488e8d8bef9SDimitry Andric FOS.PadToColumn(OverlapCol);
2489e8d8bef9SDimitry Andric FOS << "Overlap";
2490e8d8bef9SDimitry Andric FOS.PadToColumn(BaseUniqueCol);
2491e8d8bef9SDimitry Andric FOS << "Base unique";
2492e8d8bef9SDimitry Andric FOS.PadToColumn(TestUniqueCol);
2493e8d8bef9SDimitry Andric FOS << "Test unique";
2494e8d8bef9SDimitry Andric FOS.PadToColumn(BaseSampleCol);
2495e8d8bef9SDimitry Andric FOS << "Base samples";
2496e8d8bef9SDimitry Andric FOS.PadToColumn(TestSampleCol);
2497e8d8bef9SDimitry Andric FOS << "Test samples";
2498e8d8bef9SDimitry Andric FOS.PadToColumn(FuncNameCol);
2499e8d8bef9SDimitry Andric FOS << "Function name\n";
2500e8d8bef9SDimitry Andric for (const auto &F : FuncSimilarityDump) {
2501e8d8bef9SDimitry Andric double OverlapPercent =
2502e8d8bef9SDimitry Andric F.second.UnionSample > 0
2503e8d8bef9SDimitry Andric ? static_cast<double>(F.second.OverlapSample) / F.second.UnionSample
2504e8d8bef9SDimitry Andric : 0;
2505e8d8bef9SDimitry Andric double BaseUniquePercent =
2506e8d8bef9SDimitry Andric F.second.BaseSample > 0
2507e8d8bef9SDimitry Andric ? static_cast<double>(F.second.BaseUniqueSample) /
2508e8d8bef9SDimitry Andric F.second.BaseSample
2509e8d8bef9SDimitry Andric : 0;
2510e8d8bef9SDimitry Andric double TestUniquePercent =
2511e8d8bef9SDimitry Andric F.second.TestSample > 0
2512e8d8bef9SDimitry Andric ? static_cast<double>(F.second.TestUniqueSample) /
2513e8d8bef9SDimitry Andric F.second.TestSample
2514e8d8bef9SDimitry Andric : 0;
2515e8d8bef9SDimitry Andric
2516e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.BaseWeight * 100);
2517e8d8bef9SDimitry Andric FOS.PadToColumn(TestWeightCol);
2518e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.TestWeight * 100);
2519e8d8bef9SDimitry Andric FOS.PadToColumn(SimilarityCol);
2520e8d8bef9SDimitry Andric FOS << format("%.2f%%", F.second.Similarity * 100);
2521e8d8bef9SDimitry Andric FOS.PadToColumn(OverlapCol);
2522e8d8bef9SDimitry Andric FOS << format("%.2f%%", OverlapPercent * 100);
2523e8d8bef9SDimitry Andric FOS.PadToColumn(BaseUniqueCol);
2524e8d8bef9SDimitry Andric FOS << format("%.2f%%", BaseUniquePercent * 100);
2525e8d8bef9SDimitry Andric FOS.PadToColumn(TestUniqueCol);
2526e8d8bef9SDimitry Andric FOS << format("%.2f%%", TestUniquePercent * 100);
2527e8d8bef9SDimitry Andric FOS.PadToColumn(BaseSampleCol);
2528e8d8bef9SDimitry Andric FOS << F.second.BaseSample;
2529e8d8bef9SDimitry Andric FOS.PadToColumn(TestSampleCol);
2530e8d8bef9SDimitry Andric FOS << F.second.TestSample;
2531e8d8bef9SDimitry Andric FOS.PadToColumn(FuncNameCol);
2532349cc55cSDimitry Andric FOS << F.second.TestName.toString() << "\n";
2533e8d8bef9SDimitry Andric }
2534e8d8bef9SDimitry Andric }
2535e8d8bef9SDimitry Andric
dumpProgramSummary(raw_fd_ostream & OS) const2536e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpProgramSummary(raw_fd_ostream &OS) const {
2537349cc55cSDimitry Andric OS << "Profile overlap infomation for base_profile: "
2538349cc55cSDimitry Andric << ProfOverlap.BaseName.toString()
2539349cc55cSDimitry Andric << " and test_profile: " << ProfOverlap.TestName.toString()
2540349cc55cSDimitry Andric << "\nProgram level:\n";
2541e8d8bef9SDimitry Andric
2542e8d8bef9SDimitry Andric OS << " Whole program profile similarity: "
2543e8d8bef9SDimitry Andric << format("%.3f%%", ProfOverlap.Similarity * 100) << "\n";
2544e8d8bef9SDimitry Andric
2545e8d8bef9SDimitry Andric assert(ProfOverlap.UnionSample > 0 &&
2546e8d8bef9SDimitry Andric "Total samples in two profile should be greater than 0");
2547e8d8bef9SDimitry Andric double OverlapPercent =
2548e8d8bef9SDimitry Andric static_cast<double>(ProfOverlap.OverlapSample) / ProfOverlap.UnionSample;
2549e8d8bef9SDimitry Andric assert(ProfOverlap.BaseSample > 0 &&
2550e8d8bef9SDimitry Andric "Total samples in base profile should be greater than 0");
2551e8d8bef9SDimitry Andric double BaseUniquePercent = static_cast<double>(ProfOverlap.BaseUniqueSample) /
2552e8d8bef9SDimitry Andric ProfOverlap.BaseSample;
2553e8d8bef9SDimitry Andric assert(ProfOverlap.TestSample > 0 &&
2554e8d8bef9SDimitry Andric "Total samples in test profile should be greater than 0");
2555e8d8bef9SDimitry Andric double TestUniquePercent = static_cast<double>(ProfOverlap.TestUniqueSample) /
2556e8d8bef9SDimitry Andric ProfOverlap.TestSample;
2557e8d8bef9SDimitry Andric
2558e8d8bef9SDimitry Andric OS << " Whole program sample overlap: "
2559e8d8bef9SDimitry Andric << format("%.3f%%", OverlapPercent * 100) << "\n";
2560e8d8bef9SDimitry Andric OS << " percentage of samples unique in base profile: "
2561e8d8bef9SDimitry Andric << format("%.3f%%", BaseUniquePercent * 100) << "\n";
2562e8d8bef9SDimitry Andric OS << " percentage of samples unique in test profile: "
2563e8d8bef9SDimitry Andric << format("%.3f%%", TestUniquePercent * 100) << "\n";
2564e8d8bef9SDimitry Andric OS << " total samples in base profile: " << ProfOverlap.BaseSample << "\n"
2565e8d8bef9SDimitry Andric << " total samples in test profile: " << ProfOverlap.TestSample << "\n";
2566e8d8bef9SDimitry Andric
2567e8d8bef9SDimitry Andric assert(ProfOverlap.UnionCount > 0 &&
2568e8d8bef9SDimitry Andric "There should be at least one function in two input profiles");
2569e8d8bef9SDimitry Andric double FuncOverlapPercent =
2570e8d8bef9SDimitry Andric static_cast<double>(ProfOverlap.OverlapCount) / ProfOverlap.UnionCount;
2571e8d8bef9SDimitry Andric OS << " Function overlap: " << format("%.3f%%", FuncOverlapPercent * 100)
2572e8d8bef9SDimitry Andric << "\n";
2573e8d8bef9SDimitry Andric OS << " overlap functions: " << ProfOverlap.OverlapCount << "\n";
2574e8d8bef9SDimitry Andric OS << " functions unique in base profile: " << ProfOverlap.BaseUniqueCount
2575e8d8bef9SDimitry Andric << "\n";
2576e8d8bef9SDimitry Andric OS << " functions unique in test profile: " << ProfOverlap.TestUniqueCount
2577e8d8bef9SDimitry Andric << "\n";
2578e8d8bef9SDimitry Andric }
2579e8d8bef9SDimitry Andric
dumpHotFuncAndBlockOverlap(raw_fd_ostream & OS) const2580e8d8bef9SDimitry Andric void SampleOverlapAggregator::dumpHotFuncAndBlockOverlap(
2581e8d8bef9SDimitry Andric raw_fd_ostream &OS) const {
2582e8d8bef9SDimitry Andric assert(HotFuncOverlap.UnionCount > 0 &&
2583e8d8bef9SDimitry Andric "There should be at least one hot function in two input profiles");
2584e8d8bef9SDimitry Andric OS << " Hot-function overlap: "
2585e8d8bef9SDimitry Andric << format("%.3f%%", static_cast<double>(HotFuncOverlap.OverlapCount) /
2586e8d8bef9SDimitry Andric HotFuncOverlap.UnionCount * 100)
2587e8d8bef9SDimitry Andric << "\n";
2588e8d8bef9SDimitry Andric OS << " overlap hot functions: " << HotFuncOverlap.OverlapCount << "\n";
2589e8d8bef9SDimitry Andric OS << " hot functions unique in base profile: "
2590e8d8bef9SDimitry Andric << HotFuncOverlap.BaseCount - HotFuncOverlap.OverlapCount << "\n";
2591e8d8bef9SDimitry Andric OS << " hot functions unique in test profile: "
2592e8d8bef9SDimitry Andric << HotFuncOverlap.TestCount - HotFuncOverlap.OverlapCount << "\n";
2593e8d8bef9SDimitry Andric
2594e8d8bef9SDimitry Andric assert(HotBlockOverlap.UnionCount > 0 &&
2595e8d8bef9SDimitry Andric "There should be at least one hot block in two input profiles");
2596e8d8bef9SDimitry Andric OS << " Hot-block overlap: "
2597e8d8bef9SDimitry Andric << format("%.3f%%", static_cast<double>(HotBlockOverlap.OverlapCount) /
2598e8d8bef9SDimitry Andric HotBlockOverlap.UnionCount * 100)
2599e8d8bef9SDimitry Andric << "\n";
2600e8d8bef9SDimitry Andric OS << " overlap hot blocks: " << HotBlockOverlap.OverlapCount << "\n";
2601e8d8bef9SDimitry Andric OS << " hot blocks unique in base profile: "
2602e8d8bef9SDimitry Andric << HotBlockOverlap.BaseCount - HotBlockOverlap.OverlapCount << "\n";
2603e8d8bef9SDimitry Andric OS << " hot blocks unique in test profile: "
2604e8d8bef9SDimitry Andric << HotBlockOverlap.TestCount - HotBlockOverlap.OverlapCount << "\n";
2605e8d8bef9SDimitry Andric }
2606e8d8bef9SDimitry Andric
loadProfiles()2607e8d8bef9SDimitry Andric std::error_code SampleOverlapAggregator::loadProfiles() {
2608e8d8bef9SDimitry Andric using namespace sampleprof;
2609e8d8bef9SDimitry Andric
2610e8d8bef9SDimitry Andric LLVMContext Context;
261106c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
261206c3fb27SDimitry Andric auto BaseReaderOrErr = SampleProfileReader::create(BaseFilename, Context, *FS,
2613fe6060f1SDimitry Andric FSDiscriminatorPassOption);
2614e8d8bef9SDimitry Andric if (std::error_code EC = BaseReaderOrErr.getError())
2615e8d8bef9SDimitry Andric exitWithErrorCode(EC, BaseFilename);
2616e8d8bef9SDimitry Andric
261706c3fb27SDimitry Andric auto TestReaderOrErr = SampleProfileReader::create(TestFilename, Context, *FS,
2618fe6060f1SDimitry Andric FSDiscriminatorPassOption);
2619e8d8bef9SDimitry Andric if (std::error_code EC = TestReaderOrErr.getError())
2620e8d8bef9SDimitry Andric exitWithErrorCode(EC, TestFilename);
2621e8d8bef9SDimitry Andric
2622e8d8bef9SDimitry Andric BaseReader = std::move(BaseReaderOrErr.get());
2623e8d8bef9SDimitry Andric TestReader = std::move(TestReaderOrErr.get());
2624e8d8bef9SDimitry Andric
2625e8d8bef9SDimitry Andric if (std::error_code EC = BaseReader->read())
2626e8d8bef9SDimitry Andric exitWithErrorCode(EC, BaseFilename);
2627e8d8bef9SDimitry Andric if (std::error_code EC = TestReader->read())
2628e8d8bef9SDimitry Andric exitWithErrorCode(EC, TestFilename);
2629e8d8bef9SDimitry Andric if (BaseReader->profileIsProbeBased() != TestReader->profileIsProbeBased())
2630e8d8bef9SDimitry Andric exitWithError(
2631e8d8bef9SDimitry Andric "cannot compare probe-based profile with non-probe-based profile");
263281ad6265SDimitry Andric if (BaseReader->profileIsCS() != TestReader->profileIsCS())
2633fe6060f1SDimitry Andric exitWithError("cannot compare CS profile with non-CS profile");
2634e8d8bef9SDimitry Andric
2635e8d8bef9SDimitry Andric // Load BaseHotThreshold and TestHotThreshold as 99-percentile threshold in
2636e8d8bef9SDimitry Andric // profile summary.
2637e8d8bef9SDimitry Andric ProfileSummary &BasePS = BaseReader->getSummary();
2638e8d8bef9SDimitry Andric ProfileSummary &TestPS = TestReader->getSummary();
2639349cc55cSDimitry Andric BaseHotThreshold =
2640349cc55cSDimitry Andric ProfileSummaryBuilder::getHotCountThreshold(BasePS.getDetailedSummary());
2641349cc55cSDimitry Andric TestHotThreshold =
2642349cc55cSDimitry Andric ProfileSummaryBuilder::getHotCountThreshold(TestPS.getDetailedSummary());
2643349cc55cSDimitry Andric
2644e8d8bef9SDimitry Andric return std::error_code();
2645e8d8bef9SDimitry Andric }
2646e8d8bef9SDimitry Andric
overlapSampleProfile(const std::string & BaseFilename,const std::string & TestFilename,const OverlapFuncFilters & FuncFilter,uint64_t SimilarityCutoff,raw_fd_ostream & OS)2647e8d8bef9SDimitry Andric void overlapSampleProfile(const std::string &BaseFilename,
2648e8d8bef9SDimitry Andric const std::string &TestFilename,
2649e8d8bef9SDimitry Andric const OverlapFuncFilters &FuncFilter,
2650e8d8bef9SDimitry Andric uint64_t SimilarityCutoff, raw_fd_ostream &OS) {
2651e8d8bef9SDimitry Andric using namespace sampleprof;
2652e8d8bef9SDimitry Andric
2653e8d8bef9SDimitry Andric // We use 0.000005 to initialize OverlapAggr.Epsilon because the final metrics
2654e8d8bef9SDimitry Andric // report 2--3 places after decimal point in percentage numbers.
2655e8d8bef9SDimitry Andric SampleOverlapAggregator OverlapAggr(
2656e8d8bef9SDimitry Andric BaseFilename, TestFilename,
2657e8d8bef9SDimitry Andric static_cast<double>(SimilarityCutoff) / 1000000, 0.000005, FuncFilter);
2658e8d8bef9SDimitry Andric if (std::error_code EC = OverlapAggr.loadProfiles())
2659e8d8bef9SDimitry Andric exitWithErrorCode(EC);
2660e8d8bef9SDimitry Andric
2661e8d8bef9SDimitry Andric OverlapAggr.initializeSampleProfileOverlap();
2662e8d8bef9SDimitry Andric if (OverlapAggr.detectZeroSampleProfile(OS))
2663e8d8bef9SDimitry Andric return;
2664e8d8bef9SDimitry Andric
2665e8d8bef9SDimitry Andric OverlapAggr.computeSampleProfileOverlap(OS);
2666e8d8bef9SDimitry Andric
2667e8d8bef9SDimitry Andric OverlapAggr.dumpProgramSummary(OS);
2668e8d8bef9SDimitry Andric OverlapAggr.dumpHotFuncAndBlockOverlap(OS);
2669e8d8bef9SDimitry Andric OverlapAggr.dumpFuncSimilarity(OS);
2670e8d8bef9SDimitry Andric }
2671e8d8bef9SDimitry Andric
overlap_main()2672*0fca6ea1SDimitry Andric static int overlap_main() {
26730b57cec5SDimitry Andric std::error_code EC;
26745f757f3fSDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
26750b57cec5SDimitry Andric if (EC)
26765f757f3fSDimitry Andric exitWithErrorCode(EC, OutputFilename);
26770b57cec5SDimitry Andric
2678e8d8bef9SDimitry Andric if (ProfileKind == instr)
26790b57cec5SDimitry Andric overlapInstrProfile(BaseFilename, TestFilename,
26805f757f3fSDimitry Andric OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
26815f757f3fSDimitry Andric OS, IsCS);
2682e8d8bef9SDimitry Andric else
2683e8d8bef9SDimitry Andric overlapSampleProfile(BaseFilename, TestFilename,
26845f757f3fSDimitry Andric OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter},
2685e8d8bef9SDimitry Andric SimilarityCutoff, OS);
26860b57cec5SDimitry Andric
26870b57cec5SDimitry Andric return 0;
26880b57cec5SDimitry Andric }
26890b57cec5SDimitry Andric
2690fe6060f1SDimitry Andric namespace {
2691fe6060f1SDimitry Andric struct ValueSitesStats {
2692cb14a3feSDimitry Andric ValueSitesStats() = default;
2693cb14a3feSDimitry Andric uint64_t TotalNumValueSites = 0;
2694cb14a3feSDimitry Andric uint64_t TotalNumValueSitesWithValueProfile = 0;
2695cb14a3feSDimitry Andric uint64_t TotalNumValues = 0;
26960b57cec5SDimitry Andric std::vector<unsigned> ValueSitesHistogram;
2697fe6060f1SDimitry Andric };
2698fe6060f1SDimitry Andric } // namespace
26990b57cec5SDimitry Andric
traverseAllValueSites(const InstrProfRecord & Func,uint32_t VK,ValueSitesStats & Stats,raw_fd_ostream & OS,InstrProfSymtab * Symtab)27000b57cec5SDimitry Andric static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK,
27010b57cec5SDimitry Andric ValueSitesStats &Stats, raw_fd_ostream &OS,
27020b57cec5SDimitry Andric InstrProfSymtab *Symtab) {
27030b57cec5SDimitry Andric uint32_t NS = Func.getNumValueSites(VK);
27040b57cec5SDimitry Andric Stats.TotalNumValueSites += NS;
27050b57cec5SDimitry Andric for (size_t I = 0; I < NS; ++I) {
2706*0fca6ea1SDimitry Andric auto VD = Func.getValueArrayForSite(VK, I);
2707*0fca6ea1SDimitry Andric uint32_t NV = VD.size();
2708*0fca6ea1SDimitry Andric if (NV == 0)
2709*0fca6ea1SDimitry Andric continue;
27100b57cec5SDimitry Andric Stats.TotalNumValues += NV;
27110b57cec5SDimitry Andric Stats.TotalNumValueSitesWithValueProfile++;
27120b57cec5SDimitry Andric if (NV > Stats.ValueSitesHistogram.size())
27130b57cec5SDimitry Andric Stats.ValueSitesHistogram.resize(NV, 0);
27140b57cec5SDimitry Andric Stats.ValueSitesHistogram[NV - 1]++;
27150b57cec5SDimitry Andric
27160b57cec5SDimitry Andric uint64_t SiteSum = 0;
2717*0fca6ea1SDimitry Andric for (const auto &V : VD)
2718*0fca6ea1SDimitry Andric SiteSum += V.Count;
27190b57cec5SDimitry Andric if (SiteSum == 0)
27200b57cec5SDimitry Andric SiteSum = 1;
27210b57cec5SDimitry Andric
2722*0fca6ea1SDimitry Andric for (const auto &V : VD) {
27230b57cec5SDimitry Andric OS << "\t[ " << format("%2u", I) << ", ";
27240b57cec5SDimitry Andric if (Symtab == nullptr)
2725*0fca6ea1SDimitry Andric OS << format("%4" PRIu64, V.Value);
27260b57cec5SDimitry Andric else
2727*0fca6ea1SDimitry Andric OS << Symtab->getFuncOrVarName(V.Value);
2728*0fca6ea1SDimitry Andric OS << ", " << format("%10" PRId64, V.Count) << " ] ("
2729*0fca6ea1SDimitry Andric << format("%.2f%%", (V.Count * 100.0 / SiteSum)) << ")\n";
27300b57cec5SDimitry Andric }
27310b57cec5SDimitry Andric }
27320b57cec5SDimitry Andric }
27330b57cec5SDimitry Andric
showValueSitesStats(raw_fd_ostream & OS,uint32_t VK,ValueSitesStats & Stats)27340b57cec5SDimitry Andric static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
27350b57cec5SDimitry Andric ValueSitesStats &Stats) {
27360b57cec5SDimitry Andric OS << " Total number of sites: " << Stats.TotalNumValueSites << "\n";
27370b57cec5SDimitry Andric OS << " Total number of sites with values: "
27380b57cec5SDimitry Andric << Stats.TotalNumValueSitesWithValueProfile << "\n";
27390b57cec5SDimitry Andric OS << " Total number of profiled values: " << Stats.TotalNumValues << "\n";
27400b57cec5SDimitry Andric
27410b57cec5SDimitry Andric OS << " Value sites histogram:\n\tNumTargets, SiteCount\n";
27420b57cec5SDimitry Andric for (unsigned I = 0; I < Stats.ValueSitesHistogram.size(); I++) {
27430b57cec5SDimitry Andric if (Stats.ValueSitesHistogram[I] > 0)
27440b57cec5SDimitry Andric OS << "\t" << I + 1 << ", " << Stats.ValueSitesHistogram[I] << "\n";
27450b57cec5SDimitry Andric }
27460b57cec5SDimitry Andric }
27470b57cec5SDimitry Andric
showInstrProfile(ShowFormat SFormat,raw_fd_ostream & OS)27485f757f3fSDimitry Andric static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
2749bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
2750bdd1243dSDimitry Andric exitWithError("JSON output is not supported for instr profiles");
2751bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml)
2752bdd1243dSDimitry Andric exitWithError("YAML output is not supported for instr profiles");
275306c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
275406c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
27550b57cec5SDimitry Andric std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs);
27560b57cec5SDimitry Andric if (ShowDetailedSummary && Cutoffs.empty()) {
275781ad6265SDimitry Andric Cutoffs = ProfileSummaryBuilder::DefaultCutoffs;
27580b57cec5SDimitry Andric }
27590b57cec5SDimitry Andric InstrProfSummaryBuilder Builder(std::move(Cutoffs));
27600b57cec5SDimitry Andric if (Error E = ReaderOrErr.takeError())
27610b57cec5SDimitry Andric exitWithError(std::move(E), Filename);
27620b57cec5SDimitry Andric
27630b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
27640b57cec5SDimitry Andric bool IsIRInstr = Reader->isIRLevelProfile();
27650b57cec5SDimitry Andric size_t ShownFunctions = 0;
27660b57cec5SDimitry Andric size_t BelowCutoffFunctions = 0;
27670b57cec5SDimitry Andric int NumVPKind = IPVK_Last - IPVK_First + 1;
27680b57cec5SDimitry Andric std::vector<ValueSitesStats> VPStats(NumVPKind);
27690b57cec5SDimitry Andric
27700b57cec5SDimitry Andric auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
27710b57cec5SDimitry Andric const std::pair<std::string, uint64_t> &v2) {
27720b57cec5SDimitry Andric return v1.second > v2.second;
27730b57cec5SDimitry Andric };
27740b57cec5SDimitry Andric
27750b57cec5SDimitry Andric std::priority_queue<std::pair<std::string, uint64_t>,
27760b57cec5SDimitry Andric std::vector<std::pair<std::string, uint64_t>>,
27770b57cec5SDimitry Andric decltype(MinCmp)>
27780b57cec5SDimitry Andric HottestFuncs(MinCmp);
27790b57cec5SDimitry Andric
27800b57cec5SDimitry Andric if (!TextFormat && OnlyListBelow) {
27810b57cec5SDimitry Andric OS << "The list of functions with the maximum counter less than "
27825f757f3fSDimitry Andric << ShowValueCutoff << ":\n";
27830b57cec5SDimitry Andric }
27840b57cec5SDimitry Andric
27850b57cec5SDimitry Andric // Add marker so that IR-level instrumentation round-trips properly.
27860b57cec5SDimitry Andric if (TextFormat && IsIRInstr)
27870b57cec5SDimitry Andric OS << ":ir\n";
27880b57cec5SDimitry Andric
27890b57cec5SDimitry Andric for (const auto &Func : *Reader) {
27900b57cec5SDimitry Andric if (Reader->isIRLevelProfile()) {
27910b57cec5SDimitry Andric bool FuncIsCS = NamedInstrProfRecord::hasCSFlagInHash(Func.Hash);
27920b57cec5SDimitry Andric if (FuncIsCS != ShowCS)
27930b57cec5SDimitry Andric continue;
27940b57cec5SDimitry Andric }
2795349cc55cSDimitry Andric bool Show = ShowAllFunctions ||
27965f757f3fSDimitry Andric (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter));
27970b57cec5SDimitry Andric
27980b57cec5SDimitry Andric bool doTextFormatDump = (Show && TextFormat);
27990b57cec5SDimitry Andric
28000b57cec5SDimitry Andric if (doTextFormatDump) {
28010b57cec5SDimitry Andric InstrProfSymtab &Symtab = Reader->getSymtab();
28020b57cec5SDimitry Andric InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
28030b57cec5SDimitry Andric OS);
28040b57cec5SDimitry Andric continue;
28050b57cec5SDimitry Andric }
28060b57cec5SDimitry Andric
28070b57cec5SDimitry Andric assert(Func.Counts.size() > 0 && "function missing entry counter");
28080b57cec5SDimitry Andric Builder.addRecord(Func);
28090b57cec5SDimitry Andric
28101fd87a68SDimitry Andric if (ShowCovered) {
2811972a253aSDimitry Andric if (llvm::any_of(Func.Counts, [](uint64_t C) { return C; }))
28121fd87a68SDimitry Andric OS << Func.Name << "\n";
28131fd87a68SDimitry Andric continue;
28141fd87a68SDimitry Andric }
28151fd87a68SDimitry Andric
28160b57cec5SDimitry Andric uint64_t FuncMax = 0;
28170b57cec5SDimitry Andric uint64_t FuncSum = 0;
2818bdd1243dSDimitry Andric
2819bdd1243dSDimitry Andric auto PseudoKind = Func.getCountPseudoKind();
2820bdd1243dSDimitry Andric if (PseudoKind != InstrProfRecord::NotPseudo) {
2821bdd1243dSDimitry Andric if (Show) {
2822bdd1243dSDimitry Andric if (!ShownFunctions)
2823bdd1243dSDimitry Andric OS << "Counters:\n";
2824bdd1243dSDimitry Andric ++ShownFunctions;
2825bdd1243dSDimitry Andric OS << " " << Func.Name << ":\n"
2826bdd1243dSDimitry Andric << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
2827bdd1243dSDimitry Andric << " Counters: " << Func.Counts.size();
2828bdd1243dSDimitry Andric if (PseudoKind == InstrProfRecord::PseudoHot)
2829bdd1243dSDimitry Andric OS << " <PseudoHot>\n";
2830bdd1243dSDimitry Andric else if (PseudoKind == InstrProfRecord::PseudoWarm)
2831bdd1243dSDimitry Andric OS << " <PseudoWarm>\n";
2832bdd1243dSDimitry Andric else
2833bdd1243dSDimitry Andric llvm_unreachable("Unknown PseudoKind");
2834bdd1243dSDimitry Andric }
2835e8d8bef9SDimitry Andric continue;
2836bdd1243dSDimitry Andric }
2837bdd1243dSDimitry Andric
2838bdd1243dSDimitry Andric for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) {
28390b57cec5SDimitry Andric FuncMax = std::max(FuncMax, Func.Counts[I]);
28400b57cec5SDimitry Andric FuncSum += Func.Counts[I];
28410b57cec5SDimitry Andric }
28420b57cec5SDimitry Andric
28435f757f3fSDimitry Andric if (FuncMax < ShowValueCutoff) {
28440b57cec5SDimitry Andric ++BelowCutoffFunctions;
28450b57cec5SDimitry Andric if (OnlyListBelow) {
28460b57cec5SDimitry Andric OS << " " << Func.Name << ": (Max = " << FuncMax
28470b57cec5SDimitry Andric << " Sum = " << FuncSum << ")\n";
28480b57cec5SDimitry Andric }
28490b57cec5SDimitry Andric continue;
28500b57cec5SDimitry Andric } else if (OnlyListBelow)
28510b57cec5SDimitry Andric continue;
28520b57cec5SDimitry Andric
28535f757f3fSDimitry Andric if (TopNFunctions) {
28545f757f3fSDimitry Andric if (HottestFuncs.size() == TopNFunctions) {
28550b57cec5SDimitry Andric if (HottestFuncs.top().second < FuncMax) {
28560b57cec5SDimitry Andric HottestFuncs.pop();
28570b57cec5SDimitry Andric HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28580b57cec5SDimitry Andric }
28590b57cec5SDimitry Andric } else
28600b57cec5SDimitry Andric HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
28610b57cec5SDimitry Andric }
28620b57cec5SDimitry Andric
28630b57cec5SDimitry Andric if (Show) {
28640b57cec5SDimitry Andric if (!ShownFunctions)
28650b57cec5SDimitry Andric OS << "Counters:\n";
28660b57cec5SDimitry Andric
28670b57cec5SDimitry Andric ++ShownFunctions;
28680b57cec5SDimitry Andric
28690b57cec5SDimitry Andric OS << " " << Func.Name << ":\n"
28700b57cec5SDimitry Andric << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n"
28710b57cec5SDimitry Andric << " Counters: " << Func.Counts.size() << "\n";
28720b57cec5SDimitry Andric if (!IsIRInstr)
28730b57cec5SDimitry Andric OS << " Function count: " << Func.Counts[0] << "\n";
28740b57cec5SDimitry Andric
28750b57cec5SDimitry Andric if (ShowIndirectCallTargets)
28760b57cec5SDimitry Andric OS << " Indirect Call Site Count: "
28770b57cec5SDimitry Andric << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n";
28780b57cec5SDimitry Andric
2879*0fca6ea1SDimitry Andric if (ShowVTables)
2880*0fca6ea1SDimitry Andric OS << " Number of instrumented vtables: "
2881*0fca6ea1SDimitry Andric << Func.getNumValueSites(IPVK_VTableTarget) << "\n";
2882*0fca6ea1SDimitry Andric
28830b57cec5SDimitry Andric uint32_t NumMemOPCalls = Func.getNumValueSites(IPVK_MemOPSize);
28840b57cec5SDimitry Andric if (ShowMemOPSizes && NumMemOPCalls > 0)
28850b57cec5SDimitry Andric OS << " Number of Memory Intrinsics Calls: " << NumMemOPCalls
28860b57cec5SDimitry Andric << "\n";
28870b57cec5SDimitry Andric
28880b57cec5SDimitry Andric if (ShowCounts) {
28890b57cec5SDimitry Andric OS << " Block counts: [";
28900b57cec5SDimitry Andric size_t Start = (IsIRInstr ? 0 : 1);
28910b57cec5SDimitry Andric for (size_t I = Start, E = Func.Counts.size(); I < E; ++I) {
28920b57cec5SDimitry Andric OS << (I == Start ? "" : ", ") << Func.Counts[I];
28930b57cec5SDimitry Andric }
28940b57cec5SDimitry Andric OS << "]\n";
28950b57cec5SDimitry Andric }
28960b57cec5SDimitry Andric
28970b57cec5SDimitry Andric if (ShowIndirectCallTargets) {
28980b57cec5SDimitry Andric OS << " Indirect Target Results:\n";
28990b57cec5SDimitry Andric traverseAllValueSites(Func, IPVK_IndirectCallTarget,
29000b57cec5SDimitry Andric VPStats[IPVK_IndirectCallTarget], OS,
29010b57cec5SDimitry Andric &(Reader->getSymtab()));
29020b57cec5SDimitry Andric }
29030b57cec5SDimitry Andric
2904*0fca6ea1SDimitry Andric if (ShowVTables) {
2905*0fca6ea1SDimitry Andric OS << " VTable Results:\n";
2906*0fca6ea1SDimitry Andric traverseAllValueSites(Func, IPVK_VTableTarget,
2907*0fca6ea1SDimitry Andric VPStats[IPVK_VTableTarget], OS,
2908*0fca6ea1SDimitry Andric &(Reader->getSymtab()));
2909*0fca6ea1SDimitry Andric }
2910*0fca6ea1SDimitry Andric
29110b57cec5SDimitry Andric if (ShowMemOPSizes && NumMemOPCalls > 0) {
29120b57cec5SDimitry Andric OS << " Memory Intrinsic Size Results:\n";
29130b57cec5SDimitry Andric traverseAllValueSites(Func, IPVK_MemOPSize, VPStats[IPVK_MemOPSize], OS,
29140b57cec5SDimitry Andric nullptr);
29150b57cec5SDimitry Andric }
29160b57cec5SDimitry Andric }
29170b57cec5SDimitry Andric }
29180b57cec5SDimitry Andric if (Reader->hasError())
29190b57cec5SDimitry Andric exitWithError(Reader->getError(), Filename);
29200b57cec5SDimitry Andric
29211fd87a68SDimitry Andric if (TextFormat || ShowCovered)
29220b57cec5SDimitry Andric return 0;
29230b57cec5SDimitry Andric std::unique_ptr<ProfileSummary> PS(Builder.getSummary());
2924e8d8bef9SDimitry Andric bool IsIR = Reader->isIRLevelProfile();
2925e8d8bef9SDimitry Andric OS << "Instrumentation level: " << (IsIR ? "IR" : "Front-end");
2926e8d8bef9SDimitry Andric if (IsIR)
2927e8d8bef9SDimitry Andric OS << " entry_first = " << Reader->instrEntryBBEnabled();
2928e8d8bef9SDimitry Andric OS << "\n";
29295f757f3fSDimitry Andric if (ShowAllFunctions || !FuncNameFilter.empty())
29300b57cec5SDimitry Andric OS << "Functions shown: " << ShownFunctions << "\n";
29310b57cec5SDimitry Andric OS << "Total functions: " << PS->getNumFunctions() << "\n";
29325f757f3fSDimitry Andric if (ShowValueCutoff > 0) {
29335f757f3fSDimitry Andric OS << "Number of functions with maximum count (< " << ShowValueCutoff
29340b57cec5SDimitry Andric << "): " << BelowCutoffFunctions << "\n";
29355f757f3fSDimitry Andric OS << "Number of functions with maximum count (>= " << ShowValueCutoff
29360b57cec5SDimitry Andric << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n";
29370b57cec5SDimitry Andric }
29380b57cec5SDimitry Andric OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
29390b57cec5SDimitry Andric OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
29400b57cec5SDimitry Andric
29415f757f3fSDimitry Andric if (TopNFunctions) {
29420b57cec5SDimitry Andric std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
29430b57cec5SDimitry Andric while (!HottestFuncs.empty()) {
29440b57cec5SDimitry Andric SortedHottestFuncs.emplace_back(HottestFuncs.top());
29450b57cec5SDimitry Andric HottestFuncs.pop();
29460b57cec5SDimitry Andric }
29475f757f3fSDimitry Andric OS << "Top " << TopNFunctions
29480b57cec5SDimitry Andric << " functions with the largest internal block counts: \n";
29490b57cec5SDimitry Andric for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
29500b57cec5SDimitry Andric OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
29510b57cec5SDimitry Andric }
29520b57cec5SDimitry Andric
29530b57cec5SDimitry Andric if (ShownFunctions && ShowIndirectCallTargets) {
29540b57cec5SDimitry Andric OS << "Statistics for indirect call sites profile:\n";
29550b57cec5SDimitry Andric showValueSitesStats(OS, IPVK_IndirectCallTarget,
29560b57cec5SDimitry Andric VPStats[IPVK_IndirectCallTarget]);
29570b57cec5SDimitry Andric }
29580b57cec5SDimitry Andric
2959*0fca6ea1SDimitry Andric if (ShownFunctions && ShowVTables) {
2960*0fca6ea1SDimitry Andric OS << "Statistics for vtable profile:\n";
2961*0fca6ea1SDimitry Andric showValueSitesStats(OS, IPVK_VTableTarget, VPStats[IPVK_VTableTarget]);
2962*0fca6ea1SDimitry Andric }
2963*0fca6ea1SDimitry Andric
29640b57cec5SDimitry Andric if (ShownFunctions && ShowMemOPSizes) {
29650b57cec5SDimitry Andric OS << "Statistics for memory intrinsic calls sizes profile:\n";
29660b57cec5SDimitry Andric showValueSitesStats(OS, IPVK_MemOPSize, VPStats[IPVK_MemOPSize]);
29670b57cec5SDimitry Andric }
29680b57cec5SDimitry Andric
29690b57cec5SDimitry Andric if (ShowDetailedSummary) {
29700b57cec5SDimitry Andric OS << "Total number of blocks: " << PS->getNumCounts() << "\n";
29710b57cec5SDimitry Andric OS << "Total count: " << PS->getTotalCount() << "\n";
29725ffd83dbSDimitry Andric PS->printDetailedSummary(OS);
29730b57cec5SDimitry Andric }
2974fe6060f1SDimitry Andric
2975fe6060f1SDimitry Andric if (ShowBinaryIds)
2976fe6060f1SDimitry Andric if (Error E = Reader->printBinaryIds(OS))
2977fe6060f1SDimitry Andric exitWithError(std::move(E), Filename);
2978fe6060f1SDimitry Andric
2979bdd1243dSDimitry Andric if (ShowProfileVersion)
2980bdd1243dSDimitry Andric OS << "Profile version: " << Reader->getVersion() << "\n";
298106c3fb27SDimitry Andric
298206c3fb27SDimitry Andric if (ShowTemporalProfTraces) {
298306c3fb27SDimitry Andric auto &Traces = Reader->getTemporalProfTraces();
298406c3fb27SDimitry Andric OS << "Temporal Profile Traces (samples=" << Traces.size()
298506c3fb27SDimitry Andric << " seen=" << Reader->getTemporalProfTraceStreamSize() << "):\n";
298606c3fb27SDimitry Andric for (unsigned i = 0; i < Traces.size(); i++) {
298706c3fb27SDimitry Andric OS << " Temporal Profile Trace " << i << " (weight=" << Traces[i].Weight
298806c3fb27SDimitry Andric << " count=" << Traces[i].FunctionNameRefs.size() << "):\n";
298906c3fb27SDimitry Andric for (auto &NameRef : Traces[i].FunctionNameRefs)
29905f757f3fSDimitry Andric OS << " " << Reader->getSymtab().getFuncOrVarName(NameRef) << "\n";
299106c3fb27SDimitry Andric }
299206c3fb27SDimitry Andric }
299306c3fb27SDimitry Andric
29940b57cec5SDimitry Andric return 0;
29950b57cec5SDimitry Andric }
29960b57cec5SDimitry Andric
showSectionInfo(sampleprof::SampleProfileReader * Reader,raw_fd_ostream & OS)29978bcb0991SDimitry Andric static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
29988bcb0991SDimitry Andric raw_fd_ostream &OS) {
29998bcb0991SDimitry Andric if (!Reader->dumpSectionInfo(OS)) {
30008bcb0991SDimitry Andric WithColor::warning() << "-show-sec-info-only is only supported for "
30018bcb0991SDimitry Andric << "sample profile in extbinary format and is "
30028bcb0991SDimitry Andric << "ignored for other formats.\n";
30038bcb0991SDimitry Andric return;
30048bcb0991SDimitry Andric }
30058bcb0991SDimitry Andric }
30068bcb0991SDimitry Andric
30075ffd83dbSDimitry Andric namespace {
30085ffd83dbSDimitry Andric struct HotFuncInfo {
3009349cc55cSDimitry Andric std::string FuncName;
3010cb14a3feSDimitry Andric uint64_t TotalCount = 0;
3011cb14a3feSDimitry Andric double TotalCountPercent = 0.0f;
3012cb14a3feSDimitry Andric uint64_t MaxCount = 0;
3013cb14a3feSDimitry Andric uint64_t EntryCount = 0;
30145ffd83dbSDimitry Andric
3015cb14a3feSDimitry Andric HotFuncInfo() = default;
30165ffd83dbSDimitry Andric
HotFuncInfo__anon209d874b1611::HotFuncInfo30175ffd83dbSDimitry Andric HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
3018349cc55cSDimitry Andric : FuncName(FN.begin(), FN.end()), TotalCount(TS), TotalCountPercent(TSP),
3019349cc55cSDimitry Andric MaxCount(MS), EntryCount(ES) {}
30205ffd83dbSDimitry Andric };
30215ffd83dbSDimitry Andric } // namespace
30225ffd83dbSDimitry Andric
30235ffd83dbSDimitry Andric // Print out detailed information about hot functions in PrintValues vector.
30245ffd83dbSDimitry Andric // Users specify titles and offset of every columns through ColumnTitle and
30255ffd83dbSDimitry Andric // ColumnOffset. The size of ColumnTitle and ColumnOffset need to be the same
30265ffd83dbSDimitry Andric // and at least 4. Besides, users can optionally give a HotFuncMetric string to
30275ffd83dbSDimitry 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)30285ffd83dbSDimitry Andric static void dumpHotFunctionList(const std::vector<std::string> &ColumnTitle,
30295ffd83dbSDimitry Andric const std::vector<int> &ColumnOffset,
30305ffd83dbSDimitry Andric const std::vector<HotFuncInfo> &PrintValues,
30315ffd83dbSDimitry Andric uint64_t HotFuncCount, uint64_t TotalFuncCount,
30325ffd83dbSDimitry Andric uint64_t HotProfCount, uint64_t TotalProfCount,
30335ffd83dbSDimitry Andric const std::string &HotFuncMetric,
3034349cc55cSDimitry Andric uint32_t TopNFunctions, raw_fd_ostream &OS) {
3035e8d8bef9SDimitry Andric assert(ColumnOffset.size() == ColumnTitle.size() &&
3036e8d8bef9SDimitry Andric "ColumnOffset and ColumnTitle should have the same size");
3037e8d8bef9SDimitry Andric assert(ColumnTitle.size() >= 4 &&
3038e8d8bef9SDimitry Andric "ColumnTitle should have at least 4 elements");
3039e8d8bef9SDimitry Andric assert(TotalFuncCount > 0 &&
3040e8d8bef9SDimitry Andric "There should be at least one function in the profile");
30415ffd83dbSDimitry Andric double TotalProfPercent = 0;
30425ffd83dbSDimitry Andric if (TotalProfCount > 0)
3043e8d8bef9SDimitry Andric TotalProfPercent = static_cast<double>(HotProfCount) / TotalProfCount * 100;
30445ffd83dbSDimitry Andric
30455ffd83dbSDimitry Andric formatted_raw_ostream FOS(OS);
30465ffd83dbSDimitry Andric FOS << HotFuncCount << " out of " << TotalFuncCount
30475ffd83dbSDimitry Andric << " functions with profile ("
3048e8d8bef9SDimitry Andric << format("%.2f%%",
3049e8d8bef9SDimitry Andric (static_cast<double>(HotFuncCount) / TotalFuncCount * 100))
30505ffd83dbSDimitry Andric << ") are considered hot functions";
30515ffd83dbSDimitry Andric if (!HotFuncMetric.empty())
30525ffd83dbSDimitry Andric FOS << " (" << HotFuncMetric << ")";
30535ffd83dbSDimitry Andric FOS << ".\n";
30545ffd83dbSDimitry Andric FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
30555ffd83dbSDimitry Andric << format("%.2f%%", TotalProfPercent) << ") are from hot functions.\n";
30565ffd83dbSDimitry Andric
30575ffd83dbSDimitry Andric for (size_t I = 0; I < ColumnTitle.size(); ++I) {
30585ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[I]);
30595ffd83dbSDimitry Andric FOS << ColumnTitle[I];
30605ffd83dbSDimitry Andric }
30615ffd83dbSDimitry Andric FOS << "\n";
30625ffd83dbSDimitry Andric
3063349cc55cSDimitry Andric uint32_t Count = 0;
3064349cc55cSDimitry Andric for (const auto &R : PrintValues) {
3065349cc55cSDimitry Andric if (TopNFunctions && (Count++ == TopNFunctions))
3066349cc55cSDimitry Andric break;
30675ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[0]);
30685ffd83dbSDimitry Andric FOS << R.TotalCount << " (" << format("%.2f%%", R.TotalCountPercent) << ")";
30695ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[1]);
30705ffd83dbSDimitry Andric FOS << R.MaxCount;
30715ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[2]);
30725ffd83dbSDimitry Andric FOS << R.EntryCount;
30735ffd83dbSDimitry Andric FOS.PadToColumn(ColumnOffset[3]);
30745ffd83dbSDimitry Andric FOS << R.FuncName << "\n";
30755ffd83dbSDimitry Andric }
30765ffd83dbSDimitry Andric }
30775ffd83dbSDimitry Andric
showHotFunctionList(const sampleprof::SampleProfileMap & Profiles,ProfileSummary & PS,uint32_t TopN,raw_fd_ostream & OS)3078349cc55cSDimitry Andric static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles,
3079349cc55cSDimitry Andric ProfileSummary &PS, uint32_t TopN,
3080349cc55cSDimitry Andric raw_fd_ostream &OS) {
30815ffd83dbSDimitry Andric using namespace sampleprof;
30825ffd83dbSDimitry Andric
30835ffd83dbSDimitry Andric const uint32_t HotFuncCutoff = 990000;
30845ffd83dbSDimitry Andric auto &SummaryVector = PS.getDetailedSummary();
30855ffd83dbSDimitry Andric uint64_t MinCountThreshold = 0;
30865ffd83dbSDimitry Andric for (const ProfileSummaryEntry &SummaryEntry : SummaryVector) {
30875ffd83dbSDimitry Andric if (SummaryEntry.Cutoff == HotFuncCutoff) {
30885ffd83dbSDimitry Andric MinCountThreshold = SummaryEntry.MinCount;
30895ffd83dbSDimitry Andric break;
30905ffd83dbSDimitry Andric }
30915ffd83dbSDimitry Andric }
30925ffd83dbSDimitry Andric
30935ffd83dbSDimitry Andric // Traverse all functions in the profile and keep only hot functions.
30945ffd83dbSDimitry Andric // The following loop also calculates the sum of total samples of all
30955ffd83dbSDimitry Andric // functions.
30965ffd83dbSDimitry Andric std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
30975ffd83dbSDimitry Andric std::greater<uint64_t>>
30985ffd83dbSDimitry Andric HotFunc;
30995ffd83dbSDimitry Andric uint64_t ProfileTotalSample = 0;
31005ffd83dbSDimitry Andric uint64_t HotFuncSample = 0;
31015ffd83dbSDimitry Andric uint64_t HotFuncCount = 0;
3102e8d8bef9SDimitry Andric
31035ffd83dbSDimitry Andric for (const auto &I : Profiles) {
3104e8d8bef9SDimitry Andric FuncSampleStats FuncStats;
31055ffd83dbSDimitry Andric const FunctionSamples &FuncProf = I.second;
31065ffd83dbSDimitry Andric ProfileTotalSample += FuncProf.getTotalSamples();
3107e8d8bef9SDimitry Andric getFuncSampleStats(FuncProf, FuncStats, MinCountThreshold);
31085ffd83dbSDimitry Andric
3109e8d8bef9SDimitry Andric if (isFunctionHot(FuncStats, MinCountThreshold)) {
31105ffd83dbSDimitry Andric HotFunc.emplace(FuncProf.getTotalSamples(),
3111e8d8bef9SDimitry Andric std::make_pair(&(I.second), FuncStats.MaxSample));
31125ffd83dbSDimitry Andric HotFuncSample += FuncProf.getTotalSamples();
31135ffd83dbSDimitry Andric ++HotFuncCount;
31145ffd83dbSDimitry Andric }
31155ffd83dbSDimitry Andric }
31165ffd83dbSDimitry Andric
31175ffd83dbSDimitry Andric std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
31185ffd83dbSDimitry Andric "Entry sample", "Function name"};
31195ffd83dbSDimitry Andric std::vector<int> ColumnOffset{0, 24, 42, 58};
31205ffd83dbSDimitry Andric std::string Metric =
31215ffd83dbSDimitry Andric std::string("max sample >= ") + std::to_string(MinCountThreshold);
31225ffd83dbSDimitry Andric std::vector<HotFuncInfo> PrintValues;
31235ffd83dbSDimitry Andric for (const auto &FuncPair : HotFunc) {
31245ffd83dbSDimitry Andric const FunctionSamples &Func = *FuncPair.second.first;
31255ffd83dbSDimitry Andric double TotalSamplePercent =
31265ffd83dbSDimitry Andric (ProfileTotalSample > 0)
31275ffd83dbSDimitry Andric ? (Func.getTotalSamples() * 100.0) / ProfileTotalSample
31285ffd83dbSDimitry Andric : 0;
3129fcaf7f86SDimitry Andric PrintValues.emplace_back(
3130fcaf7f86SDimitry Andric HotFuncInfo(Func.getContext().toString(), Func.getTotalSamples(),
3131fcaf7f86SDimitry Andric TotalSamplePercent, FuncPair.second.second,
3132fcaf7f86SDimitry Andric Func.getHeadSamplesEstimate()));
31335ffd83dbSDimitry Andric }
31345ffd83dbSDimitry Andric dumpHotFunctionList(ColumnTitle, ColumnOffset, PrintValues, HotFuncCount,
31355ffd83dbSDimitry Andric Profiles.size(), HotFuncSample, ProfileTotalSample,
3136349cc55cSDimitry Andric Metric, TopN, OS);
31375ffd83dbSDimitry Andric
31385ffd83dbSDimitry Andric return 0;
31395ffd83dbSDimitry Andric }
31405ffd83dbSDimitry Andric
showSampleProfile(ShowFormat SFormat,raw_fd_ostream & OS)31415f757f3fSDimitry Andric static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3142bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml)
3143bdd1243dSDimitry Andric exitWithError("YAML output is not supported for sample profiles");
31440b57cec5SDimitry Andric using namespace sampleprof;
31450b57cec5SDimitry Andric LLVMContext Context;
314606c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
314706c3fb27SDimitry Andric auto ReaderOrErr = SampleProfileReader::create(Filename, Context, *FS,
314806c3fb27SDimitry Andric FSDiscriminatorPassOption);
31490b57cec5SDimitry Andric if (std::error_code EC = ReaderOrErr.getError())
31500b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
31510b57cec5SDimitry Andric
31520b57cec5SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
31538bcb0991SDimitry Andric if (ShowSectionInfoOnly) {
31548bcb0991SDimitry Andric showSectionInfo(Reader.get(), OS);
31558bcb0991SDimitry Andric return 0;
31568bcb0991SDimitry Andric }
31578bcb0991SDimitry Andric
31580b57cec5SDimitry Andric if (std::error_code EC = Reader->read())
31590b57cec5SDimitry Andric exitWithErrorCode(EC, Filename);
31600b57cec5SDimitry Andric
31615f757f3fSDimitry Andric if (ShowAllFunctions || FuncNameFilter.empty()) {
3162bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3163bdd1243dSDimitry Andric Reader->dumpJson(OS);
31640b57cec5SDimitry Andric else
3165bdd1243dSDimitry Andric Reader->dump(OS);
3166bdd1243dSDimitry Andric } else {
3167bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3168bdd1243dSDimitry Andric exitWithError(
3169bdd1243dSDimitry Andric "the JSON format is supported only when all functions are to "
3170bdd1243dSDimitry Andric "be printed");
3171bdd1243dSDimitry Andric
3172349cc55cSDimitry Andric // TODO: parse context string to support filtering by contexts.
31735f757f3fSDimitry Andric FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter));
31745f757f3fSDimitry Andric Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS);
3175bdd1243dSDimitry Andric }
31760b57cec5SDimitry Andric
31778bcb0991SDimitry Andric if (ShowProfileSymbolList) {
31788bcb0991SDimitry Andric std::unique_ptr<sampleprof::ProfileSymbolList> ReaderList =
31798bcb0991SDimitry Andric Reader->getProfileSymbolList();
31808bcb0991SDimitry Andric ReaderList->dump(OS);
31818bcb0991SDimitry Andric }
31828bcb0991SDimitry Andric
31835ffd83dbSDimitry Andric if (ShowDetailedSummary) {
31845ffd83dbSDimitry Andric auto &PS = Reader->getSummary();
31855ffd83dbSDimitry Andric PS.printSummary(OS);
31865ffd83dbSDimitry Andric PS.printDetailedSummary(OS);
31875ffd83dbSDimitry Andric }
31885ffd83dbSDimitry Andric
31895f757f3fSDimitry Andric if (ShowHotFuncList || TopNFunctions)
31905f757f3fSDimitry Andric showHotFunctionList(Reader->getProfiles(), Reader->getSummary(),
31915f757f3fSDimitry Andric TopNFunctions, OS);
31925ffd83dbSDimitry Andric
31930b57cec5SDimitry Andric return 0;
31940b57cec5SDimitry Andric }
31950b57cec5SDimitry Andric
showMemProfProfile(ShowFormat SFormat,raw_fd_ostream & OS)31965f757f3fSDimitry Andric static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) {
3197bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3198bdd1243dSDimitry Andric exitWithError("JSON output is not supported for MemProf");
319981ad6265SDimitry Andric auto ReaderOr = llvm::memprof::RawMemProfReader::create(
320081ad6265SDimitry Andric Filename, ProfiledBinary, /*KeepNames=*/true);
32014824e7fdSDimitry Andric if (Error E = ReaderOr.takeError())
320281ad6265SDimitry Andric // Since the error can be related to the profile or the binary we do not
320381ad6265SDimitry Andric // pass whence. Instead additional context is provided where necessary in
320481ad6265SDimitry Andric // the error message.
320581ad6265SDimitry Andric exitWithError(std::move(E), /*Whence*/ "");
32064824e7fdSDimitry Andric
32074824e7fdSDimitry Andric std::unique_ptr<llvm::memprof::RawMemProfReader> Reader(
32084824e7fdSDimitry Andric ReaderOr.get().release());
320981ad6265SDimitry Andric
321081ad6265SDimitry Andric Reader->printYAML(OS);
32114824e7fdSDimitry Andric return 0;
32124824e7fdSDimitry Andric }
32134824e7fdSDimitry Andric
showDebugInfoCorrelation(const std::string & Filename,ShowFormat SFormat,raw_fd_ostream & OS)321404eeddc0SDimitry Andric static int showDebugInfoCorrelation(const std::string &Filename,
3215bdd1243dSDimitry Andric ShowFormat SFormat, raw_fd_ostream &OS) {
3216bdd1243dSDimitry Andric if (SFormat == ShowFormat::Json)
3217bdd1243dSDimitry Andric exitWithError("JSON output is not supported for debug info correlation");
321804eeddc0SDimitry Andric std::unique_ptr<InstrProfCorrelator> Correlator;
32195f757f3fSDimitry Andric if (auto Err =
32205f757f3fSDimitry Andric InstrProfCorrelator::get(Filename, InstrProfCorrelator::DEBUG_INFO)
32215f757f3fSDimitry Andric .moveInto(Correlator))
322204eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
3223bdd1243dSDimitry Andric if (SFormat == ShowFormat::Yaml) {
32245f757f3fSDimitry Andric if (auto Err = Correlator->dumpYaml(MaxDbgCorrelationWarnings, OS))
3225bdd1243dSDimitry Andric exitWithError(std::move(Err), Filename);
3226bdd1243dSDimitry Andric return 0;
3227bdd1243dSDimitry Andric }
3228bdd1243dSDimitry Andric
32295f757f3fSDimitry Andric if (auto Err = Correlator->correlateProfileData(MaxDbgCorrelationWarnings))
323004eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
323104eeddc0SDimitry Andric
323204eeddc0SDimitry Andric InstrProfSymtab Symtab;
323304eeddc0SDimitry Andric if (auto Err = Symtab.create(
323404eeddc0SDimitry Andric StringRef(Correlator->getNamesPointer(), Correlator->getNamesSize())))
323504eeddc0SDimitry Andric exitWithError(std::move(Err), Filename);
323604eeddc0SDimitry Andric
323704eeddc0SDimitry Andric if (ShowProfileSymbolList)
323804eeddc0SDimitry Andric Symtab.dumpNames(OS);
323904eeddc0SDimitry Andric // TODO: Read "Profile Data Type" from debug info to compute and show how many
324004eeddc0SDimitry Andric // counters the section holds.
324104eeddc0SDimitry Andric if (ShowDetailedSummary)
324204eeddc0SDimitry Andric OS << "Counters section size: 0x"
324304eeddc0SDimitry Andric << Twine::utohexstr(Correlator->getCountersSectionSize()) << " bytes\n";
324404eeddc0SDimitry Andric OS << "Found " << Correlator->getDataSize() << " functions\n";
324504eeddc0SDimitry Andric
324604eeddc0SDimitry Andric return 0;
324704eeddc0SDimitry Andric }
324804eeddc0SDimitry Andric
show_main(StringRef ProgName)3249*0fca6ea1SDimitry Andric static int show_main(StringRef ProgName) {
325004eeddc0SDimitry Andric if (Filename.empty() && DebugInfoFilename.empty())
325104eeddc0SDimitry Andric exitWithError(
325204eeddc0SDimitry Andric "the positional argument '<profdata-file>' is required unless '--" +
325304eeddc0SDimitry Andric DebugInfoFilename.ArgStr + "' is provided");
325404eeddc0SDimitry Andric
3255e8d8bef9SDimitry Andric if (Filename == OutputFilename) {
3256*0fca6ea1SDimitry Andric errs() << ProgName
3257*0fca6ea1SDimitry Andric << " show: Input file name cannot be the same as the output file "
3258*0fca6ea1SDimitry Andric "name!\n";
32590b57cec5SDimitry Andric return 1;
32600b57cec5SDimitry Andric }
3261bdd1243dSDimitry Andric if (JsonFormat)
3262bdd1243dSDimitry Andric SFormat = ShowFormat::Json;
32630b57cec5SDimitry Andric
32640b57cec5SDimitry Andric std::error_code EC;
3265fe6060f1SDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
32660b57cec5SDimitry Andric if (EC)
32670b57cec5SDimitry Andric exitWithErrorCode(EC, OutputFilename);
32680b57cec5SDimitry Andric
32695f757f3fSDimitry Andric if (ShowAllFunctions && !FuncNameFilter.empty())
32700b57cec5SDimitry Andric WithColor::warning() << "-function argument ignored: showing all functions\n";
32710b57cec5SDimitry Andric
327204eeddc0SDimitry Andric if (!DebugInfoFilename.empty())
32735f757f3fSDimitry Andric return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS);
327404eeddc0SDimitry Andric
32755f757f3fSDimitry Andric if (ShowProfileKind == instr)
32765f757f3fSDimitry Andric return showInstrProfile(SFormat, OS);
32775f757f3fSDimitry Andric if (ShowProfileKind == sample)
32785f757f3fSDimitry Andric return showSampleProfile(SFormat, OS);
32795f757f3fSDimitry Andric return showMemProfProfile(SFormat, OS);
32800b57cec5SDimitry Andric }
32810b57cec5SDimitry Andric
order_main()3282*0fca6ea1SDimitry Andric static int order_main() {
328306c3fb27SDimitry Andric std::error_code EC;
328406c3fb27SDimitry Andric raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF);
328506c3fb27SDimitry Andric if (EC)
328606c3fb27SDimitry Andric exitWithErrorCode(EC, OutputFilename);
328706c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem();
328806c3fb27SDimitry Andric auto ReaderOrErr = InstrProfReader::create(Filename, *FS);
328906c3fb27SDimitry Andric if (Error E = ReaderOrErr.takeError())
329006c3fb27SDimitry Andric exitWithError(std::move(E), Filename);
329106c3fb27SDimitry Andric
329206c3fb27SDimitry Andric auto Reader = std::move(ReaderOrErr.get());
329306c3fb27SDimitry Andric for (auto &I : *Reader) {
329406c3fb27SDimitry Andric // Read all entries
329506c3fb27SDimitry Andric (void)I;
329606c3fb27SDimitry Andric }
3297*0fca6ea1SDimitry Andric ArrayRef Traces = Reader->getTemporalProfTraces();
3298*0fca6ea1SDimitry Andric if (NumTestTraces && NumTestTraces >= Traces.size())
3299*0fca6ea1SDimitry Andric exitWithError(
3300*0fca6ea1SDimitry Andric "--" + NumTestTraces.ArgStr +
3301*0fca6ea1SDimitry Andric " must be smaller than the total number of traces: expected: < " +
3302*0fca6ea1SDimitry Andric Twine(Traces.size()) + ", actual: " + Twine(NumTestTraces));
3303*0fca6ea1SDimitry Andric ArrayRef TestTraces = Traces.take_back(NumTestTraces);
3304*0fca6ea1SDimitry Andric Traces = Traces.drop_back(NumTestTraces);
3305*0fca6ea1SDimitry Andric
3306*0fca6ea1SDimitry Andric std::vector<BPFunctionNode> Nodes;
3307*0fca6ea1SDimitry Andric TemporalProfTraceTy::createBPFunctionNodes(Traces, Nodes);
330806c3fb27SDimitry Andric BalancedPartitioningConfig Config;
330906c3fb27SDimitry Andric BalancedPartitioning BP(Config);
331006c3fb27SDimitry Andric BP.run(Nodes);
331106c3fb27SDimitry Andric
33121db9f3b2SDimitry Andric OS << "# Ordered " << Nodes.size() << " functions\n";
3313*0fca6ea1SDimitry Andric if (!TestTraces.empty()) {
3314*0fca6ea1SDimitry Andric // Since we don't know the symbol sizes, we assume 32 functions per page.
3315*0fca6ea1SDimitry Andric DenseMap<BPFunctionNode::IDT, unsigned> IdToPageNumber;
3316*0fca6ea1SDimitry Andric for (auto &Node : Nodes)
3317*0fca6ea1SDimitry Andric IdToPageNumber[Node.Id] = IdToPageNumber.size() / 32;
3318*0fca6ea1SDimitry Andric
3319*0fca6ea1SDimitry Andric SmallSet<unsigned, 0> TouchedPages;
3320*0fca6ea1SDimitry Andric unsigned Area = 0;
3321*0fca6ea1SDimitry Andric for (auto &Trace : TestTraces) {
3322*0fca6ea1SDimitry Andric for (auto Id : Trace.FunctionNameRefs) {
3323*0fca6ea1SDimitry Andric auto It = IdToPageNumber.find(Id);
3324*0fca6ea1SDimitry Andric if (It == IdToPageNumber.end())
3325*0fca6ea1SDimitry Andric continue;
3326*0fca6ea1SDimitry Andric TouchedPages.insert(It->getSecond());
3327*0fca6ea1SDimitry Andric Area += TouchedPages.size();
3328*0fca6ea1SDimitry Andric }
3329*0fca6ea1SDimitry Andric TouchedPages.clear();
3330*0fca6ea1SDimitry Andric }
3331*0fca6ea1SDimitry Andric OS << "# Total area under the page fault curve: " << (float)Area << "\n";
3332*0fca6ea1SDimitry Andric }
33331db9f3b2SDimitry Andric OS << "# Warning: Mach-O may prefix symbols with \"_\" depending on the "
33341db9f3b2SDimitry Andric "linkage and this output does not take that into account. Some "
33351db9f3b2SDimitry Andric "post-processing may be required before passing to the linker via "
33361db9f3b2SDimitry Andric "-order_file.\n";
333706c3fb27SDimitry Andric for (auto &N : Nodes) {
33385f757f3fSDimitry Andric auto [Filename, ParsedFuncName] =
3339*0fca6ea1SDimitry Andric getParsedIRPGOName(Reader->getSymtab().getFuncOrVarName(N.Id));
33405f757f3fSDimitry Andric if (!Filename.empty())
334106c3fb27SDimitry Andric OS << "# " << Filename << "\n";
33425f757f3fSDimitry Andric OS << ParsedFuncName << "\n";
334306c3fb27SDimitry Andric }
334406c3fb27SDimitry Andric return 0;
334506c3fb27SDimitry Andric }
334606c3fb27SDimitry Andric
llvm_profdata_main(int argc,char ** argvNonConst,const llvm::ToolContext &)334706c3fb27SDimitry Andric int llvm_profdata_main(int argc, char **argvNonConst,
334806c3fb27SDimitry Andric const llvm::ToolContext &) {
3349bdd1243dSDimitry Andric const char **argv = const_cast<const char **>(argvNonConst);
33500b57cec5SDimitry Andric
33510b57cec5SDimitry Andric StringRef ProgName(sys::path::filename(argv[0]));
33520b57cec5SDimitry Andric
33535f757f3fSDimitry Andric if (argc < 2) {
33545f757f3fSDimitry Andric errs() << ProgName
33555f757f3fSDimitry Andric << ": No subcommand specified! Run llvm-profata --help for usage.\n";
33565f757f3fSDimitry Andric return 1;
33570b57cec5SDimitry Andric }
33580b57cec5SDimitry Andric
33595f757f3fSDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");
33600b57cec5SDimitry Andric
33615f757f3fSDimitry Andric if (ShowSubcommand)
3362*0fca6ea1SDimitry Andric return show_main(ProgName);
336306c3fb27SDimitry Andric
33645f757f3fSDimitry Andric if (OrderSubcommand)
3365*0fca6ea1SDimitry Andric return order_main();
33660b57cec5SDimitry Andric
33675f757f3fSDimitry Andric if (OverlapSubcommand)
3368*0fca6ea1SDimitry Andric return overlap_main();
33690b57cec5SDimitry Andric
33705f757f3fSDimitry Andric if (MergeSubcommand)
3371*0fca6ea1SDimitry Andric return merge_main(ProgName);
33725f757f3fSDimitry Andric
33735f757f3fSDimitry Andric errs() << ProgName
33745f757f3fSDimitry Andric << ": Unknown command. Run llvm-profdata --help for usage.\n";
33750b57cec5SDimitry Andric return 1;
33760b57cec5SDimitry Andric }
3377