3 //============================================================================
7 // Host side implementation of the infrastructure trace facilities.
9 //============================================================================
10 //####COPYRIGHTBEGIN####
12 // ----------------------------------------------------------------------------
13 // Copyright (C) 2002 Bart Veer
14 // Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
16 // This file is part of the eCos host tools.
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)
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
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.
32 // ----------------------------------------------------------------------------
34 //####COPYRIGHTEND####
35 //============================================================================
36 //#####DESCRIPTIONBEGIN####
42 // Purpose: To provide a host-side implementation of the eCos tracing
45 //####DESCRIPTIONEND####
46 //============================================================================
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
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"
60 // Without this #define the tracing enums and prototypes are
62 #define CYGDBG_USE_TRACING
63 #include "cyg/infra/cyg_trac.h"
65 // The standard C++ string class is used extensively
68 // Add a few C headers
77 // -------------------------------------------------------------------------
78 // The tracing macros end up calling one of the following routines:
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, ... )
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
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
104 // The basic implementation of this trace code is as follows:
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
110 // 2) the various trace functions simply update this array and the
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.
118 // 4) the callback function does all the hardware of the formatting
122 //{{{ Types and statics
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
130 typedef struct trace_entry {
137 CYG_ADDRWORD data[8];
140 #ifndef CYGNUM_INFRA_TRACE_VECTOR_SIZE
141 # define CYGNUM_INFRA_TRACE_VECTOR_SIZE 2048
144 static trace_entry tracevec[CYGNUM_INFRA_TRACE_VECTOR_SIZE];
145 static volatile int trace_index = 0;
147 // Forward declaration of the callback function, for convenience.
148 static void trace_callback(void (*)(const char*));
150 // Has the callback been installed yet?
151 static bool callback_installed = false;
154 //{{{ The trace functions themselves
156 // ----------------------------------------------------------------------------
157 // The functions that get called by the trace macros. Typically these work
160 // 1) read and increment the trace index. This makes tracing marginally usable
161 // in multi-threaded systems.
163 // 2) invalidate the entry that is about to be updated. Again this helps a bit
164 // with multi-threaded systems.
166 // 3) fill in all the fields as per the command-line arguments, zeroing
169 // 4) set the valid flag to true, which means the contents can now be output.
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.
175 cyg_tracenomsg(const char* fn, const char* file, cyg_uint32 line)
178 tracevec[i].valid = false;
179 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
181 tracevec[i].what = cyg_trace_trace;
183 tracevec[i].file = file;
184 tracevec[i].line = line;
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;
196 if (!callback_installed) {
197 cyg_assert_install_failure_callback("Trace", &trace_callback);
198 callback_installed = true;
203 cyg_tracemsg(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char* msg)
206 tracevec[i].valid = false;
207 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
209 tracevec[i].what = what;
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;
224 if (!callback_installed) {
225 cyg_assert_install_failure_callback("Trace", &trace_callback);
226 callback_installed = true;
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)
235 tracevec[i].valid = false;
236 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
238 tracevec[i].what = what;
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;
253 if (!callback_installed) {
254 cyg_assert_install_failure_callback("Trace", &trace_callback);
255 callback_installed = true;
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)
265 tracevec[i].valid = false;
266 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
268 tracevec[i].what = what;
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;
283 if (!callback_installed) {
284 cyg_assert_install_failure_callback("Trace", &trace_callback);
285 callback_installed = true;
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)
296 tracevec[i].valid = false;
297 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
299 tracevec[i].what = what;
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;
314 if (!callback_installed) {
315 cyg_assert_install_failure_callback("Trace", &trace_callback);
316 callback_installed = true;
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)
328 tracevec[i].valid = false;
329 trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
331 tracevec[i].what = what;
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;
346 if (!callback_installed) {
347 cyg_assert_install_failure_callback("Trace", &trace_callback);
348 callback_installed = true;
353 //{{{ Output callback
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.
360 // The trace_callback() function loops through the various entries in the
361 // vector, ignoring invalid ones, and invoking output_entry() for the
364 // There are a number of utility routines:
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.
370 // trim_linenum() formats the linenumber sensibly.
372 // trim_function() is used to parse a __PRETTY_FUNCTION__ value
373 // and produce something more manageable.
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.
380 trim_file(const char* file)
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;
387 return std::string(max_filename_len, ' ');
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)) {
401 std::string result = "";
403 for ( ;(*pEnd != '\0') && (i < max_filename_len); i++, pEnd++) {
406 for ( ; i < max_filename_len; i++) {
413 // The linenumber output should be up to four digits, right-padded
414 // with spaces. sprintf() will do the trick nicely.
417 trim_linenum(cyg_uint32 line)
420 sprintf(buf, "%-4d", (int) line);
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.
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.
434 trim_function(const char* fn)
443 // This implements the target-side behaviour.
445 // First locate the opening bracket. The function name can
446 // be identified by walking backwards from that.
448 for (s = fn; ('\0' != *s) && ('(' != *s); s++);
449 for ( ; (s > fn) && (*s != ' '); s--);
452 std::string result = "";
453 while ( ('\0' != *s) && ('(' != *s) )
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.
464 trim_string(const char * arg)
466 const int max_string_len = 20;
468 std::string result = "";
473 for ( i = 0; (i < max_string_len) && ('\0' != *arg) && isprint(*arg); i++, arg++) {
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.
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.
489 parse_msg(const char* msg, trace_entry& entry)
494 // Keep track of the number of arguments in the trace_entry
495 // that have been processed.
498 // A utility buffer for sprintf(), e.g. for integer-> string conversions.
501 std::string result = "";
502 for ( ; '\0' != *msg; msg++) {
509 // We have a format string. Extract all of it.
510 std::string format = "%";
513 // The first part of the format string may be one or more flags.
514 while ( ('-' == *msg) || ('+' == *msg) || (' ' == *msg) ||
515 ('#' == *msg) || ('0' == *msg) ) {
519 // Next comes the width. If this is an asterix it is necessary to
520 // substitute in an actual argument.
522 int width = (args_index < 8) ? (int) entry.data[args_index++] : 0;
523 sprintf(util_buf, "%d", width);
527 // Otherwise the width should be one or more digits
528 while( isdigit(*msg) ) {
533 // Look for a precision, again coping with an asterix.
537 int precision = (args_index < 8) ? (int) entry.data[args_index++] : 0;
538 sprintf(util_buf, "%d", precision);
542 // The precision should be one or more digits, with an optional -
546 while (isdigit(*msg)) {
552 // Now look for h,l and L. These have to be remembered.
553 bool short_version = false;
554 bool long_version = false;
557 short_version = true;
558 } else if (('l' == *msg) || ('L' == *msg)) {
563 // The end of the format string has been reached.
564 int format_ch = *msg;
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 ) {
572 CYG_ADDRWORD val = entry.data[args_index++];
574 switch( format_ch ) {
585 // "format" contains the appropriate format string.
586 // Invoke sprintf() using util_buf, doing the
587 // appropriate cast, and then append the output
590 // This is not totally robust. If a ridiculous
591 // precision has been specified then util_buf may
594 sprintf(util_buf, format.c_str(), (long) val);
596 // The implicit cast rules mean that shorts do not
597 // require any special attention.
598 sprintf(util_buf, format.c_str(), (int) val);
604 sprintf(util_buf, format.c_str(), (int) val);
609 sprintf(util_buf, format.c_str(), (void *) val);
615 std::string data = trim_string((char *) val);
616 sprintf(util_buf, format.c_str(), data.c_str());
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
632 // ----------------------------------------------------------------------------
636 output_entry(void (*pOutputFn)(const char*), trace_entry& entry)
638 std::string output = trim_file(entry.file) + " " +
639 trim_linenum(entry.line) + " " +
640 trim_function(entry.fn) + " ";
641 if (0 != entry.msg) {
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 += " ?";
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 += "? ";
660 (*pOutputFn)(output.c_str());
664 trace_callback( void (*pOutputFn)(const char*))
666 if ((trace_index < 0) || (trace_index >= CYGNUM_INFRA_TRACE_VECTOR_SIZE))
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.
672 for (i = trace_index - 1; i >= 0; i--) {
673 if (tracevec[i].valid) {
674 output_entry(pOutputFn, tracevec[i]);
677 for (i = (CYGNUM_INFRA_TRACE_VECTOR_SIZE - 1); i >= trace_index; i--) {
678 if (tracevec[i].valid) {
679 output_entry(pOutputFn, tracevec[i]);