Error Helpers in C



I’ve been writing a lot of C lately for a game I am working on. I am not a perfect programmer and I would like to catch my bugs before they arise. Thus, I am attempting to learn Rust in my free time.

Backtrace

Printing a backtrace in C is not incredibly difficult to accomplish. Although the following is fairly primitive, it will aid in your ability to discern what is happening in your program

/// @file core/backtrace.c #include <execinfo.h> #include "backtrace.h" /// @brief Prints a backtrace of up to the last 256 calls /// /// @param [out] fd The file descriptor ID to write to void print_backtrace ( int fd ) { void * array [ 256 ]; size_t size = backtrace ( array , 256 ); backtrace_symbols_fd ( array , size , fd ); }

The output will look like the following

bin/example(print_backtrace+0x22)[0x403002] bin/example(test_block_insertion+0x218)[0x402b68] bin/example(main+0x2f)[0x402eef] /usr/lib/libc.so.6(__libc_start_main+0xf1)[0x7f3e3cb5c291] bin/example(_start+0x2a)[0x4027fa]

As you can see, in a method test_block_insertion I call print_backtrace . This is incredibly helpful if you need to determine how deep in the program your issue occurred.

Panic

I’ve been tinkering with Rust in my free time to get a better understanding of it and I have come to enjoy the panic! macro it employs. I like the verb and decided that it would work perfectly in my daily use.

With some alterations, I just wanted panic to do exactly what its name suggests, I want the program to panic with a message and abort.

/// @file core/panic.h #pragma once #include <stdlib.h> #include <stdio.h> #include "backtrace.h" #ifdef NDEBUG #define panic(message) #else /// @brief Causes the program to abort and print a message /// /// @param [in] message The error message you wish to spit out to stderr. #define panic(message) \ do { \ fprintf(stderr, "panicked at: %s:%d

", __FILE__, __LINE__); \ fprintf(stderr, "--> %s %s

", message); \ fprintf(stderr, "--> STACKTRACE START

"); \ print_backtrace(2); \ fprintf(stderr, "--> STACKTRACE END

"); \ fflush(stderr); \ abort(); \ } while (0) #endif

Example usage would be like this

int some_function ( uint8_t * data , size_t length ) { if ( length == 0 ) { panic ( "Zero length buffer provided!" ); } // // consume data // return 1 ; }

Output from this macro will look like the following.

panicked at: /home/warmwaffles/code/example/rbp_test.c:173 --> message: Failed to insert the block correctly --> START STACKTRACE bin/rbp_test(print_stacktrace+0x22)[0x403002] bin/rbp_test(test_block_insertion+0x218)[0x402b68] bin/rbp_test(main+0x2f)[0x402eef] /usr/lib/libc.so.6(__libc_start_main+0xf1)[0x7f3e3cb5c291] bin/rbp_test(_start+0x2a)[0x4027fa] --> END STACKTRACE Aborted (core dumped)

I wanted the core dump to take place so that I can inspect it if I need to. Luck favors the prepared, and I always like to be prepared.

Assert

I use assert(expr) liberally through out my code to ensure that my program operates as I intend it to. Sometimes I make a mistake and would like to be notified where it happened and how deep in the call stack it did.

Unfortunately vanilla assert(expr) does not do this. But it is a simple enough macro to override and provide a little more meta information about where it failed and why.

/// @file core/assert.h #pragma once #include <stdlib.h> #include <stdio.h> #include "backtrace.h" #ifdef NDEBUG #define assert(expr) #else #define assert(expr) \ if(!(expr)) { \ fprintf(stderr, "assertion (%s) failed at: %s:%d

", #expr, __FILE__, __LINE__); \ fprintf(stderr, "--> STACKTRACE START

"); \ print_backtrace(2); \ fprintf(stderr, "--> STACKTRACE END

"); \ fflush(stderr); \ abort(); \ } #endif

As you can see it looks almost exactly the same as panic(message) does. However, I want the expression to be spit out into stderr so that I can see what expression failed.

assertion (1 == 0) failed at: /home/warmwaffles/code/example.c:170 --> STACKTRACE START bin/example(print_backtrace+0x22)[0x4015a2] bin/example(test_block_insertion+0x1cc)[0x40134c] bin/example(main+0x2f)[0x40148f] /usr/lib/libc.so.6(__libc_start_main+0xf1)[0x7f989aaec291] bin/example(_start+0x2a)[0x40102a] --> STACKTRACE END Aborted (core dumped)

Found these little bits of code to be useful, and figured others would probably find it useful as well.