text: //===- DebugCounter.cpp - Debug Counter Facilities ------------------------===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "mlir/Support/DebugCounter.h"
  #include "llvm/Support/CommandLine.h"
  #include "llvm/Support/Debug.h"
  #include "llvm/Support/Format.h"
  #include "llvm/Support/ManagedStatic.h"
  
  using namespace mlir;
  
  //===----------------------------------------------------------------------===//
  // DebugCounter CommandLine Options
  //===----------------------------------------------------------------------===//
  
  namespace {
  /// This struct contains command line options that can be used to initialize
  /// various bits of a DebugCounter. This uses a struct wrapper to avoid the need
  /// for global command line options.
  struct DebugCounterOptions {
    llvm::cl::list<std::string> counters{
        "mlir-debug-counter",
        llvm::cl::desc(
            "Comma separated list of debug counter skip and count arguments"),
        llvm::cl::CommaSeparated, llvm::cl::ZeroOrMore};
  
    llvm::cl::opt<bool> printCounterInfo{
        "mlir-print-debug-counter", llvm::cl::init(false), llvm::cl::Optional,
        llvm::cl::desc("Print out debug counter information after all counters "
                       "have been accumulated")};
  };
  } // namespace
  
  static llvm::ManagedStatic<DebugCounterOptions> clOptions;
  
  //===----------------------------------------------------------------------===//
  // DebugCounter
  //===----------------------------------------------------------------------===//
  
  DebugCounter::DebugCounter() { applyCLOptions(); }
  
  DebugCounter::~DebugCounter() {
    // Print information when destroyed, iff command line option is specified.
    if (clOptions.isConstructed() && clOptions->printCounterInfo)
      print(llvm::dbgs());
  }
  
  /// Add a counter for the given debug action tag. `countToSkip` is the number
  /// of counter executions to skip before enabling execution of the action.
  /// `countToStopAfter` is the number of executions of the counter to allow
  /// before preventing the action from executing any more.
  void DebugCounter::addCounter(StringRef actionTag, int64_t countToSkip,
                                int64_t countToStopAfter) {
    assert(!counters.count(actionTag) &&
           "a counter for the given action was already registered");
    counters.try_emplace(actionTag, countToSkip, countToStopAfter);
  }
  
  // Register a counter with the specified name.
  FailureOr<bool> DebugCounter::shouldExecute(StringRef tag,
                                              StringRef description) {
    auto counterIt = counters.find(tag);
    if (counterIt == counters.end())
      return true;
  
    ++counterIt->second.count;
  
    // We only execute while the `countToSkip` is not smaller than `count`, and
    // `countToStopAfter + countToSkip` is larger than `count`. Negative counters
    // always execute.
    if (counterIt->second.countToSkip < 0)
      return true;
    if (counterIt->second.countToSkip >= counterIt->second.count)
      return false;
    if (counterIt->second.countToStopAfter < 0)
      return true;
    return counterIt->second.countToStopAfter + counterIt->second.countToSkip >=
           counterIt->second.count;
  }
  
  void DebugCounter::print(raw_ostream &os) const {
    // Order the registered counters by name.
    SmallVector<const llvm::StringMapEntry<Counter> *, 16> sortedCounters(
        llvm::make_pointer_range(counters));
    llvm::sort(sortedCounters, [](const llvm::StringMapEntry<Counter> *lhs,
                                  const llvm::StringMapEntry<Counter> *rhs) {
      return lhs->getKey() < rhs->getKey();
    });
  
    os << "DebugCounter counters:\n";
    for (const llvm::StringMapEntry<Counter> *counter : sortedCounters) {
      os << llvm::left_justify(counter->getKey(), 32) << ": {"
         << counter->second.count << "," << counter->second.countToSkip << ","
         << counter->second.countToStopAfter << "}\n";
    }
  }
  
  /// Register a set of useful command-line options that can be used to configure
  /// various flags within the DebugCounter. These flags are used when
  /// constructing a DebugCounter for initialization.
  void DebugCounter::registerCLOptions() {
  #ifndef NDEBUG
    // Make sure that the options struct has been initialized.
    *clOptions;
  #endif
  }
  
  // This is called by the command line parser when it sees a value for the
  // debug-counter option defined above.
  void DebugCounter::applyCLOptions() {
    if (!clOptions.isConstructed())
      return;
  
    for (StringRef arg : clOptions->counters) {
      if (arg.empty())
        continue;
  
      // Debug counter arguments are expected to be in the form: `counter=value`.
      StringRef counterName, counterValueStr;
      std::tie(counterName, counterValueStr) = arg.split('=');
      if (counterValueStr.empty()) {
        llvm::errs() << "error: expected DebugCounter argument to have an `=` "
                        "separating the counter name and value, but the provided "
                        "argument was: `"
                     << arg << "`\n";
        llvm::report_fatal_error(
            "Invalid DebugCounter command-line configuration");
      }
  
      // Extract the counter value.
      int64_t counterValue;
      if (counterValueStr.getAsInteger(0, counterValue)) {
        llvm::errs() << "error: expected DebugCounter counter value to be "
                        "numeric, but got `"
                     << counterValueStr << "`\n";
        llvm::report_fatal_error(
            "Invalid DebugCounter command-line configuration");
      }
  
      // Now we need to see if this is the skip or the count, remove the suffix,
      // and add it to the counter values.
      if (counterName.consume_back("-skip")) {
        counters[counterName].countToSkip = counterValue;
  
      } else if (counterName.consume_back("-count")) {
        counters[counterName].countToStopAfter = counterValue;
  
      } else {
        llvm::errs() << "error: expected DebugCounter counter name to end with "
                        "either `-skip` or `-count`, but got`"
                     << counterName << "`\n";
        llvm::report_fatal_error(
            "Invalid DebugCounter command-line configuration");
      }
    }
  }
  