0

I have this C code that implements an algorithm in the context of computer science research. The mentioned code contains many printf and other functions calls that output formatted information about the state of the algorithm and key values in particular points. This output is very useful/clear from a learning perspective.

I would like to somehow preserve and access this information if desired, but not hurting the performance if I don't want such information.

I thought about some DEBUG macro definition, and injected a bunch of ifdefs:

#define DEBUG
...
#ifdef DEBUG
    printf("At this point...");
#endif

But this makes the code ugly and difficult to read.

I am using Git, so perhaps some sort of alternate version could be a better approach to access the "learning" or "clean" version of the code.

So my concrete questions are related with practicality and coding conventions:

  1. Is there a practical way to accomplish this?
  2. Is this just a flat out bad practice, and I shouldn't keep code doing this sort of thing?
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
onlycparra
  • 607
  • 4
  • 22
  • I normally keep changes for debugging in a single revision on a private branch. So, I checkout branch X, if I want to see my debug info, i just cherry-pick the private revision (single revision, so cherry-pick works fine). This also means that if I start working on this branch, when I am ready to push it, I make sure to remove the debug revision from history so it's like I never did that. – eftshift0 Jul 08 '21 at 20:48
  • You can make it a stub function, and use a function pointer that refers to the implementation with/without debugging code. You could then even use a macro to avoid all function calls in production. The original code shouldn't have used `printf` anyway. – Cheatah Jul 08 '21 at 20:53
  • 2
    [#define macro for debug printing in C?](https://stackoverflow.com/q/1644868) – 001 Jul 08 '21 at 21:02

2 Answers2

2

First, performance isn't a problem until your application doesn't meet actual performance requirements. Then you profile your application, measure it, and find the hotspots.

Then you fix the problems you find.

If you have a global debug level, you can do something like this:

extern volatile atomic unsigned int globalLogLevel;

void logDebugFunc( unsigned int level, const char *function, int line,
    const char *filename, const char *format, ... );

#define LOG_DEBUG( level, format, ... ) \
do \
{ \
    if ( level >= globalLogLevel ) \
    { \
        logDebugFunc( level, __func__, __LINE__,\
            __FILE__, format, __VA_ARGS__ ); \
    } \
} while ( 0 )

(note that this automatically captures function, file, and line number - that way you know exactly where every debug log entry came from)

You can vary the logging levels, in this example higher numbers for more detailed log entry, lower numbers for rarer, more important log entries such as important events, errors, or warnings.

Then it just becomes how to update/change the globalLogLevel while the process is running. There are all kinds of options there - you can use a real-time signal to send a new value, or you can redefine globalLogLevel to something else - a function for a more complex calculation, a pointer into a mmap()'d file where you can change log level directly.

You can make something like this as complex as you'd like, and with judicious use the performance penalty won't be drastic. The hit from the if statement might not even be measurable unless you embed it into some super-tight loop that runs a lot.

Even better, you not only don't have to recompile your process to enable logging, you can reset the log level while the process is running.

You'll need something like this to figure out why you get that intermittent bad result from data, but only on one system of eight, and only when the moon is full. Because just about the only way to reliably and quickly solve problems like that is to have the code tell you everything it does.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
1
/* C file with debug log function */
#ifdef DEBUG
    mysuperdebug_log_function( /* parameters */ )
    {
        /* .... */
    }
#endif


/* .h file */
#ifdef DEBUG
    mysuperdebug_log_function( /* parameters */ );
    #define DEBUGLOG(...)  mysuperdebug_log_function(__VA_ARGS__)
#else
    #define DEBUGLOG(...)
#endif

In your code include the ".h" file and use the macro when you want to output the debug/log informations

0___________
  • 60,014
  • 4
  • 34
  • 74