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\n", __FILE__, __LINE__); \
fprintf(stderr, "--> %s %s\n", message); \
fprintf(stderr, "--> STACKTRACE START\n"); \
print_backtrace(2); \
fprintf(stderr, "--> STACKTRACE END\n"); \
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\n", #expr, __FILE__, __LINE__); \
fprintf(stderr, "--> STACKTRACE START\n"); \
print_backtrace(2); \
fprintf(stderr, "--> STACKTRACE END\n"); \
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.