views:

293

answers:

4

In some projects I've done in C, I've liked using the following macros which work similar to Perl's warn and die subroutines:

#include <stdio.h>
#include <stdlib.h>

#define warn(...) \
    fprintf(stderr, __VA_ARGS__); \
    fprintf(stderr, " at %s line %d\n", __FILE__, __LINE__)

#define die(...) \
    warn(__VA_ARGS__); \
    exit(0xFF)

Does anything exist like Perl's carp, croak, cluck, and confess subroutines from Carp? I'd like to have something to report errors from the users perspective.

If not, I know there are backtrace() and backtrace_symbols() functions in glibc that along with the -rdynamic gcc option can supply me with a backtrace of function names and code addresses. But i want something a little better; with access to file, line, and function names in the call stack like Perl's caller subroutine. with that I'd be able to write my own libcarp for use in my c programs.

EDIT: 2009-10-19

I'm thinking of creating something that uses gdb when available on basename(argv[0]), then processes the stack trace to produce the different types of messages I want. It should be able to determine if im not in a debuggable executable, or a system without gdb, in which case, carp and cluck will become warns and craok and confess will become dies.

Ive never used gdb like this before (Ive only run it with my program at the beginning, not while its already running). But I found a few functions in glib (g_on_error_stack_trace and stack_trace) that looks very close to what i want to do: It forks a gdb process with the arguments basename(argv[0]) and the process id, then writes to its stdin (which has been redirected to a pipe) the command "backtrace" followed by a "quit". It then reads from its result and parses it the way it likes. This is almost exactly what i need to do.

+1  A: 

Well, I never tried to show the call stack, but for my programs I used to do the following.

First, I define a function that do the actual logging. This is just an example; please note that this function is highly insecure (buffer overrun anyone?)

void strLog(char *file, char *function, int line, char *fmt, ...)
{
     char buf[1024];
     va_list args;

     va_start(args, fmt);
     vsprintf(buf, fmt, args);
     va_end(args);

     fprintf(stderr, "%s:%s:%d:%s\n", file, function, line, buf);
}

However, this is not very practical. What is practical is to use a macro to call this function.

#define die( ... ) \
        strLog( __FILE__, __PRETTY_FUNCTION__, \
        __LINE__, __VA_ARGS__ )

Then you can call just like printf().

if (answer == 42) die("Oh, %d of course.", answer);

and you would get something like this:

main.c:10:somefunc: Oh, 42 of course.

Well, no backtrace but something's something.

Leonardo Herrera
It looks like Jake has already got that far though.
Kinopiko
Use vsnprintf() to prevent buffer overrun. Or use two calls to fprintf() - one for the file/function/line info and the other as vfprintf() to deal with the user-supplied formatting. Everyone's code seems to be forgetting to print the program name - argv[0]; that requires some setup discipline, though.
Jonathan Leffler
If you want to prevent buffer overrun, the easiest way to do it is to call `vasprintf` rather than `vsnprintf`.
Kinopiko
+1  A: 

But I want something a little better with access to file, line, and function names in the call stack like Perl's caller subroutine.

The problem is that this requires help from the programmer to decide where the boundary appears between your library code and the 'caller' subroutine. Perl uses some magic (aka heuristics) to do that; maybe you could do the same with the backtrace functions. But it is not trivial, in general.

Jonathan Leffler
The magic referred to here is the caller function which is supplied was one of the built in functions. There is no standard function in the C language definition that supplies an equivalent capability.
David Harris
I meant "The magic referred to here is the caller function which is supplied as one of the perl built in functions. There is no standard function in the C language definition that supplies an equivalent capability."
David Harris
@David: I know what you're getting at, and it is hard explain what would be the equivalent in C - not least because there isn't an equivalent in standard C environments. And my understanding is that the Perl 'caller' built-in works out from the Perl stack or related data what the calling function is.
Jonathan Leffler
A: 

I wrote backtrace routine for my embedded C (gcc) apps. It uses the -gstabs information if available to look up function names. One caveat is that the elf file must be somewhere the program can find it, in order to look in the stabs segment. In my embedded apps, the elf file is sitting in flash and I have a pointer to it. In your case you'd have to write some code to read it in off the disk.

I'm pretty sure that the file and line number are also in the stabs segment.

Does this sound like something that might help you?

NXT
+1  A: 

It appears that nothing exists quite like the Carp module for use in C programs, so i wrote a small library to do it at github Its not yet complete: I intend to add some tests and docs, but it works (on my machine at least).

The library has the following exports defined for use:

warn, die
carp, croak
cluck, confess

and I've added e-varieties of the previous ones for adding errno strings to the warning since i thought it would be useful:

ewarn, edie
ecarp, ecroak
ecluck, econfess

For example if you are writing a library and want to carp about a problem, just use

carp("%d is not a Fibonacci number!", 54);

And it will display the file and line number of the first function calling into your library.

Perl's Carp module uses a different package, instead of a file, to find the suspected subroutine. It also uses the @ISA array or @CARP_NOT recursively to determine which subroutine is outside of a trusted group of packages. I intend to add something similar to this. If the top of the stacktrace is within the trusted scope, then the carp reverts to a cluck (which shows a full stacktrace of the problem) just as this library will do.

Jake