| /* 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> | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the Do What The Fuck You Want To | |
| * Public License as published by Banlu Kemiyatorn. See | |
| * http://sam.zoy.org/projects/COPYING.WTFPL for more details. | |
| * | |
| * 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; | |
| } |