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