Custom print function with _TIME_, _FILE_, _FUNCTION_, _LINE_ - c

I have following code in debug.h:
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef DEBUG
int al_debug(const char *format,
const char * time,
const char * file,
const char * function,
int line,
...);
#define debug(fmt, args...) al_debug(fmt, __TIME__, __FILE__, __FUNCTION__, __LINE__, args...)
#else /* IF DEBUG NOT DEFINED*/
#define debug(fmt, ...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
#endif /* END OF __DEBUG_H__ */
In debug.c:
#include <stdarg.h>
#include <string.h>
#define __WRAP_FUNCTION__
#include "debug.h"
#ifdef DEBUG
int debug(const char *format,
const char * time,
const char * file,
const char * function,
int line,
...)
{
int done=0;
va_list arg;
va_start(arg, format);
done = vfprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
done += vfprintf(stdout, format, arg);
va_end(arg);
return done;
}
#endif
And after compiling it I am getting following errors:
gcc -g -Wall -DDEBUG -c debug.c -o d_debug.o
debug.c:16:1: error: expected declaration specifiers or ‘...’ before string constant
debug.c:16:1: error: expected declaration specifiers or ‘...’ before string constant
debug.c:11:5: error: expected declaration specifiers or ‘...’ before ‘__FUNCTION__’
debug.c:16:1: error: expected declaration specifiers or ‘...’ before numeric constant
How I can fix this problem? What is problem here? Thank you in advance.

1) Change function name
// int debug(
int al_debug(
2) Use regular fprintf()
// done = vfprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
done = fprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
3) Start at line. Use parameter before ...
// va_start(arg, format);
va_start(arg, line);
4) Pedantic note: add done check for < 0 after each print.
done = fprintf(stdout, "%s :%s:%s:%d", time, file, function, line);
if (done >= 0) {
int done_former = done
done = vfprintf(stdout, format, arg);
if (done >= 0) done += done_former;
}
va_end(arg);
return done;
5) Change args...) to args) #leeduhem
[Edit] to work with no args after format.
int al_debug(const char * time, const char * file, const char * function,
int line, const char *format, ...);
#define debug(...) al_debug(__TIME__, __FILE__, __func__, __LINE__, __VA_ARGS__)
// #leeduhem for __VA_ARGS__
#else /* IF DEBUG NOT DEFINED*/
#define debug(...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
int al_debug(const char * time, const char * file, const char * function,
int line, const char *format, ...) {
int done = 0;
va_list arg;
va_start(arg, format); // change to `format`
...

Fixed version:
debug.h
#ifndef __DEBUG_H__
#define __DEBUG_H__
#ifdef DEBUG
int al_debug(const char *time, const char *file, const char *func, int line, const char * format, ...);
#define debug(...) al_debug(__TIME__, __FILE__, __func__, __LINE__, __VA_ARGS__)
#else /* IF DEBUG NOT DEFINED*/
#define debug(...) /* DO NOT PRINT ANYTHING IF DEBUG IS NOT PRESENT */
#endif /* END OF DEBUG */
#endif /* END OF __DEBUG_H__ */
debug.c
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define __WRAP_FUNCTION__
#include "debug.h"
#ifdef DEBUG
int al_debug(const char *time, const char *file, const char *func, int line, const char *format, ...)
{
int done=0;
va_list arg;
va_start(arg, format);
done = fprintf(stdout, "%s :%s:%s:%d: ", time, file, func, line);
done += vfprintf(stdout, format, arg);
va_end(arg);
return done;
}
#endif
test.c
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
void foo(void)
{
debug("debugg...\n");
debug("%s and %d\n", "debug information", 42);
}
int
main(int argc, char *argv[])
{
foo();
exit(EXIT_SUCCESS);
}
Testing:
$ gcc -g -Wall -I. -DDEBUG debug.c test.c
test.c: In function ‘main’:
test.c:12:10: warning: unused parameter ‘argc’ [-Wunused-parameter]
test.c:12:22: warning: unused parameter ‘argv’ [-Wunused-parameter]
$ ./a.out
10:46:40 :test.c:foo:8: debugg...
10:46:40 :test.c:foo:9: debug information and 42
$ gcc -g -Wall -I. debug.c test.c
test.c: In function ‘main’:
test.c:12:10: warning: unused parameter ‘argc’ [-Wunused-parameter]
test.c:12:22: warning: unused parameter ‘argv’ [-Wunused-parameter]
$ ./a.out
$
A few links that may be helpful:
6.20 Macros with a Variable Number of Arguments
6.47 Function Names as Strings

Related

Reimplementation of printf() using printf() in C

I'm making a embedded linux project and I want to do a simple debug messages library where I can disable my debug messages (using pre-compilation directives) when my code is in production phase and substitute for some type of log in a database (in the future). I'm having trouble with reimplementation of printf() because of va_list. This is my code until now:
My source file:
#include "debug_msgs.h"
#define ENABLE_DEBUG_MSGS 1U
#if ENABLE_DEBUG_MSGS
int print(const char *fmt, ...) {
int n = -1;
va_list ap;
va_start(ap, fmt);
n = printf(fmt, ap);
va_end(ap);
return n;
}
#else
int print(const char *fmt, ...) {
return 0;
}
#endif
My header file:
#ifndef DEBUG_MSGS_H
#define DEBUG_MSGS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
int print(const char *fmt, ...);
#endif
My problem is:
My implementation compiles (I know this doesn't mean nothing), but when I do a simple test like:
int num1 = 10;
int num2 = 100;
int num3 = 1000;
float floating = 3.14;
char str[] = {"Please work, please"};
int number=-1;
number = print("\nTesting %d %d %d %f %s\n", num1, num2, num3, floating, str);
printf("\nnumber = %d\n",number); //Just to check the return
I get this output:
Testing -1095005212 100 -1095005212 -0.000002 H*�
number = 52
Complementary informations:
This is my platform info:
Linux mdm9607 3.18.44 #1 PREEMPT Tue Sep 13 19:45:33 UTC 2022 armv7l GNU/Linux
My compiler info:
arm-oe-linux-gnueabi-gcc
My $CFLAGS:
-O2 -fexpensive-optimizations -frename-registers -fomit-frame-pointer -MMD -MP
My $LDFLAGS:
-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed
LDFLAGS += -lumdp -lumdpcommon
LDFLAGS += -lpthread -lxml2 -lrt
LDFLAGS += -lm
Instead of printf, you should be using vprintf or vfprintf with a va_list argument:
#include <stdarg.h>
#include <stdio.h>
int print(const char *fmt, ...) {
int n;
va_list ap;
va_start(ap, fmt);
n = vprintf(fmt, ap);
va_end(ap);
return n;
}
The erroneous code compiles because printf, as a vararg function, can accept any argument types after the format string. Yet you can avoid type mismatches for printf arguments by enabling more compiler warnings: gcc -Wall -Wextra -Werror would at least complain about using printf with a non constant format string and would perform compile time type checking if the format string is a string literal.

How to update a vargs to send to printf

I have the following code where I format a message that has been passed via a logger:
va_list args;
va_start(args, level);
// pop the msg and do some string replaces
char *msg = va_arg(args, char*);
ssize_t len = str_replace(tmp, "%(msg)s", msg, output, 1024);
// now how to get it 'back in' so I can print it?'
// msg = output
vfprintf(stderr, output, args);
The above code prints nothing, as it seems to have 'popped' the msg without me placing the updated msg back in there. What would be a possible way to do this?
Instead of fiddling with the format string, you could print the variadic arguments passed to the log function to a string first, then print that string with a regular fprintf via the %s format.
You haven't posted your function signature, but it looks as if you passed the format string as first variadic argument. That's unusual. I've changed the signature so that the format string is the last non-variadic arg. That's common and it also opens up the possibility to chech your format strings for correctness. (See below.)
Here's an example implementation:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
enum {
FATAL, ERROR, WARNING, INFO, NLEVEL
};
void say(int level, const char *fmt, ...)
{
static const char *templates[NLEVEL] = {
"\033[1m!!! FATAL: %s !!!\033[0m\n",
"ERROR: %s\n",
"Warning: %s\n",
"%s Just sayin'.\n"
};
const char *tmp;
char output[1024];
va_list args;
// print arguments to temporary string
va_start(args, fmt);
vsnprintf(output, sizeof(output), fmt, args);
va_end(args);
// find and print template string with message
if (level < 0) level = 0;
if (level >= NLEVEL) level = INFO;
tmp = templates[level];
fprintf(stderr, tmp, output);
va_end(args);
if (level == FATAL) exit(1);
}
Use it like this:
int main(int argc, char *argv[])
{
int a = 6;
int b = 7;
double Tmin = -273.15;
say(INFO, "%d * %d == %d.", a, b, a * b);
say(INFO, "The %s is %s.", "apple", "red and juicy");
say(WARNING, "Temperature falls below Tmin (%gC)", Tmin);
say(ERROR, "That should %s have happened.",
(rand() % 2) ? "never" : "not really");
say(FATAL, "Out of coffee!");
return 0;
}
Finally, you can make your logging functions safer and let the compiler check the validity of format strings. GCC and Clang do this with arguments, the Microsoft compiler does it with SAL, the Source-code Annotation Language. Other compilers may have their own method to do such checks. (More likely, they just emulate one of the GC or MS systems.)
With a little macro dance in the header you can cater for both variants:
#if defined(_MSC_VER)
#define PRINTF_FMT _Printf_format_string_
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PRINTF_ARGS(IFMT, IARGS) __attribute__((format(printf, IFMT, IARGS)))
#endif
#ifndef PRINTF_ARGS
#define PRINTF_ARGS(IFMT, IARGS)
#endif
#ifndef PRINTF_FMT
#define PRINTF_FMT
#endif
void say(int level, PRINTF_FMT const char *fmt, ...) PRINTF_ARGS(2, 3);
Now a call like this:
say(WARNING, "Meta: Wrong formt for %d.", ~0ull);
warns you about the wrong format specifier.

Can I know the file and function names from where my function is called if it's from another .c?

I'm developing a library and I would like to know some data about the caller of one of the functions I'm offering. In particular, I would need to know the file name, function name and line where my function (a redefined malloc) is being called.
EDIT: Here's a minimum working example where I can detect when a user calls malloc and "redirect" him to my own malloc function:
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "myLib.h"
int main(){
printf("Inside main, asking for memory\n");
int *p = malloc(sizeof(int));
*p = 3;
free(p);
return 0;
}
myLib.c:
#include "myLib.h"
void * myAlloc (size_t size){
void * p = NULL;
fprintf(stderr, "Inside my own malloc\n");
p = (malloc)(size);
return p;
}
#undef malloc
#define malloc(size) myAlloc(size)
myLib.h:
#ifndef MYLIB_H
#define MYLIB_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define malloc(size) myAlloc(size)
void * myAlloc(size_t size);
#endif
I've tried using _FILE_ _func_ and _LINE_ keywords, but I can't make it work since it's in a different module.
You could:
//mylib.h
#ifndef MYLIB_H
#define MYLIB_H
#include <stdlib.h>
// replace malloc in case it's already a macro
#ifdef malloc
#undef malloc
#endif
// I believe that from the standards point of view, this is undefined behavior
#define malloc(size) my_alloc(size, __FILE__, __LINE__, __func__)
#ifdef __GNUC__
// Allow compiler to do static checking.
__attribute__((__alloc_size__(1), __malloc__))
#endif
void *my_alloc(size_t size, const char *file, int line, const char *func);
// ^^^^^^^^ I do not like camelCase case - one snake case to rule them all.
#endif
// mylib.c
#include "mylib.h" // do not ever mix uppercase and lowercase in filenames
#undef malloc // undef malloc so we don't call ourselves recursively
#include <stdio.h>
void *my_alloc(size_t size, const char *file, int line, const char *func){
fprintf(stderr, "Och my god, you wouldn't believe it!\n"
"A function %s in file %s at line %d called malloc!\n",
func, file, line);
return malloc(size);
}
You might also see how assert does it. If you are aiming at glibc, read glibc docs replacing malloc.
Still as you discovered a user may do (malloc)(size) cicumvent macro expansion. You could do:
void *my_alloc(size_t size, const char *file, int line, const char *func);
static inline void *MY_ALLOC(size_t size) {
return my_alloc(size, NULL, 0, NULL);
}
#define MY_ALLOC(size) my_alloc(size, __FILE__, __LINE__, __func__)
// if called with `malloc()` then MY_ALLOC is expanded
// if called as `(malloc)`, then just expands to MY_ALLOC.
#define malloc MY_ALLOC
int main() {
malloc(10); // calls my_alloc(10, "main.c", 62, "main");
(malloc)(20); // calls my_alloc(20, NULL, 0, NULL);
}
GLIBC defines hidden symbols for malloc(), free()... which are called __libc_malloc(), __libc_free()...
So, you can tremendously simplify your debug macros.
In m.h, just define the following:
#if DEBUG_LEVEL > 0
extern void *__libc_malloc (size_t bytes);
extern void *myMalloc(size_t size, const char *filename, const char *funcname, int line);
#define malloc(size) myMalloc(size, __FILE__, __FUNCTION__, __LINE__)
#endif
Then you can write a program defining myMalloc() as follow (e.g. file name is m.c):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "m.h"
#if DEBUG_LEVEL > 0
void *myMalloc(
size_t size,
const char *filename,
const char *funcname,
int line
) {
fprintf(stderr, "malloc(%zu) called from %s/%s()#%d\n", size, filename, funcname, line);
return __libc_malloc(size);
}
#endif
char *dup_str(char *string) {
char *str = malloc(strlen(string) + 1);
strcpy(str, string);
return str;
}
int main(int ac, char *av[]) {
char *str;
if (av[1]) {
str = dup_str(av[1]);
} else {
str = dup_str("NULL");
}
printf("String = '%s'\n", str);
free(str);
return 0;
}
When you compile this example program in non debug mode:
$ gcc m.c -DDEBUG_LEVEL=0
$ ./a.out azerty
String = 'azerty'
When you compile your program in debug mode:
$ gcc m.c -DDEBUG_LEVEL=1
$ ./a.out azerty
malloc(7) called from m.c/dup_str()#27
String = 'azerty'

function polymorphisms depending on debug level

I would like to change below code:
#ifdef CONSOLE_VERBOSE_DEBUG
printf("this debug error message is printed to the console");
#elseif FILE_VERBOSE_DEBUG
FILE *log = fopen(...);
...
fprintf();
fclose();
#else
((void) 0) // no debugging
into something like
callThePropperDebugFct("message");
and point this one call to functions declared in the properly included header files depending on which DEBUG level is defined
I know it has to do with c polymorphisms and function pointers, but I can not wrap my head around how to do this
Define functions to log your message in some log.c file
Define your callThePropperDebugFct macro to call the right function
log.c
void log_to_console(const char message)
{
printf("%s", message);
}
void log_to_file(const char *name, const char *message)
{
FILE *f = fopen(name, "a");
if (!f) return;
fprintf(f, message);
fclose(f);
}
log.h
void log_to_console(const char message);
void log_to_file(const char *name, const char *message);
#ifdef CONSOLE_VERBOSE_DEBUG
#define callThePropperDebugFct(message) log_to_console(message);
#elseif FILE_VERBOSE_DEBUG
#define callThePropperDebugFct(message) log_to_file(LOG_FILE, message)
#else
#define callThePropperDebugFct(message)
#endif
But you can go further with some variadic macros and variable numbers of arguments functions:
log.c
#include <stdarg.h>
#include <stdio.h>
void log_to_console(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
//vsnprintf(buffer, sizeof buffe, fmt, args);
vprintf(fmt, args);
va_end(args);
}
void log_to_file(const char *name, const char *fmt, ...)
{
FILE *f = fopen(name, "a");
if (!f) return;
va_list args;
va_start(args, fmt);
vfprintf(f, fmt, args);
va_end(args);
fclose(f);
}
log.h
void log_to_console(const char *fmt, ...);
void log_to_file(const char *name, const char *fmt, ...);
#ifdef CONSOLE_VERBOSE_DEBUG
#define callThePropperDebugFct(...) log_to_console("s", __VA_ARGS__)
#elseif FILE_VERBOSE_DEBUG
#define callThePropperDebugFct(...) log_to_file(LOG_FILE, "%s", __VA_ARGS__)
#else
#define callThePropperDebugFct(...)
#endif
#if DEBUG_LEVEL
#ifndef LOG_OUTPUT
#define LOG_OUTPUT stdio
#endif
log_function(DEBUG_LEVEL, LOG_OUTPUT, const char *fmt, ...);
#endif
#ifdef __GNUC__
#define ATTR __attribute__((format (printf, 3, 4)));
#else
/* .. */
#endif
void ATTR log_function(int DEBUG_LEVEL, FILE *LOG_OUTPUT, const char *fmt, ...)
{
va_list args;
/* some code */
vfprintf(LOG_OUTPUT, fmt, args);
}

GDB cannot tell different source files when compiling them within #include?

Just a small program of stdarg.h, when I organizing source file in a single source file as following:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void
print(const char *, ...);
void
print(const char *format, ...)
{
va_list vlist;
va_start(vlist, format);
vfprintf(stdout, format, vlist);
va_end(vlist);
return;
}
int
main(int argc, char **argv)
{
const char *message = "Just a test";
print("==> %s <==\n", message);
return EXIT_SUCCESS;
}
The below code in a single file works well under gdb when debugging, it stopped where I expected.
But when I organizing the code in 3 files: print.c main.c test.c
/* print.c */
void
print(const char *, ...);
void
print(const char *format, ...)
{
va_list vlist;
va_start(vlist, format);
vfprintf(stdout, format, vlist);
va_end(vlist);
return;
}
/* main.c */
int
main(int argc, char **argv)
{
const char *message = "Just a test";
print("==> %s <==\n", message);
return EXIT_SUCCESS;
}
/* test.c */
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "print.c"
#include "main.c"
I use command gcc -g test.c, and run it under linux. When breaking at print, it refused to break and continue to run to the end, within a warning "error removing breakpoint 0".
I suppose that maybe it related to stdarg.h, because I am always organizing code in that way, but the first time met the problem.

Resources