source: SVN/cambria/redboot/host/infra/trace.cxx @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 22.2 KB
Line 
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
130typedef 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
144static trace_entry  tracevec[CYGNUM_INFRA_TRACE_VECTOR_SIZE];
145static volatile int trace_index = 0;
146
147// Forward declaration of the callback function, for convenience.
148static void trace_callback(void (*)(const char*));
149
150// Has the callback been installed yet?
151static 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
174extern "C" void
175cyg_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
202extern "C" void
203cyg_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
230extern "C" void
231cyg_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
259extern "C" void
260cyg_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
289extern "C" void
290cyg_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
320extern "C" void
321cyg_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
379static std::string
380trim_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
416static std::string
417trim_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.
433static std::string
434trim_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.
463static std::string
464trim_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
488static std::string
489parse_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
635static void
636output_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
663static void
664trace_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//}}}
Note: See TracBrowser for help on using the repository browser.