OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [host/] [infra/] [trace.cxx] - Blame information for rev 787

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.