[llvm-dev] DebugCounter and traps (original) (raw)

David Greene via llvm-dev llvm-dev at lists.llvm.org
Mon Sep 24 11:17:04 PDT 2018


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


More information about the llvm-dev mailing list