================
@@ -3471,32 +3171,574 @@ static int order_main() {
return 0;
}
-int main(int argc, const char *argv[]) {
- InitLLVM X(argc, argv);
- StringRef ProgName(sys::path::filename(argv[0]));
+static void reportCmdLineError(const Twine &Message) {
+ WithColor::error(errs(), ProgramName) << Message << "\n";
+}
+
+template <typename T>
+static bool parseNumericOption(const opt::Arg *A, T &Value) {
+ if (!A)
+ return true;
+ StringRef V = A->getValue();
+ T Parsed{};
+ if (!llvm::to_integer(V, Parsed, 0)) {
+ if (!std::numeric_limits<T>::is_signed && V == "-1") {
+ Value = std::numeric_limits<T>::max();
+ return true;
+ }
+ reportCmdLineError(Twine("invalid argument '") + V + "' for option '" +
+ A->getSpelling() + "'");
+ return false;
+ }
+ Value = Parsed;
+ return true;
+}
+
+static bool applyLibraryOptions(const opt::InputArgList &Args) {
+ SmallVector<std::string, 16> CLStrings;
+ CLStrings.push_back(ProgramName);
+
+ auto AddFlag = [&](unsigned OptID, StringRef Spelling) {
+ if (Args.hasArg(OptID))
+ CLStrings.push_back(Spelling.str());
+ };
+ auto AddUInt = [&](unsigned OptID, StringRef Spelling) -> bool {
+ if (const opt::Arg *A = Args.getLastArg(OptID)) {
+ uint64_t Value = 0;
+ if (!parseNumericOption(A, Value))
+ return false;
+ CLStrings.push_back((Spelling + Twine("=") + Twine(Value)).str());
+ }
+ return true;
+ };
+ auto AddInt = [&](unsigned OptID, StringRef Spelling) -> bool {
+ if (const opt::Arg *A = Args.getLastArg(OptID)) {
+ int Value = 0;
+ if (!parseNumericOption(A, Value))
+ return false;
+ CLStrings.push_back((Spelling + Twine("=") + Twine(Value)).str());
+ }
+ return true;
+ };
+
+ AddFlag(OPT_profile_isfs, "--profile-isfs");
+ AddFlag(OPT_generate_merged_base_profiles,
"--generate-merged-base-profiles");
+ if (!AddUInt(OPT_profile_symbol_list_cutoff, "--profile-symbol-list-cutoff"))
+ return false;
+ AddFlag(OPT_extbinary_write_vtable_type_prof,
+ "--extbinary-write-vtable-type-prof");
+
+ AddFlag(OPT_profile_summary_contextless, "--profile-summary-contextless");
+ if (!AddInt(OPT_profile_summary_cutoff_hot, "--profile-summary-cutoff-hot"))
+ return false;
+ if (!AddInt(OPT_profile_summary_cutoff_cold,
"--profile-summary-cutoff-cold"))
+ return false;
+ if (!AddUInt(OPT_profile_summary_hot_count, "--profile-summary-hot-count"))
+ return false;
+ if (!AddUInt(OPT_profile_summary_cold_count, "--profile-summary-cold-count"))
+ return false;
+ if (!AddUInt(OPT_profile_summary_huge_working_set_size_threshold,
+ "--profile-summary-huge-working-set-size-threshold"))
+ return false;
+ if (!AddUInt(OPT_profile_summary_large_working_set_size_threshold,
+ "--profile-summary-large-working-set-size-threshold"))
+ return false;
+
+ if (CLStrings.size() == 1)
+ return true;
+
+ SmallVector<char *, 16> CLArgs;
+ for (std::string &S : CLStrings)
+ CLArgs.push_back(S.data());
+
+ return cl::ParseCommandLineOptions(CLArgs.size(), CLArgs.data(),
+ /*Overview=*/"", /*Errs=*/nullptr,
+ /*VFS=*/nullptr,
+ /*EnvVar=*/nullptr,
+ /*LongOptionsUseDoubleDash=*/false);
+}
+
+static bool parseFloatOption(const opt::Arg *A, float &Value) {
+ if (!A)
+ return true;
+ StringRef V = A->getValue();
+ double Parsed;
+ if (V.getAsDouble(Parsed)) {
+ reportCmdLineError(Twine("invalid argument '") + V + "' for option '" +
+ A->getSpelling() + "'");
+ return false;
+ }
+ Value = static_cast<float>(Parsed);
+ return true;
+}
+
+static bool parseCutoffValues(const opt::InputArgList &Args,
+ std::vector<uint32_t> &Cutoffs) {
+ Cutoffs.clear();
+ for (const opt::Arg *A : Args.filtered(OPT_detailed_summary_cutoffs)) {
+ SmallVector<StringRef, 4> Parts;
+ StringRef(A->getValue())
+ .split(Parts, ',', /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ for (StringRef Part : Parts) {
+ uint32_t Parsed;
+ if (!llvm::to_integer(Part, Parsed, 0)) {
+ reportCmdLineError(Twine("invalid argument '") + Part +
+ "' for option '" + A->getSpelling() + "'");
+ return false;
+ }
+ Cutoffs.push_back(Parsed);
+ }
+ }
+ return true;
+}
+
+static bool parseFSDiscriminatorPassArg(const opt::InputArgList &Args) {
+ const opt::Arg *A = Args.getLastArg(OPT_fs_discriminator_pass);
+ if (!A)
+ return true;
+
+ StringRef Value = A->getValue();
+ auto Parsed = StringSwitch<std::optional<FSDiscriminatorPass>>(Value)
+ .Case("Base", FSDiscriminatorPass::Base)
+ .Case("base", FSDiscriminatorPass::Base)
+ .Case("Pass1", FSDiscriminatorPass::Pass1)
+ .Case("pass1", FSDiscriminatorPass::Pass1)
+ .Case("Pass2", FSDiscriminatorPass::Pass2)
+ .Case("pass2", FSDiscriminatorPass::Pass2)
+ .Case("Pass3", FSDiscriminatorPass::Pass3)
+ .Case("pass3", FSDiscriminatorPass::Pass3)
+ .Case("PassLast", FSDiscriminatorPass::PassLast)
+ .Case("pass-last", FSDiscriminatorPass::PassLast)
+ .Case("passlast", FSDiscriminatorPass::PassLast)
+ .Default(std::nullopt);
+ if (!Parsed) {
+ reportCmdLineError(Twine("invalid argument '") + Value + "' for option '" +
+ A->getSpelling() + "'");
+ return false;
+ }
+ FSDiscriminatorPassOption = *Parsed;
+ return true;
+}
+
+static bool validateSubcommandOptions(const opt::InputArgList &Args,
+ StringRef Subcommand) {
+ bool Valid = true;
+ for (const opt::Arg *A : Args) {
+ if (A->getOption().matches(OPT_UNKNOWN) ||
+ A->getOption().matches(OPT_INPUT))
+ continue;
+ // Options without an explicit subcommand are available everywhere.
+ if (A->getOption().matches(OPT_help) ||
A->getOption().matches(OPT_version))
+ continue;
+ if (A->getOption().isRegisteredSC(Subcommand))
+ continue;
+ reportCmdLineError(Twine("unknown command line argument '") +
+ A->getSpelling() + "' for subcommand '" + Subcommand +
+ "'. Try: '" + ProgramName + " " + Subcommand +
+ " --help'");
+ Valid = false;
+ }
+ return Valid;
+}
+
+static bool parseMergeOptions(const opt::InputArgList &Args,
+ ArrayRef<StringRef> Positionals) {
+ OutputFilename = Args.getLastArgValue(OPT_output, "-").str();
+ if (const opt::Arg *A = Args.getLastArg(OPT_sample, OPT_instr))
+ ProfileKind = A->getOption().matches(OPT_sample) ? sample : instr;
+ if (!applyLibraryOptions(Args))
+ return false;
+
+ if (!parseNumericOption(
+ Args.getLastArg(OPT_max_debug_info_correlation_warnings),
+ MaxDbgCorrelationWarnings))
+ return false;
+ ProfiledBinary = Args.getLastArgValue(OPT_profiled_binary).str();
+ DebugInfoFilename = Args.getLastArgValue(OPT_debug_info).str();
+ BinaryFilename = Args.getLastArgValue(OPT_binary_file).str();
+ DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory);
+ DebugInfod = Args.hasArg(OPT_debuginfod);
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_correlate)) {
+ StringRef V = A->getValue();
+ auto Parsed = StringSwitch<std::optional<ProfCorrelatorKind>>(V)
+ .Case("", InstrProfCorrelator::NONE)
+ .Case("debug-info", InstrProfCorrelator::DEBUG_INFO)
+ .Case("binary", InstrProfCorrelator::BINARY)
+ .Default(std::nullopt);
+ if (!Parsed) {
+ reportCmdLineError(Twine("invalid argument '") + V + "' for option '" +
+ A->getSpelling() + "'");
+ return false;
+ }
+ BIDFetcherProfileCorrelate = *Parsed;
+ }
+
+ FuncNameFilter = Args.getLastArgValue(OPT_function).str();
+ InputFilenames.clear();
+ InputFilenames.reserve(Positionals.size());
+ for (StringRef Pos : Positionals)
+ InputFilenames.emplace_back(Pos.str());
+ WeightedInputFilenames = Args.getAllArgValues(OPT_weighted_input);
+
+ OutputFormat = PF_Ext_Binary;
+ if (const opt::Arg *Fmt =
+ Args.getLastArg(OPT_binary, OPT_extbinary, OPT_text, OPT_gcc)) {
+ if (Fmt->getOption().matches(OPT_binary))
+ OutputFormat = PF_Binary;
+ else if (Fmt->getOption().matches(OPT_gcc))
+ OutputFormat = PF_GCC;
+ else if (Fmt->getOption().matches(OPT_text))
+ OutputFormat = PF_Text;
+ else
+ OutputFormat = PF_Ext_Binary;
+ }
+
+ InputFilenamesFile = Args.getLastArgValue(OPT_input_files).str();
+ DumpInputFileList = Args.hasArg(OPT_dump_input_file_list);
+ RemappingFile = Args.getLastArgValue(OPT_remapping_file).str();
+ UseMD5 = Args.hasArg(OPT_use_md5);
+ CompressAllSections = Args.hasArg(OPT_compress_all_sections);
+ SampleMergeColdContext = Args.hasArg(OPT_sample_merge_cold_context);
+ SampleTrimColdContext = Args.hasArg(OPT_sample_trim_cold_context);
+ if (!parseNumericOption(
+ Args.getLastArg(OPT_sample_frame_depth_for_cold_context),
+ SampleColdContextFrameDepth))
+ return false;
+ if (!parseNumericOption(Args.getLastArg(OPT_output_size_limit),
+ OutputSizeLimit))
+ return false;
+ GenPartialProfile = Args.hasArg(OPT_gen_partial_profile);
+ SplitLayout = Args.hasArg(OPT_split_layout);
+ SupplInstrWithSample =
+ Args.getLastArgValue(OPT_supplement_instr_with_sample).str();
+ if (!parseFloatOption(Args.getLastArg(OPT_zero_counter_threshold),
+ ZeroCounterThreshold))
+ return false;
+ if (!parseNumericOption(Args.getLastArg(OPT_suppl_min_size_threshold),
+ SupplMinSizeThreshold))
+ return false;
+ if (!parseNumericOption(Args.getLastArg(OPT_instr_prof_cold_threshold),
+ InstrProfColdThreshold))
+ return false;
+ if (!parseNumericOption(
+ Args.getLastArg(OPT_temporal_profile_trace_reservoir_size),
+ TemporalProfTraceReservoirSize))
+ return false;
+ if (!parseNumericOption(
+ Args.getLastArg(OPT_temporal_profile_max_trace_length),
+ TemporalProfMaxTraceLength))
+ return false;
+ FuncNameNegativeFilter = Args.getLastArgValue(OPT_no_function).str();
+
+ StringRef FailureModeValue = Args.getLastArgValue(OPT_failure_mode, "any");
+ auto ParsedFailMode =
+ StringSwitch<std::optional<FailureMode>>(FailureModeValue)
+ .Case("warn", warnOnly)
+ .Case("any", failIfAnyAreInvalid)
+ .Case("all", failIfAllAreInvalid)
+ .Default(std::nullopt);
+ if (!ParsedFailMode) {
+ reportCmdLineError(Twine("invalid argument '") + FailureModeValue +
+ "' for option '--failure-mode'");
+ return false;
+ }
+ FailMode = *ParsedFailMode;
+
+ OutputSparse = Args.hasArg(OPT_sparse);
+ if (!parseNumericOption(Args.getLastArg(OPT_num_threads), NumThreads))
+ return false;
+ ProfileSymbolListFile = Args.getLastArgValue(OPT_prof_sym_list).str();
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_convert_sample_profile_layout)) {
+ StringRef Layout = A->getValue();
+ auto ParsedLayout =
StringSwitch<std::optional<SampleProfileLayout>>(Layout)
+ .Case("nest", SPL_Nest)
+ .Case("flat", SPL_Flat)
+ .Default(std::nullopt);
+ if (!ParsedLayout) {
+ reportCmdLineError(Twine("invalid argument '") + Layout +
+ "' for option '" + A->getSpelling() + "'");
+ return false;
+ }
+ ProfileLayout = *ParsedLayout;
+ }
+
+ DropProfileSymbolList = Args.hasArg(OPT_drop_profile_symbol_list);
+ KeepVTableSymbols = Args.hasArg(OPT_keep_vtable_symbols);
+ DoWritePrevVersion = Args.hasArg(OPT_write_prev_version);
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_memprof_version)) {
+ StringRef Version = A->getValue();
+ auto ParsedVersion =
+ StringSwitch<std::optional<memprof::IndexedVersion>>(Version)
+ .Case("2", memprof::Version2)
+ .Case("3", memprof::Version3)
+ .Case("4", memprof::Version4)
+ .Default(std::nullopt);
+ if (!ParsedVersion) {
+ reportCmdLineError(Twine("invalid argument '") + Version +
+ "' for option '" + A->getSpelling() + "'");
+ return false;
+ }
+ MemProfVersionRequested = *ParsedVersion;
+ }
+
+ MemProfFullSchema = Args.hasArg(OPT_memprof_full_schema);
+ MemprofGenerateRandomHotness = Args.hasArg(OPT_memprof_random_hotness);
+ if (!parseNumericOption(Args.getLastArg(OPT_memprof_random_hotness_seed),
+ MemprofGenerateRandomHotnessSeed))
+ return false;
+
+ if (!parseFSDiscriminatorPassArg(Args))
+ return false;
+
+ return true;
+}
+
+static bool parseShowOptions(const opt::InputArgList &Args,
+ ArrayRef<StringRef> Positionals) {
+ OutputFilename = Args.getLastArgValue(OPT_output, "-").str();
+ DebugInfoFilename = Args.getLastArgValue(OPT_debug_info).str();
+ ProfiledBinary = Args.getLastArgValue(OPT_profiled_binary).str();
+ FuncNameFilter = Args.getLastArgValue(OPT_function).str();
+ if (!Positionals.empty()) {
+ Filename = Positionals.front().str();
+ if (Positionals.size() > 1) {
+ reportCmdLineError("too many positional arguments");
+ return false;
+ }
+ }
+ if (Filename.empty() && DebugInfoFilename.empty()) {
+ reportCmdLineError(
+ "the positional argument '<profdata-file>' is required unless "
+ "'--debug-info' is provided");
+ return false;
+ }
+ if (!Filename.empty() && OutputFilename == Filename) {
+ reportCmdLineError("show: Input file name cannot be the same as the "
+ "output file name!");
+ return false;
+ }
+
+ if (!parseNumericOption(
+ Args.getLastArg(OPT_max_debug_info_correlation_warnings),
+ MaxDbgCorrelationWarnings))
+ return false;
+ if (!applyLibraryOptions(Args))
+ return false;
+
+ ShowCounts = Args.hasArg(OPT_counts);
+ if (const opt::Arg *A = Args.getLastArg(OPT_show_format)) {
+ StringRef Value = A->getValue();
+ auto Parsed = StringSwitch<std::optional<ShowFormat>>(Value)
+ .Case("text", ShowFormat::Text)
+ .Case("json", ShowFormat::Json)
+ .Case("yaml", ShowFormat::Yaml)
+ .Default(std::nullopt);
+ if (!Parsed) {
+ reportCmdLineError(Twine("invalid argument '") + Value +
+ "' for option '" + A->getSpelling() + "'");
+ return false;
+ }
+ SFormat = *Parsed;
+ }
+ TextFormat = Args.hasArg(OPT_text);
+ JsonFormat = Args.hasArg(OPT_json);
+ ShowIndirectCallTargets = Args.hasArg(OPT_ic_targets);
+ ShowVTables = Args.hasArg(OPT_show_vtables);
+ ShowMemOPSizes = Args.hasArg(OPT_memop_sizes);
+ ShowDetailedSummary = Args.hasArg(OPT_detailed_summary);
+ if (!parseCutoffValues(Args, DetailedSummaryCutoffs))
+ return false;
+ ShowHotFuncList = Args.hasArg(OPT_hot_func_list);
+ ShowAllFunctions = Args.hasArg(OPT_all_functions);
+ ShowCS = Args.hasArg(OPT_showcs);
+ if (!parseNumericOption(Args.getLastArg(OPT_topn), TopNFunctions))
+ return false;
+ if (!parseNumericOption(Args.getLastArg(OPT_value_cutoff), ShowValueCutoff))
+ return false;
+ OnlyListBelow = Args.hasArg(OPT_list_below_cutoff);
+ ShowProfileSymbolList = Args.hasArg(OPT_show_prof_sym_list);
+ ShowSectionInfoOnly = Args.hasArg(OPT_show_sec_info_only);
+ ShowBinaryIds = Args.hasArg(OPT_binary_ids);
+ ShowTemporalProfTraces = Args.hasArg(OPT_temporal_profile_traces);
+ ShowCovered = Args.hasArg(OPT_covered);
+ ShowProfileVersion = Args.hasArg(OPT_profile_version);
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_memory, OPT_sample, OPT_instr)) {
+ if (A->getOption().matches(OPT_memory))
+ ShowProfileKind = memory;
+ else if (A->getOption().matches(OPT_sample))
+ ShowProfileKind = sample;
+ else
+ ShowProfileKind = instr;
+ }
+
+ if (!parseFSDiscriminatorPassArg(Args))
+ return false;
+
+ return true;
+}
+
+static bool parseOverlapOptions(const opt::InputArgList &Args,
+ ArrayRef<StringRef> Positionals) {
+ OutputFilename = Args.getLastArgValue(OPT_output, "-").str();
+ if (Positionals.size() != 2) {
+ reportCmdLineError("overlap requires two positional profile filenames");
+ return false;
+ }
+ BaseFilename = Positionals[0].str();
+ TestFilename = Positionals[1].str();
+
+ if (const opt::Arg *A = Args.getLastArg(OPT_sample, OPT_instr))
+ ProfileKind = A->getOption().matches(OPT_sample) ? sample : instr;
+
+ FuncNameFilter = Args.getLastArgValue(OPT_function).str();
+ if (!parseNumericOption(Args.getLastArg(OPT_similarity_cutoff),
+ SimilarityCutoff))
+ return false;
+ IsCS = Args.hasArg(OPT_cs);
+ if (!parseNumericOption(Args.getLastArg(OPT_value_cutoff),
+ OverlapValueCutoff))
+ return false;
+
+ if (!parseFSDiscriminatorPassArg(Args))
+ return false;
+
+ return true;
+}
- if (argc < 2) {
- errs()
- << ProgName
- << ": No subcommand specified! Run llvm-profdata --help for usage.\n";
+static bool parseOrderOptions(const opt::InputArgList &Args,
+ ArrayRef<StringRef> Positionals) {
+ OutputFilename = Args.getLastArgValue(OPT_output, "-").str();
+ if (!Positionals.empty()) {
+ Filename = Positionals.front().str();
+ if (Positionals.size() > 1) {
+ reportCmdLineError("too many positional arguments");
+ return false;
+ }
+ }
+ if (!parseNumericOption(Args.getLastArg(OPT_num_test_traces), NumTestTraces))
+ return false;
+ return true;
+}
+
+int llvm_profdata_main(int argc, char **argv, const llvm::ToolContext &) {
+ BumpPtrAllocator Alloc;
+ StringSaver Saver(Alloc);
+
+ ProgramName = sys::path::stem(argv[0]).str();
+
+ ProfdataOptTable Tbl;
+
+ if (argc == 1) {
+ errs() << ProgramName << ": No subcommand specified! Run " << ProgramName
+ << " --help for usage.\n";
return 1;
}
- cl::ParseCommandLineOptions(argc, argv, "LLVM profile data\n");
+ bool HadParseError = false;
+ opt::InputArgList Args =
+ Tbl.parseArgs(argc - 1, argv + 1, OPT_UNKNOWN, Saver, [&](StringRef Msg)
{
+ WithColor::error(errs(), ProgramName) << Msg << "\n";
+ HadParseError = true;
+ });
+ if (HadParseError)
+ return 1;
+
+ bool HadSubcommandError = false;
+ SmallVector<StringRef, 4> OtherPositionals;
+ auto HandleMultipleSubcommands = [&](ArrayRef<StringRef> SubCommands) {
+ HadSubcommandError = true;
+ WithColor::error(errs(), ProgramName) << "multiple subcommands specified:";
+ for (StringRef SC : SubCommands)
+ errs() << " '" << SC << "'";
+ errs() << "\n";
+ };
+ auto HandleOtherPositionals = [&](ArrayRef<StringRef> Positionals) {
+ OtherPositionals.append(Positionals.begin(), Positionals.end());
+ };
- if (ShowSubcommand)
- return show_main(ProgName);
+ auto IsKnownSubcommand = [&](StringRef Name) {
+ return llvm::any_of(
+ Tbl.getSubCommands(),
+ [&](const opt::OptTable::SubCommand &SC) { return Name == SC.Name; });
+ };
- if (OrderSubcommand)
- return order_main();
+ StringRef RawFirstArg = argc > 1 ? StringRef(argv[1]) : StringRef();
+ StringRef RawSubcommand =
+ IsKnownSubcommand(RawFirstArg) ? RawFirstArg : StringRef();
- if (OverlapSubcommand)
- return overlap_main();
+ StringRef Subcommand = Args.getSubCommand(
+ Tbl.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
+ if (HadSubcommandError)
+ return 1;
- if (MergeSubcommand)
- return merge_main(ProgName);
+ if (Subcommand.empty() && !OtherPositionals.empty() &&
+ IsKnownSubcommand(OtherPositionals.front())) {
+ Subcommand = OtherPositionals.front();
+ OtherPositionals.erase(OtherPositionals.begin());
+ }
+ if (Subcommand.empty() && !RawSubcommand.empty()) {
+ Subcommand = RawSubcommand;
+ auto It = llvm::find(OtherPositionals, Subcommand);
+ if (It != OtherPositionals.end())
+ OtherPositionals.erase(It);
+ }
+
+ auto HasRawFlag = [&](StringRef Flag) {
+ return llvm::is_contained(OtherPositionals, Flag) || RawFirstArg == Flag;
+ };
+
+ if (Args.hasArg(OPT_help) || HasRawFlag("--help")) {
----------------
Prabhuk wrote:
Can you provide an example invocation and what case is not being handled
correctly?
IIUC you are trying to handle the help flag here. You should be passing the
helptext from your Opts.td file which you seem to be doing. So I cant fathom
what is the purpose of this help text handling. If the library is not handling
this correctly we must fix that first anyway.
https://github.com/llvm/llvm-project/pull/177868
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits