Skip to content

Commit dc6a84f

Browse files
committed
[mlir] Add support for DebugCounters using the new DebugAction infrastructure
DebugCounters allow for selectively enabling the execution of a debug action based upon a "counter". This counter is comprised of two components that are used in the control of execution of an action, a "skip" value and a "count" value. The "skip" value is used to skip a certain number of initial executions of a debug action. The "count" value is used to prevent a debug action from executing after it has executed for a set number of times (not including any executions that have been skipped). For example, a counter for a debug action with `skip=47` and `count=2`, would skip the first 47 executions, then execute twice, and finally prevent any further executions. This is effectively the same as the DebugCounter infrastructure in LLVM, but using the DebugAction infrastructure in MLIR. We can't simply reuse the DebugCounter support already present in LLVM due to its heavy reliance on global constructors (which are not allowed in MLIR). The DebugAction infrastructure already nicely supports the debug counter use case, and promotes the separation of policy and mechanism design philosophy. Differential Revision: https://reviews.llvm.org/D96395
1 parent 72d5afa commit dc6a84f

File tree

7 files changed

+354
-2
lines changed

7 files changed

+354
-2
lines changed

mlir/docs/DebugActions.md

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ The exact definition of an `external entity` is left opaque, to allow for more
2323
interesting handlers. The set of possible action queries is detailed in the
2424
[`action manager`](#debug-action-manager) section below.
2525

26-
(TODO: Add connection to existing handlers when they are added)
27-
2826
## Debug Action
2927

3028
A `debug action` is essentially a marker for a type of action that may be
@@ -169,3 +167,73 @@ struct MyPatternHandler : public DebugActionManager::GenericHandler {
169167
StringRef actionDesc);
170168
};
171169
```
170+
171+
### Common Action Handlers
172+
173+
MLIR provides several common debug action handlers for immediate use that have
174+
proven useful in general.
175+
176+
#### DebugCounter
177+
178+
When debugging a compiler issue,
179+
["bisection"](https://en.wikipedia.org/wiki/Bisection_\(software_engineering\))
180+
is a useful technique for locating the root cause of the issue. `Debug Counters`
181+
enable using this technique for debug actions by attaching a counter value to a
182+
specific debug action and enabling/disabling execution of this action based on
183+
the value of the counter. The counter controls the execution of the action with
184+
a "skip" and "count" value. The "skip" value is used to skip a certain number of
185+
initial executions of a debug action. The "count" value is used to prevent a
186+
debug action from executing after it has executed for a set number of times (not
187+
including any executions that have been skipped). If the "skip" value is
188+
negative, the action will always execute. If the "count" value is negative, the
189+
action will always execute after the "skip" value has been reached. For example,
190+
a counter for a debug action with `skip=47` and `count=2`, would skip the first
191+
47 executions, then execute twice, and finally prevent any further executions.
192+
With a bit of tooling, the values to use for the counter can be automatically
193+
selected; allowing for finding the exact execution of a debug action that
194+
potentially causes the bug being investigated.
195+
196+
Note: The DebugCounter action handler does not support multi-threaded execution,
197+
and should only be used in MLIRContexts where multi-threading is disabled (e.g.
198+
via `-mlir-disable-threading`).
199+
200+
##### CommandLine Configuration
201+
202+
The `DebugCounter` handler provides several that allow for configuring counters.
203+
The main option is `mlir-debug-counter`, which accepts a comma separated list of
204+
`<count-name>=<counter-value>`. A `<counter-name>` is the debug action tag to
205+
attach the counter, suffixed with either `-skip` or `-count`. A `-skip` suffix
206+
will set the "skip" value of the counter. A `-count` suffix will set the "count"
207+
value of the counter. The `<counter-value>` component is a numeric value to use
208+
for the counter. An example is shown below using `ApplyPatternAction` defined
209+
above:
210+
211+
```shell
212+
$ mlir-opt foo.mlir -mlir-debug-counter=apply-pattern-skip=47,apply-pattern-count=2
213+
```
214+
215+
The above configuration would skip the first 47 executions of
216+
`ApplyPatternAction`, then execute twice, and finally prevent any further
217+
executions.
218+
219+
Note: Each counter currently only has one `skip` and one `count` value, meaning
220+
that sequences of `skip`/`count` will not be chained.
221+
222+
The `mlir-print-debug-counter` option may be used to print out debug counter
223+
information after all counters have been accumulated. The information is printed
224+
in the following format:
225+
226+
```shell
227+
DebugCounter counters:
228+
<action-tag> : {<current-count>,<skip>,<count>}
229+
```
230+
231+
For example, using the options above we can see how many times an action is
232+
executed:
233+
234+
```shell
235+
$ mlir-opt foo.mlir -mlir-debug-counter=apply-pattern-skip=-1 -mlir-print-debug-counter
236+
237+
DebugCounter counters:
238+
apply-pattern : {370,-1,-1}
239+
```
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===- DebugCounter.h - Debug Counter support -------------------*- C++ -*-===//
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+
#ifndef MLIR_SUPPORT_DEBUGCOUNTER_H
10+
#define MLIR_SUPPORT_DEBUGCOUNTER_H
11+
12+
#include "mlir/Support/DebugAction.h"
13+
#include "llvm/ADT/StringMap.h"
14+
#include <string>
15+
16+
namespace mlir {
17+
18+
/// This class implements a debug action handler that attaches a counter value
19+
/// to debug actions and enables/disables execution of these action based on the
20+
/// value of the counter. The counter controls the execution of the action with
21+
/// a "skip" and "count" value. The "skip" value is used to skip a certain
22+
/// number of initial executions of a debug action. The "count" value is used to
23+
/// prevent a debug action from executing after it has executed for a set number
24+
/// of times (not including any executions that have been skipped). For example,
25+
/// a counter for a debug action with `skip=47` and `count=2`, would skip the
26+
/// first 47 executions, then execute twice, and finally prevent any further
27+
/// executions.
28+
class DebugCounter : public DebugActionManager::GenericHandler {
29+
public:
30+
DebugCounter();
31+
~DebugCounter() override;
32+
33+
/// Add a counter for the given debug action tag. `countToSkip` is the number
34+
/// of counter executions to skip before enabling execution of the action.
35+
/// `countToStopAfter` is the number of executions of the counter to allow
36+
/// before preventing the action from executing any more.
37+
void addCounter(StringRef actionTag, int64_t countToSkip,
38+
int64_t countToStopAfter);
39+
40+
/// Register a counter with the specified name.
41+
FailureOr<bool> shouldExecute(StringRef tag, StringRef description) final;
42+
43+
/// Print the counters that have been registered with this instance to the
44+
/// provided output stream.
45+
void print(raw_ostream &os) const;
46+
47+
/// Register the command line options for debug counters.
48+
static void registerCLOptions();
49+
50+
private:
51+
/// Apply the registered CL options to this debug counter instance.
52+
void applyCLOptions();
53+
54+
/// This struct represents a specific counter being tracked.
55+
struct Counter {
56+
Counter(int64_t countToSkip = 0, int64_t countToStopAfter = -1)
57+
: count(0), countToSkip(countToSkip),
58+
countToStopAfter(countToStopAfter) {}
59+
60+
/// The current count of this counter.
61+
int64_t count;
62+
/// The number of initial executions of this counter to skip.
63+
int64_t countToSkip;
64+
/// The number of times to execute this counter before stopping.
65+
int64_t countToStopAfter;
66+
};
67+
68+
/// A mapping between a given action tag and its counter information.
69+
llvm::StringMap<Counter> counters;
70+
};
71+
72+
} // namespace mlir
73+
74+
#endif

mlir/lib/Support/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
set(LLVM_OPTIONAL_SOURCES
2+
DebugCounter.cpp
23
FileUtilities.cpp
34
IndentedOstream.cpp
45
MlirOptMain.cpp
@@ -7,6 +8,7 @@ set(LLVM_OPTIONAL_SOURCES
78
)
89

910
add_mlir_library(MLIRSupport
11+
DebugCounter.cpp
1012
FileUtilities.cpp
1113
StorageUniquer.cpp
1214
ToolUtilities.cpp

mlir/lib/Support/DebugCounter.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
}

mlir/lib/Support/MlirOptMain.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "mlir/Parser.h"
2323
#include "mlir/Pass/Pass.h"
2424
#include "mlir/Pass/PassManager.h"
25+
#include "mlir/Support/DebugCounter.h"
2526
#include "mlir/Support/FileUtilities.h"
2627
#include "mlir/Support/ToolUtilities.h"
2728
#include "llvm/Support/CommandLine.h"
@@ -100,6 +101,7 @@ static LogicalResult processBuffer(raw_ostream &os,
100101
context.loadAllAvailableDialects();
101102
context.allowUnregisteredDialects(allowUnregisteredDialects);
102103
context.printOpOnDiagnostic(!verifyDiagnostics);
104+
context.getDebugActionManager().registerActionHandler<DebugCounter>();
103105

104106
// If we are in verify diagnostics mode then we have a lot of work to do,
105107
// otherwise just perform the actions without worrying about it.
@@ -193,6 +195,7 @@ LogicalResult mlir::MlirOptMain(int argc, char **argv, llvm::StringRef toolName,
193195
registerAsmPrinterCLOptions();
194196
registerMLIRContextCLOptions();
195197
registerPassManagerCLOptions();
198+
DebugCounter::registerCLOptions();
196199
PassPipelineCLParser passPipeline("", "Compiler passes to run");
197200

198201
// Build the list of dialects as a header for the --help message.

mlir/unittests/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_mlir_unittest(MLIRSupportTests
22
DebugActionTest.cpp
3+
DebugCounterTest.cpp
34
IndentedOstreamTest.cpp
45
MathExtrasTest.cpp
56
)

0 commit comments

Comments
 (0)