================
@@ -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();
----------------
Prabhuk wrote:

You need to do the parsing from command line options. It is provided by the 
library through the ProfdataOptTable initialized at the top of this file. See 
how this example use parseArgs to do this: 

https://github.com/llvm/llvm-project/blob/main/llvm/examples/OptSubcommand/llvm-hello-sub.cpp#L78-L79
 

https://github.com/llvm/llvm-project/pull/177868
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to