I know little about __attribute__ and __cyg_profile_function_enter. Of course, they are both GNU C features.
I'm learning C++ to compile Nginx modules. I always try to convert the C code samples to C++.
Here is a simple sample of C:
#include <stdio.h>
int depth_ = -1;
#ifdef __GNUC__
void __cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
void _cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
#define sustainable(fn, caller) \
do {\
printf("%d, %s: fn = %p, caller = %p\n", depth_, __FUNCTION__, fn, caller); \
} while(0)
void __cyg_profile_func_enter(void *fn, void *caller){
printf("Enter:\n");
depth_++;
sustainable(fn, caller);
}
void __cyg_profile_func_exit(void *fn, void *caller){
printf("Exit:\n");
depth_--;
sustainable(fn, caller);
}
#endif
void sustain(){
depth_ = 100;
}
int main(){
depth_ = 10;
sustain();
//Suture suture;
//suture.sultry();
return 0;
}
sh$ gcc -finstrument-functions ...
It displays Enter: 101, __cyg_profile_func_enter: fn = 0x400645, caller = 0x4006ba and Exit: 100, __cyg_profile_func_exit: fn = 0x400645, caller = 0x4006ba circularly
This is the C++ code:
#include <iostream>
using namespace std;
int depth_ = -1;
#ifdef __GNUC__
extern "C" {
void __cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *, void *) __attribute__((no_instrument_function));
#define sustainable(fn, caller) \
do{ \
printf("%d, %s: fn = %p, caller = %p\n", depth_, __FUNCTION__, fn, caller); \
} while(0)
void __cyg_profile_func_enter(void *fn, void *caller){
printf("Enter:\n");
depth_++;
sustainable(fn, caller);
}
void __cyg_profile_func_exit(void *fn, void *caller){
printf("Exit:\n");
depth_--;
sustainable(fn, caller);
}
}
#endif
void sustain(){
depth_ = 100;
}
class Suture
{
public:
void sultry(){
}
};
int main(int argc, char **argv){
sustain();
Suture suture;
suture.sultry;
}
Then I compile with
sh$ g++ -std=c++11 -finstrument-functions ....
It displays Enter: 2, __cyg_profile_func_enter: fn = 0x400925, caller = 0x40099b and Exit: 1, __cyg_profile_func_exit: fn = 0x400925, caller = 0x40099b circularly.
It's weird. Why the depth_ = 100 works with gcc but not with g++?
What function do you expect fn to be the address of? I'm not seeing function calls in main() that should take place after the assignment of depth_.
If you're seeing repeated calls at all, they must be due to library functions, which could well be performed before the depth_ = 100; assignment.
Related
I wrote a c-code designed for linux platform.
Now, I want to make it cross-platform so to use in Windows as-well.
In my code, I dlopen an so file and utilize the functions inside it.
Below is how my code looks like. But I just found out that in windows, the way to load and use dynamic library is quite different.
void *mydynlib
mydynlib= dlopen("/libpath/dynlib.so",RTLD_LAZY);
void (*dynfunc1)() = dlsym(mydynlib,"dynfunc1");
void (*dynfunc2)(char*, char*, double) = dlsym(mydynlib,"dynfunc2");
int (*dynfunc3)() = dlsym(mydynlib,"dynfunc3");
From what I found, I need to use LoadLibrary & GetProcAddress instead of dlopen & dlsym. However, I do not know how to convert above line for windows using those. I've tried to search some examples for hours but couldn't find exact solution. If someone had this kind of experience, please give me a tip.
Excuse me if this is too obvious problem. I'm quite new to C. I usually write my program in python.
Once in my youth I created something like this:
/* dlfcn.h */
#ifndef DLFCN_H
#define DLFCN_H
#define RTLD_GLOBAL 0x100 /* do not hide entries in this module */
#define RTLD_LOCAL 0x000 /* hide entries in this module */
#define RTLD_LAZY 0x000 /* accept unresolved externs */
#define RTLD_NOW 0x001 /* abort if module has unresolved externs */
/*
How to call in Windows:
void *h = dlopen ("path\\library.dll", flags)
void (*fun)() = dlsym (h, "entry")
*/
#ifdef __cplusplus
extern "C" {
#endif
void *dlopen (const char *filename, int flag);
int dlclose (void *handle);
void *dlsym (void *handle, const char *name);
const char *dlerror (void);
#ifdef __cplusplus
}
#endif
#endif
and dlfcn.c:
/* dlfcn.c */
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
static struct {
long lasterror;
const char *err_rutin;
} var = {
0,
NULL
};
void *dlopen (const char *filename, int flags)
{
HINSTANCE hInst;
hInst= LoadLibrary (filename);
if (hInst==NULL) {
var.lasterror = GetLastError ();
var.err_rutin = "dlopen";
}
return hInst;
}
int dlclose (void *handle)
{
BOOL ok;
int rc= 0;
ok= FreeLibrary ((HINSTANCE)handle);
if (! ok) {
var.lasterror = GetLastError ();
var.err_rutin = "dlclose";
rc= -1;
}
return rc;
}
void *dlsym (void *handle, const char *name)
{
FARPROC fp;
fp= GetProcAddress ((HINSTANCE)handle, name);
if (!fp) {
var.lasterror = GetLastError ();
var.err_rutin = "dlsym";
}
return (void *)(intptr_t)fp;
}
const char *dlerror (void)
{
static char errstr [88];
if (var.lasterror) {
sprintf (errstr, "%s error #%ld", var.err_rutin, var.lasterror);
return errstr;
} else {
return NULL;
}
}
You could use a set of macros that change depending on the OS you're on:
#ifdef __linux__
#define LIBTYPE void*
#define OPENLIB(libname) dlopen((libname), RTLD_LAZY)
#define LIBFUNC(lib, fn) dlsym((lib), (fn))
#elif defined(WINVER)
#define LIBTYPE HINSTANCE
#define OPENLIB(libname) LoadLibraryW(L ## libname)
#define LIBFUNC(lib, fn) GetProcAddress((lib), (fn))
#endif
My purpose is quite simple: when I start the mongoose server, the server will initialize a variable which is defined by me. To do this, I hooked __libc_start_main. Then when the server receives a request, it will print out that initialized variable. To do this, I hooked recv. Below is my code.
#include <string>
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "common-structure.h"
# define dprintf(fmt...)
data datainfo; //defined in common-structure.h
typedef int (*main_type)(int, char**, char**);
struct arg_type
{
char **argv;
int (*main_func) (int, char **, char **);
};
main_type saved_init_func = NULL;
void tern_init_func(int argc, char **argv, char **env){
dprintf("%04d: __tern_init_func() called.\n", (int) pthread_self());
if(saved_init_func)
saved_init_func(argc, argv, env);
datainfo.age = 10;
}
typedef void (*fini_type)(void*);
fini_type saved_fini_func = NULL;
extern "C" int my_main(int argc, char **pt, char **aa)
{
int ret;
arg_type *args = (arg_type*)pt;
dprintf("%04d: __libc_start_main() called.\n", (int) pthread_self());
ret = args->main_func(argc, args->argv, aa);
return ret;
}
extern "C" int __libc_start_main(
void *func_ptr,
int argc,
char* argv[],
void (*init_func)(void),
void (*fini_func)(void),
void (*rtld_fini_func)(void),
void *stack_end)
{
typedef void (*fnptr_type)(void);
typedef int (*orig_func_type)(void *, int, char *[], fnptr_type,
fnptr_type, fnptr_type, void*);
orig_func_type orig_func;
arg_type args;
void * handle;
int ret;
// Get lib path.
Dl_info dli;
dladdr((void *)dlsym, &dli);
std::string libPath = dli.dli_fname;
libPath = dli.dli_fname;
size_t lastSlash = libPath.find_last_of("/");
libPath = libPath.substr(0, lastSlash);
libPath += "/libc.so.6";
libPath = "/lib/x86_64-linux-gnu/libc.so.6";
if(!(handle=dlopen(libPath.c_str(), RTLD_LAZY))) {
puts("dlopen error");
abort();
}
orig_func = (orig_func_type) dlsym(handle, "__libc_start_main");
if(dlerror()) {
puts("dlerror");
abort();
}
dlclose(handle);
dprintf("%04d: __libc_start_main is hooked.\n", (int) pthread_self());
args.argv = argv;
args.main_func = (main_type)func_ptr;
saved_init_func = (main_type)init_func;
saved_fini_func = (fini_type)rtld_fini_func;
ret = orig_func((void*)my_main, argc, (char**)(&args),
(fnptr_type)tern_init_func, (fnptr_type)fini_func,
rtld_fini_func, stack_end);
return ret;
}
//hook recv
extern "C" ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
ssize_t (*orig_recv)(int sockfd, void *buf, size_t len, int flags);
orig_recv = dlsym(RTLD_NEXT, "recv");
orig_recv(sockfd, buf, len, flags);
printf("age is %d\n", datainfo.age);
}
However, when I makefile, I get the error: invalid conversion from ‘void*’ to ‘ssize_t (*)(int, void*, size_t, int) coming from dlsym(RTLD_NEXT, "recv");. My another question is can I achieve my goal in this way? If not, what is the correct way?
C++ is much more strongly typed than C, you need to explicitly cast void * to the correct type.
For example:
extern "C" ssize_t recv(int sockfd, void *buf, size_t len, int flags)
{
using orig_recv_t = ssize_t (*)(int, void *, size_t, int);
// Or for pre C++11 compilers: typedef ssize_t (*orig_recv_t)(int, void *, size_t, int);
orig_recv_t orig_recv;
orig_recv = reinterpret_cast<orig_recv_t>(dlsym(RTLD_NEXT, "recv"));
orig_recv(sockfd, buf, len, flags);
printf("age is %d\n", datainfo.age);
}
This is a class I'm doing to organize every memory block I allocate using malloc/calloc, so I can easily dispose of them later. The following is the definition of this class:
#include "memmanager.h"
std::vector<void*> MemoryManager::GarbageCollector(0);
void MemoryManager::AddToGC(void* myThing)
{
if(__DEBUG__MODE__)
MainDebugger.Log("Adding 1 element to GC: %p", "Info", myThing);
MemoryManager::GarbageCollector.push_back(myThing);
return;
}
void MemoryManager::MultiAddToGC(int args, void* myThing, ...)
{
if(args < 1)
return;
#ifdef _DEBUG
MainDebugger.Log("Adding %i element to GC", "Info", args);
#endif
va_list chrstr;
va_start(chrstr, args);
for(int x = 0; x < args; x++)
MemoryManager::GarbageCollector.push_back(va_arg(chrstr, void*));
va_end(chrstr);
return;
}
void MemoryManager::Flush()
{
int lasterror = 0;
#ifdef _DEBUG
MainDebugger.Log("Call to MemoryManager::Flush() with %i items in the GC", "Info", MemoryManager::GarbageCollector.size());
#endif
for(unsigned int x = 0; x < MemoryManager::GarbageCollector.size(); x++)
{
errno = lasterror = 0;
free(GarbageCollector[x]);
lasterror = errno;
if(lasterror > 0)
MainDebugger.Log("MemoryManager::Flush() Error: %s (%i : %p)", "Error", strerror(lasterror), x, GarbageCollector[x]);
}
GarbageCollector.clear();
return;
}
The problem seems to be with the function "MultiAddToGC". When I do this in my main file:
MemoryManager::MultiAddToGC(3,tst,mfile,testfile);
MemoryManager::Flush();
It works fine if I'm in Debug mode (I'm doing this in VC++ 2010). But if I change to Release mode, it gives me an error inside MemoryManager::Flush() while calling the free() function (something about the stack being corrupted). I can continue, if I continue, I get the following in my log:
15:12:26 f 00 (0 fps) | Error > MemoryManager::Flush() Error: Invalid
argument (2 : 00D44784)
However, if I do this:
MemoryManager::AddToGC(tst);
MemoryManager::AddToGC(mfile);
MemoryManager::AddToGC(testfile);
MemoryManager::Flush();
It works both in Debug, and release. No errors. So I'm assuming the error is in MultiAddToGC(), but I can't find it. The following code is the header, "memmanager.h":
#ifndef __MEMMANAGER_H__
#define __MEMMANAGER_H__
#include <vector>
#include <malloc.h>
#include <stdarg.h>
#include "..\core.h"
#include "..\DEBUGGER\debugger.h"
extern bool __DEBUG__MODE__;
extern GameDebugger MainDebugger;
class MemoryManager {
public:
static void MemoryManager::AddToGC(void*);
static void MemoryManager::MultiAddToGC(int, void*,...);
static void MemoryManager::Flush();
private:
static std::vector<void*> GarbageCollector;
protected:
};
#endif
Any help/tips/advise is wellcome.
In va_start(chrstr, args);, the second parameter should be the last named parameter, so myThing (but you probably want to remove myThing in fact).
but I would write
template <typename...Ts>
void MemoryManager::MultiAddToGC(Ts*... myThings)
{
if(sizeof...(myThings) < 1)
return;
#ifdef _DEBUG
MainDebugger.Log("Adding %i elements to GC", "Info", sizeof...(myThings));
#endif
int dummy[] = {(MemoryManager::GarbageCollector.push_back(myThings), 0)...};
(void) dummy; // avoid warning for unused variable
}
or simply
void MemoryManager::MultiAddToGC(const std::initializer_list<void*>& myThings)
{
if(myThings.size() < 1)
return;
#ifdef _DEBUG
MainDebugger.Log("Adding %i elements to GC", "Info", myThings.size());
#endif
MemoryManager::GarbageCollector.insert(MemoryManager::GarbageCollector.begin(),
myThings.begin(), myThings.end());
}
I'm trying to implement a basic application on an embedded device using lwIP.
I got the basic project set up, the retarget-ing works as expected.
However when I add lwIP into the project and call lwip_init() I have no more output. If I leave out the call to lwip_init() everything is back to normal.
What's more interesting, is that if I call fopen() the same behavior appears as before, I have no other output on the serial. I get no output no matter if I use the retarget-ed printf() function or if I use the driver's function USART_SendData().
What did I got wrong?
I posted below the retarget.c file and my Main.cpp (if I un-comment either BLOCK 1 or BLOCK 2, I have no more output) along with the initialization code.
Note: the retarget-ed functions send data to USART3 and USART_SendData() sends it to USART1.
I also tried to use the ITM port for debugging, but I get the same behavior.
retarget.c:
#include <stdio.h>
#include <LibSTM/stm32f10x_usart.h>
#include <rt_sys.h>
#pragma import(__use_no_semihosting_swi)
struct __FILE { int handle; };
FILE __stdout;
FILE __stdin;
FILE __stderr;
FILE *__aeabi_stdin;
FILE *__aeabi_stdout;
FILE *__aeabi_stderr;
int fputc(int ch, FILE *f)
{
while (!(USART3->SR & 0x0080));
USART3->DR = ch;
return (ch);
}
int ferror(FILE *f)
{
return EOF;
}
void _ttywrch(int ch)
{
fputc(ch, stdout);
}
void _sys_exit(int return_code)
{
label: goto label;
}
//retargeting since otherwise lwIP inclusion produces errors
//http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3844.html
#define DEFAULT_HANDLE 0x100
const char __stdin_name[] = "my_stdin";
const char __stdout_name[] = "my_stdout";
const char __stderr_name[] = "my_stderr";
int _sys_write(FILEHANDLE fh, const unsigned char * buf, unsigned len, int mode){
int i;
for(i=0;i<len;i++)
{
fputc(buf[i], stdout);
}
return 0;
}
FILEHANDLE _sys_open(const char* name, int openmode){
return DEFAULT_HANDLE;
}
int _sys_istty(FILEHANDLE fh){
return 0;
}
int _sys_seek(FILEHANDLE fh, long pos){
return -1;
}
long _sys_flen(FILEHANDLE fh){
return 0;
}
int _sys_close(FILEHANDLE fh){
return 0;
}
int _sys_ensure(FILEHANDLE fh){
return 0;
}
main.cpp:
#include <HW/HWSetup.h>
#include <stdio.h>
#include "lwip/init.h"
int main()
{
HWSetup();
printf("Start!\r\n");
/* //BLOCK 1
FILE *f;
f = fopen("foobar", "r");
*/
/* //BLOCK 2
lwip_init();
*/
printf("End!\r\n");
while(1){
USART_SendData(USART1, 'x');
}
}
HWSetup.cpp:
#include <HW/HWSetup.h>
#include <stdio.h>
void SetupGPIO()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
}
void SetupTraces()
{
GPIO_InitTypeDef GPIO_InitStructure;
// Trace GPIO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Trace USART
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 921600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
}
void SetupUSART_RS232()
{
GPIO_InitTypeDef GPIO_InitStructure;
// USART1 GPIO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1,&USART_InitStructure);
}
void HWSetup()
{
SetupGPIO();
SetupUSART_RS232();
SetupTraces();
}
I also tried debugging the code, but after I step in at BX R0 (which I expect to get me into main()) I just have to stop debugging since I cannot see where it's going, nor I have any other control over the debugger:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
The following code worked for me with a LPC1788 and µVision5 with and without microlib:
//retarget minimum requirements to work with printf
#include <stdio.h>
#include <rt_misc.h>
#ifndef __MICROLIB
#pragma import(__use_no_semihosting_swi) // some projects need this, some don't like it
#endif
#include <rt_sys.h>
extern void $Super$$_sys_open(void);
extern int sendchar(int ch); //needed for printf
extern int getch(void); //needed for scanf
FILEHANDLE $Sub$$_sys_open(const char *name, int openmode)
{
return 1; /* everything goes to the same output */
}
extern void $Super$$_sys_close(void);
int $Sub$$_sys_close(FILEHANDLE fh)
{
return 0;
}
extern void $Super$$_sys_write(void);
int $Sub$$_sys_write(FILEHANDLE fh, const unsigned char *buf,
unsigned len, int mode)
{
//your_device_write(buf, len);
return 0;
}
extern void $Super$$_sys_read(void);
int $Sub$$_sys_read(FILEHANDLE fh, unsigned char *buf,
unsigned len, int mode)
{
return -1; /* not supported */
}
extern void $Super$$_ttywrch(void);
void $Sub$$_ttywrch(int ch)
{
//char c = ch;
//your_device_write(&c, 1);
sendchar(ch);
}
extern void $Super$$_sys_istty(void);
int $Sub$$_sys_istty(FILEHANDLE fh)
{
return 0; /* buffered output */
}
extern void $Super$$_sys_seek(void);
int $Sub$$_sys_seek(FILEHANDLE fh, long pos)
{
return -1; /* not supported */
}
extern void $Super$$_sys_flen(void);
long $Sub$$_sys_flen(FILEHANDLE fh)
{
return -1; /* not supported */
}
extern void $Super$$_sys_exit(void);
long $Sub$$_sys_exit(FILEHANDLE fh)
{
return -1; /* not supported */
}
//eof
hope, it helps you, too.
Don't forget to initialize uart in main, or try to put initialization in _sys_open()
Question
I have a method to wrap functions by replacing them with a macro so that I can log the call and the return code. Here's an example which works:
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
void LogRet(char *fn, char *file, char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
The macro for the function it substitutes calls the function and logs the function name and where it was called from as well as the return code. Wrapping any function uses the same syntax and just needs the function name replaced in the macro 3 times. What I would like to do is create wrapper macro where the macro redefine for foo would be something similar to:
#define foo(args, ...) WRAPPPER(foo)
I understand the basics of stringify and double stringify but I can't even get the WRAPPER macro to do the real function call. Ideally, I'd like to get it down to single WRAP(foo) statement. The reason is, I have around 100 or more functions I'd like to wrap and it would like to do it simple from one simple force include file. I'm coming to the conclusion that it's not possible but I thought I'd ask here before abandoning the idea. I'm using clang and vc++ if that makes any difference but it would be nice to have this on any compiler as I debug a lot of different systems.
Adaptation of Jonathan Leffler's answer
As I'm new here I wasn't sure if this should be a separate answer or and edit update. This is essentially Jonathan Leffler's answer. It's in a functional example. While the 2 functions it calls are pointless, the goal was to have a macro that could wrap any function with any arg list. My main use is in logging the use flow in a large code library that has a problem. Also, my original sample has one glaring weakness. By storing the return code in a global it is not thread safe without cumbersome preparation in TLS. The global is now removed and the macro no longer uses the sequence operator to return the value, it is preserved and returned by the logging function. Also, as Augurar pointed out and shown in Jonathan's example. If the macro is declared in the same file as the function declaration, it requires parentheses.
#include <stdio.h>
#include <string.h>
int foo(int a, int b);
int bar(int a, char *b, int *c);
#if defined (_DEBUG) || defined (DEBUG)
// Short version of __FILE__ without path requires runtime parsing
#define __SFILE__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#ifdef WIN32
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __FUNCTION__, __LINE__, (func)(__VA_ARGS__))
#else
#define WRAPPER(func, ...) LogRet(#func, __SFILE__, __func__, __LINE__, (func)(__VA_ARGS__))
#endif
inline int LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
return ret;
}
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
#endif
int main(void)
{
int x = foo(1, 2);
bar(2, "doubled", &x);
return 0;
}
#ifdef foo
#undef foo
#undef bar
#endif
// If and only if the function definition is in the same file with the macros, you must either undefine the macros or
// parenthesize the function e.g. int (foo)(int a, int b) { ... }
int foo(int a, int b)
{
printf("%d + %d = %d\n", a, b, a + b);
return a + b;
}
int (bar)(int a, char *b, int *c)
{
printf("%d %s = %d\n", *c, b, a * *c);
return *c * a;
}
Release build output:
1 + 2 = 3
3 doubled = 6
Debug build output:
1 + 2 = 3
test.cpp.main.35: foo() ret:00000003
3 doubled = 6
test.cpp.main.36: bar() ret:00000006
The main benefit is not having to find every occurrence of foo() or bar() in the code to insert a debug print to log the call and result or whatever debug code you want to insert.
This code looks as if it meets your requirements:
#include <stdio.h>
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
#define foo(args, ...) (rc = (foo)(args, ##__VA_ARGS__), LogRet("foo", __FILE__, __FUNCTION__, __LINE__, rc), rc)
#define bar(args, ...) (rc = (bar)(args, ##__VA_ARGS__), LogRet("bar", __FILE__, __FUNCTION__, __LINE__, rc), rc)
extern void caller1(void);
void caller1(void)
{
int d;
int e = foo(1, 2);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
#undef foo
#undef bar
#define WRAPPER(func, ...) ((rc = (func)(__VA_ARGS__)), LogRet(#func, __FILE__, __func__, __LINE__, rc), rc)
#define foo(...) WRAPPER(foo, __VA_ARGS__)
#define bar(...) WRAPPER(bar, __VA_ARGS__)
extern void caller2(void);
void caller2(void)
{
int d;
int e = foo(2, 3);
int f = bar(3, "abc", &d);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
Example output:
wrapper.c.caller1.23: foo() ret:00000000
wrapper.c.caller1.24: bar() ret:00000003
caller1(): 0 + 0 + 3 = 3
wrapper.c.caller2.41: foo() ret:00000002
wrapper.c.caller2.42: bar() ret:00000003
caller2(): 0 + 2 + 3 = 5
Pre-processed source code (excluding the output from #include <stdio.h>):
# 2 "wrapper.c" 2
int rc;
int foo(int a, int b);
int bar(int a, char *b, int *c);
extern void LogRet(const char *fn, const char *file, const char *from, int ln, int ret);
void LogRet(const char *fn, const char *file, const char *from, int ln, int ret)
{
printf("%s.%s.%d: %s() ret:%08x\n", file, from, ln, fn, ret);
}
extern void caller1(void);
void caller1(void)
{
int d;
int e = (rc = (foo)(1, 2), LogRet("foo", "wrapper.c", __FUNCTION__, 23, rc), rc);
int f = (rc = (bar)(3, "abc", &d), LogRet("bar", "wrapper.c", __FUNCTION__, 24, rc), rc);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
# 36 "wrapper.c"
extern void caller2(void);
void caller2(void)
{
int d;
int e = ((rc = (foo)(2, 3)), LogRet("foo", "wrapper.c", __func__, 41, rc), rc);
int f = ((rc = (bar)(3, "abc", &d)), LogRet("bar", "wrapper.c", __func__, 42, rc), rc);
printf("%s(): %d + %d + %d = %d\n", __func__, d, e, f, d + e + f);
}
int (foo)(int a, int b)
{
return (a + b) % 3;
}
int (bar)(int a, char *b, int *c)
{
int d = b[a];
*c = d;
return a + d;
}
int main(void)
{
caller1();
caller2();
return 0;
}
Tested with GCC 4.8.2 on Mac OS X 10.9.2.