1#include "llvm/Support/DebugCounter.h"
2
3#include "DebugOptions.h"
4
5#include "llvm/Support/CommandLine.h"
6#include "llvm/Support/Format.h"
7#include "llvm/Support/ManagedStatic.h"
8
9using namespace llvm;
10
11namespace llvm {
12
13void DebugCounter::printChunks(raw_ostream &OS,
14 ArrayRef<IntegerInclusiveInterval> Chunks) {
15 IntegerInclusiveIntervalUtils::printIntervals(OS, Intervals: Chunks, Separator: ':');
16}
17
18} // namespace llvm
19
20namespace {
21// This class overrides the default list implementation of printing so we
22// can pretty print the list of debug counter options. This type of
23// dynamic option is pretty rare (basically this and pass lists).
24class DebugCounterList : public cl::list<std::string, DebugCounter> {
25private:
26 using Base = cl::list<std::string, DebugCounter>;
27
28public:
29 template <class... Mods>
30 explicit DebugCounterList(Mods &&... Ms) : Base(std::forward<Mods>(Ms)...) {}
31
32private:
33 void printOptionInfo(size_t GlobalWidth) const override {
34 // This is a variant of from generic_parser_base::printOptionInfo. Sadly,
35 // it's not easy to make it more usable. We could get it to print these as
36 // options if we were a cl::opt and registered them, but lists don't have
37 // options, nor does the parser for std::string. The other mechanisms for
38 // options are global and would pollute the global namespace with our
39 // counters. Rather than go that route, we have just overridden the
40 // printing, which only a few things call anyway.
41 outs() << " -" << ArgStr;
42 // All of the other options in CommandLine.cpp use ArgStr.size() + 6 for
43 // width, so we do the same.
44 Option::printHelpStr(HelpStr, Indent: GlobalWidth, FirstLineIndentedBy: ArgStr.size() + 6);
45 const auto &CounterInstance = DebugCounter::instance();
46 for (const auto &Entry : CounterInstance) {
47 const auto &[Name, Desc] = CounterInstance.getCounterDesc(Info: Entry.second);
48 size_t NumSpaces = GlobalWidth - Name.size() - 8;
49 outs() << " =" << Name;
50 outs().indent(NumSpaces) << " - " << Desc << '\n';
51 }
52 }
53};
54
55// All global objects associated to the DebugCounter, including the DebugCounter
56// itself, are owned by a single global instance of the DebugCounterOwner
57// struct. This makes it easier to control the order in which constructors and
58// destructors are run.
59struct DebugCounterOwner : DebugCounter {
60 DebugCounterList DebugCounterOption{
61 "debug-counter", cl::Hidden,
62 cl::desc("Comma separated list of debug counter skip and count"),
63 cl::CommaSeparated, cl::location<DebugCounter>(L&: *this)};
64 cl::opt<bool, true> PrintDebugCounter{
65 "print-debug-counter",
66 cl::Hidden,
67 cl::location(L&: this->ShouldPrintCounter),
68 cl::init(Val: false),
69 cl::desc("Print out debug counter info after all counters accumulated"),
70 cl::callback(CB: [&](const bool &Value) {
71 if (Value)
72 activateAllCounters();
73 })};
74 cl::opt<bool, true> PrintDebugCounterQueries{
75 "print-debug-counter-queries",
76 cl::Hidden,
77 cl::location(L&: this->ShouldPrintCounterQueries),
78 cl::init(Val: false),
79 cl::desc("Print out each query of an enabled debug counter")};
80 cl::opt<bool, true> BreakOnLastCount{
81 "debug-counter-break-on-last",
82 cl::Hidden,
83 cl::location(L&: this->BreakOnLast),
84 cl::init(Val: false),
85 cl::desc("Insert a break point on the last enabled count of a "
86 "chunks list")};
87
88 DebugCounterOwner() {
89 // Our destructor uses the debug stream. By referencing it here, we
90 // ensure that its destructor runs after our destructor.
91 (void)dbgs();
92 }
93
94 // Print information when destroyed, iff command line option is specified.
95 ~DebugCounterOwner() {
96 if (ShouldPrintCounter)
97 print(OS&: dbgs());
98 }
99};
100
101} // anonymous namespace
102
103// Use ManagedStatic instead of function-local static variable to ensure
104// the destructor (which accesses counters and streams) runs during
105// llvm_shutdown() rather than at some unspecified point.
106static ManagedStatic<DebugCounterOwner> Owner;
107
108void llvm::initDebugCounterOptions() { (void)DebugCounter::instance(); }
109
110DebugCounter &DebugCounter::instance() { return *Owner; }
111
112// This is called by the command line parser when it sees a value for the
113// debug-counter option defined above.
114void DebugCounter::push_back(const std::string &Val) {
115 if (Val.empty())
116 return;
117
118 // The strings should come in as counter=chunk_list
119 auto CounterPair = StringRef(Val).split(Separator: '=');
120 if (CounterPair.second.empty()) {
121 errs() << "DebugCounter Error: " << Val << " does not have an = in it\n";
122 exit(status: 1);
123 }
124 StringRef CounterName = CounterPair.first;
125
126 CounterInfo *Counter = getCounterInfo(Name: CounterName);
127 if (!Counter) {
128 errs() << "DebugCounter Error: " << CounterName
129 << " is not a registered counter\n";
130 return;
131 }
132
133 auto ExpectedChunks =
134 IntegerInclusiveIntervalUtils::parseIntervals(IntervalStr: CounterPair.second, Separator: ':');
135 if (!ExpectedChunks) {
136 handleAllErrors(E: ExpectedChunks.takeError(), Handlers: [&](const StringError &E) {
137 errs() << "DebugCounter Error: " << E.getMessage() << "\n";
138 });
139 exit(status: 1);
140 }
141 Counter->Chunks = std::move(*ExpectedChunks);
142 Counter->Active = Counter->IsSet = true;
143}
144
145void DebugCounter::print(raw_ostream &OS) const {
146 SmallVector<StringRef, 16> CounterNames(Counters.keys());
147 sort(C&: CounterNames);
148
149 OS << "Counters and values:\n";
150 for (StringRef CounterName : CounterNames) {
151 const CounterInfo *C = getCounterInfo(Name: CounterName);
152 OS << left_justify(Str: C->Name, Width: 32) << ": {" << C->Count << ",";
153 printChunks(OS, Chunks: C->Chunks);
154 OS << "}\n";
155 }
156}
157
158bool DebugCounter::handleCounterIncrement(CounterInfo &Info) {
159 int64_t CurrCount = Info.Count++;
160 uint64_t CurrIdx = Info.CurrChunkIdx;
161
162 if (Info.Chunks.empty())
163 return true;
164 if (CurrIdx >= Info.Chunks.size())
165 return false;
166
167 bool Res = Info.Chunks[CurrIdx].contains(Value: CurrCount);
168 if (BreakOnLast && CurrIdx == (Info.Chunks.size() - 1) &&
169 CurrCount == Info.Chunks[CurrIdx].getEnd()) {
170 LLVM_BUILTIN_DEBUGTRAP;
171 }
172 if (CurrCount > Info.Chunks[CurrIdx].getEnd()) {
173 Info.CurrChunkIdx++;
174
175 /// Handle consecutive blocks.
176 if (Info.CurrChunkIdx < Info.Chunks.size() &&
177 CurrCount == Info.Chunks[Info.CurrChunkIdx].getBegin())
178 return true;
179 }
180 return Res;
181}
182
183bool DebugCounter::shouldExecuteImpl(CounterInfo &Counter) {
184 auto &Us = instance();
185 bool Res = Us.handleCounterIncrement(Info&: Counter);
186 if (Us.ShouldPrintCounterQueries && Counter.IsSet) {
187 dbgs() << "DebugCounter " << Counter.Name << "=" << (Counter.Count - 1)
188 << (Res ? " execute" : " skip") << "\n";
189 }
190 return Res;
191}
192
193#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
194LLVM_DUMP_METHOD void DebugCounter::dump() const {
195 print(dbgs());
196}
197#endif
198