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