1 /*
2 (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org)
3 (c) Copyright 2000-2004 Convergence (integrated media) GmbH
4
5 All rights reserved.
6
7 Written by Denis Oliver Kropp <dok@directfb.org>,
8 Andreas Hundt <andi@fischlustig.de>,
9 Sven Neumann <neo@directfb.org>,
10 Ville Syrjälä <syrjala@sci.fi> and
11 Claudio Ciccani <klan@users.sf.net>.
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
22
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the
25 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 Boston, MA 02111-1307, USA.
27 */
28
29 #include <config.h>
30
31 #include <pthread.h>
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/mman.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <string.h>
41
42 #include <direct/build.h>
43 #include <direct/list.h>
44 #include <direct/log.h>
45 #include <direct/memcpy.h>
46 #include <direct/messages.h>
47 #include <direct/system.h>
48 #include <direct/thread.h>
49 #include <direct/trace.h>
50 #include <direct/util.h>
51
52
53 #ifdef PIC
54 #define DYNAMIC_LINKING
55 #endif
56
57
58 #if DIRECT_BUILD_TRACE
59
60 #ifdef DYNAMIC_LINKING
61 #include <dlfcn.h>
62 #endif
63
64 #define MAX_BUFFERS 200
65 #define MAX_LEVEL 200
66
67 #define NAME_LEN 92
68
69
70 typedef enum {
71 TF_NONE = 0x00000000,
72
73 TF_DEBUG = 0x00000001
74 } TraceFlags;
75
76 typedef struct {
77 void *addr;
78 TraceFlags flags;
79 } Trace;
80
81 struct __D_DirectTraceBuffer {
82 pid_t tid;
83 char *name;
84 int level;
85 bool in_trace;
86 Trace trace[MAX_LEVEL];
87 };
88
89 /**************************************************************************************************/
90
91 static DirectTraceBuffer *buffers[MAX_BUFFERS];
92 static int buffers_num = 0;
93 #if HAVE_DECL_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
94 static pthread_mutex_t buffers_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
95 #else
96 static pthread_mutex_t buffers_lock = PTHREAD_MUTEX_INITIALIZER;
97 #endif
98 static pthread_key_t trace_key = -1;
99
100 /**************************************************************************************************/
101
102 __attribute__((no_instrument_function))
103 static void
buffer_destroy(void * arg)104 buffer_destroy( void *arg )
105 {
106 int i;
107 DirectTraceBuffer *buffer = arg;
108
109 pthread_mutex_lock( &buffers_lock );
110
111 /* Remove from list. */
112 for (i=0; i<buffers_num; i++) {
113 if (buffers[i] == buffer)
114 break;
115 }
116
117 for (; i<buffers_num-1; i++)
118 buffers[i] = buffers[i+1];
119
120 buffers_num--;
121
122 /* Deallocate the buffer. */
123 direct_trace_free_buffer( buffer );
124
125 pthread_mutex_unlock( &buffers_lock );
126 }
127
128 __attribute__((no_instrument_function))
129 static inline DirectTraceBuffer *
get_trace_buffer(void)130 get_trace_buffer( void )
131 {
132 DirectTraceBuffer *buffer;
133
134 buffer = pthread_getspecific( trace_key );
135 if (!buffer) {
136 const char *name = direct_thread_self_name();
137
138 pthread_mutex_lock( &buffers_lock );
139
140 if (!buffers_num)
141 pthread_key_create( &trace_key, buffer_destroy );
142 else if (buffers_num == MAX_BUFFERS) {
143 D_ERROR( "Direct/Trace: Maximum number of threads (%d) reached!\n", MAX_BUFFERS );
144 pthread_mutex_unlock( &buffers_lock );
145 return NULL;
146 }
147
148 pthread_setspecific( trace_key,
149 buffer = calloc( 1, sizeof(DirectTraceBuffer) ) );
150
151 buffer->tid = direct_gettid();
152 buffer->name = name ? strdup( name ) : NULL;
153
154 buffers[buffers_num++] = buffer;
155
156 pthread_mutex_unlock( &buffers_lock );
157 }
158
159 return buffer;
160 }
161
162 /**************************************************************************************************/
163
164 typedef struct {
165 long offset;
166 char name[NAME_LEN];
167 } Symbol;
168
169 typedef struct {
170 DirectLink link;
171
172 char *filename;
173 Symbol *symbols;
174 int capacity;
175 int num_symbols;
176 } SymbolTable;
177
178 static DirectLink *tables = NULL;
179 #if HAVE_DECL_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
180 static pthread_mutex_t tables_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
181 #else
182 static pthread_mutex_t tables_lock = PTHREAD_MUTEX_INITIALIZER;
183 #endif
184
185
186 __attribute__((no_instrument_function))
187 static void
add_symbol(SymbolTable * table,long offset,const char * name)188 add_symbol( SymbolTable *table, long offset, const char *name )
189 {
190 Symbol *symbol;
191
192 if (table->num_symbols == table->capacity) {
193 Symbol *symbols;
194 int capacity = table->capacity * 2;
195
196 if (!capacity)
197 capacity = 256;
198
199 symbols = malloc( capacity * sizeof(Symbol) );
200 if (!symbols) {
201 D_WARN( "out of memory" );
202 return;
203 }
204
205 direct_memcpy( symbols, table->symbols, table->num_symbols * sizeof(Symbol) );
206
207 free( table->symbols );
208
209 table->symbols = symbols;
210 table->capacity = capacity;
211 }
212
213 symbol = &table->symbols[ table->num_symbols++ ];
214
215 symbol->offset = offset;
216
217 direct_snputs( symbol->name, name, NAME_LEN );
218 }
219
220 __attribute__((no_instrument_function))
221 static SymbolTable *
load_symbols(const char * filename)222 load_symbols( const char *filename )
223 {
224 SymbolTable *table;
225 FILE *fp = NULL;
226 bool is_pipe = false;
227 char file[1024];
228 char line[1024];
229 int command_len;
230 char *command;
231 const char *full_path = filename;
232 char *tmp;
233
234 if (filename) {
235 if (access( filename, R_OK ) < 0 && errno == ENOENT) {
236 int len;
237
238 if ((len = readlink( "/proc/self/exe", file, sizeof(file) - 1 )) < 0) {
239 D_PERROR( "Direct/Trace: readlink( \"/proc/self/exe\" ) failed!\n" );
240 return NULL;
241 }
242
243 file[len] = 0;
244
245
246 tmp = strrchr( file, '/' );
247 if (!tmp)
248 return NULL;
249
250 if (strcmp( filename, tmp + 1 ))
251 return NULL;
252
253 full_path = file;
254 }
255 }
256 else {
257 int len;
258
259 if ((len = readlink( "/proc/self/exe", file, sizeof(file) - 1 )) < 0) {
260 D_PERROR( "Direct/Trace: readlink( \"/proc/self/exe\" ) failed!\n" );
261 return NULL;
262 }
263
264 file[len] = 0;
265
266 full_path = file;
267 }
268
269 command_len = strlen( full_path ) + 32;
270 command = alloca( command_len );
271
272 /* First check if there's an "nm-n" file. */
273 tmp = strrchr( full_path, '/' );
274 if (!tmp)
275 return NULL;
276
277 *tmp = 0;
278 snprintf( command, command_len, "%s/nm-n.%s", full_path, tmp + 1 );
279 *tmp = '/';
280
281 if (access( command, R_OK ) == 0) {
282 fp = fopen( command, "r" );
283 if (!fp)
284 D_PERROR( "Direct/Trace: fopen( \"%s\", \"r\" ) failed!\n", command );
285 }
286 else {
287 snprintf( command, command_len, "%s.nm", full_path );
288 if (access( command, R_OK ) == 0) {
289 fp = fopen( command, "r" );
290 if (!fp)
291 D_PERROR( "Direct/Trace: fopen( \"%s\", \"r\" ) failed!\n", command );
292 }
293 }
294
295 /* Fallback to live mode. */
296 if (!fp) {
297 snprintf( command, command_len, "nm -n %s", full_path );
298
299 fp = popen( command, "r" );
300 if (!fp) {
301 D_PERROR( "Direct/Trace: popen( \"%s\", \"r\" ) failed!\n", command );
302 return NULL;
303 }
304
305 is_pipe = true;
306 }
307
308 table = calloc( 1, sizeof(SymbolTable) );
309 if (!table) {
310 D_OOM();
311 goto out;
312 }
313
314 if (filename)
315 table->filename = strdup( filename );
316
317 while (fgets( line, sizeof(line), fp )) {
318 int n;
319 int digits = sizeof(long) * 2;
320 long offset = 0;
321 int length = strlen(line);
322
323 if (line[0] == ' ' || length < (digits + 5) || line[length-1] != '\n')
324 continue;
325
326 if (line[digits + 1] != 't' && line[digits + 1] != 'T' && line[digits + 1] != 'W')
327 continue;
328
329 if (line[digits] != ' ' || line[digits + 2] != ' ' || line[digits + 3] == '.')
330 continue;
331
332 for (n=0; n<digits; n++) {
333 char c = line[n];
334
335 offset <<= 4;
336
337 if (c >= '0' && c <= '9')
338 offset |= c - '0';
339 else
340 offset |= c - 'a' + 10;
341 }
342
343 line[length-1] = 0;
344
345 add_symbol( table, offset, line + digits + 3 );
346 }
347
348 out:
349 if (is_pipe)
350 pclose( fp );
351 else
352 fclose( fp );
353
354 return table;
355 }
356
357 __attribute__((no_instrument_function))
358 static int
compare_symbols(const void * x,const void * y)359 compare_symbols(const void *x, const void *y)
360 {
361 return *((const long*) x) - *((const long*) y);
362 }
363
364 __attribute__((no_instrument_function))
365 static SymbolTable *
find_table(const char * filename)366 find_table( const char *filename )
367 {
368 SymbolTable *table;
369
370 if (filename) {
371 direct_list_foreach (table, tables) {
372 if (table->filename && !strcmp( filename, table->filename ))
373 return table;
374 }
375 }
376 else {
377 direct_list_foreach (table, tables) {
378 if (!table->filename)
379 return table;
380 }
381 }
382
383 return NULL;
384 }
385
386 /**************************************************************************************************/
387
388 __attribute__((no_instrument_function))
389 const char *
direct_trace_lookup_symbol(const char * filename,long offset)390 direct_trace_lookup_symbol( const char *filename, long offset )
391 {
392 Symbol *symbol;
393 SymbolTable *table;
394
395 pthread_mutex_lock( &tables_lock );
396
397 table = find_table( filename );
398 if (!table) {
399 table = load_symbols( filename );
400 if (!table) {
401 pthread_mutex_unlock( &tables_lock );
402 return false;
403 }
404
405 direct_list_prepend( &tables, &table->link );
406 }
407
408 pthread_mutex_unlock( &tables_lock );
409
410 symbol = bsearch( &offset, table->symbols, table->num_symbols,
411 sizeof(Symbol), compare_symbols );
412
413 return symbol ? symbol->name : NULL;
414 }
415
416 __attribute__((no_instrument_function))
417 const char *
direct_trace_lookup_file(void * address,void ** ret_base)418 direct_trace_lookup_file( void *address, void **ret_base )
419 {
420 #ifdef DYNAMIC_LINKING
421 Dl_info info;
422
423 if (dladdr( address, &info )) {
424 if (ret_base)
425 *ret_base = info.dli_fbase;
426
427 return info.dli_fname;
428 }
429 else
430 #endif
431 {
432 if (ret_base)
433 *ret_base = NULL;
434 }
435
436 return NULL;
437 }
438
439 __attribute__((no_instrument_function))
440 static void
direct_trace_print_stack_recursive(DirectTraceBuffer * buffer,int level,int levels)441 direct_trace_print_stack_recursive( DirectTraceBuffer *buffer, int level, int levels )
442 {
443 void *fn = buffer->trace[level].addr;
444
445 #ifdef DYNAMIC_LINKING
446 Dl_info info;
447 #endif
448
449 if (level == levels) {
450 /* lock, print header, unroll stack */
451
452 direct_log_lock( NULL );
453
454 if (buffer->name)
455 direct_log_printf( NULL, "(-) [%5d: -STACK- '%s']\n", buffer->tid, buffer->name );
456 else
457 direct_log_printf( NULL, "(-) [%5d: -STACK- ]\n", buffer->tid );
458
459 return;
460 }
461
462 #ifdef DYNAMIC_LINKING
463 if (dladdr( fn, &info )) {
464 if (info.dli_fname) {
465 const char *symbol = NULL;//info.dli_sname;
466
467 if (!symbol) {
468 symbol = direct_trace_lookup_symbol(info.dli_fname, (long)(fn - info.dli_fbase));
469 if (!symbol) {
470 symbol = direct_trace_lookup_symbol(info.dli_fname, (long)(fn));
471 if (!symbol) {
472 if (info.dli_sname)
473 symbol = info.dli_sname;
474 else
475 symbol = "??";
476 }
477 }
478 }
479
480 direct_trace_print_stack_recursive( buffer, level+1, levels );
481 direct_log_printf( NULL, " #%-2d 0x%08lx in %s () from %s [%p]\n",
482 levels - level - 1, (unsigned long) fn, symbol, info.dli_fname, info.dli_fbase );
483 }
484 else if (info.dli_sname) {
485 direct_trace_print_stack_recursive( buffer, level+1, levels );
486 direct_log_printf( NULL, " #%-2d 0x%08lx in %s ()\n",
487 levels - level - 1, (unsigned long) fn, info.dli_sname );
488 }
489 else {
490 direct_trace_print_stack_recursive( buffer, level+1, levels );
491 direct_log_printf( NULL, " #%-2d 0x%08lx in ?? ()\n",
492 levels - level - 1, (unsigned long) fn );
493 }
494 }
495 else
496 #endif
497 {
498 const char *symbol = direct_trace_lookup_symbol(NULL, (long)(fn));
499
500 direct_trace_print_stack_recursive( buffer, level+1, levels );
501 direct_log_printf( NULL, " #%-2d 0x%08lx in %s ()\n",
502 levels - level - 1, (unsigned long) fn, symbol ? symbol : "??" );
503 }
504
505 if (level == 0) {
506 /* stack unrolled, print end, unlock */
507 direct_log_printf( NULL, "\n" );
508 direct_log_unlock( NULL );
509 }
510 }
511
512 __attribute__((no_instrument_function))
513 void
direct_trace_print_stack(DirectTraceBuffer * buffer)514 direct_trace_print_stack( DirectTraceBuffer *buffer )
515 {
516 int level;
517
518 if (!direct_config->trace)
519 return;
520
521 if (!buffer)
522 buffer = get_trace_buffer();
523
524 if (buffer->in_trace)
525 return;
526
527 buffer->in_trace = true;
528
529
530 level = buffer->level;
531 if (level > MAX_LEVEL) {
532 D_WARN( "only showing %d of %d items", MAX_LEVEL, level );
533 level = MAX_LEVEL;
534 }
535 else if (level == 0) {
536 buffer->in_trace = false;
537 return;
538 }
539
540 /* we are starting with the last entry (0) */
541 direct_trace_print_stack_recursive( buffer, 0, level );
542
543 buffer->in_trace = false;
544 }
545
546 __attribute__((no_instrument_function))
547 void
direct_trace_print_stacks(void)548 direct_trace_print_stacks( void )
549 {
550 int i;
551 DirectTraceBuffer *buffer = get_trace_buffer();
552
553 if (buffer->level)
554 direct_trace_print_stack( buffer );
555
556 pthread_mutex_lock( &buffers_lock );
557
558 for (i=0; i<buffers_num; i++) {
559 if (buffers[i] != buffer && buffers[i]->level)
560 direct_trace_print_stack( buffers[i] );
561 }
562
563 pthread_mutex_unlock( &buffers_lock );
564 }
565
566 __attribute__((no_instrument_function))
567 int
direct_trace_debug_indent(void)568 direct_trace_debug_indent( void )
569 {
570 int in;
571 DirectTraceBuffer *buffer = get_trace_buffer();
572 int level = buffer->level - 1;
573
574 if (level < 0)
575 return 0;
576
577 buffer->trace[level--].flags |= TF_DEBUG;
578
579 for (in=0; level>=0; level--) {
580 if (buffer->trace[level].flags & TF_DEBUG)
581 in++;
582 }
583
584 return in;
585 }
586
587 __attribute__((no_instrument_function))
588 DirectTraceBuffer *
direct_trace_copy_buffer(DirectTraceBuffer * buffer)589 direct_trace_copy_buffer( DirectTraceBuffer *buffer )
590 {
591 int level;
592 DirectTraceBuffer *copy;
593
594 if (!buffer)
595 buffer = get_trace_buffer();
596
597 level = buffer->level;
598 if (level > MAX_LEVEL) {
599 D_WARN( "only copying %d of %d items", MAX_LEVEL, level );
600 level = MAX_LEVEL;
601 }
602
603 copy = calloc( 1, sizeof(*buffer) - sizeof(buffer->trace) + sizeof(buffer->trace[0]) * level );
604 if (!copy)
605 return NULL;
606
607 if (buffer->name)
608 copy->name = strdup( buffer->name );
609
610 copy->tid = buffer->tid;
611 copy->level = buffer->level;
612
613 direct_memcpy( copy->trace, buffer->trace, level * sizeof(buffer->trace[0]) );
614
615 return copy;
616 }
617
618 __attribute__((no_instrument_function))
619 void
direct_trace_free_buffer(DirectTraceBuffer * buffer)620 direct_trace_free_buffer( DirectTraceBuffer *buffer )
621 {
622 if (buffer->name)
623 free( buffer->name );
624
625 free( buffer );
626 }
627
628 /**************************************************************************************************/
629
630 __attribute__((no_instrument_function))
631 void
__cyg_profile_func_enter(void * this_fn,void * call_site)632 __cyg_profile_func_enter (void *this_fn,
633 void *call_site)
634 {
635 if (direct_config->trace) {
636 DirectTraceBuffer *buffer = get_trace_buffer();
637 int level = buffer->level++;
638 Trace *trace = &buffer->trace[level];
639
640 if (level < MAX_LEVEL) {
641 trace->addr = this_fn;
642 trace->flags = TF_NONE;
643 }
644 }
645 }
646
647 __attribute__((no_instrument_function))
648 void
__cyg_profile_func_exit(void * this_fn,void * call_site)649 __cyg_profile_func_exit (void *this_fn,
650 void *call_site)
651 {
652 if (direct_config->trace) {
653 DirectTraceBuffer *buffer = get_trace_buffer();
654
655 if (buffer->level > 0)
656 buffer->level--;
657 }
658 }
659
660 #else
661
662 const char *
direct_trace_lookup_symbol(const char * filename,long offset)663 direct_trace_lookup_symbol( const char *filename, long offset )
664 {
665 return NULL;
666 }
667
668 const char *
direct_trace_lookup_file(void * address,void ** ret_base)669 direct_trace_lookup_file( void *address, void **ret_base )
670 {
671 if (ret_base)
672 *ret_base = NULL;
673
674 return NULL;
675 }
676
677 void
direct_trace_print_stack(DirectTraceBuffer * buffer)678 direct_trace_print_stack( DirectTraceBuffer *buffer )
679 {
680 }
681
682 void
direct_trace_print_stacks(void)683 direct_trace_print_stacks( void )
684 {
685 }
686
687 int
direct_trace_debug_indent(void)688 direct_trace_debug_indent( void )
689 {
690 return 0;
691 }
692
693 DirectTraceBuffer *
direct_trace_copy_buffer(DirectTraceBuffer * buffer)694 direct_trace_copy_buffer( DirectTraceBuffer *buffer )
695 {
696 return NULL;
697 }
698
699 void
direct_trace_free_buffer(DirectTraceBuffer * buffer)700 direct_trace_free_buffer( DirectTraceBuffer *buffer )
701 {
702 }
703
704 #endif
705
706