Is there a C equivalent for Perl's Carp module?
Asked Answered
G

4

7

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.

Garrulous answered 19/10, 2009 at 2:26 Comment(4)
For those unfamiliar with Perl, carp and croak do what the example warn and die macros do, only with file and line from the caller of the current sub. cluck and confess do the same, only with a complete stack trace. (some details simplified)Nakesha
Docs at: perldoc.perl.org/Carp.htmlGlee
For true multi-statement macros, it's highly recommended to wrap them in do { ... } while(0), to make use as a statement work as expected.Lothair
With the "user" being the programmer, the probably mostly correct way to handle calling a function with wrong parameters is to use abort() or assert(0), as it is a programming error. The examine the core dump to get the details. That's not nice, but programming errors aren't nice anyway.Warfeld
E
1

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.

Erechtheum answered 19/10, 2009 at 12:37 Comment(2)
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.Extremism
If you want to prevent buffer overrun, the easiest way to do it is to call vasprintf rather than vsnprintf.Hammering
E
1

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.

Extremism answered 19/10, 2009 at 15:8 Comment(3)
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.Achondrite
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."Achondrite
@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.Extremism
G
1

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.

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.

Garrulous answered 25/10, 2009 at 21:50 Comment(1)
NB: As of 2012-09-06, the Github link is 404.Extremism
E
0

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?

Edging answered 21/10, 2009 at 13:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.