================
@@ -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")) {
----------------
dzbarsky wrote:

even as top-level options I was seeing these not resolving if no command was 
selected, so needed this workaround. Maybe I missed something?

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