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