diff --git a/llvm/test/tools/llvm-profdata/errors.test b/llvm/test/tools/llvm-profdata/errors.test index bd7d491b9a6b6..6cf2a5ffac6e2 100644 --- a/llvm/test/tools/llvm-profdata/errors.test +++ b/llvm/test/tools/llvm-profdata/errors.test @@ -5,4 +5,4 @@ RUN: not llvm-profdata show file -o file 2>&1 | FileCheck %s --check-prefix SHOW SHOW-OUT: Input file name cannot be the same as the output file name! RUN: not llvm-profdata 2>&1 | FileCheck %s --check-prefix EMPTY -EMPTY: No command specified! +EMPTY: No subcommand specified! diff --git a/llvm/test/tools/llvm-profdata/version.test b/llvm/test/tools/llvm-profdata/version.test index 773c438707345..fb247fad3a92a 100644 --- a/llvm/test/tools/llvm-profdata/version.test +++ b/llvm/test/tools/llvm-profdata/version.test @@ -1,4 +1,3 @@ # RUN: llvm-profdata --version | FileCheck %s -# CHECK: llvm-profdata # CHECK: LLVM version {{.*}} diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index e7e7f8228d7d9..b9df3f97c8fa9 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -47,9 +47,34 @@ using namespace llvm; -// We use this string to indicate that there are -// multiple static functions map to the same name. -const std::string DuplicateNameStr = "----"; +// https://llvm.org/docs/CommandGuide/llvm-profdata.html has documentations +// on each subcommand. +cl::SubCommand ShowSubcommand( + "show", + "Takes a profile data file and displays the profiles. See detailed " + "documentation in " + "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-show"); +cl::SubCommand OrderSubcommand( + "order", + "Reads temporal profiling traces from a profile and outputs a function " + "order that reduces the number of page faults for those traces. See " + "detailed documentation in " + "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-order"); +cl::SubCommand OverlapSubcommand( + "overlap", + "Computes and displays the overlap between two profiles. See detailed " + "documentation in " + "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-overlap"); +cl::SubCommand MergeSubcommand( + "merge", + "Takes several profiles and merge them together. See detailed " + "documentation in " + "https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge"); + +namespace { +enum ProfileKinds { instr, sample, memory }; +enum FailureMode { warnOnly, failIfAnyAreInvalid, failIfAllAreInvalid }; +} // namespace enum ProfileFormat { PF_None = 0, @@ -62,6 +87,326 @@ enum ProfileFormat { enum class ShowFormat { Text, Json, Yaml }; +// Common options. +cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file"), + cl::sub(ShowSubcommand), + cl::sub(OrderSubcommand), + cl::sub(OverlapSubcommand), + cl::sub(MergeSubcommand)); +// NOTE: cl::alias must not have cl::sub(), since aliased option's cl::sub() +// will be used. llvm::cl::alias::done() method asserts this condition. +cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + +// Options common to at least two commands. +cl::opt ProfileKind( + cl::desc("Profile kind:"), cl::sub(MergeSubcommand), + cl::sub(OverlapSubcommand), cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"))); +cl::opt Filename(cl::Positional, cl::desc(""), + cl::sub(ShowSubcommand), + cl::sub(OrderSubcommand)); +cl::opt MaxDbgCorrelationWarnings( + "max-debug-info-correlation-warnings", + cl::desc("The maximum number of warnings to emit when correlating " + "profile from debug info (0 = no limit)"), + cl::sub(MergeSubcommand), cl::sub(ShowSubcommand), cl::init(5)); +cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected."), + cl::sub(ShowSubcommand), cl::sub(MergeSubcommand)); +cl::opt DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc( + "For show, read and extract profile metadata from debug info and show " + "the functions it found. For merge, use the provided debug info to " + "correlate the raw profile."), + cl::sub(ShowSubcommand), cl::sub(MergeSubcommand)); +cl::opt FuncNameFilter( + "function", + cl::desc("Details for matching functions. For overlapping CSSPGO, this " + "takes a function name with calling context."), + cl::sub(ShowSubcommand), cl::sub(OverlapSubcommand)); + +// TODO: Consider creating a template class (e.g., MergeOption, ShowOption) to +// factor out the common cl::sub in cl::opt constructor for subcommand-specific +// options. + +// Options specific to merge subcommand. +cl::list InputFilenames(cl::Positional, cl::sub(MergeSubcommand), + cl::desc("")); +cl::list WeightedInputFilenames("weighted-input", + cl::sub(MergeSubcommand), + cl::desc(",")); +cl::opt OutputFormat( + cl::desc("Format of output profile"), cl::sub(MergeSubcommand), + cl::init(PF_Ext_Binary), + cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding"), + clEnumValN(PF_Ext_Binary, "extbinary", + "Extensible binary encoding " + "(default)"), + clEnumValN(PF_Text, "text", "Text encoding"), + clEnumValN(PF_GCC, "gcc", + "GCC encoding (only meaningful for -sample)"))); +cl::opt + InputFilenamesFile("input-files", cl::init(""), cl::sub(MergeSubcommand), + cl::desc("Path to file containing newline-separated " + "[,] entries")); +cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), + cl::aliasopt(InputFilenamesFile)); +cl::opt DumpInputFileList( + "dump-input-file-list", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Dump the list of input files and their weights, then exit")); +cl::opt RemappingFile("remapping-file", cl::value_desc("file"), + cl::sub(MergeSubcommand), + cl::desc("Symbol remapping file")); +cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), + cl::aliasopt(RemappingFile)); +cl::opt + UseMD5("use-md5", cl::init(false), cl::Hidden, + cl::desc("Choose to use MD5 to represent string in name table (only " + "meaningful for -extbinary)"), + cl::sub(MergeSubcommand)); +cl::opt CompressAllSections( + "compress-all-sections", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Compress all sections when writing the profile (only " + "meaningful for -extbinary)")); +cl::opt SampleMergeColdContext( + "sample-merge-cold-context", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc( + "Merge context sample profiles whose count is below cold threshold")); +cl::opt SampleTrimColdContext( + "sample-trim-cold-context", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc( + "Trim context sample profiles whose count is below cold threshold")); +cl::opt SampleColdContextFrameDepth( + "sample-frame-depth-for-cold-context", cl::init(1), + cl::sub(MergeSubcommand), + cl::desc("Keep the last K frames while merging cold profile. 1 means the " + "context-less base profile")); +cl::opt OutputSizeLimit( + "output-size-limit", cl::init(0), cl::Hidden, cl::sub(MergeSubcommand), + cl::desc("Trim cold functions until profile size is below specified " + "limit in bytes. This uses a heursitic and functions may be " + "excessively trimmed")); +cl::opt GenPartialProfile( + "gen-partial-profile", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Generate a partial profile (only meaningful for -extbinary)")); +cl::opt SupplInstrWithSample( + "supplement-instr-with-sample", cl::init(""), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Supplement an instr profile with sample profile, to correct " + "the profile unrepresentativeness issue. The sample " + "profile is the input of the flag. Output will be in instr " + "format (The flag only works with -instr)")); +cl::opt ZeroCounterThreshold( + "zero-counter-threshold", cl::init(0.7), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("For the function which is cold in instr profile but hot in " + "sample profile, if the ratio of the number of zero counters " + "divided by the total number of counters is above the " + "threshold, the profile of the function will be regarded as " + "being harmful for performance and will be dropped.")); +cl::opt SupplMinSizeThreshold( + "suppl-min-size-threshold", cl::init(10), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("If the size of a function is smaller than the threshold, " + "assume it can be inlined by PGO early inliner and it won't " + "be adjusted based on sample profile.")); +cl::opt InstrProfColdThreshold( + "instr-prof-cold-threshold", cl::init(0), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("User specified cold threshold for instr profile which will " + "override the cold threshold got from profile summary. ")); +// WARNING: This reservoir size value is propagated to any input indexed +// profiles for simplicity. Changing this value between invocations could +// result in sample bias. +cl::opt TemporalProfTraceReservoirSize( + "temporal-profile-trace-reservoir-size", cl::init(100), + cl::sub(MergeSubcommand), + cl::desc("The maximum number of stored temporal profile traces (default: " + "100)")); +cl::opt TemporalProfMaxTraceLength( + "temporal-profile-max-trace-length", cl::init(10000), + cl::sub(MergeSubcommand), + cl::desc("The maximum length of a single temporal profile trace " + "(default: 10000)")); + +cl::opt + FailMode("failure-mode", cl::init(failIfAnyAreInvalid), + cl::desc("Failure mode:"), cl::sub(MergeSubcommand), + cl::values(clEnumValN(warnOnly, "warn", + "Do not fail and just print warnings."), + clEnumValN(failIfAnyAreInvalid, "any", + "Fail if any profile is invalid."), + clEnumValN(failIfAllAreInvalid, "all", + "Fail only if all profiles are invalid."))); + +cl::opt OutputSparse( + "sparse", cl::init(false), cl::sub(MergeSubcommand), + cl::desc("Generate a sparse profile (only meaningful for -instr)")); +cl::opt NumThreads( + "num-threads", cl::init(0), cl::sub(MergeSubcommand), + cl::desc("Number of merge threads to use (default: autodetect)")); +cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), + cl::aliasopt(NumThreads)); + +cl::opt ProfileSymbolListFile( + "prof-sym-list", cl::init(""), cl::sub(MergeSubcommand), + cl::desc("Path to file containing the list of function symbols " + "used to populate profile symbol list")); + +cl::opt ProfileLayout( + "convert-sample-profile-layout", + cl::desc("Convert the generated profile to a profile with a new layout"), + cl::sub(MergeSubcommand), cl::init(SPL_None), + cl::values( + clEnumValN(SPL_Nest, "nest", + "Nested profile, the input should be CS flat profile"), + clEnumValN(SPL_Flat, "flat", + "Profile with nested inlinee flatten out"))); + +cl::opt DropProfileSymbolList( + "drop-profile-symbol-list", cl::init(false), cl::Hidden, + cl::sub(MergeSubcommand), + cl::desc("Drop the profile symbol list when merging AutoFDO profiles " + "(only meaningful for -sample)")); + +// Options specific to overlap subcommand. +cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc(""), + cl::sub(OverlapSubcommand)); +cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc(""), + cl::sub(OverlapSubcommand)); + +cl::opt SimilarityCutoff( + "similarity-cutoff", cl::init(0), + cl::desc("For sample profiles, list function names (with calling context " + "for csspgo) for overlapped functions " + "with similarities below the cutoff (percentage times 10000)."), + cl::sub(OverlapSubcommand)); + +cl::opt IsCS( + "cs", cl::init(false), + cl::desc("For context sensitive PGO counts. Does not work with CSSPGO."), + cl::sub(OverlapSubcommand)); + +cl::opt OverlapValueCutoff( + "value-cutoff", cl::init(-1), + cl::desc( + "Function level overlap information for every function (with calling " + "context for csspgo) in test " + "profile with max count value greater then the parameter value"), + cl::sub(OverlapSubcommand)); + +// Options unique to show subcommand. +cl::opt ShowCounts("counts", cl::init(false), + cl::desc("Show counter values for shown functions"), + cl::sub(ShowSubcommand)); +cl::opt + SFormat("show-format", cl::init(ShowFormat::Text), + cl::desc("Emit output in the selected format if supported"), + cl::sub(ShowSubcommand), + cl::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); +// TODO: Consider replacing this with `--show-format=text-encoding`. +cl::opt + TextFormat("text", cl::init(false), + cl::desc("Show instr profile data in text dump format"), + cl::sub(ShowSubcommand)); +cl::opt + JsonFormat("json", + cl::desc("Show sample profile data in the JSON format " + "(deprecated, please use --show-format=json)"), + cl::sub(ShowSubcommand)); +cl::opt ShowIndirectCallTargets( + "ic-targets", cl::init(false), + cl::desc("Show indirect call site target values for shown functions"), + cl::sub(ShowSubcommand)); +cl::opt ShowMemOPSizes( + "memop-sizes", cl::init(false), + cl::desc("Show the profiled sizes of the memory intrinsic calls " + "for shown functions"), + cl::sub(ShowSubcommand)); +cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), + cl::desc("Show detailed profile summary"), + cl::sub(ShowSubcommand)); +cl::list DetailedSummaryCutoffs( + cl::CommaSeparated, "detailed-summary-cutoffs", + cl::desc( + "Cutoff percentages (times 10000) for generating detailed summary"), + cl::value_desc("800000,901000,999999"), cl::sub(ShowSubcommand)); +cl::opt + ShowHotFuncList("hot-func-list", cl::init(false), + cl::desc("Show profile summary of a list of hot functions"), + cl::sub(ShowSubcommand)); +cl::opt ShowAllFunctions("all-functions", cl::init(false), + cl::desc("Details for each and every function"), + cl::sub(ShowSubcommand)); +cl::opt ShowCS("showcs", cl::init(false), + cl::desc("Show context sensitive counts"), + cl::sub(ShowSubcommand)); +cl::opt ShowProfileKind( + cl::desc("Profile kind supported by show:"), cl::sub(ShowSubcommand), + cl::init(instr), + cl::values(clEnumVal(instr, "Instrumentation profile (default)"), + clEnumVal(sample, "Sample profile"), + clEnumVal(memory, "MemProf memory access profile"))); +cl::opt TopNFunctions( + "topn", cl::init(0), + cl::desc("Show the list of functions with the largest internal counts"), + cl::sub(ShowSubcommand)); +cl::opt ShowValueCutoff( + "value-cutoff", cl::init(0), + cl::desc("Set the count value cutoff. Functions with the maximum count " + "less than this value will not be printed out. (Default is 0)"), + cl::sub(ShowSubcommand)); +cl::opt OnlyListBelow( + "list-below-cutoff", cl::init(false), + cl::desc("Only output names of functions whose max count values are " + "below the cutoff value"), + cl::sub(ShowSubcommand)); +cl::opt ShowProfileSymbolList( + "show-prof-sym-list", cl::init(false), + cl::desc("Show profile symbol list if it exists in the profile. "), + cl::sub(ShowSubcommand)); +cl::opt ShowSectionInfoOnly( + "show-sec-info-only", cl::init(false), + cl::desc("Show the information of each section in the sample profile. " + "The flag is only usable when the sample profile is in " + "extbinary format"), + cl::sub(ShowSubcommand)); +cl::opt ShowBinaryIds("binary-ids", cl::init(false), + cl::desc("Show binary ids in the profile. "), + cl::sub(ShowSubcommand)); +cl::opt ShowTemporalProfTraces( + "temporal-profile-traces", + cl::desc("Show temporal profile traces in the profile."), + cl::sub(ShowSubcommand)); + +cl::opt + ShowCovered("covered", cl::init(false), + cl::desc("Show only the functions that have been executed."), + cl::sub(ShowSubcommand)); + +cl::opt ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. "), + cl::sub(ShowSubcommand)); + +// We use this string to indicate that there are +// multiple static functions map to the same name. +const std::string DuplicateNameStr = "----"; + static void warn(Twine Message, std::string Whence = "", std::string Hint = "") { WithColor::warning(); @@ -112,11 +457,6 @@ static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { exitWithError(EC.message(), std::string(Whence)); } -namespace { -enum ProfileKinds { instr, sample, memory }; -enum FailureMode { warnOnly, failIfAnyAreInvalid, failIfAllAreInvalid }; -} // namespace - static void warnOrExitGivenError(FailureMode FailMode, std::error_code EC, StringRef Whence = "") { if (FailMode == failIfAnyAreInvalid) @@ -435,13 +775,12 @@ static void writeInstrProfile(StringRef OutputFilename, } } -static void -mergeInstrProfile(const WeightedFileVector &Inputs, StringRef DebugInfoFilename, - SymbolRemapper *Remapper, StringRef OutputFilename, - ProfileFormat OutputFormat, uint64_t TraceReservoirSize, - uint64_t MaxTraceLength, int MaxDbgCorrelationWarnings, - bool OutputSparse, unsigned NumThreads, FailureMode FailMode, - const StringRef ProfiledBinary) { +static void mergeInstrProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, + int MaxDbgCorrelationWarnings, + const StringRef ProfiledBinary) { + const uint64_t TraceReservoirSize = TemporalProfTraceReservoirSize.getValue(); + const uint64_t MaxTraceLength = TemporalProfMaxTraceLength.getValue(); if (OutputFormat == PF_Compact_Binary) exitWithError("Compact Binary is deprecated"); if (OutputFormat != PF_Binary && OutputFormat != PF_Ext_Binary && @@ -875,11 +1214,11 @@ adjustInstrProfile(std::unique_ptr &WC, /// should be dropped. \p InstrProfColdThreshold is the user specified /// cold threshold which will override the cold threshold got from the /// instr profile summary. -static void supplementInstrProfile( - const WeightedFileVector &Inputs, StringRef SampleFilename, - StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, - unsigned SupplMinSizeThreshold, float ZeroCounterThreshold, - unsigned InstrProfColdThreshold) { +static void supplementInstrProfile(const WeightedFileVector &Inputs, + StringRef SampleFilename, bool OutputSparse, + unsigned SupplMinSizeThreshold, + float ZeroCounterThreshold, + unsigned InstrProfColdThreshold) { if (OutputFilename.compare("-") == 0) exitWithError("cannot write indexed profdata format to stdout"); if (Inputs.size() != 1) @@ -1012,15 +1351,10 @@ static void handleExtBinaryWriter(sampleprof::SampleProfileWriter &Writer, } } -static void -mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, - StringRef OutputFilename, ProfileFormat OutputFormat, - StringRef ProfileSymbolListFile, bool CompressAllSections, - bool UseMD5, bool GenPartialProfile, - SampleProfileLayout ProfileLayout, - bool SampleMergeColdContext, bool SampleTrimColdContext, - bool SampleColdContextFrameDepth, FailureMode FailMode, - bool DropProfileSymbolList, size_t OutputSizeLimit) { +static void mergeSampleProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, + StringRef ProfileSymbolListFile, + size_t OutputSizeLimit) { using namespace sampleprof; SampleProfileMap ProfileMap; SmallVector, 5> Readers; @@ -1191,147 +1525,6 @@ static void parseInputFilenamesFile(MemoryBuffer *Buffer, } static int merge_main(int argc, const char *argv[]) { - cl::list InputFilenames(cl::Positional, - cl::desc("")); - cl::list WeightedInputFilenames("weighted-input", - cl::desc(",")); - cl::opt InputFilenamesFile( - "input-files", cl::init(""), - cl::desc("Path to file containing newline-separated " - "[,] entries")); - cl::alias InputFilenamesFileA("f", cl::desc("Alias for --input-files"), - cl::aliasopt(InputFilenamesFile)); - cl::opt DumpInputFileList( - "dump-input-file-list", cl::init(false), cl::Hidden, - cl::desc("Dump the list of input files and their weights, then exit")); - cl::opt RemappingFile("remapping-file", cl::value_desc("file"), - cl::desc("Symbol remapping file")); - cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), - cl::aliasopt(RemappingFile)); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::opt OutputFormat( - cl::desc("Format of output profile"), cl::init(PF_Ext_Binary), - cl::values( - clEnumValN(PF_Binary, "binary", "Binary encoding"), - clEnumValN(PF_Ext_Binary, "extbinary", "Extensible binary encoding " - "(default)"), - clEnumValN(PF_Text, "text", "Text encoding"), - clEnumValN(PF_GCC, "gcc", - "GCC encoding (only meaningful for -sample)"))); - cl::opt FailureMode( - "failure-mode", cl::init(failIfAnyAreInvalid), cl::desc("Failure mode:"), - cl::values( - clEnumValN(warnOnly, "warn", "Do not fail and just print warnings."), - clEnumValN(failIfAnyAreInvalid, "any", - "Fail if any profile is invalid."), - clEnumValN(failIfAllAreInvalid, "all", - "Fail only if all profiles are invalid."))); - cl::opt OutputSparse("sparse", cl::init(false), - cl::desc("Generate a sparse profile (only meaningful for -instr)")); - cl::opt NumThreads( - "num-threads", cl::init(0), - cl::desc("Number of merge threads to use (default: autodetect)")); - cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), - cl::aliasopt(NumThreads)); - cl::opt ProfileSymbolListFile( - "prof-sym-list", cl::init(""), - cl::desc("Path to file containing the list of function symbols " - "used to populate profile symbol list")); - cl::opt CompressAllSections( - "compress-all-sections", cl::init(false), cl::Hidden, - cl::desc("Compress all sections when writing the profile (only " - "meaningful for -extbinary)")); - cl::opt UseMD5( - "use-md5", cl::init(false), cl::Hidden, - cl::desc("Choose to use MD5 to represent string in name table (only " - "meaningful for -extbinary)")); - cl::opt SampleMergeColdContext( - "sample-merge-cold-context", cl::init(false), cl::Hidden, - cl::desc( - "Merge context sample profiles whose count is below cold threshold")); - cl::opt SampleTrimColdContext( - "sample-trim-cold-context", cl::init(false), cl::Hidden, - cl::desc( - "Trim context sample profiles whose count is below cold threshold")); - cl::opt SampleColdContextFrameDepth( - "sample-frame-depth-for-cold-context", cl::init(1), - cl::desc("Keep the last K frames while merging cold profile. 1 means the " - "context-less base profile")); - cl::opt OutputSizeLimit( - "output-size-limit", cl::init(0), cl::Hidden, - cl::desc("Trim cold functions until profile size is below specified " - "limit in bytes. This uses a heursitic and functions may be " - "excessively trimmed")); - cl::opt GenPartialProfile( - "gen-partial-profile", cl::init(false), cl::Hidden, - cl::desc("Generate a partial profile (only meaningful for -extbinary)")); - cl::opt SupplInstrWithSample( - "supplement-instr-with-sample", cl::init(""), cl::Hidden, - cl::desc("Supplement an instr profile with sample profile, to correct " - "the profile unrepresentativeness issue. The sample " - "profile is the input of the flag. Output will be in instr " - "format (The flag only works with -instr)")); - cl::opt ZeroCounterThreshold( - "zero-counter-threshold", cl::init(0.7), cl::Hidden, - cl::desc("For the function which is cold in instr profile but hot in " - "sample profile, if the ratio of the number of zero counters " - "divided by the total number of counters is above the " - "threshold, the profile of the function will be regarded as " - "being harmful for performance and will be dropped.")); - cl::opt SupplMinSizeThreshold( - "suppl-min-size-threshold", cl::init(10), cl::Hidden, - cl::desc("If the size of a function is smaller than the threshold, " - "assume it can be inlined by PGO early inliner and it won't " - "be adjusted based on sample profile.")); - cl::opt InstrProfColdThreshold( - "instr-prof-cold-threshold", cl::init(0), cl::Hidden, - cl::desc("User specified cold threshold for instr profile which will " - "override the cold threshold got from profile summary. ")); - cl::opt ProfileLayout( - "convert-sample-profile-layout", - cl::desc("Convert the generated profile to a profile with a new layout"), - cl::init(SPL_None), - cl::values( - clEnumValN(SPL_Nest, "nest", - "Nested profile, the input should be CS flat profile"), - clEnumValN(SPL_Flat, "flat", - "Profile with nested inlinee flatten out"))); - cl::opt DebugInfoFilename( - "debug-info", cl::init(""), - cl::desc("Use the provided debug info to correlate the raw profile.")); - cl::opt MaxDbgCorrelationWarnings( - "max-debug-info-correlation-warnings", - cl::desc("The maximum number of warnings to emit when correlating " - "profile from debug info (0 = no limit)"), - cl::init(5)); - cl::opt ProfiledBinary( - "profiled-binary", cl::init(""), - cl::desc("Path to binary from which the profile was collected.")); - cl::opt DropProfileSymbolList( - "drop-profile-symbol-list", cl::init(false), cl::Hidden, - cl::desc("Drop the profile symbol list when merging AutoFDO profiles " - "(only meaningful for -sample)")); - // WARNING: This reservoir size value is propagated to any input indexed - // profiles for simplicity. Changing this value between invocations could - // result in sample bias. - cl::opt TemporalProfTraceReservoirSize( - "temporal-profile-trace-reservoir-size", cl::init(100), - cl::desc("The maximum number of stored temporal profile traces (default: " - "100)")); - cl::opt TemporalProfMaxTraceLength( - "temporal-profile-max-trace-length", cl::init(10000), - cl::desc("The maximum length of a single temporal profile trace " - "(default: 10000)")); - - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); - WeightedFileVector WeightedInputs; for (StringRef Filename : InputFilenames) addWeightedInput(WeightedInputs, {std::string(Filename), 1}); @@ -1345,7 +1538,7 @@ static int merge_main(int argc, const char *argv[]) { if (WeightedInputs.empty()) exitWithError("no input files specified. See " + - sys::path::filename(argv[0]) + " -help"); + sys::path::filename(argv[0]) + " " + argv[1] + " -help"); if (DumpInputFileList) { for (auto &WF : WeightedInputs) @@ -1362,25 +1555,18 @@ static int merge_main(int argc, const char *argv[]) { exitWithError( "-supplement-instr-with-sample can only work with -instr. "); - supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputFilename, - OutputFormat, OutputSparse, SupplMinSizeThreshold, - ZeroCounterThreshold, InstrProfColdThreshold); + supplementInstrProfile(WeightedInputs, SupplInstrWithSample, OutputSparse, + SupplMinSizeThreshold, ZeroCounterThreshold, + InstrProfColdThreshold); return 0; } if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, DebugInfoFilename, Remapper.get(), - OutputFilename, OutputFormat, - TemporalProfTraceReservoirSize, - TemporalProfMaxTraceLength, MaxDbgCorrelationWarnings, - OutputSparse, NumThreads, FailureMode, ProfiledBinary); + mergeInstrProfile(WeightedInputs, Remapper.get(), MaxDbgCorrelationWarnings, + ProfiledBinary); else - mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, - OutputFormat, ProfileSymbolListFile, CompressAllSections, - UseMD5, GenPartialProfile, ProfileLayout, - SampleMergeColdContext, SampleTrimColdContext, - SampleColdContextFrameDepth, FailureMode, - DropProfileSymbolList, OutputSizeLimit); + mergeSampleProfile(WeightedInputs, Remapper.get(), ProfileSymbolListFile, + OutputSizeLimit); return 0; } @@ -2339,49 +2525,18 @@ void overlapSampleProfile(const std::string &BaseFilename, } static int overlap_main(int argc, const char *argv[]) { - cl::opt BaseFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt TestFilename(cl::Positional, cl::Required, - cl::desc("")); - cl::opt Output("output", cl::value_desc("output"), cl::init("-"), - cl::desc("Output file")); - cl::alias OutputA("o", cl::desc("Alias for --output"), cl::aliasopt(Output)); - cl::opt IsCS( - "cs", cl::init(false), - cl::desc("For context sensitive PGO counts. Does not work with CSSPGO.")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(-1), - cl::desc( - "Function level overlap information for every function (with calling " - "context for csspgo) in test " - "profile with max count value greater then the parameter value")); - cl::opt FuncNameFilter( - "function", - cl::desc("Function level overlap information for matching functions. For " - "CSSPGO this takes a a function name with calling context")); - cl::opt SimilarityCutoff( - "similarity-cutoff", cl::init(0), - cl::desc("For sample profiles, list function names (with calling context " - "for csspgo) for overlapped functions " - "with similarities below the cutoff (percentage times 10000).")); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"))); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data overlap tool\n"); - std::error_code EC; - raw_fd_ostream OS(Output.data(), EC, sys::fs::OF_TextWithCRLF); + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) - exitWithErrorCode(EC, Output); + exitWithErrorCode(EC, OutputFilename); if (ProfileKind == instr) overlapInstrProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, OS, - IsCS); + OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter}, + OS, IsCS); else overlapSampleProfile(BaseFilename, TestFilename, - OverlapFuncFilters{ValueCutoff, FuncNameFilter}, + OverlapFuncFilters{OverlapValueCutoff, FuncNameFilter}, SimilarityCutoff, OS); return 0; @@ -2447,14 +2602,7 @@ static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, } } -static int showInstrProfile( - const std::string &Filename, bool ShowCounts, uint32_t TopN, - bool ShowIndirectCallTargets, bool ShowMemOPSizes, bool ShowDetailedSummary, - std::vector DetailedSummaryCutoffs, bool ShowAllFunctions, - bool ShowCS, uint64_t ValueCutoff, bool OnlyListBelow, - const std::string &ShowFunction, bool TextFormat, bool ShowBinaryIds, - bool ShowCovered, bool ShowProfileVersion, bool ShowTemporalProfTraces, - ShowFormat SFormat, raw_fd_ostream &OS) { +static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for instr profiles"); if (SFormat == ShowFormat::Yaml) @@ -2488,7 +2636,7 @@ static int showInstrProfile( if (!TextFormat && OnlyListBelow) { OS << "The list of functions with the maximum counter less than " - << ValueCutoff << ":\n"; + << ShowValueCutoff << ":\n"; } // Add marker so that IR-level instrumentation round-trips properly. @@ -2502,7 +2650,7 @@ static int showInstrProfile( continue; } bool Show = ShowAllFunctions || - (!ShowFunction.empty() && Func.Name.contains(ShowFunction)); + (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter)); bool doTextFormatDump = (Show && TextFormat); @@ -2549,7 +2697,7 @@ static int showInstrProfile( FuncSum += Func.Counts[I]; } - if (FuncMax < ValueCutoff) { + if (FuncMax < ShowValueCutoff) { ++BelowCutoffFunctions; if (OnlyListBelow) { OS << " " << Func.Name << ": (Max = " << FuncMax @@ -2559,8 +2707,8 @@ static int showInstrProfile( } else if (OnlyListBelow) continue; - if (TopN) { - if (HottestFuncs.size() == TopN) { + if (TopNFunctions) { + if (HottestFuncs.size() == TopNFunctions) { if (HottestFuncs.top().second < FuncMax) { HottestFuncs.pop(); HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); @@ -2624,25 +2772,25 @@ static int showInstrProfile( if (IsIR) OS << " entry_first = " << Reader->instrEntryBBEnabled(); OS << "\n"; - if (ShowAllFunctions || !ShowFunction.empty()) + if (ShowAllFunctions || !FuncNameFilter.empty()) OS << "Functions shown: " << ShownFunctions << "\n"; OS << "Total functions: " << PS->getNumFunctions() << "\n"; - if (ValueCutoff > 0) { - OS << "Number of functions with maximum count (< " << ValueCutoff + if (ShowValueCutoff > 0) { + OS << "Number of functions with maximum count (< " << ShowValueCutoff << "): " << BelowCutoffFunctions << "\n"; - OS << "Number of functions with maximum count (>= " << ValueCutoff + OS << "Number of functions with maximum count (>= " << ShowValueCutoff << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; } OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - if (TopN) { + if (TopNFunctions) { std::vector> SortedHottestFuncs; while (!HottestFuncs.empty()) { SortedHottestFuncs.emplace_back(HottestFuncs.top()); HottestFuncs.pop(); } - OS << "Top " << TopN + OS << "Top " << TopNFunctions << " functions with the largest internal block counts: \n"; for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; @@ -2832,13 +2980,7 @@ static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, return 0; } -static int showSampleProfile(const std::string &Filename, bool ShowCounts, - uint32_t TopN, bool ShowAllFunctions, - bool ShowDetailedSummary, - const std::string &ShowFunction, - bool ShowProfileSymbolList, - bool ShowSectionInfoOnly, bool ShowHotFuncList, - ShowFormat SFormat, raw_fd_ostream &OS) { +static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Yaml) exitWithError("YAML output is not supported for sample profiles"); using namespace sampleprof; @@ -2858,7 +3000,7 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, if (std::error_code EC = Reader->read()) exitWithErrorCode(EC, Filename); - if (ShowAllFunctions || ShowFunction.empty()) { + if (ShowAllFunctions || FuncNameFilter.empty()) { if (SFormat == ShowFormat::Json) Reader->dumpJson(OS); else @@ -2870,7 +3012,7 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, "be printed"); // TODO: parse context string to support filtering by contexts. - FunctionSamples *FS = Reader->getSamplesFor(StringRef(ShowFunction)); + FunctionSamples *FS = Reader->getSamplesFor(StringRef(FuncNameFilter)); Reader->dumpFunctionProfile(FS ? *FS : FunctionSamples(), OS); } @@ -2886,15 +3028,14 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts, PS.printDetailedSummary(OS); } - if (ShowHotFuncList || TopN) - showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); + if (ShowHotFuncList || TopNFunctions) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), + TopNFunctions, OS); return 0; } -static int showMemProfProfile(const std::string &Filename, - const std::string &ProfiledBinary, - ShowFormat SFormat, raw_fd_ostream &OS) { +static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for MemProf"); auto ReaderOr = llvm::memprof::RawMemProfReader::create( @@ -2913,9 +3054,6 @@ static int showMemProfProfile(const std::string &Filename, } static int showDebugInfoCorrelation(const std::string &Filename, - bool ShowDetailedSummary, - bool ShowProfileSymbolList, - int MaxDbgCorrelationWarnings, ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for debug info correlation"); @@ -2951,107 +3089,13 @@ static int showDebugInfoCorrelation(const std::string &Filename, } static int show_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::desc("")); - - cl::opt ShowCounts("counts", cl::init(false), - cl::desc("Show counter values for shown functions")); - cl::opt SFormat( - "show-format", cl::init(ShowFormat::Text), - cl::desc("Emit output in the selected format if supported"), - cl::values(clEnumValN(ShowFormat::Text, "text", - "emit normal text output (default)"), - clEnumValN(ShowFormat::Json, "json", "emit JSON"), - clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); - // TODO: Consider replacing this with `--show-format=text-encoding`. - cl::opt TextFormat( - "text", cl::init(false), - cl::desc("Show instr profile data in text dump format")); - cl::opt JsonFormat( - "json", cl::desc("Show sample profile data in the JSON format " - "(deprecated, please use --show-format=json)")); - cl::opt ShowIndirectCallTargets( - "ic-targets", cl::init(false), - cl::desc("Show indirect call site target values for shown functions")); - cl::opt ShowMemOPSizes( - "memop-sizes", cl::init(false), - cl::desc("Show the profiled sizes of the memory intrinsic calls " - "for shown functions")); - cl::opt ShowDetailedSummary("detailed-summary", cl::init(false), - cl::desc("Show detailed profile summary")); - cl::list DetailedSummaryCutoffs( - cl::CommaSeparated, "detailed-summary-cutoffs", - cl::desc( - "Cutoff percentages (times 10000) for generating detailed summary"), - cl::value_desc("800000,901000,999999")); - cl::opt ShowHotFuncList( - "hot-func-list", cl::init(false), - cl::desc("Show profile summary of a list of hot functions")); - cl::opt ShowAllFunctions("all-functions", cl::init(false), - cl::desc("Details for every function")); - cl::opt ShowCS("showcs", cl::init(false), - cl::desc("Show context sensitive counts")); - cl::opt ShowFunction("function", - cl::desc("Details for matching functions")); - - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::init(instr), - cl::values(clEnumVal(instr, "Instrumentation profile (default)"), - clEnumVal(sample, "Sample profile"), - clEnumVal(memory, "MemProf memory access profile"))); - cl::opt TopNFunctions( - "topn", cl::init(0), - cl::desc("Show the list of functions with the largest internal counts")); - cl::opt ValueCutoff( - "value-cutoff", cl::init(0), - cl::desc("Set the count value cutoff. Functions with the maximum count " - "less than this value will not be printed out. (Default is 0)")); - cl::opt OnlyListBelow( - "list-below-cutoff", cl::init(false), - cl::desc("Only output names of functions whose max count values are " - "below the cutoff value")); - cl::opt ShowProfileSymbolList( - "show-prof-sym-list", cl::init(false), - cl::desc("Show profile symbol list if it exists in the profile. ")); - cl::opt ShowSectionInfoOnly( - "show-sec-info-only", cl::init(false), - cl::desc("Show the information of each section in the sample profile. " - "The flag is only usable when the sample profile is in " - "extbinary format")); - cl::opt ShowBinaryIds("binary-ids", cl::init(false), - cl::desc("Show binary ids in the profile. ")); - cl::opt ShowTemporalProfTraces( - "temporal-profile-traces", - cl::desc("Show temporal profile traces in the profile.")); - cl::opt DebugInfoFilename( - "debug-info", cl::init(""), - cl::desc("Read and extract profile metadata from debug info and show " - "the functions it found.")); - cl::opt MaxDbgCorrelationWarnings( - "max-debug-info-correlation-warnings", - cl::desc("The maximum number of warnings to emit when correlating " - "profile from debug info (0 = no limit)"), - cl::init(5)); - cl::opt ShowCovered( - "covered", cl::init(false), - cl::desc("Show only the functions that have been executed.")); - cl::opt ProfiledBinary( - "profiled-binary", cl::init(""), - cl::desc("Path to binary from which the profile was collected.")); - cl::opt ShowProfileVersion("profile-version", cl::init(false), - cl::desc("Show profile version. ")); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - if (Filename.empty() && DebugInfoFilename.empty()) exitWithError( "the positional argument '' is required unless '--" + DebugInfoFilename.ArgStr + "' is provided"); if (Filename == OutputFilename) { - errs() << sys::path::filename(argv[0]) + errs() << sys::path::filename(argv[0]) << " " << argv[1] << ": Input file name cannot be the same as the output file name!\n"; return 1; } @@ -3063,37 +3107,20 @@ static int show_main(int argc, const char *argv[]) { if (EC) exitWithErrorCode(EC, OutputFilename); - if (ShowAllFunctions && !ShowFunction.empty()) + if (ShowAllFunctions && !FuncNameFilter.empty()) WithColor::warning() << "-function argument ignored: showing all functions\n"; if (!DebugInfoFilename.empty()) - return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, - ShowProfileSymbolList, - MaxDbgCorrelationWarnings, SFormat, OS); + return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS); - if (ProfileKind == instr) - return showInstrProfile( - Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, - ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowCS, ValueCutoff, OnlyListBelow, ShowFunction, - TextFormat, ShowBinaryIds, ShowCovered, ShowProfileVersion, - ShowTemporalProfTraces, SFormat, OS); - if (ProfileKind == sample) - return showSampleProfile(Filename, ShowCounts, TopNFunctions, - ShowAllFunctions, ShowDetailedSummary, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); - return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); + if (ShowProfileKind == instr) + return showInstrProfile(SFormat, OS); + if (ShowProfileKind == sample) + return showSampleProfile(SFormat, OS); + return showMemProfProfile(SFormat, OS); } static int order_main(int argc, const char *argv[]) { - cl::opt Filename(cl::Positional, cl::desc("")); - cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file")); - cl::alias OutputFilenameA("o", cl::desc("Alias for --output"), - cl::aliasopt(OutputFilename)); - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data order\n"); - std::error_code EC; raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); if (EC) @@ -3125,66 +3152,34 @@ static int order_main(int argc, const char *argv[]) { return 0; } -typedef int (*llvm_profdata_subcommand)(int, const char *[]); - -static std::tuple - llvm_profdata_subcommands[] = { - {"merge", merge_main}, - {"show", show_main}, - {"order", order_main}, - {"overlap", overlap_main}, -}; - int llvm_profdata_main(int argc, char **argvNonConst, const llvm::ToolContext &) { const char **argv = const_cast(argvNonConst); InitLLVM X(argc, argv); StringRef ProgName(sys::path::filename(argv[0])); - if (argc > 1) { - llvm_profdata_subcommand func = nullptr; - for (auto [subcmd_name, subcmd_action] : llvm_profdata_subcommands) - if (subcmd_name == argv[1]) - func = subcmd_action; + if (argc < 2) { + errs() << ProgName + << ": No subcommand specified! Run llvm-profata --help for usage.\n"; + return 1; + } - if (func) { - std::string Invocation(ProgName.str() + " " + argv[1]); - argv[1] = Invocation.c_str(); - return func(argc - 1, argv + 1); - } + cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n"); - if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || - strcmp(argv[1], "--help") == 0) { - - errs() << "OVERVIEW: LLVM profile data tools\n\n" - << "USAGE: " << ProgName << " [args...]\n" - << "USAGE: " << ProgName << " -help\n\n" - << "See each individual command --help for more details.\n" - << "Available commands: " - << join(map_range(llvm_profdata_subcommands, - [](auto const &KV) { return std::get<0>(KV); }), - ", ") - << "\n"; - return 0; - } + if (ShowSubcommand) + return show_main(argc, argv); - if (strcmp(argv[1], "--version") == 0) { - outs() << ProgName << '\n'; - cl::PrintVersionMessage(); - return 0; - } - } + if (OrderSubcommand) + return order_main(argc, argv); - if (argc < 2) - errs() << ProgName << ": No command specified!\n"; - else - errs() << ProgName << ": Unknown command!\n"; + if (OverlapSubcommand) + return overlap_main(argc, argv); + + if (MergeSubcommand) + return merge_main(argc, argv); - errs() << "USAGE: " << ProgName << " <" - << join(map_range(llvm_profdata_subcommands, - [](auto const &KV) { return std::get<0>(KV); }), - "|") - << "> [args...]\n"; + errs() << ProgName + << ": Unknown command. Run llvm-profdata --help for usage.\n"; return 1; }