1dc6a84fcSRiver Riddle //===- DebugCounter.cpp - Debug Counter Facilities ------------------------===//
2dc6a84fcSRiver Riddle //
3dc6a84fcSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4dc6a84fcSRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
5dc6a84fcSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dc6a84fcSRiver Riddle //
7dc6a84fcSRiver Riddle //===----------------------------------------------------------------------===//
8dc6a84fcSRiver Riddle 
9dc6a84fcSRiver Riddle #include "mlir/Support/DebugCounter.h"
10dc6a84fcSRiver Riddle #include "llvm/Support/CommandLine.h"
1141711c0cSNico Weber #include "llvm/Support/Debug.h"
12dc6a84fcSRiver Riddle #include "llvm/Support/Format.h"
13dc6a84fcSRiver Riddle #include "llvm/Support/ManagedStatic.h"
14dc6a84fcSRiver Riddle 
15dc6a84fcSRiver Riddle using namespace mlir;
16dc6a84fcSRiver Riddle 
17dc6a84fcSRiver Riddle //===----------------------------------------------------------------------===//
18dc6a84fcSRiver Riddle // DebugCounter CommandLine Options
19dc6a84fcSRiver Riddle //===----------------------------------------------------------------------===//
20dc6a84fcSRiver Riddle 
21dc6a84fcSRiver Riddle namespace {
22dc6a84fcSRiver Riddle /// This struct contains command line options that can be used to initialize
23dc6a84fcSRiver Riddle /// various bits of a DebugCounter. This uses a struct wrapper to avoid the need
24dc6a84fcSRiver Riddle /// for global command line options.
25dc6a84fcSRiver Riddle struct DebugCounterOptions {
26dc6a84fcSRiver Riddle   llvm::cl::list<std::string> counters{
27dc6a84fcSRiver Riddle       "mlir-debug-counter",
28dc6a84fcSRiver Riddle       llvm::cl::desc(
29dc6a84fcSRiver Riddle           "Comma separated list of debug counter skip and count arguments"),
30*62a4e6abSFangrui Song       llvm::cl::CommaSeparated};
31dc6a84fcSRiver Riddle 
32dc6a84fcSRiver Riddle   llvm::cl::opt<bool> printCounterInfo{
33dc6a84fcSRiver Riddle       "mlir-print-debug-counter", llvm::cl::init(false), llvm::cl::Optional,
34dc6a84fcSRiver Riddle       llvm::cl::desc("Print out debug counter information after all counters "
35dc6a84fcSRiver Riddle                      "have been accumulated")};
36dc6a84fcSRiver Riddle };
37be0a7e9fSMehdi Amini } // namespace
38dc6a84fcSRiver Riddle 
39dc6a84fcSRiver Riddle static llvm::ManagedStatic<DebugCounterOptions> clOptions;
40dc6a84fcSRiver Riddle 
41dc6a84fcSRiver Riddle //===----------------------------------------------------------------------===//
42dc6a84fcSRiver Riddle // DebugCounter
43dc6a84fcSRiver Riddle //===----------------------------------------------------------------------===//
44dc6a84fcSRiver Riddle 
DebugCounter()45dc6a84fcSRiver Riddle DebugCounter::DebugCounter() { applyCLOptions(); }
46dc6a84fcSRiver Riddle 
~DebugCounter()47dc6a84fcSRiver Riddle DebugCounter::~DebugCounter() {
48dc6a84fcSRiver Riddle   // Print information when destroyed, iff command line option is specified.
49dc6a84fcSRiver Riddle   if (clOptions.isConstructed() && clOptions->printCounterInfo)
50dc6a84fcSRiver Riddle     print(llvm::dbgs());
51dc6a84fcSRiver Riddle }
52dc6a84fcSRiver Riddle 
53dc6a84fcSRiver Riddle /// Add a counter for the given debug action tag. `countToSkip` is the number
54dc6a84fcSRiver Riddle /// of counter executions to skip before enabling execution of the action.
55dc6a84fcSRiver Riddle /// `countToStopAfter` is the number of executions of the counter to allow
56dc6a84fcSRiver Riddle /// before preventing the action from executing any more.
addCounter(StringRef actionTag,int64_t countToSkip,int64_t countToStopAfter)57dc6a84fcSRiver Riddle void DebugCounter::addCounter(StringRef actionTag, int64_t countToSkip,
58dc6a84fcSRiver Riddle                               int64_t countToStopAfter) {
59dc6a84fcSRiver Riddle   assert(!counters.count(actionTag) &&
60dc6a84fcSRiver Riddle          "a counter for the given action was already registered");
61dc6a84fcSRiver Riddle   counters.try_emplace(actionTag, countToSkip, countToStopAfter);
62dc6a84fcSRiver Riddle }
63dc6a84fcSRiver Riddle 
64dc6a84fcSRiver Riddle // Register a counter with the specified name.
shouldExecute(StringRef tag,StringRef description)65dc6a84fcSRiver Riddle FailureOr<bool> DebugCounter::shouldExecute(StringRef tag,
66dc6a84fcSRiver Riddle                                             StringRef description) {
67dc6a84fcSRiver Riddle   auto counterIt = counters.find(tag);
68dc6a84fcSRiver Riddle   if (counterIt == counters.end())
69dc6a84fcSRiver Riddle     return true;
70dc6a84fcSRiver Riddle 
71dc6a84fcSRiver Riddle   ++counterIt->second.count;
72dc6a84fcSRiver Riddle 
73dc6a84fcSRiver Riddle   // We only execute while the `countToSkip` is not smaller than `count`, and
74dc6a84fcSRiver Riddle   // `countToStopAfter + countToSkip` is larger than `count`. Negative counters
75dc6a84fcSRiver Riddle   // always execute.
76dc6a84fcSRiver Riddle   if (counterIt->second.countToSkip < 0)
77dc6a84fcSRiver Riddle     return true;
78dc6a84fcSRiver Riddle   if (counterIt->second.countToSkip >= counterIt->second.count)
79dc6a84fcSRiver Riddle     return false;
80dc6a84fcSRiver Riddle   if (counterIt->second.countToStopAfter < 0)
81dc6a84fcSRiver Riddle     return true;
82dc6a84fcSRiver Riddle   return counterIt->second.countToStopAfter + counterIt->second.countToSkip >=
83dc6a84fcSRiver Riddle          counterIt->second.count;
84dc6a84fcSRiver Riddle }
85dc6a84fcSRiver Riddle 
print(raw_ostream & os) const86dc6a84fcSRiver Riddle void DebugCounter::print(raw_ostream &os) const {
87dc6a84fcSRiver Riddle   // Order the registered counters by name.
88dc6a84fcSRiver Riddle   SmallVector<const llvm::StringMapEntry<Counter> *, 16> sortedCounters(
89dc6a84fcSRiver Riddle       llvm::make_pointer_range(counters));
90ba8e336aSBenjamin Kramer   llvm::array_pod_sort(sortedCounters.begin(), sortedCounters.end(),
91ba8e336aSBenjamin Kramer                        [](const decltype(sortedCounters)::value_type *lhs,
92ba8e336aSBenjamin Kramer                           const decltype(sortedCounters)::value_type *rhs) {
93ba8e336aSBenjamin Kramer                          return (*lhs)->getKey().compare((*rhs)->getKey());
94dc6a84fcSRiver Riddle                        });
95dc6a84fcSRiver Riddle 
96dc6a84fcSRiver Riddle   os << "DebugCounter counters:\n";
97dc6a84fcSRiver Riddle   for (const llvm::StringMapEntry<Counter> *counter : sortedCounters) {
98dc6a84fcSRiver Riddle     os << llvm::left_justify(counter->getKey(), 32) << ": {"
99dc6a84fcSRiver Riddle        << counter->second.count << "," << counter->second.countToSkip << ","
100dc6a84fcSRiver Riddle        << counter->second.countToStopAfter << "}\n";
101dc6a84fcSRiver Riddle   }
102dc6a84fcSRiver Riddle }
103dc6a84fcSRiver Riddle 
104dc6a84fcSRiver Riddle /// Register a set of useful command-line options that can be used to configure
105dc6a84fcSRiver Riddle /// various flags within the DebugCounter. These flags are used when
106dc6a84fcSRiver Riddle /// constructing a DebugCounter for initialization.
registerCLOptions()107dc6a84fcSRiver Riddle void DebugCounter::registerCLOptions() {
108dc6a84fcSRiver Riddle #ifndef NDEBUG
109dc6a84fcSRiver Riddle   // Make sure that the options struct has been initialized.
110dc6a84fcSRiver Riddle   *clOptions;
111dc6a84fcSRiver Riddle #endif
112dc6a84fcSRiver Riddle }
113dc6a84fcSRiver Riddle 
114dc6a84fcSRiver Riddle // This is called by the command line parser when it sees a value for the
115dc6a84fcSRiver Riddle // debug-counter option defined above.
applyCLOptions()116dc6a84fcSRiver Riddle void DebugCounter::applyCLOptions() {
117dc6a84fcSRiver Riddle   if (!clOptions.isConstructed())
118dc6a84fcSRiver Riddle     return;
119dc6a84fcSRiver Riddle 
120dc6a84fcSRiver Riddle   for (StringRef arg : clOptions->counters) {
121dc6a84fcSRiver Riddle     if (arg.empty())
122dc6a84fcSRiver Riddle       continue;
123dc6a84fcSRiver Riddle 
124dc6a84fcSRiver Riddle     // Debug counter arguments are expected to be in the form: `counter=value`.
125dc6a84fcSRiver Riddle     StringRef counterName, counterValueStr;
126dc6a84fcSRiver Riddle     std::tie(counterName, counterValueStr) = arg.split('=');
127dc6a84fcSRiver Riddle     if (counterValueStr.empty()) {
128dc6a84fcSRiver Riddle       llvm::errs() << "error: expected DebugCounter argument to have an `=` "
129dc6a84fcSRiver Riddle                       "separating the counter name and value, but the provided "
130dc6a84fcSRiver Riddle                       "argument was: `"
131dc6a84fcSRiver Riddle                    << arg << "`\n";
132dc6a84fcSRiver Riddle       llvm::report_fatal_error(
133dc6a84fcSRiver Riddle           "Invalid DebugCounter command-line configuration");
134dc6a84fcSRiver Riddle     }
135dc6a84fcSRiver Riddle 
136dc6a84fcSRiver Riddle     // Extract the counter value.
137dc6a84fcSRiver Riddle     int64_t counterValue;
138dc6a84fcSRiver Riddle     if (counterValueStr.getAsInteger(0, counterValue)) {
139dc6a84fcSRiver Riddle       llvm::errs() << "error: expected DebugCounter counter value to be "
140dc6a84fcSRiver Riddle                       "numeric, but got `"
141dc6a84fcSRiver Riddle                    << counterValueStr << "`\n";
142dc6a84fcSRiver Riddle       llvm::report_fatal_error(
143dc6a84fcSRiver Riddle           "Invalid DebugCounter command-line configuration");
144dc6a84fcSRiver Riddle     }
145dc6a84fcSRiver Riddle 
146dc6a84fcSRiver Riddle     // Now we need to see if this is the skip or the count, remove the suffix,
147dc6a84fcSRiver Riddle     // and add it to the counter values.
148dc6a84fcSRiver Riddle     if (counterName.consume_back("-skip")) {
149dc6a84fcSRiver Riddle       counters[counterName].countToSkip = counterValue;
150dc6a84fcSRiver Riddle 
151dc6a84fcSRiver Riddle     } else if (counterName.consume_back("-count")) {
152dc6a84fcSRiver Riddle       counters[counterName].countToStopAfter = counterValue;
153dc6a84fcSRiver Riddle 
154dc6a84fcSRiver Riddle     } else {
155dc6a84fcSRiver Riddle       llvm::errs() << "error: expected DebugCounter counter name to end with "
156dc6a84fcSRiver Riddle                       "either `-skip` or `-count`, but got`"
157dc6a84fcSRiver Riddle                    << counterName << "`\n";
158dc6a84fcSRiver Riddle       llvm::report_fatal_error(
159dc6a84fcSRiver Riddle           "Invalid DebugCounter command-line configuration");
160dc6a84fcSRiver Riddle     }
161dc6a84fcSRiver Riddle   }
162dc6a84fcSRiver Riddle }
163