summaryrefslogtreecommitdiffstats
path: root/twin/tools/xreply/xreply.c
blob: ce5584190b32609edd803acbc62c560a251f4c68 (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*

 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 occured.

*/

#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;

typedef Status (*xreply_ptr_t)(Display*,xReply*,int,Bool);

static xreply_ptr_t xreply_ptr = NULL;
static int xreply_backtrace_set = 0;
static int xreply_backtrace_type = 0;

struct xreply_struct
    {
    char* key;
    char* text;
    int count;
    };
static struct xreply_struct backtraces[ MAX_BACKTRACES ];
static int backtraces_size = 0;

static int xreply_compare( const void* left, const void* right )
    {
    int left_count = ((struct xreply_struct*)left)->count;
    int right_count = ((struct xreply_struct*)right)->count;
    return right_count - left_count;
    }

static void xreply_print(void)
    {
    char tmp[ 1024 ];
    int fd;
    fd = open( "/proc/self/cmdline", O_RDONLY );
    if( fd >= 0 )
        {
        read( fd, tmp, 1024 );
        tmp[ 1023 ] = '\0';
        close( fd );
        }
    fprintf( stderr, "XREPLY (%d : %s): %ld\n", getpid(), tmp, ___xreply_reply_count );
    if( xreply_backtrace_type < 0 )
        {
        int i;
        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 );
        }
    }

static void xreply_backtrace()
    {
    void* trace[256];
    int n = backtrace(trace, 256);
    char** strings = backtrace_symbols (trace, n);

    if( xreply_backtrace_type > 0 )
        {
        fprintf( stderr, "%ld [\n", ___xreply_reply_count );
        if( n > xreply_backtrace_type )
            n = xreply_backtrace_type;
        int i;
        for( i = 0;
             i < n;
             ++i )
            fprintf( stderr, "%d: %s\n", i, strings[ i ] );
        fprintf( stderr, "]\n" );
        }
    else
        {
        char stack[ 256 * 20 ];
        int pos = 0;
        int i;
        stack[ 0 ] = '\0';
        if( n > -xreply_backtrace_type )
            n = -xreply_backtrace_type;
        for( i = 0;
             i < n;
             ++i )
            {
            const char* start = strrchr( strings[ i ], '[' );
            if( start == NULL )
                assert( !"No [ in address." );
            long addr;
            if( sscanf( start + 1, "0x%lx", &addr ) != 1 )
                assert( !"Failed to parse address." );
            if( sizeof( void* ) == 4 )
                {
                sprintf( stack + pos, "0x%8lx", addr );
                pos += 10;
                }
            else if( sizeof( void* ) == 8 )
                {
                sprintf( stack + pos, "0x%16lx", addr );
                pos += 18;
                }
            else
                assert( !"Unknown sizeof( void* )." );
            }
        for( i = 0;
             i < backtraces_size;
             ++i )
            if( strcmp( backtraces[ i ].key, stack ) == 0 )
                {
                ++backtraces[ i ].count;
                break;
                }
        if( i == backtraces_size )
            {
            int stack_text_size = 10;
            char* stack_text;
            char* stack_text_pos;
            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 );
            backtraces[ backtraces_size ].text = stack_text;
            backtraces[ backtraces_size ].count = 1;
            ++backtraces_size;
            if( backtraces_size >= MAX_BACKTRACES )
                assert( !"MAX_BACKTRACES reached." );
            }
        }
    free (strings);
    }

Status
_XReply (dpy, rep, extra, discard)
    Display *dpy;
    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 )
        ++___xreply_reply_count;
    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 );
            else // <number> - print the backtrace every time
                xreply_backtrace_type = atoi( getenv( "XREPLY_BACKTRACE" ));
            }
        else
            xreply_backtrace_type = 0;
        }
    if( xreply_backtrace_type != 0 )
        xreply_backtrace();
    if( xreply_ptr == NULL )
        {
        xreply_ptr = (xreply_ptr_t)dlsym( RTLD_NEXT, "_XReply" );
        if( xreply_ptr == NULL )
            assert( !"dlsym() failed." );
        atexit( xreply_print );
        }
    return xreply_ptr( dpy, rep, extra, discard );
    }