2007-04-29 17:35:43 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
LD_PRELOAD library that gives statistic on number of roundtrips in an application.
|
|
|
|
|
|
|
|
$XREPLY_BACKTRACE defines whether and how backtraces will be printed for every
|
|
|
|
roundtrip. If not set, only total number of roundtrips is printed after the process
|
|
|
|
exits. If set to a number, backtrace for every roundtrip will be printed, and the
|
|
|
|
backtraces will be as deep as the given number. If set to C<number> (e.g. C10),
|
|
|
|
the backtraces will be "compressed" - every backtrace will be printed only once
|
|
|
|
after the process exits, together with number of times it occurred.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <execinfo.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <X11/Xlibint.h>
|
|
|
|
|
|
|
|
/* Since these symbols are weak, the apps can provide their own, and therefore
|
|
|
|
e.g. temporarily suspend counting of roundtrips. At least theoretically,
|
|
|
|
I haven't really tried it.
|
|
|
|
*/
|
|
|
|
__attribute((weak)) long ___xreply_reply_count = 0;
|
|
|
|
__attribute((weak)) int ___xreply_reply_enabled = 1;
|
|
|
|
|
|
|
|
#define MAX_BACKTRACES 1024
|
|
|
|
|
|
|
|
extern long ___xreply_reply_count;
|
|
|
|
extern int ___xreply_reply_enabled;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
typedef Status(*xreply_ptr_t)(Display*, xReply*, int, Bool);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
static xreply_ptr_t xreply_ptr = NULL;
|
|
|
|
static int xreply_backtrace_set = 0;
|
|
|
|
static int xreply_backtrace_type = 0;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
struct xreply_struct {
|
2007-04-29 17:35:43 +00:00
|
|
|
char* key;
|
|
|
|
char* text;
|
|
|
|
int count;
|
2011-01-30 14:34:42 +00:00
|
|
|
};
|
2007-04-29 17:35:43 +00:00
|
|
|
static struct xreply_struct backtraces[ MAX_BACKTRACES ];
|
|
|
|
static int backtraces_size = 0;
|
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
static int xreply_compare(const void* left, const void* right)
|
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
int left_count = ((struct xreply_struct*)left)->count;
|
|
|
|
int right_count = ((struct xreply_struct*)right)->count;
|
|
|
|
return right_count - left_count;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
static void xreply_print(void)
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
char tmp[ 1024 ];
|
|
|
|
int fd;
|
2011-01-30 14:34:42 +00:00
|
|
|
fd = open("/proc/self/cmdline", O_RDONLY);
|
|
|
|
if(fd >= 0) {
|
|
|
|
read(fd, tmp, 1024);
|
2007-04-29 17:35:43 +00:00
|
|
|
tmp[ 1023 ] = '\0';
|
2011-01-30 14:34:42 +00:00
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
fprintf(stderr, "XREPLY (%d : %s): %ld\n", getpid(), tmp, ___xreply_reply_count);
|
|
|
|
if(xreply_backtrace_type < 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
int i;
|
2011-01-30 14:34:42 +00:00
|
|
|
qsort(backtraces, backtraces_size, sizeof(struct xreply_struct), xreply_compare);
|
|
|
|
for(i = 0;
|
|
|
|
i < backtraces_size;
|
|
|
|
++i)
|
|
|
|
fprintf(stderr, "%d:%s\n\n", backtraces[ i ].count, backtraces[ i ].text);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
static void xreply_backtrace()
|
2011-01-30 14:34:42 +00:00
|
|
|
{
|
2007-04-29 17:35:43 +00:00
|
|
|
void* trace[256];
|
|
|
|
int n = backtrace(trace, 256);
|
2011-01-30 14:34:42 +00:00
|
|
|
char** strings = backtrace_symbols(trace, n);
|
2007-04-29 17:35:43 +00:00
|
|
|
|
2011-01-30 14:34:42 +00:00
|
|
|
if(xreply_backtrace_type > 0) {
|
|
|
|
fprintf(stderr, "%ld [\n", ___xreply_reply_count);
|
|
|
|
if(n > xreply_backtrace_type)
|
2007-04-29 17:35:43 +00:00
|
|
|
n = xreply_backtrace_type;
|
|
|
|
int i;
|
2011-01-30 14:34:42 +00:00
|
|
|
for(i = 0;
|
|
|
|
i < n;
|
|
|
|
++i)
|
|
|
|
fprintf(stderr, "%d: %s\n", i, strings[ i ]);
|
|
|
|
fprintf(stderr, "]\n");
|
|
|
|
} else {
|
2007-04-29 17:35:43 +00:00
|
|
|
char stack[ 256 * 20 ];
|
|
|
|
int pos = 0;
|
|
|
|
int i;
|
|
|
|
stack[ 0 ] = '\0';
|
2011-01-30 14:34:42 +00:00
|
|
|
if(n > -xreply_backtrace_type)
|
2007-04-29 17:35:43 +00:00
|
|
|
n = -xreply_backtrace_type;
|
2011-01-30 14:34:42 +00:00
|
|
|
for(i = 0;
|
|
|
|
i < n;
|
|
|
|
++i) {
|
|
|
|
const char* start = strrchr(strings[ i ], '[');
|
|
|
|
if(start == NULL)
|
|
|
|
assert(!"No [ in address.");
|
2007-04-29 17:35:43 +00:00
|
|
|
long addr;
|
2011-01-30 14:34:42 +00:00
|
|
|
if(sscanf(start + 1, "0x%lx", &addr) != 1)
|
|
|
|
assert(!"Failed to parse address.");
|
|
|
|
if(sizeof(void*) == 4) {
|
|
|
|
sprintf(stack + pos, "0x%8lx", addr);
|
2007-04-29 17:35:43 +00:00
|
|
|
pos += 10;
|
2011-01-30 14:34:42 +00:00
|
|
|
} else if(sizeof(void*) == 8) {
|
|
|
|
sprintf(stack + pos, "0x%16lx", addr);
|
2007-04-29 17:35:43 +00:00
|
|
|
pos += 18;
|
2011-01-30 14:34:42 +00:00
|
|
|
} else
|
|
|
|
assert(!"Unknown sizeof( void* ).");
|
|
|
|
}
|
|
|
|
for(i = 0;
|
|
|
|
i < backtraces_size;
|
|
|
|
++i)
|
|
|
|
if(strcmp(backtraces[ i ].key, stack) == 0) {
|
2007-04-29 17:35:43 +00:00
|
|
|
++backtraces[ i ].count;
|
|
|
|
break;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if(i == backtraces_size) {
|
2007-04-29 17:35:43 +00:00
|
|
|
int stack_text_size = 10;
|
|
|
|
char* stack_text;
|
|
|
|
char* stack_text_pos;
|
2011-01-30 14:34:42 +00:00
|
|
|
for(i = 0;
|
|
|
|
i < n;
|
|
|
|
++i)
|
|
|
|
stack_text_size += strlen(strings[ i ]) + 5;
|
|
|
|
stack_text = stack_text_pos = malloc(stack_text_size);
|
|
|
|
for(i = 0;
|
|
|
|
i < n;
|
|
|
|
++i) {
|
|
|
|
stack_text_pos = stpcpy(stack_text_pos, "\n");
|
|
|
|
stack_text_pos = stpcpy(stack_text_pos, strings[ i ]);
|
|
|
|
}
|
|
|
|
backtraces[ backtraces_size ].key = strdup(stack);
|
2007-04-29 17:35:43 +00:00
|
|
|
backtraces[ backtraces_size ].text = stack_text;
|
|
|
|
backtraces[ backtraces_size ].count = 1;
|
|
|
|
++backtraces_size;
|
2011-01-30 14:34:42 +00:00
|
|
|
if(backtraces_size >= MAX_BACKTRACES)
|
|
|
|
assert(!"MAX_BACKTRACES reached.");
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
free(strings);
|
|
|
|
}
|
2007-04-29 17:35:43 +00:00
|
|
|
|
|
|
|
Status
|
2011-01-30 14:34:42 +00:00
|
|
|
_XReply(dpy, rep, extra, discard)
|
|
|
|
register Display *dpy;
|
|
|
|
register xReply *rep;
|
|
|
|
int extra; /* number of 32-bit words expected after the reply */
|
|
|
|
Bool discard; /* should I discard data following "extra" words? */
|
|
|
|
{
|
|
|
|
if(___xreply_reply_enabled)
|
2007-04-29 17:35:43 +00:00
|
|
|
++___xreply_reply_count;
|
2011-01-30 14:34:42 +00:00
|
|
|
if(xreply_backtrace_set == 0) {
|
|
|
|
if(getenv("XREPLY_BACKTRACE") != NULL) {
|
|
|
|
// C<number> - compress backtraces, saved as negative value in xreply_backtrace_type
|
|
|
|
if(getenv("XREPLY_BACKTRACE")[ 0 ] == 'C')
|
|
|
|
xreply_backtrace_type = -atoi(getenv("XREPLY_BACKTRACE") + 1);
|
2007-04-29 17:35:43 +00:00
|
|
|
else // <number> - print the backtrace every time
|
2011-01-30 14:34:42 +00:00
|
|
|
xreply_backtrace_type = atoi(getenv("XREPLY_BACKTRACE"));
|
|
|
|
} else
|
2007-04-29 17:35:43 +00:00
|
|
|
xreply_backtrace_type = 0;
|
2011-01-30 14:34:42 +00:00
|
|
|
}
|
|
|
|
if(xreply_backtrace_type != 0)
|
2007-04-29 17:35:43 +00:00
|
|
|
xreply_backtrace();
|
2011-01-30 14:34:42 +00:00
|
|
|
if(xreply_ptr == NULL) {
|
|
|
|
xreply_ptr = (xreply_ptr_t)dlsym(RTLD_NEXT, "_XReply");
|
|
|
|
if(xreply_ptr == NULL)
|
|
|
|
assert(!"dlsym() failed.");
|
|
|
|
atexit(xreply_print);
|
2007-04-29 17:35:43 +00:00
|
|
|
}
|
2011-01-30 14:34:42 +00:00
|
|
|
return xreply_ptr(dpy, rep, extra, discard);
|
|
|
|
}
|