Skip to content

Commit d46e373

Browse files
authored
[DebugCounter] Add support for non-continous ranges. (#89470)
1 parent 1227171 commit d46e373

20 files changed

+426
-140
lines changed

llvm/docs/ProgrammersManual.rst

+30-8
Original file line numberDiff line numberDiff line change
@@ -1362,16 +1362,14 @@ Whatever code you want that control, use ``DebugCounter::shouldExecute`` to cont
13621362
if (DebugCounter::shouldExecute(DeleteAnInstruction))
13631363
I->eraseFromParent();
13641364

1365-
That's all you have to do. Now, using opt, you can control when this code triggers using
1366-
the '``--debug-counter``' option. There are two counters provided, ``skip`` and ``count``.
1367-
``skip`` is the number of times to skip execution of the codepath. ``count`` is the number
1368-
of times, once we are done skipping, to execute the codepath.
1365+
That's all you have to do. Now, using opt, you can control when this code triggers using
1366+
the '``--debug-counter``' Options.To specify when to execute the codepath.
13691367

13701368
.. code-block:: none
13711369
1372-
$ opt --debug-counter=passname-delete-instruction-skip=1,passname-delete-instruction-count=2 -passname
1370+
$ opt --debug-counter=passname-delete-instruction=2-3 -passname
13731371
1374-
This will skip the above code the first time we hit it, then execute it twice, then skip the rest of the executions.
1372+
This will skip the above code the first two times we hit it, then execute it 2 times, then skip the rest of the executions.
13751373

13761374
So if executed on the following code:
13771375

@@ -1385,8 +1383,32 @@ So if executed on the following code:
13851383
It would delete number ``%2`` and ``%3``.
13861384

13871385
A utility is provided in `utils/bisect-skip-count` to binary search
1388-
skip and count arguments. It can be used to automatically minimize the
1389-
skip and count for a debug-counter variable.
1386+
the begin and end of the range argument. It can be used to automatically minimize the
1387+
range for a debug-counter variable.
1388+
1389+
A more general utility is provided in `llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp` to minimize debug counter chunks lists.
1390+
1391+
How to use reduce-chunk-list:
1392+
First, Figure out the number of calls to the debug counter you want to minimize.
1393+
To do so, run the compilation command causing you want to minimize with `-print-debug-counter` adding a `-mllvm` if needed.
1394+
Than find the line with the counter of interest. it should look like:
1395+
.. code-block:: none
1396+
1397+
my-counter : {5678,empty}
1398+
1399+
The number of calls to `my-counter` is 5678
1400+
1401+
Than Find the minimum set of chunks that is interesting, with `reduce-chunk-list`.
1402+
Build a reproducer script like:
1403+
.. code-block:: bash
1404+
1405+
#! /bin/bash
1406+
opt -debug-counter=my-counter=$1
1407+
# ... Test result of the command. Failure of the script is considered interesting
1408+
1409+
Than run `reduce-chunk-list my-script.sh 0-5678 2>&1 | tee dump_bisect`
1410+
This command may take some time.
1411+
but when it is done, it will print the result like: `Minimal Chunks = 0:1:5:11-12:33-34`
13901412

13911413
.. _ViewGraph:
13921414

llvm/include/llvm/Support/DebugCounter.h

+48-40
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
/// debug. That is where debug counting steps in. You can instrument the pass
1818
/// with a debug counter before it does a certain thing, and depending on the
1919
/// counts, it will either execute that thing or not. The debug counter itself
20-
/// consists of a skip and a count. Skip is the number of times shouldExecute
21-
/// needs to be called before it returns true. Count is the number of times to
22-
/// return true once Skip is 0. So a skip=47, count=2 ,would skip the first 47
23-
/// executions by returning false from shouldExecute, then execute twice, and
24-
/// then return false again.
25-
/// Note that a counter set to a negative number will always execute.
26-
/// For a concrete example, during predicateinfo creation, the renaming pass
27-
/// replaces each use with a renamed use.
20+
/// consists of a list of chunks (inclusive numeric ranges). `shouldExecute`
21+
/// returns true iff the list is empty or the current count is in one of the
22+
/// chunks.
23+
///
24+
/// Note that a counter set to a negative number will always execute. For a
25+
/// concrete example, during predicateinfo creation, the renaming pass replaces
26+
/// each use with a renamed use.
2827
////
2928
/// If I use DEBUG_COUNTER to create a counter called "predicateinfo", and
3029
/// variable name RenameCounter, and then instrument this renaming with a debug
@@ -34,15 +33,16 @@
3433
/// <continue or return or whatever not executing looks like>
3534
///
3635
/// Now I can, from the command line, make it rename or not rename certain uses
37-
/// by setting the skip and count.
36+
/// by setting the chunk list.
3837
/// So for example
39-
/// bin/opt -debug-counter=predicateinfo-skip=47,predicateinfo-count=1
38+
/// bin/opt -debug-counter=predicateinfo=47
4039
/// will skip renaming the first 47 uses, then rename one, then skip the rest.
4140
//===----------------------------------------------------------------------===//
4241

4342
#ifndef LLVM_SUPPORT_DEBUGCOUNTER_H
4443
#define LLVM_SUPPORT_DEBUGCOUNTER_H
4544

45+
#include "llvm/ADT/ArrayRef.h"
4646
#include "llvm/ADT/DenseMap.h"
4747
#include "llvm/ADT/StringRef.h"
4848
#include "llvm/ADT/UniqueVector.h"
@@ -55,6 +55,19 @@ class raw_ostream;
5555

5656
class DebugCounter {
5757
public:
58+
struct Chunk {
59+
int64_t Begin;
60+
int64_t End;
61+
void print(llvm::raw_ostream &OS);
62+
bool contains(int64_t Idx) { return Idx >= Begin && Idx <= End; }
63+
};
64+
65+
static void printChunks(raw_ostream &OS, ArrayRef<Chunk>);
66+
67+
/// Return true on parsing error and print the error message on the
68+
/// llvm::errs()
69+
static bool parseChunks(StringRef Str, SmallVector<Chunk> &Res);
70+
5871
/// Returns a reference to the singleton instance.
5972
static DebugCounter &instance();
6073

@@ -69,29 +82,12 @@ class DebugCounter {
6982
static unsigned registerCounter(StringRef Name, StringRef Desc) {
7083
return instance().addCounter(std::string(Name), std::string(Desc));
7184
}
85+
static bool shouldExecuteImpl(unsigned CounterName);
86+
7287
inline static bool shouldExecute(unsigned CounterName) {
7388
if (!isCountingEnabled())
7489
return true;
75-
76-
auto &Us = instance();
77-
auto Result = Us.Counters.find(CounterName);
78-
if (Result != Us.Counters.end()) {
79-
auto &CounterInfo = Result->second;
80-
++CounterInfo.Count;
81-
82-
// We only execute while the Skip is not smaller than Count,
83-
// and the StopAfter + Skip is larger than Count.
84-
// Negative counters always execute.
85-
if (CounterInfo.Skip < 0)
86-
return true;
87-
if (CounterInfo.Skip >= CounterInfo.Count)
88-
return false;
89-
if (CounterInfo.StopAfter < 0)
90-
return true;
91-
return CounterInfo.StopAfter + CounterInfo.Skip >= CounterInfo.Count;
92-
}
93-
// Didn't find the counter, should we warn?
94-
return true;
90+
return shouldExecuteImpl(CounterName);
9591
}
9692

9793
// Return true if a given counter had values set (either programatically or on
@@ -101,18 +97,25 @@ class DebugCounter {
10197
return instance().Counters[ID].IsSet;
10298
}
10399

104-
// Return the Count for a counter. This only works for set counters.
105-
static int64_t getCounterValue(unsigned ID) {
100+
struct CounterState {
101+
int64_t Count;
102+
uint64_t ChunkIdx;
103+
};
104+
105+
// Return the state of a counter. This only works for set counters.
106+
static CounterState getCounterState(unsigned ID) {
106107
auto &Us = instance();
107108
auto Result = Us.Counters.find(ID);
108109
assert(Result != Us.Counters.end() && "Asking about a non-set counter");
109-
return Result->second.Count;
110+
return {Result->second.Count, Result->second.CurrChunkIdx};
110111
}
111112

112-
// Set a registered counter to a given Count value.
113-
static void setCounterValue(unsigned ID, int64_t Count) {
113+
// Set a registered counter to a given state.
114+
static void setCounterState(unsigned ID, CounterState State) {
114115
auto &Us = instance();
115-
Us.Counters[ID].Count = Count;
116+
auto &Counter = Us.Counters[ID];
117+
Counter.Count = State.Count;
118+
Counter.CurrChunkIdx = State.ChunkIdx;
116119
}
117120

118121
// Dump or print the current counter set into llvm::dbgs().
@@ -152,11 +155,11 @@ class DebugCounter {
152155
#ifdef NDEBUG
153156
return false;
154157
#else
155-
return instance().Enabled;
158+
return instance().Enabled || instance().ShouldPrintCounter;
156159
#endif
157160
}
158161

159-
private:
162+
protected:
160163
unsigned addCounter(const std::string &Name, const std::string &Desc) {
161164
unsigned Result = RegisteredCounters.insert(Name);
162165
Counters[Result] = {};
@@ -166,17 +169,22 @@ class DebugCounter {
166169
// Struct to store counter info.
167170
struct CounterInfo {
168171
int64_t Count = 0;
169-
int64_t Skip = 0;
170-
int64_t StopAfter = -1;
172+
uint64_t CurrChunkIdx = 0;
171173
bool IsSet = false;
172174
std::string Desc;
175+
SmallVector<Chunk> Chunks;
173176
};
177+
174178
DenseMap<unsigned, CounterInfo> Counters;
175179
CounterVector RegisteredCounters;
176180

177181
// Whether we should do DebugCounting at all. DebugCounters aren't
178182
// thread-safe, so this should always be false in multithreaded scenarios.
179183
bool Enabled = false;
184+
185+
bool ShouldPrintCounter = false;
186+
187+
bool BreakOnLast = false;
180188
};
181189

182190
#define DEBUG_COUNTER(VARNAME, COUNTERNAME, DESC) \

0 commit comments

Comments
 (0)