Skip to content

Files

Latest commit

be4a326 · Mar 7, 2025

History

History
264 lines (200 loc) · 9.82 KB
·

DebugCounters.md

File metadata and controls

264 lines (200 loc) · 9.82 KB
·

Debug Counters

Debug counters are a general infrastructure for counting arbitrary things.

There are two types of debug counters: static and dynamic. Static counters do not add anything to the code generated by the compiler, but it counts events at compile-time while dynamic counters are incremented at runtime and are used to count events in the code generated by the compiler.

1. Using Debug Counters

Debug counters can be inserted either at the tree level or at the instruction level. Note that counters are not actually inserted unless the counter name and type match the provided compiler options (see Specifying Output below).

In Trees

The different overloads of TR::Compilation::prependDebugCounter allow you to inject counter bumps into trees.

TR::DebugCounter::prependDebugCounter(comp(), "inliner.callSites/failed/coldCallee/1", treetop);

In Instructions

The different overloads of TR::CodeGenerator::generateDebugCounter allow you to inject counter bumps into instructions.

cg->generateDebugCounter("RegisterAllocator/Exchange/FPR", 1, TR::DebugCounter::Free);

Fidelity

One (optional) argument when calling TR::Compilation::prependDebugCounter or TR::CodeGenerator::generateDebugCounter is fidelity. Consumers typically use values from the TR::DebugCounter::Fidelities enum. This allows the caller to estimate how expensive the debug counter will be to compute at runtime (e.g. Exorbitant for counters that frequently occur in hot paths, or Moderate for counters in cold paths).

A certain fidelity can be excluded by using -Xjit:debugCounterFidelity=## option, which disables dynamic debug counters with the fidelity rating below the specified number.

2. Static and Dynamic Counters

Whenever you use a counter via either TR::Compilation::prependDebugCounter or TR::CodeGenerator::generateDebugCounter, two counters are actually created: one static, one dynamic.

  • Dynamic counters are implemented by injecting nodes into trees or instructions into the generated code, and they count how often the particular code path is reached at runtime.
  • Static counters do not add anything to the code generated by the JIT, but serve to count events at compilation when the prependDebugCounter or generateDebugCounter method is called.

The delta used for each counter can be specified separately using the available overloads, though they are the same by default.

incStaticDebugCounter creates a static debug counter without a dynamic counterpart. Static debug counters are used quite a bit in the optimizer to count compile events that we do not need or cannot count at runtime. That is to say static-only counters can and do exist, but dynamic counters get static counterparts by default since knowing the ratio of the number of counters inserted vs the number of counter bumps observed at runtime is generally useful for a dynamic counter.

3. Naming Scheme

Counter names modify the display processing logic for counters, which affects how the results are shown to the user. Special characters in a counter name signal the debug counter facility to treat a counter in a special way.

Slash

/

The most important special character is the slash. A slash in a counter name is meant to resemble a slash in a filename: it represents a hierarchical containment relation between counters. Asking for a counter with a name like "a/b" actually provides two counters: "a" and "a/b". The debug counter runtime will ensure that each increment of "a/b" also increments "a".

For example, suppose you added the following counters to the inliner:

prependDebugCounter("callSites/inlined", tt);
...
prependDebugCounter("callSites/notInlined", tt);

This will actually produce three counters with the following names:

  1. callSites
  2. callSites/inlined
  3. callSites/notInlined

Increment trees will be inserted for counters 2 and 3, and the runtime will make sure their values are rolled into counter 1. Counter 1 is referred to as a "denominator counter" of counters 2 and 3. You may end up with a report that looks like this:

1: callSites                |       10000 |   __   1 __
2: callSites/inlined        |        8000 |     80.00% |  __   2 __
3: callSites/notInlined     |        2000 |     20.00% |  __   3 __

The first column is the raw counter values. To the right are additional columns that give ratios between counters. In this example, the second column has a header of "__ 1__" meaning that the values below are relative to counter 1. The rightmost column just lists the counter numbers again, making it easier visually to follow the (sometimes very long) lines horizontally. Note that the compiled code would only contain counters 2 & 3, and the denominator (1) is computed from them.

Colon

:

A colon in a counter name is similar to a slash, except it indicates that the denominator should not be computed automatically. Rather, the developer will insert the denominator counter separately. The colon simply indicates that the user wants to compute the ratio between two counters. Asking for a counter with a name of the form "a:b" actually provides two counters: "a" and "a:b". The ratio between these two counters will be displayed in the counter report, but increments to "a:b" will not be counted as increments to "a".

For example:

prependDebugCounter("frames", tt);
...
prependDebugCounter("frames:#autos", tt, numAutos);

The JIT option to be used here is

-Xjit:'debugCounters={frames*}'

This produces two counters that are unrelated except that the compiler will report their ratio when the run is complete:

1: frames         |  3914036134 |   __   1 __
2: frames:#autos  | 22680880758 |      5.79  |  __   2 __

Looking down from the "__ 1__" heading, you can see that each frame has an average of 5.79 autos.

Equals

=

The equals sign indicates to the debug counter display routine that the remainder of the counter name should be sorted numerically rather than alphabetically. It has no other special meaning.

Parentheses

()

Parentheses are used to mark a verbatim section of a counter name, within which the other special characters lose their special meaning. For example, asking for a counter with a name of the form "(a/b)" produces just one counter. Without the special meaning of parentheses, this would have produced two counters: "(a" and "(a/b)".

Parentheses can be nested.

Hash

#

The hash symbol has no particular special meaning, but is used by convention to signify counters that are suitable for producing a histogram. Generally you can include a hash symbol in any debug counter that is incremented by some value other than 1, such as "#parameters".

Format Specifiers

If you require formatted debug counter names (subsequences) beginning with %, use TR::DebugCounter::debugCounterName(TR::Compilation *comp, const char *format, ...).

For example:

TR::DebugCounter::prependDebugCounter(comp(), TR::DebugCounter::debugCounterName(comp(), "inlineClone.location/array/(%s)", comp()->signature()), callTree);

Other naming considerations

Counters can have arbitrary names, though care should be taken with certain special characters:

  • Whitespace and other shell-control characters may make them awkward to specify on the command line
  • Characters with special meanings in the compiler's regular expressions will make them awkward to filter

4. Specifying Output

Environment Variables

TR_DebugCounterFileName

Specifies the file to which the debug counter output should be appended (creating the file if necessary), relative to the current working directory. If not specified, debug counter output is written to stdout.

For example:

export TR_DebugCounterFileName=debugcounters.txt

JIT Options

A full listing of debug counter-related JIT options can be found by exporting {debugCounter} to the TR_Options environment variable.

For example: TR_Options='help={*debugCounter*}' testjit

At least one debug counter option must be specified for debug counter output to be produced.

debugCounters=

Specifies a regular expression that is matched against dynamic debug counter names to determine which are included in the output.

For example: -Xjit:debugCounters={arraycopy/16Bit*|arraycopy/32Bit*|arraycopy/64Bit*}

staticDebugCounters=

Specifies a regular expression that is matched against static debug counter names to determine which are included in the output.

For example: -Xjit:staticDebugCounters={vt-helper/*}