summaryrefslogtreecommitdiff
path: root/gtk2_ardour/gprofhelper.c
blob: 2732f76033524ce27b48244b429b8dc7c0490e2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* 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 -nostdlib -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 _init(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", 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;
}