Aussie AI

Multiple Levels of Debug Tracing

  • Book Excerpt from "Generative AI in C++"
  • by David Spuler, Ph.D.

Multiple Levels of Debug Tracing

Once you've used these debug methods for a while, you start to see that you get too much output. For a while, you're just commenting and uncommenting calls to the debug routines. A more sustainable solution is to add numeric levels of tracing, where a higher number gets more verbose.

To make this work well, we declare both a Boolean overall flag and a numeric level:

    extern bool g_aussie_debug_enabled;
    extern int g_aussie_debug_level;

Here's the macros to enable and disable the basic level:

    #define aussie_debug_off()  ( \
        g_aussie_debug_enabled = false, \
        g_aussie_debug_level = 0)

    #define aussie_debug_on()  ( \
        g_aussie_debug_enabled = true, \
        g_aussie_debug_level = 1 )

And here's the new macro that sets a numeric level of debug tracing (higher number means more verbose):

    #define aussie_debug_set_level(lvl)  ( \
        g_aussie_debug_enabled = (((lvl) != 0)), \
        g_aussie_debug_level = (lvl) )

Here's what a basic debug macro looks like:

    #define ydbglevel(lvl,fmt,...)  ( \
        g_aussie_debug_enabled && \
        (lvl) <= g_aussie_debug_level && \
        fprintf(stderr, (fmt), __VA_ARGS__ ))
    ...
    ydbglevel(1, "Hello world");
    ydbglevel(2, "More details");

Now we see the reason for having two global variables. In non-debug mode, the only cost is a single Boolean flag test, rather than a more costly integer “<” operation.

And for convenience we might add multiple macro name versions for different levels:

    #define ydbglevel1(fmt) (ydebuglevel(1, (fmt)))
    #define ydbglevel2(fmt) (ydebuglevel(2, (fmt)))
    ...
    ydbglevel1("Hello world");
    ydbglevel2("More details");

Very volatile. Note that if you are altering debug tracing levels inside a symbolic debugger (e.g. gdb) or IDE debugger, you might want to consider declaring the global level variables with the “volatile” qualifier. This applies in this situation because their values can be changed (by you!) in a dynamic way that the optimizer cannot predict. On the other hand, you can skip this, as this issue won't affect production usage, and only rarely impacts your own interactive debugging usage.

BYO debug printf: All of the above examples are quite fast in execution, but heavy in space usage. They will be adding a fair amount of executable code for each “ydebug” statement. I'm not sure that I really should care that much about the code size, but anyway, we could fix it easily by declaring our own variable-argument debug printf-like function.

 

Next:

Up: Table of Contents

Buy: Generative AI in C++: Coding Transformers and LLMs

Generative AI in C++ The new AI programming book by Aussie AI co-founders:
  • AI coding in C++
  • Transformer engine speedups
  • LLM models
  • Phone and desktop AI
  • Code examples
  • Research citations

Get your copy from Amazon: Generative AI in C++