Home > Blockchain >  C function that executes some code when the function that called it exits
C function that executes some code when the function that called it exits

Time:08-19

I want a function which achieves something like the behavior shown in the pseudo C code listed. I figure that this might be possible via the use of function pointers?

If this pattern I've dreamed up is terrible/impossible to implement, I'm more than happy to accept alternatives as an answer if it achieves the same kind of functionality.

void log_func(const char* name, /*other args*/) {
  printf("called function: %s\n", name);
  if (/*calling function exited*/)
    printf("exited function: %s\n", name);
}

void example_func() {
  log_func(__func__); // __func__ macro is expanded to be function name as a c-string
  printf("This function does nothing\n");
}

output:

called function: example_func
This function does nothing
exited function: example_func

CodePudding user response:

Something like this works for functions returning void. Would have to be more clever for arbitrary returns.

#include<stdio.h>

#define WRAPPER
#ifdef WRAPPER
#define LOG_FUNC(fname, ...) \
    do { \
    printf("called function: %s\n", #fname); \
    fname(__VA_ARGS__); \
    printf("exited function: %s\n", #fname); \
    } while(0)
#else
#define LOG_FUNC(fname, ...) \
    fname(__VA_ARGS__)
#endif
     
void phello(int n, char *s) {
    for(int i=0; i<n; i  ) {
        printf("%s\n", s);
    }
}

int main()
{  
    LOG_FUNC(phello, 3, "Hello, World!");
}

Output

called function: phello
Hello, World!
Hello, World!
Hello, World!
exited function: phello

CodePudding user response:

If you need to do this for debugging in gcc, the GCC Function Instrumentation feature can help.

An example:

/* demo.c */

void example_func_2(void)
{

}

void example_func_1(void)
{
    example_func_2();
}

int main(void)
{
    example_func_1();
    return 0;
}

/* instrument.c */

#include <stdio.h>
#include <dlfcn.h>

void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void *callee, void *caller)
{
    Dl_info info;

    printf("Entering:");
    if (dladdr(callee, &info))
    {
        printf(" callee : %-20s", info.dli_sname ? info.dli_sname : "<unknown>");
    }
    if (dladdr(caller, &info))
    {
        printf(" caller : %s", info.dli_sname ? info.dli_sname : "<unknown>");
    }
    printf("\n");
}

void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *callee, void *caller)
{
    Dl_info info;

    printf("Exiting: ");
    if (dladdr(callee, &info))
    {
        printf(" callee : %-20s", info.dli_sname ? info.dli_sname : "<unknown>");
    }
    if (dladdr(caller, &info))
    {
        printf(" caller : %s", info.dli_sname ? info.dli_sname : "<unknown>");
    }
    printf("\n");
}

Compile with:

gcc -Wall -Wpedantic -Wextra -finstrument-functions -export-dynamic -D_GNU_SOURCE demo.c instrument.c -o demo -ldl

Run the program:

./demo

The ouput is:

Entering: callee : main                 caller : <unknown>
Entering: callee : example_func_1       caller : main
Entering: callee : example_func_2       caller : example_func_1
Exiting:  callee : example_func_2       caller : example_func_1
Exiting:  callee : example_func_1       caller : main
Exiting:  callee : main                 caller : <unknown>
  • Related