Super-Simple gprof
Published 2011-1-25Goal
Get started with gprof
.
Terms
gprof
- shows call graphs, processing time, etc for your applicationgprof-helper.so
- This library must be prefix-loaded in order to correct innate problems withgprof
gmon.out
is generated by your application, not bygprof
a.out
substitute this with the name of your application whereever you see it in documentationgcc
must use-pg
as a prefix (not suffix) compile option for both the compiler and linker
Example
Let's say we have a multi-threaded application called hello
. This is how we get gprof
info for it:
First compile it with -pg
for gprof
support:
CFLAGS="-pg"
LDFLAGS="-pg"
gcc ${CFLAGS} -o hello.o -c hello.c
gcc ${LDFLAGS} -o hello hello.o
Then compile the gprof-helper.c
monkey-patch (hot-fix)
wget http://sam.zoy.org/writings/programming/gprof-helper.c
gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl
Now load your application with the gprof
fix pre-loaded and interpret the results with gprof
LD_PRELOAD=./gprof-helper.so hello
mv gmon.out hello.gmon.out
gprof hello hello.gmon.out > hello.gprof.results.txt
cat hello.gprof.results.txt
Note: If your application is a server, you must implement some sort of remote that can cause it to exit cleanly (return 0
, not SIGINT
, etc)
Appendix
I advise you to get gprof-helper.c
from the original source:
"HOWTO: using gprof
with multithreaded applications"
However, since it doesn't appear to be under active development (it's Jan 2011 now and the library hasn't been updated since Dec 2004) I provide it here for convenience.
gprof-helper.c
:
/* 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 ---- 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;
}
By AJ ONeal
Did I make your day?
Buy me a coffee
(you can learn about the bigger picture I'm working towards on my patreon page )