I've developed some debugging tools and wanted to see if there is
interest in it.
DebugCounters are great for hunting down problems. Recently I came
across the need to debug an issue involving aliasing. I had to do
something like this:
DEBUG_COUNTER(AliasCheck, ...);
auto AliasResult = queryAliasing(...);
if (!mayAlias(AliasResult)) {
if (!DebugCounter::shouldExecute(AliasCheck)) {
AliasResult = makeAlias(AliasResult);
}
}
useAliasResultAndDoSomething(AliasResult);
The idea here is that if the counter is NOT within the [skip, count]
range, then everything should always be considered to alias. Otherwise
within the range the unmodified alias result should be used. Note that
the counter is actually counting instances of "no alias" results.
I needed to see what queryAliasing was doing on the "bad"
transformation. Unfortunately, DebugCounter has no way to test the
state of the counter without incrementing it, so I couldn't set a
conditional breakpoint on the call to queryAliasing. And conditional
breakpoints are really slow anyway.
Not wanting to modify DebugCounter directly, I worked on tools for
triggering breakpoints and made a new kind of DebugCounter that can be
associated with a trap:
TrappingDebugCounter AliasCheck(...)
AliasCheck.trapIf(AliasCheck.EachExecution());
auto AliasResult = queryAliasing(...);
if (!mayAlias(AliasResult)) {
if (!DebugCounter::shouldExecute(AliasCheck)) {
AliasResult = makeAlias(AliasResult);
}
}
useAliasResultAndDoSomething(AliasResult);
trapIf takes a predicate and raises a trap if the predicate evaluates to
true. It is built on lower-level utilities so that developers can add
traps without needing to use TrappingDebugCounter. There's a standalone
trapIf, for example.
An alternative is to cache the result of shouldExecute:
DEBUG_COUNTER(AliasCheck, ...);
bool shouldExecute = DebugCounter::shouldExecute(AliasCheck);
if (shouldExecute) {
volatile int DoNotDelete;
DoNotDelete = 1;
}
auto AliasResult = queryAliasing(...);
if (!mayAlias(AliasResult)) {
if (!shouldExecute) {
AliasResult = makeAlias(AliasResult);
}
}
useAliasResultAndDoSomething(AliasResult);
Then one can set a breakpoint on the guarded store to DoNotDelete. This
is a bit ugly though and doesn't convey the higher-level intent the way
TrappingDebugCounter does. That seems important to me, because just
like DebugCounters, I anticipate TrappingDebugCounters being permanent
entities in the source base, activated when needed for debugging.
volatile variables don't seem like a good fit for that kind of use.
The above also doesn't provide some other nice features of
TrappingDebugCounter such as control of trapping behavior through the
command line.
I originally developed this on an older LLVM version and DebugCounter
changed in the interim to hide the skip and count values from clients.
This made TrappingDebugCounter messier and prevented me from porting
over some nice features (like trap-on-last-execution). If there's
interest I might go ahead and make those values available to clients
again.
I would also need some help porting this to non-POSIX systems. I know
how to raise a trap on X86-64 using inline asm but I don't know how to
do that for all of our other supported host platforms. It also turns
out that unit tests for this needs to involve signal handlers and that's
another area where I don't know what to do for non-POSIX systems.
So...any interest in this?
-David