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

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [tools/] [src/] [infra/] [trace.cxx] - Blame information for rev 321

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

Line No. Rev Author Line
1 26 unneback
//{{{  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
//}}}

powered by: WebSVN 2.1.0

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