UNIX to Linux Porting: A Comprehensive Reference

E.1. gprof helper Module for Multithreaded Applications

gprof, when used with multithreaded applications on certain kernels (such as Linux), profiles only the main thread. To work around this situation, we include the gprof helper module from http://sam.zoy.org/writings/programming/gprof.html. Basically, gprof uses the internal ITIMER_PROF timer, which makes the kernel deliver a signal to the application whenever it expires. This module passes this timer data to all spawned threads.

To use the module, compile the following:

$ gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl

Type the following in the command line:

$ LD_PRELOAD=./gprof-helper.so your_program

You should see something like the following on the screen:

pthreads: using profiling hooks for gprof

/* gprof-helper.c -- preload library to profile pthread-enabled programs * * Authors: Sam Hocevar <sam at zoy dot org> * Daniel Jönsson <danieljo at fagotten dot org> * * Reprinted with permission from Sam Hocevar. * * Compilation example: * gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl * * Usage example: * LD_PRELOAD=./gprof-helper.so your_program */ #define _GNU_SOURCE #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <pthread.h> static void * wrapper_routine(void *); /* Original pthread function */ static int (*pthread_create_orig)(pthread_t *__restrict, __const pthread_attr_t *__restrict, void *(*)(void *), void *__restrict) = NULL; /* Library initialization function */ void wooinit(void) __attribute__((constructor)); void wooinit(void) { pthread_create_orig = dlsym(RTLD_NEXT, "pthread_create"); fprintf(stderr, "pthreads: using profiling hooks for gprof\n"); if(pthread_create_orig == NULL) { char *error = dlerror(); if(error == NULL) { error = "pthread_create is NULL"; } fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } } /* Our data structure passed to the wrapper */ typedef struct wrapper_s { void * (*start_routine)(void *); void * arg; pthread_mutex_t lock; pthread_cond_t wait; struct itimerval itimer; } wrapper_t; /* The wrapper function in charge for setting the itimer value */ static void * wrapper_routine(void * data) { /* Put user data in thread-local variables */ void * (*start_routine)(void *) = ((wrapper_t*)data)->start_routine; void * arg = ((wrapper_t*)data)->arg; /* Set the profile timer value */ setitimer(ITIMER_PROF, &((wrapper_t*)data)->itimer, NULL); /* Tell the calling thread that we don't need its data anymore */ pthread_mutex_lock(&((wrapper_t*)data)->lock); pthread_cond_signal(&((wrapper_t*)data)->wait); pthread_mutex_unlock(&((wrapper_t*)data)->lock); /* Call the real function */ return start_routine(arg); } /* Our wrapper function for the real pthread_create() */ int pthread_create(pthread_t *__restrict thread, __const pthread_attr_t *__restrict attr, void * (*start_routine)(void *), void *__restrict arg) { wrapper_t wrapper_data; int i_return; /* Initialize the wrapper structure */ wrapper_data.start_routine = start_routine; wrapper_data.arg = arg; getitimer(ITIMER_PROF, &wrapper_data.itimer); pthread_cond_init(&wrapper_data.wait, NULL); pthread_mutex_init(&wrapper_data.lock, NULL); pthread_mutex_lock(&wrapper_data.lock); /* The real pthread_create call */ i_return = pthread_create_orig(thread, attr, &wrapper_routine, &wrapper_data); /* If the thread was successfully spawned, wait for the data * to be released */ if(i_return == 0) { pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock); } pthread_mutex_unlock(&wrapper_data.lock); pthread_mutex_destroy(&wrapper_data.lock); pthread_cond_destroy(&wrapper_data.wait); return i_return; }

Категории