From e9b590a65994c8e8a4bcd02859a9e7a17fd657b1 Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Tue, 7 Nov 2023 12:02:10 -0800 Subject: [PATCH 1/7] [nfc][llvm-profdata] Move show options to its namespace --- llvm/tools/llvm-profdata/llvm-profdata.cpp | 252 ++++++++++----------- 1 file changed, 115 insertions(+), 137 deletions(-) diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index e7e7f8228d7d9..f8e6bf5dd9b99 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -2387,6 +2387,7 @@ static int overlap_main(int argc, const char *argv[]) { return 0; } +namespace show_llvmprofdata { namespace { struct ValueSitesStats { ValueSitesStats() @@ -2447,14 +2448,101 @@ 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) { +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. ")); + +static int showInstrProfile(const std::string &Filename, ShowFormat SFormat, + raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for instr profiles"); if (SFormat == ShowFormat::Yaml) @@ -2559,8 +2647,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)); @@ -2636,13 +2724,13 @@ static int showInstrProfile( 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 +2920,8 @@ 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(const std::string &Filename, ShowFormat SFormat, + raw_fd_ostream &OS) { if (SFormat == ShowFormat::Yaml) exitWithError("YAML output is not supported for sample profiles"); using namespace sampleprof; @@ -2886,15 +2969,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(const std::string &Filename, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for MemProf"); auto ReaderOr = llvm::memprof::RawMemProfReader::create( @@ -2913,10 +2995,7 @@ 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) { + raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for debug info correlation"); std::unique_ptr Correlator; @@ -2950,101 +3029,8 @@ static int showDebugInfoCorrelation(const std::string &Filename, return 0; } -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. ")); +static int main(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - if (Filename.empty() && DebugInfoFilename.empty()) exitWithError( "the positional argument '' is required unless '--" + @@ -3067,25 +3053,17 @@ static int show_main(int argc, const char *argv[]) { WithColor::warning() << "-function argument ignored: showing all functions\n"; if (!DebugInfoFilename.empty()) - return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, - ShowProfileSymbolList, - MaxDbgCorrelationWarnings, SFormat, OS); + return showDebugInfoCorrelation(DebugInfoFilename, 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); + return showInstrProfile(Filename, SFormat, OS); if (ProfileKind == sample) - return showSampleProfile(Filename, ShowCounts, TopNFunctions, - ShowAllFunctions, ShowDetailedSummary, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); - return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); + return showSampleProfile(Filename, SFormat, OS); + return showMemProfProfile(Filename, SFormat, OS); } +} // namespace show_llvmprofdata + static int order_main(int argc, const char *argv[]) { cl::opt Filename(cl::Positional, cl::desc("")); cl::opt OutputFilename("output", cl::value_desc("output"), @@ -3130,7 +3108,7 @@ typedef int (*llvm_profdata_subcommand)(int, const char *[]); static std::tuple llvm_profdata_subcommands[] = { {"merge", merge_main}, - {"show", show_main}, + {"show", show_llvmprofdata::main}, {"order", order_main}, {"overlap", overlap_main}, }; From 02a0de693395c5de06678acf54d52b9e3af2477c Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Tue, 7 Nov 2023 17:52:18 -0800 Subject: [PATCH 2/7] Add show_options namespace and move show_options from function scope to namespace. - The change uses cl::SubCommand feature that allow to register options under a subcommand. - Without using cl::SubCommand, the options in the show name namespace will take a registered name as global variables, causing runtime errors when a function-scope static option tries to add a function of a same name. - Make changes in the CommandLine library accordingly. --- llvm/include/llvm/Support/CommandLine.h | 2 +- llvm/lib/Support/CommandLine.cpp | 7 + llvm/tools/llvm-profdata/llvm-profdata.cpp | 293 +++++++++++++-------- 3 files changed, 185 insertions(+), 117 deletions(-) diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h index 58ef176551b68..ab62607b2a307 100644 --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -481,7 +481,7 @@ struct sub { sub(SubCommand &S) : Sub(S) {} - template void apply(Opt &O) const { O.addSubCommand(Sub); } + void apply(Option &O) const { O.addSubCommand(Sub); } }; // Specify a callback function to be called when an option is seen. diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index 55633d7cafa47..a7e0cae8b855d 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -1667,6 +1667,13 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value, LongOptionsUseDoubleDash, HaveDoubleDash); + // If Handler is not found in a specialized subcommand, look up handler + // in the top-level subcommand. + // cl::opt without cl::sub belongs to top-level subcommand. + if (!Handler && ChosenSubCommand != &SubCommand::getTopLevel()) + Handler = LookupLongOption(SubCommand::getTopLevel(), ArgName, Value, + LongOptionsUseDoubleDash, HaveDoubleDash); + // Check to see if this "option" is really a prefixed or grouped argument. if (!Handler && !(LongOptionsUseDoubleDash && HaveDoubleDash)) Handler = HandlePrefixedOrGroupedOption(ArgName, Value, ErrorParsing, diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index f8e6bf5dd9b99..fd00798b8bc95 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -47,6 +47,8 @@ using namespace llvm; +cl::SubCommand ShowSubcommand("show", "show profile data"); + // We use this string to indicate that there are // multiple static functions map to the same name. const std::string DuplicateNameStr = "----"; @@ -2387,7 +2389,6 @@ static int overlap_main(int argc, const char *argv[]) { return 0; } -namespace show_llvmprofdata { namespace { struct ValueSitesStats { ValueSitesStats() @@ -2448,101 +2449,14 @@ static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK, } } -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. ")); - -static int showInstrProfile(const std::string &Filename, ShowFormat SFormat, - raw_fd_ostream &OS) { +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) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for instr profiles"); if (SFormat == ShowFormat::Yaml) @@ -2647,8 +2561,8 @@ static int showInstrProfile(const std::string &Filename, ShowFormat SFormat, } else if (OnlyListBelow) continue; - if (TopNFunctions) { - if (HottestFuncs.size() == TopNFunctions) { + if (TopN) { + if (HottestFuncs.size() == TopN) { if (HottestFuncs.top().second < FuncMax) { HottestFuncs.pop(); HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax)); @@ -2724,13 +2638,13 @@ static int showInstrProfile(const std::string &Filename, ShowFormat SFormat, OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; - if (TopNFunctions) { + if (TopN) { std::vector> SortedHottestFuncs; while (!HottestFuncs.empty()) { SortedHottestFuncs.emplace_back(HottestFuncs.top()); HottestFuncs.pop(); } - OS << "Top " << TopNFunctions + OS << "Top " << TopN << " functions with the largest internal block counts: \n"; for (auto &hotfunc : llvm::reverse(SortedHottestFuncs)) OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n"; @@ -2920,8 +2834,13 @@ static int showHotFunctionList(const sampleprof::SampleProfileMap &Profiles, return 0; } -static int showSampleProfile(const std::string &Filename, ShowFormat SFormat, - raw_fd_ostream &OS) { +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) { if (SFormat == ShowFormat::Yaml) exitWithError("YAML output is not supported for sample profiles"); using namespace sampleprof; @@ -2969,14 +2888,15 @@ static int showSampleProfile(const std::string &Filename, ShowFormat SFormat, PS.printDetailedSummary(OS); } - if (ShowHotFuncList || TopNFunctions) - showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), - TopNFunctions, OS); + if (ShowHotFuncList || TopN) + showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), TopN, OS); return 0; } -static int showMemProfProfile(const std::string &Filename, raw_fd_ostream &OS) { +static int showMemProfProfile(const std::string &Filename, + const std::string &ProfiledBinary, + ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for MemProf"); auto ReaderOr = llvm::memprof::RawMemProfReader::create( @@ -2995,7 +2915,10 @@ static int showMemProfProfile(const std::string &Filename, raw_fd_ostream &OS) { } static int showDebugInfoCorrelation(const std::string &Filename, - raw_fd_ostream &OS) { + 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"); std::unique_ptr Correlator; @@ -3029,15 +2952,139 @@ static int showDebugInfoCorrelation(const std::string &Filename, return 0; } -static int main(int argc, const char *argv[]) { +namespace show_options { +cl::opt Filename(cl::Positional, cl::desc(""), + cl::sub(ShowSubcommand)); + +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::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")), + cl::sub(ShowSubcommand)); +// 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 ShowFunction("function", + cl::desc("Details for matching functions"), + cl::sub(ShowSubcommand)); + +cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file"), + cl::sub(ShowSubcommand)); +// 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)); +cl::opt ProfileKind( + cl::desc("Profile kind:"), 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 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::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 DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Read and extract profile metadata from debug info and show " + "the functions it found."), + cl::sub(ShowSubcommand)); +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::sub(ShowSubcommand)); +cl::opt + ShowCovered("covered", cl::init(false), + cl::desc("Show only the functions that have been executed."), + cl::sub(ShowSubcommand)); +cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected."), + cl::sub(ShowSubcommand)); +cl::opt ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. "), + cl::sub(ShowSubcommand)); +} // namespace show_options + +static int show_main(int argc, const char *argv[]) { + using namespace show_options; 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; } @@ -3053,17 +3100,25 @@ static int main(int argc, const char *argv[]) { WithColor::warning() << "-function argument ignored: showing all functions\n"; if (!DebugInfoFilename.empty()) - return showDebugInfoCorrelation(DebugInfoFilename, OS); + return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, + ShowProfileSymbolList, + MaxDbgCorrelationWarnings, SFormat, OS); if (ProfileKind == instr) - return showInstrProfile(Filename, SFormat, OS); + 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, SFormat, OS); - return showMemProfProfile(Filename, SFormat, OS); + return showSampleProfile(Filename, ShowCounts, TopNFunctions, + ShowAllFunctions, ShowDetailedSummary, + ShowFunction, ShowProfileSymbolList, + ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); + return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); } -} // namespace show_llvmprofdata - static int order_main(int argc, const char *argv[]) { cl::opt Filename(cl::Positional, cl::desc("")); cl::opt OutputFilename("output", cl::value_desc("output"), @@ -3108,7 +3163,6 @@ typedef int (*llvm_profdata_subcommand)(int, const char *[]); static std::tuple llvm_profdata_subcommands[] = { {"merge", merge_main}, - {"show", show_llvmprofdata::main}, {"order", order_main}, {"overlap", overlap_main}, }; @@ -3132,6 +3186,13 @@ int llvm_profdata_main(int argc, char **argvNonConst, return func(argc - 1, argv + 1); } + // "show" subcommand and its options uses the subcommands supported by + // llvm::cl. See this post + // (https://lists.llvm.org/pipermail/llvm-dev/2016-June/101804.html) for + // a brief introduction. + if (strcmp(argv[1], "show") == 0) + return show_main(argc, argv); + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0) { From c9bdcf96a25c321685a8ee3a8a2844fd624a32d7 Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Wed, 8 Nov 2023 09:59:56 -0800 Subject: [PATCH 3/7] resolve feedback --- llvm/tools/llvm-profdata/llvm-profdata.cpp | 306 ++++++++++----------- 1 file changed, 139 insertions(+), 167 deletions(-) diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index fd00798b8bc95..fdf841a9f88b2 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -2401,6 +2401,127 @@ struct ValueSitesStats { }; } // namespace +namespace show { +cl::opt Filename(cl::Positional, cl::desc(""), + cl::sub(ShowSubcommand)); + +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::values(clEnumValN(ShowFormat::Text, "text", + "emit normal text output (default)"), + clEnumValN(ShowFormat::Json, "json", "emit JSON"), + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")), + cl::sub(ShowSubcommand)); +// 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 ShowFunction("function", + cl::desc("Details for matching functions"), + cl::sub(ShowSubcommand)); + +cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file"), + cl::sub(ShowSubcommand)); +// 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)); +cl::opt ProfileKind( + cl::desc("Profile kind:"), 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 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::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 DebugInfoFilename( + "debug-info", cl::init(""), + cl::desc("Read and extract profile metadata from debug info and show " + "the functions it found."), + cl::sub(ShowSubcommand)); +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::sub(ShowSubcommand)); +cl::opt + ShowCovered("covered", cl::init(false), + cl::desc("Show only the functions that have been executed."), + cl::sub(ShowSubcommand)); +cl::opt ProfiledBinary( + "profiled-binary", cl::init(""), + cl::desc("Path to binary from which the profile was collected."), + cl::sub(ShowSubcommand)); +cl::opt ShowProfileVersion("profile-version", cl::init(false), + cl::desc("Show profile version. "), + cl::sub(ShowSubcommand)); + static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, ValueSitesStats &Stats, raw_fd_ostream &OS, InstrProfSymtab *Symtab) { @@ -2449,14 +2570,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) @@ -2561,8 +2675,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)); @@ -2638,13 +2752,13 @@ static int showInstrProfile( 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"; @@ -2834,13 +2948,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; @@ -2888,15 +2996,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( @@ -2914,11 +3021,7 @@ static int showMemProfProfile(const std::string &Filename, return 0; } -static int showDebugInfoCorrelation(const std::string &Filename, - bool ShowDetailedSummary, - bool ShowProfileSymbolList, - int MaxDbgCorrelationWarnings, - ShowFormat SFormat, raw_fd_ostream &OS) { +static int showDebugInfoCorrelation(ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for debug info correlation"); std::unique_ptr Correlator; @@ -2952,130 +3055,7 @@ static int showDebugInfoCorrelation(const std::string &Filename, return 0; } -namespace show_options { -cl::opt Filename(cl::Positional, cl::desc(""), - cl::sub(ShowSubcommand)); - -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::values(clEnumValN(ShowFormat::Text, "text", - "emit normal text output (default)"), - clEnumValN(ShowFormat::Json, "json", "emit JSON"), - clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML")), - cl::sub(ShowSubcommand)); -// 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 ShowFunction("function", - cl::desc("Details for matching functions"), - cl::sub(ShowSubcommand)); - -cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file"), - cl::sub(ShowSubcommand)); -// 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)); -cl::opt ProfileKind( - cl::desc("Profile kind:"), 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 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::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 DebugInfoFilename( - "debug-info", cl::init(""), - cl::desc("Read and extract profile metadata from debug info and show " - "the functions it found."), - cl::sub(ShowSubcommand)); -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::sub(ShowSubcommand)); -cl::opt - ShowCovered("covered", cl::init(false), - cl::desc("Show only the functions that have been executed."), - cl::sub(ShowSubcommand)); -cl::opt ProfiledBinary( - "profiled-binary", cl::init(""), - cl::desc("Path to binary from which the profile was collected."), - cl::sub(ShowSubcommand)); -cl::opt ShowProfileVersion("profile-version", cl::init(false), - cl::desc("Show profile version. "), - cl::sub(ShowSubcommand)); -} // namespace show_options - static int show_main(int argc, const char *argv[]) { - using namespace show_options; cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); if (Filename.empty() && DebugInfoFilename.empty()) @@ -3100,25 +3080,17 @@ static int show_main(int argc, const char *argv[]) { WithColor::warning() << "-function argument ignored: showing all functions\n"; if (!DebugInfoFilename.empty()) - return showDebugInfoCorrelation(DebugInfoFilename, ShowDetailedSummary, - ShowProfileSymbolList, - MaxDbgCorrelationWarnings, SFormat, OS); + return showDebugInfoCorrelation(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); + return showInstrProfile(SFormat, OS); if (ProfileKind == sample) - return showSampleProfile(Filename, ShowCounts, TopNFunctions, - ShowAllFunctions, ShowDetailedSummary, - ShowFunction, ShowProfileSymbolList, - ShowSectionInfoOnly, ShowHotFuncList, SFormat, OS); - return showMemProfProfile(Filename, ProfiledBinary, SFormat, OS); + return showSampleProfile(SFormat, OS); + return showMemProfProfile(SFormat, OS); } +} // namespace show + static int order_main(int argc, const char *argv[]) { cl::opt Filename(cl::Positional, cl::desc("")); cl::opt OutputFilename("output", cl::value_desc("output"), @@ -3191,7 +3163,7 @@ int llvm_profdata_main(int argc, char **argvNonConst, // (https://lists.llvm.org/pipermail/llvm-dev/2016-June/101804.html) for // a brief introduction. if (strcmp(argv[1], "show") == 0) - return show_main(argc, argv); + return show::show_main(argc, argv); if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0) { From 770e16741913b1e42a39ea70fa0a6c1b1a2c91e1 Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Fri, 10 Nov 2023 15:51:18 -0800 Subject: [PATCH 4/7] Refactor llvm-profdata to use cl::subcommand --- llvm/test/tools/llvm-profdata/errors.test | 2 +- llvm/test/tools/llvm-profdata/version.test | 1 - llvm/tools/llvm-profdata/llvm-profdata.cpp | 630 ++++++++++----------- 3 files changed, 303 insertions(+), 330 deletions(-) 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 fdf841a9f88b2..9aa3e4b04ee0a 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -47,7 +47,20 @@ using namespace llvm; -cl::SubCommand ShowSubcommand("show", "show profile data"); +// 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"); +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"); +cl::SubCommand + OverlapSubcommand("overlap", + "Computes and displays the overlap between two profiles"); +cl::SubCommand MergeSubcommand("merge", "Merges profiles"); // We use this string to indicate that there are // multiple static functions map to the same name. @@ -437,13 +450,195 @@ 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) { +// 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)); + +// 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)")); + +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 && @@ -877,11 +1072,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) @@ -1014,15 +1209,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; @@ -1193,147 +1383,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}); @@ -1347,7 +1396,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) @@ -1364,28 +1413,55 @@ 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; } +// Overlap options. +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 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)); + +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)); + /// Computer the overlap b/w profile BaseFilename and profile TestFilename. static void overlapInstrProfile(const std::string &BaseFilename, const std::string &TestFilename, @@ -2341,49 +2417,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; @@ -2401,21 +2446,20 @@ struct ValueSitesStats { }; } // namespace -namespace show { -cl::opt Filename(cl::Positional, cl::desc(""), - cl::sub(ShowSubcommand)); - +// Options unique to show subcommand. +// TODO: Consider creating a template class ShowOption to factor out the common +// cl::sub in cl::opt constructor. 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")), - cl::sub(ShowSubcommand)); + clEnumValN(ShowFormat::Yaml, "yaml", "emit YAML"))); // TODO: Consider replacing this with `--show-format=text-encoding`. cl::opt TextFormat("text", cl::init(false), @@ -2453,19 +2497,9 @@ cl::opt ShowAllFunctions("all-functions", cl::init(false), cl::opt ShowCS("showcs", cl::init(false), cl::desc("Show context sensitive counts"), cl::sub(ShowSubcommand)); -cl::opt ShowFunction("function", - cl::desc("Details for matching functions"), - cl::sub(ShowSubcommand)); - -cl::opt OutputFilename("output", cl::value_desc("output"), - cl::init("-"), cl::desc("Output file"), - cl::sub(ShowSubcommand)); -// 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)); -cl::opt ProfileKind( - cl::desc("Profile kind:"), cl::sub(ShowSubcommand), cl::init(instr), +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"))); @@ -2473,7 +2507,7 @@ cl::opt TopNFunctions( "topn", cl::init(0), cl::desc("Show the list of functions with the largest internal counts"), cl::sub(ShowSubcommand)); -cl::opt ValueCutoff( +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)"), @@ -2500,24 +2534,12 @@ cl::opt ShowTemporalProfTraces( "temporal-profile-traces", cl::desc("Show temporal profile traces in the profile."), cl::sub(ShowSubcommand)); -cl::opt DebugInfoFilename( - "debug-info", cl::init(""), - cl::desc("Read and extract profile metadata from debug info and show " - "the functions it found."), - cl::sub(ShowSubcommand)); -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::sub(ShowSubcommand)); + cl::opt ShowCovered("covered", cl::init(false), cl::desc("Show only the functions that have been executed."), cl::sub(ShowSubcommand)); -cl::opt ProfiledBinary( - "profiled-binary", cl::init(""), - cl::desc("Path to binary from which the profile was collected."), - cl::sub(ShowSubcommand)); + cl::opt ShowProfileVersion("profile-version", cl::init(false), cl::desc("Show profile version. "), cl::sub(ShowSubcommand)); @@ -2604,7 +2626,7 @@ static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) { 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. @@ -2618,7 +2640,7 @@ static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) { continue; } bool Show = ShowAllFunctions || - (!ShowFunction.empty() && Func.Name.contains(ShowFunction)); + (!FuncNameFilter.empty() && Func.Name.contains(FuncNameFilter)); bool doTextFormatDump = (Show && TextFormat); @@ -2665,7 +2687,7 @@ static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) { FuncSum += Func.Counts[I]; } - if (FuncMax < ValueCutoff) { + if (FuncMax < ShowValueCutoff) { ++BelowCutoffFunctions; if (OnlyListBelow) { OS << " " << Func.Name << ": (Max = " << FuncMax @@ -2740,13 +2762,13 @@ static int showInstrProfile(ShowFormat SFormat, raw_fd_ostream &OS) { 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"; @@ -2968,7 +2990,7 @@ static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) { 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 @@ -2980,7 +3002,7 @@ static int showSampleProfile(ShowFormat SFormat, raw_fd_ostream &OS) { "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); } @@ -3021,7 +3043,8 @@ static int showMemProfProfile(ShowFormat SFormat, raw_fd_ostream &OS) { return 0; } -static int showDebugInfoCorrelation(ShowFormat SFormat, raw_fd_ostream &OS) { +static int showDebugInfoCorrelation(const std::string &Filename, + ShowFormat SFormat, raw_fd_ostream &OS) { if (SFormat == ShowFormat::Json) exitWithError("JSON output is not supported for debug info correlation"); std::unique_ptr Correlator; @@ -3056,8 +3079,6 @@ static int showDebugInfoCorrelation(ShowFormat SFormat, raw_fd_ostream &OS) { } static int show_main(int argc, const char *argv[]) { - cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); - if (Filename.empty() && DebugInfoFilename.empty()) exitWithError( "the positional argument '' is required unless '--" + @@ -3076,29 +3097,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(SFormat, OS); + return showDebugInfoCorrelation(DebugInfoFilename, SFormat, OS); - if (ProfileKind == instr) + if (ShowProfileKind == instr) return showInstrProfile(SFormat, OS); - if (ProfileKind == sample) + if (ShowProfileKind == sample) return showSampleProfile(SFormat, OS); return showMemProfProfile(SFormat, OS); } -} // namespace show - 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) @@ -3130,72 +3142,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}, - {"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"); - // "show" subcommand and its options uses the subcommands supported by - // llvm::cl. See this post - // (https://lists.llvm.org/pipermail/llvm-dev/2016-June/101804.html) for - // a brief introduction. - if (strcmp(argv[1], "show") == 0) - return show::show_main(argc, argv); - - 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; } From 6fc1bfa4df1914f1ff00a464cc513b234bc01013 Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Fri, 10 Nov 2023 16:21:02 -0800 Subject: [PATCH 5/7] undo one-line added during debugging --- llvm/include/llvm/Support/CommandLine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h index ab62607b2a307..58ef176551b68 100644 --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -481,7 +481,7 @@ struct sub { sub(SubCommand &S) : Sub(S) {} - void apply(Option &O) const { O.addSubCommand(Sub); } + template void apply(Opt &O) const { O.addSubCommand(Sub); } }; // Specify a callback function to be called when an option is seen. From 0f3951d810e85e434d17bf0acb853203ebb70496 Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Mon, 13 Nov 2023 15:51:35 -0800 Subject: [PATCH 6/7] resolve feedback --- llvm/tools/llvm-profdata/llvm-profdata.cpp | 848 +++++++++++---------- 1 file changed, 429 insertions(+), 419 deletions(-) diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index 9aa3e4b04ee0a..bb05887360450 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -49,18 +49,343 @@ using namespace llvm; // 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"); -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"); -cl::SubCommand - OverlapSubcommand("overlap", - "Computes and displays the overlap between two profiles"); -cl::SubCommand MergeSubcommand("merge", "Merges profiles"); +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"); + +// 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. @@ -352,286 +677,103 @@ static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, WC->Errors.emplace_back(make_error(ErrCode, Msg), Filename); return; - } - - auto Reader = std::move(ReaderOrErr.get()); - if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { - consumeError(std::move(E)); - WC->Errors.emplace_back( - make_error( - "Merge IR generated profile with Clang generated profile.", - std::error_code()), - Filename); - return; - } - - for (auto &I : *Reader) { - if (Remapper) - I.Name = (*Remapper)(I.Name); - const StringRef FuncName = I.Name; - bool Reported = false; - WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { - if (Reported) { - consumeError(std::move(E)); - return; - } - Reported = true; - // Only show hint the first time an error occurs. - auto [ErrCode, Msg] = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{WC->ErrLock}; - bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second; - handleMergeWriterError(make_error(ErrCode, Msg), - Input.Filename, FuncName, firstTime); - }); - } - - if (Reader->hasTemporalProfile()) { - auto &Traces = Reader->getTemporalProfTraces(Input.Weight); - if (!Traces.empty()) - WC->Writer.addTemporalProfileTraces( - Traces, Reader->getTemporalProfTraceStreamSize()); - } - if (Reader->hasError()) { - if (Error E = Reader->getError()) { - WC->Errors.emplace_back(std::move(E), Filename); - return; - } - } - - std::vector BinaryIds; - if (Error E = Reader->readBinaryIds(BinaryIds)) { - WC->Errors.emplace_back(std::move(E), Filename); - return; - } - WC->Writer.addBinaryIds(BinaryIds); - - if (ReaderWarning) { - WC->Errors.emplace_back(std::move(ReaderWarning->first), - ReaderWarning->second); - } -} - -/// Merge the \p Src writer context into \p Dst. -static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { - for (auto &ErrorPair : Src->Errors) - Dst->Errors.push_back(std::move(ErrorPair)); - Src->Errors.clear(); - - if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) - exitWithError(std::move(E)); - - Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { - auto [ErrorCode, Msg] = InstrProfError::take(std::move(E)); - std::unique_lock ErrGuard{Dst->ErrLock}; - bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second; - if (firstTime) - warn(toString(make_error(ErrorCode, Msg))); - }); -} - -static void writeInstrProfile(StringRef OutputFilename, - ProfileFormat OutputFormat, - InstrProfWriter &Writer) { - std::error_code EC; - raw_fd_ostream Output(OutputFilename.data(), EC, - OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF - : sys::fs::OF_None); - if (EC) - exitWithErrorCode(EC, OutputFilename); - - if (OutputFormat == PF_Text) { - if (Error E = Writer.writeText(Output)) - warn(std::move(E)); - } else { - if (Output.is_displayed()) - exitWithError("cannot write a non-text format profile to the terminal"); - if (Error E = Writer.write(Output)) - warn(std::move(E)); - } -} - -// 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)); - -// 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."))); + auto Reader = std::move(ReaderOrErr.get()); + if (Error E = WC->Writer.mergeProfileKind(Reader->getProfileKind())) { + consumeError(std::move(E)); + WC->Errors.emplace_back( + make_error( + "Merge IR generated profile with Clang generated profile.", + std::error_code()), + Filename); + return; + } -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)); + for (auto &I : *Reader) { + if (Remapper) + I.Name = (*Remapper)(I.Name); + const StringRef FuncName = I.Name; + bool Reported = false; + WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { + if (Reported) { + consumeError(std::move(E)); + return; + } + Reported = true; + // Only show hint the first time an error occurs. + auto [ErrCode, Msg] = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{WC->ErrLock}; + bool firstTime = WC->WriterErrorCodes.insert(ErrCode).second; + handleMergeWriterError(make_error(ErrCode, Msg), + Input.Filename, FuncName, firstTime); + }); + } -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")); + if (Reader->hasTemporalProfile()) { + auto &Traces = Reader->getTemporalProfTraces(Input.Weight); + if (!Traces.empty()) + WC->Writer.addTemporalProfileTraces( + Traces, Reader->getTemporalProfTraceStreamSize()); + } + if (Reader->hasError()) { + if (Error E = Reader->getError()) { + WC->Errors.emplace_back(std::move(E), Filename); + return; + } + } -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"))); + std::vector BinaryIds; + if (Error E = Reader->readBinaryIds(BinaryIds)) { + WC->Errors.emplace_back(std::move(E), Filename); + return; + } + WC->Writer.addBinaryIds(BinaryIds); -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)")); + if (ReaderWarning) { + WC->Errors.emplace_back(std::move(ReaderWarning->first), + ReaderWarning->second); + } +} + +/// Merge the \p Src writer context into \p Dst. +static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { + for (auto &ErrorPair : Src->Errors) + Dst->Errors.push_back(std::move(ErrorPair)); + Src->Errors.clear(); + + if (Error E = Dst->Writer.mergeProfileKind(Src->Writer.getProfileKind())) + exitWithError(std::move(E)); + + Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) { + auto [ErrorCode, Msg] = InstrProfError::take(std::move(E)); + std::unique_lock ErrGuard{Dst->ErrLock}; + bool firstTime = Dst->WriterErrorCodes.insert(ErrorCode).second; + if (firstTime) + warn(toString(make_error(ErrorCode, Msg))); + }); +} + +static void writeInstrProfile(StringRef OutputFilename, + ProfileFormat OutputFormat, + InstrProfWriter &Writer) { + std::error_code EC; + raw_fd_ostream Output(OutputFilename.data(), EC, + OutputFormat == PF_Text ? sys::fs::OF_TextWithCRLF + : sys::fs::OF_None); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + if (OutputFormat == PF_Text) { + if (Error E = Writer.writeText(Output)) + warn(std::move(E)); + } else { + if (Output.is_displayed()) + exitWithError("cannot write a non-text format profile to the terminal"); + if (Error E = Writer.write(Output)) + warn(std::move(E)); + } +} static void mergeInstrProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, @@ -1428,40 +1570,6 @@ static int merge_main(int argc, const char *argv[]) { return 0; } -// Overlap options. -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 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)); - -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)); - /// Computer the overlap b/w profile BaseFilename and profile TestFilename. static void overlapInstrProfile(const std::string &BaseFilename, const std::string &TestFilename, @@ -2446,104 +2554,6 @@ struct ValueSitesStats { }; } // namespace -// Options unique to show subcommand. -// TODO: Consider creating a template class ShowOption to factor out the common -// cl::sub in cl::opt constructor. -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)); - static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, ValueSitesStats &Stats, raw_fd_ostream &OS, InstrProfSymtab *Symtab) { From 5b9e7dcc8c79390c0981fc01800075c37b46935d Mon Sep 17 00:00:00 2001 From: Mingming Liu Date: Mon, 13 Nov 2023 21:23:58 -0800 Subject: [PATCH 7/7] move enum definition before option definition --- llvm/tools/llvm-profdata/llvm-profdata.cpp | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index bb05887360450..b9df3f97c8fa9 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -71,6 +71,22 @@ cl::SubCommand MergeSubcommand( "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, + PF_Text, + PF_Compact_Binary, // Deprecated + PF_Ext_Binary, + PF_GCC, + PF_Binary +}; + +enum class ShowFormat { Text, Json, Yaml }; + // Common options. cl::opt OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::desc("Output file"), @@ -391,17 +407,6 @@ cl::opt ShowProfileVersion("profile-version", cl::init(false), // multiple static functions map to the same name. const std::string DuplicateNameStr = "----"; -enum ProfileFormat { - PF_None = 0, - PF_Text, - PF_Compact_Binary, // Deprecated - PF_Ext_Binary, - PF_GCC, - PF_Binary -}; - -enum class ShowFormat { Text, Json, Yaml }; - static void warn(Twine Message, std::string Whence = "", std::string Hint = "") { WithColor::warning(); @@ -452,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)