]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/infra/trace.cxx
Initial revision
[karo-tx-redboot.git] / tools / src / infra / trace.cxx
1 //{{{  Banner                                           
2
3 //============================================================================
4 //
5 //      trace.cxx
6 //
7 //      Host side implementation of the infrastructure trace facilities.
8 //
9 //============================================================================
10 //####COPYRIGHTBEGIN####
11 //                                                                          
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
15 //
16 // This file is part of the eCos host tools.
17 //
18 // This program is free software; you can redistribute it and/or modify it 
19 // under the terms of the GNU General Public License as published by the Free 
20 // Software Foundation; either version 2 of the License, or (at your option) 
21 // any later version.
22 // 
23 // This program is distributed in the hope that it will be useful, but WITHOUT 
24 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
25 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
26 // more details.
27 // 
28 // You should have received a copy of the GNU General Public License along with
29 // this program; if not, write to the Free Software Foundation, Inc., 
30 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31 //
32 // ----------------------------------------------------------------------------
33 //                                                                          
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
37 //
38 // Author(s):   bartv
39 // Contact(s):  bartv
40 // Date:        1998/12/07
41 // Version:     0.01
42 // Purpose:     To provide a host-side implementation of the eCos tracing
43 //              facilities.
44 //
45 //####DESCRIPTIONEND####
46 //============================================================================
47
48 //}}}
49 //{{{  #include's                                       
50
51 // Make sure that the host-side extensions get prototyped
52 // as well. Note that the tracing code needs to interact
53 // with the assertion facilities to set up an appropriate
54 // callback.
55 #define CYG_DECLARE_HOST_ASSERTION_SUPPORT
56 #include "pkgconf/infra.h"
57 #include "cyg/infra/cyg_type.h"
58 #include "cyg/infra/cyg_ass.h"
59
60 // Without this #define the tracing enums and prototypes are
61 // not visible.
62 #define CYGDBG_USE_TRACING
63 #include "cyg/infra/cyg_trac.h"
64
65 // The standard C++ string class is used extensively
66 #include <string>
67
68 // Add a few C headers
69 #include <cctype>
70 #include <cstring>
71 #include <cstdio>
72
73 //}}}
74
75 //{{{  Description                                      
76
77 // -------------------------------------------------------------------------
78 // The tracing macros end up calling one of the following routines:
79 //
80 // void cyg_tracenomsg(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line)
81 // void cyg_tracemsg(  ..., const char* msg)
82 // void cyg_tracemsg2( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1 )
83 // void cyg_tracemsg4( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
84 // void cyg_tracemsg6( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
85 // void cyg_tracemsg8( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
86 //
87 // For the 2/4/6/8 variants the msg argument is essentially a printf()
88 // style format string. However the intention is that the implementation
89 // of the trace code can delay doing the formatting until the trace
90 // information is actually needed (with obvious consequences for
91 // generated strings). Such an implementation would significantly 
92 // reduce the overheads associated with tracing, and is what is implemented
93 // here.
94 //
95 // CYG_ADDRWORD is likely to be either "int" or the platform-specific
96 // 64 bit data type: it should be big enough to hold either a pointer
97 // or any normal integral type. This causes problems on machines which
98 // have e.g. 32 bit int and 64 bit long: any 32 bit quantities will
99 // have been converted to 64 bit quantities in the calling code, and
100 // it is no longer possible to just pass the format string to sprintf().
101 // Instead what amounts to a re-implementation of sprintf() is needed
102 // here.
103 //
104 // The basic implementation of this trace code is as follows:
105 //
106 // 1) a static array of data structures to hold the trace data. The
107 //    size can be configured. There is a current index into this
108 //    array.
109 //
110 // 2) the various trace functions simply update this array and the
111 //    counter.
112 //
113 // 3) all of the trace functions also check a static to see whether
114 //    or not it is necessary to install a trace handler. This cannot
115 //    be done by means of a static object due to constructor priority
116 //    ordering problems.
117 //
118 // 4) the callback function does all the hardware of the formatting
119 //    etc.
120
121 //}}}
122 //{{{  Types and statics                                
123
124 // ----------------------------------------------------------------------------
125 // A data structure rather than a class is used to hold the trace data.
126 // This guarantees that the array gets put in the bss section and is properly
127 // zeroed. A "valid" field in the structure can be checked when dumping the
128 // array.
129
130 typedef struct trace_entry {
131     bool            valid;
132     cyg_uint32      what;
133     cyg_uint32      line;
134     const char*     fn;
135     const char*     file;
136     const char*     msg;
137     CYG_ADDRWORD    data[8];
138 } trace_entry;
139
140 #ifndef CYGNUM_INFRA_TRACE_VECTOR_SIZE
141 # define CYGNUM_INFRA_TRACE_VECTOR_SIZE 2048
142 #endif
143
144 static trace_entry  tracevec[CYGNUM_INFRA_TRACE_VECTOR_SIZE];
145 static volatile int trace_index = 0;
146
147 // Forward declaration of the callback function, for convenience.
148 static void trace_callback(void (*)(const char*));
149
150 // Has the callback been installed yet?
151 static bool callback_installed = false;
152
153 //}}}
154 //{{{  The trace functions themselves                   
155
156 // ----------------------------------------------------------------------------
157 // The functions that get called by the trace macros. Typically these work
158 // as follows:
159 //
160 // 1) read and increment the trace index. This makes tracing marginally usable
161 //    in multi-threaded systems.
162 //
163 // 2) invalidate the entry that is about to be updated. Again this helps a bit
164 //    with multi-threaded systems.
165 //
166 // 3) fill in all the fields as per the command-line arguments, zeroing
167 //    unused fields.
168 //
169 // 4) set the valid flag to true, which means the contents can now be output.
170 //
171 // This is by no means sufficient to guarantee that a call to dump the trace
172 // vector in some other thread can work safely, but it may help a little bit.
173
174 extern "C" void
175 cyg_tracenomsg(const char* fn, const char* file, cyg_uint32 line)
176 {
177     int i               = trace_index;
178     tracevec[i].valid   = false;
179     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
180
181     tracevec[i].what    = cyg_trace_trace;
182     tracevec[i].fn      = fn;
183     tracevec[i].file    = file;
184     tracevec[i].line    = line;
185     tracevec[i].msg     = 0;
186     tracevec[i].data[0] = 0;
187     tracevec[i].data[1] = 0;
188     tracevec[i].data[2] = 0;
189     tracevec[i].data[3] = 0;
190     tracevec[i].data[4] = 0;
191     tracevec[i].data[5] = 0;
192     tracevec[i].data[6] = 0;
193     tracevec[i].data[7] = 0;
194     tracevec[i].valid   = true;
195
196     if (!callback_installed) {
197         cyg_assert_install_failure_callback("Trace", &trace_callback);
198         callback_installed = true;
199     }
200 }
201
202 extern "C" void
203 cyg_tracemsg(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char* msg)
204 {
205     int i               = trace_index;
206     tracevec[i].valid   = false;
207     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
208
209     tracevec[i].what    = what;
210     tracevec[i].fn      = fn;
211     tracevec[i].file    = file;
212     tracevec[i].line    = line;
213     tracevec[i].msg     = msg;
214     tracevec[i].data[0] = 0;
215     tracevec[i].data[1] = 0;
216     tracevec[i].data[2] = 0;
217     tracevec[i].data[3] = 0;
218     tracevec[i].data[4] = 0;
219     tracevec[i].data[5] = 0;
220     tracevec[i].data[6] = 0;
221     tracevec[i].data[7] = 0;
222     tracevec[i].valid   = true;
223
224     if (!callback_installed) {
225         cyg_assert_install_failure_callback("Trace", &trace_callback);
226         callback_installed = true;
227     }
228 }
229
230 extern "C" void
231 cyg_tracemsg2(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char *msg,
232               CYG_ADDRWORD arg0, CYG_ADDRWORD arg1)
233 {
234     int i               = trace_index;
235     tracevec[i].valid   = false;
236     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
237
238     tracevec[i].what    = what;
239     tracevec[i].fn      = fn;
240     tracevec[i].file    = file;
241     tracevec[i].line    = line;
242     tracevec[i].msg     = msg;
243     tracevec[i].data[0] = arg0;
244     tracevec[i].data[1] = arg1;
245     tracevec[i].data[2] = 0;
246     tracevec[i].data[3] = 0;
247     tracevec[i].data[4] = 0;
248     tracevec[i].data[5] = 0;
249     tracevec[i].data[6] = 0;
250     tracevec[i].data[7] = 0;
251     tracevec[i].valid   = true;
252
253     if (!callback_installed) {
254         cyg_assert_install_failure_callback("Trace", &trace_callback);
255         callback_installed = true;
256     }
257 }
258
259 extern "C" void
260 cyg_tracemsg4(cyg_uint32 what, const char *fn, const char* file, cyg_uint32 line, const char *msg,
261               CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
262               CYG_ADDRWORD arg2, CYG_ADDRWORD arg3)
263 {
264     int i               = trace_index;
265     tracevec[i].valid   = false;
266     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
267
268     tracevec[i].what    = what;
269     tracevec[i].fn      = fn;
270     tracevec[i].file    = file;
271     tracevec[i].line    = line;
272     tracevec[i].msg     = msg;
273     tracevec[i].data[0] = arg0;
274     tracevec[i].data[1] = arg1;
275     tracevec[i].data[2] = arg2;
276     tracevec[i].data[3] = arg3;
277     tracevec[i].data[4] = 0;
278     tracevec[i].data[5] = 0;
279     tracevec[i].data[6] = 0;
280     tracevec[i].data[7] = 0;
281     tracevec[i].valid   = true;
282
283     if (!callback_installed) {
284         cyg_assert_install_failure_callback("Trace", &trace_callback);
285         callback_installed = true;
286     }
287 }
288
289 extern "C" void
290 cyg_tracemsg6(cyg_uint32 what, const char *fn, const char* file, cyg_uint32 line, const char *msg,
291               CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
292               CYG_ADDRWORD arg2, CYG_ADDRWORD arg3,
293               CYG_ADDRWORD arg4, CYG_ADDRWORD arg5)
294 {
295     int i               = trace_index;
296     tracevec[i].valid   = false;
297     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
298
299     tracevec[i].what    = what;
300     tracevec[i].fn      = fn;
301     tracevec[i].file    = file;
302     tracevec[i].line    = line;
303     tracevec[i].msg     = msg;
304     tracevec[i].data[0] = arg0;
305     tracevec[i].data[1] = arg1;
306     tracevec[i].data[2] = arg2;
307     tracevec[i].data[3] = arg3;
308     tracevec[i].data[4] = arg4;
309     tracevec[i].data[5] = arg5;
310     tracevec[i].data[6] = 0;
311     tracevec[i].data[7] = 0;
312     tracevec[i].valid   = true;
313
314     if (!callback_installed) {
315         cyg_assert_install_failure_callback("Trace", &trace_callback);
316         callback_installed = true;
317     }
318 }
319
320 extern "C" void
321 cyg_tracemsg8(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char *msg,
322               CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
323               CYG_ADDRWORD arg2, CYG_ADDRWORD arg3,
324               CYG_ADDRWORD arg4, CYG_ADDRWORD arg5,
325               CYG_ADDRWORD arg6, CYG_ADDRWORD arg7)
326 {
327     int i               = trace_index;
328     tracevec[i].valid   = false;
329     trace_index         = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
330
331     tracevec[i].what    = what;
332     tracevec[i].fn      = fn;
333     tracevec[i].file    = file;
334     tracevec[i].line    = line;
335     tracevec[i].msg     = msg;
336     tracevec[i].data[0] = arg0;
337     tracevec[i].data[1] = arg1;
338     tracevec[i].data[2] = arg2;
339     tracevec[i].data[3] = arg3;
340     tracevec[i].data[4] = arg4;
341     tracevec[i].data[5] = arg5;
342     tracevec[i].data[6] = arg6;
343     tracevec[i].data[7] = arg7;
344     tracevec[i].valid   = true;
345
346     if (!callback_installed) {
347         cyg_assert_install_failure_callback("Trace", &trace_callback);
348         callback_installed = true;
349     }
350 }
351
352 //}}}
353 //{{{  Output callback                                  
354
355 // ----------------------------------------------------------------------------
356 // Dumping the output. The assertion code will invoke a single callback
357 // function, cyg_trace_dummy::trace_callback(), with a function pointer
358 // that can be used for the actual output.
359 //
360 // The trace_callback() function loops through the various entries in the
361 // vector, ignoring invalid ones, and invoking output_entry() for the
362 // valid ones.
363 //
364 // There are a number of utility routines:
365 //
366 //     trim_file() is used to take a full pathname and return just the
367 //     final part of it as a C++ string. There is an upper bound on the
368 //     length of this string.
369 //
370 //     trim_linenum() formats the linenumber sensibly.
371 //
372 //     trim_function() is used to parse a __PRETTY_FUNCTION__ value
373 //     and produce something more manageable.
374 //
375 //     parse_msg() is used to construct the full trace message.
376 //     Because of possible 32/64 bit confusion it is not possible
377 //     to just use sprintf() for this.
378
379 static std::string
380 trim_file(const char* file)
381 {
382     // If the output is to look reasonable then the result should be a
383     // fixed length. 20 characters is reasonable for now.
384     const int max_filename_len = 20;
385     
386     if (0 == file) {
387         return std::string(max_filename_len, ' ');
388     }
389
390     // Move to the end of the string, and then back again until
391     // a directory separator is found. Given the number of levels
392     // in a typical eCos directory hierarchy it is probably not
393     // worthwhile outputting any of that information.
394     const char * pEnd = file + strlen(file);
395     while ((pEnd > file) && ('/' != *pEnd) && ('\\' != *pEnd)) {
396         pEnd--;
397     }
398     if (pEnd != file)
399         pEnd++;
400
401     std::string result = "";
402     int         i      = 0;
403     for ( ;(*pEnd != '\0') && (i < max_filename_len); i++, pEnd++) {
404         result += *pEnd;
405     }
406     for ( ; i < max_filename_len; i++) {
407         result += ' ';
408     }
409
410     return result;
411 }
412
413 // The linenumber output should be up to four digits, right-padded
414 // with spaces. sprintf() will do the trick nicely.
415
416 static std::string
417 trim_linenum(cyg_uint32 line)
418 {
419     char buf[32];
420     sprintf(buf, "%-4d", (int) line);
421     return buf;
422 }
423
424 // Extract a function name. On the target side function names
425 // are usually obtained via __PRETTY_FUNCTION__, and the resulting
426 // output is a bit on the large side: return value, arguments, etc
427 // are all included. On the host side the function name is normally
428 // supplied explicitly and should not be trimmed at all.
429 //
430 // Padding is not appropriate since the function name is likely
431 // to be followed immediately by the argument list. No maximum
432 // length is imposed - arguably that is a bad idea.
433 static std::string
434 trim_function(const char* fn)
435 {
436     if (0 == fn) {
437         return "<unknown>";
438     }
439
440 #if 1
441     return fn;
442 #else
443     // This implements the target-side behaviour.
444     //
445     // First locate the opening bracket. The function name can
446     // be identified by walking backwards from that.
447     const char *s;
448     for (s = fn; ('\0' != *s) && ('(' != *s); s++);
449     for ( ; (s > fn) && (*s != ' '); s--);
450     if ( s > fn) s++;
451
452     std::string result = "";
453     while ( ('\0' != *s) && ('(' != *s) )
454         result += *s++;
455
456     return result;
457 #endif
458 }
459
460 // The trace format string contained a %s. It is necessary to check
461 // whether the argument is still valid, and return a suitable
462 // approximation to the actual data.
463 static std::string
464 trim_string(const char * arg)
465 {
466     const int max_string_len = 20;
467
468     std::string result = "";
469     if (0 == arg) {
470         return result;
471     }
472     int i;
473     for ( i = 0; (i < max_string_len) && ('\0' != *arg) && isprint(*arg); i++, arg++) {
474         result += *arg;
475     }
476     return result;
477 }
478
479 // ----------------------------------------------------------------------------
480 // Parse a printf() style format string and do the appropriate expansions.
481 // Because of possible confusion between 32 and 64 bit integers it is not
482 // possible to use sprintf() itself.
483 //
484 // It is assumed that the format string is valid, as are most of the
485 // arguments. The possible exception is %s arguments where a little bit of
486 // checking happens first.
487
488 static std::string
489 parse_msg(const char* msg, trace_entry& entry)
490 {
491     if (0 == msg) {
492         return "";
493     }
494     // Keep track of the number of arguments in the trace_entry
495     // that have been processed.
496     int args_index = 0;
497
498     // A utility buffer for sprintf(), e.g. for integer-> string conversions.
499     char util_buf[64];
500     
501     std::string result = "";
502     for ( ; '\0' != *msg; msg++) {
503         
504         if ('%' != *msg) {
505             result += *msg;
506             continue;
507         }
508
509         // We have a format string. Extract all of it.
510         std::string format = "%";
511         msg++;
512
513         // The first part of the format string may be one or more flags.
514         while ( ('-' == *msg) || ('+' == *msg) || (' ' == *msg) ||
515                 ('#' == *msg) || ('0' == *msg) ) {
516             format += *msg++;
517         }
518
519         // Next comes the width. If this is an asterix it is necessary to
520         // substitute in an actual argument.
521         if ('*' == *msg) {
522             int width = (args_index < 8) ? (int) entry.data[args_index++] : 0;
523             sprintf(util_buf, "%d", width);
524             format += util_buf;
525             msg++;
526         } else {
527             // Otherwise the width should be one or more digits
528             while( isdigit(*msg) ) {
529                 format += *msg++;
530             }
531         }
532
533         // Look for a precision, again coping with an asterix.
534         if ('.' == *msg) {
535             format += *msg++;
536             if ('*' == *msg) {
537                 int precision = (args_index < 8) ? (int) entry.data[args_index++] : 0;
538                 sprintf(util_buf, "%d", precision);
539                 format += util_buf;
540                 msg++;
541             } else {
542                 // The precision should be one or more digits, with an optional -
543                 if ('-' == *msg) {
544                     format += *msg++;
545                 }
546                 while (isdigit(*msg)) {
547                     format += *msg++;
548                 }
549             }
550         }
551
552         // Now look for h,l and L. These have to be remembered.
553         bool short_version = false;
554         bool long_version  = false;
555         if ('h' == *msg) {
556             format        += *msg++;
557             short_version  = true;
558         } else if (('l' == *msg) || ('L' == *msg)) {
559             format        += *msg++;
560             long_version   = true;
561         }
562
563         // The end of the format string has been reached.
564         int format_ch  = *msg;
565         format        += *msg;
566
567         // If we have already formatted too many arguments, there is no point
568         // in trying to do the actual formatting.
569         if ( 8 <= args_index ) {
570             continue;
571         }
572         CYG_ADDRWORD val = entry.data[args_index++];
573
574         switch( format_ch ) {
575           case '%' :
576               result += '%';
577               break;
578               
579           case 'd' :
580           case 'i' :
581           case 'o' :
582           case 'u' :
583           case 'x' :
584           case 'X' : 
585               // "format" contains the appropriate format string.
586               // Invoke sprintf() using util_buf, doing the
587               // appropriate cast, and then append the output
588               // of util_buf.
589               //
590               // This is not totally robust. If a ridiculous
591               // precision has been specified then util_buf may
592               // overflow.
593               if (long_version) {
594                   sprintf(util_buf, format.c_str(), (long) val);
595               } else {
596                   // The implicit cast rules mean that shorts do not
597                   // require any special attention.
598                   sprintf(util_buf, format.c_str(), (int) val);
599               }
600               result += util_buf;
601               break;
602
603           case 'c' :
604               sprintf(util_buf, format.c_str(), (int) val);
605               result += util_buf;
606               break;
607               
608           case 'p' :
609               sprintf(util_buf, format.c_str(), (void *) val);
610               result += util_buf;
611               break;
612               
613           case 's' :
614           {
615               std::string data = trim_string((char *) val);
616               sprintf(util_buf, format.c_str(), data.c_str());
617               result += util_buf;
618               break;
619           }
620
621           default :
622               // Any attempt to do floating point conversions would be
623               // rather tricky given the casts that have been applied.
624               // There is no point in doing anything for unrecognised
625               // sequences.
626               break;
627         }
628     }
629     return result;
630 }
631
632 // ----------------------------------------------------------------------------
633
634
635 static void
636 output_entry(void (*pOutputFn)(const char*), trace_entry& entry)
637 {
638     std::string output  = trim_file(entry.file)    + " " +
639                           trim_linenum(entry.line) + " " +
640                           trim_function(entry.fn)  + " ";
641     if (0 != entry.msg) {
642         
643         switch( entry.what) {
644           case cyg_trace_trace  : output += " '"; break;
645           case cyg_trace_enter  : output += "{{"; break;
646           case cyg_trace_args   : output += "(("; break;
647           case cyg_trace_return : output += "}}"; break;
648           default               : output += " ?";
649         }
650         output += parse_msg(entry.msg, entry);
651         switch( entry.what) {
652           case cyg_trace_trace  : output += "' "; break;
653           case cyg_trace_enter  : break;
654           case cyg_trace_args   : output += "))"; break;
655           case cyg_trace_return : break;
656           default               : output += "? ";
657         }
658     }
659     output += "\n";
660     (*pOutputFn)(output.c_str());
661 }
662
663 static void
664 trace_callback( void (*pOutputFn)(const char*))
665 {
666     if ((trace_index < 0) || (trace_index >= CYGNUM_INFRA_TRACE_VECTOR_SIZE))
667         return;
668     
669     // Start at the last entry and work back down to zero, skipping
670     // invalid ones. Then go to the top and work back to the current index.
671     int i;
672     for (i = trace_index - 1; i >= 0; i--) {
673         if (tracevec[i].valid) {
674             output_entry(pOutputFn, tracevec[i]);
675         }
676     }
677     for (i = (CYGNUM_INFRA_TRACE_VECTOR_SIZE - 1); i >= trace_index; i--) {
678         if (tracevec[i].valid) {
679             output_entry(pOutputFn, tracevec[i]);
680         }
681     }
682 }
683
684 //}}}