trace.cc revision 1031
1/*
2 * Copyright (c) 2001-2004 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <ctype.h>
30#include <fstream>
31#include <iostream>
32#include <list>
33#include <string>
34#include <vector>
35
36#include "base/misc.hh"
37#include "base/trace.hh"
38#include "base/str.hh"
39
40using namespace std;
41
42namespace Trace {
43const string DefaultName("global");
44FlagVec flags(NumFlags, false);
45
46//
47// This variable holds the output stream for debug information.  Other
48// than setting up/redirecting this stream, do *NOT* reference this
49// directly; use DebugOut() (see below) to access this stream for
50// output.
51//
52ostream *dprintf_stream = &cerr;
53
54ObjectMatch ignore;
55
56Log theLog;
57
58Log::Log()
59{
60    size = 0;
61    buffer = NULL;
62}
63
64
65void
66Log::init(int _size)
67{
68    if (buffer != NULL) {
69        fatal("Trace::Log::init called twice!");
70    }
71
72    size = _size;
73
74    buffer = new (Record *)[size];
75
76    for (int i = 0; i < size; ++i) {
77        buffer[i] = NULL;
78    }
79
80    nextRecPtr = &buffer[0];
81    wrapRecPtr = &buffer[size];
82}
83
84
85Log::~Log()
86{
87    for (int i = 0; i < size; ++i) {
88        delete buffer[i];
89    }
90
91    delete [] buffer;
92}
93
94
95void
96Log::append(Record *rec)
97{
98    // dump record to output stream if there's one open
99    if (dprintf_stream != NULL) {
100        rec->dump(*dprintf_stream);
101    }
102
103    // no buffering: justget rid of it now
104    if (buffer == NULL) {
105        delete rec;
106        return;
107    }
108
109    Record *oldRec = *nextRecPtr;
110
111    if (oldRec != NULL) {
112        // log has wrapped: overwrite
113        delete oldRec;
114    }
115
116    *nextRecPtr = rec;
117
118    if (++nextRecPtr == wrapRecPtr) {
119        nextRecPtr = &buffer[0];
120    }
121}
122
123
124void
125Log::dump(ostream &os)
126{
127    if (buffer == NULL) {
128        return;
129    }
130
131    Record **bufPtr = nextRecPtr;
132
133    if (*bufPtr == NULL) {
134        // next record slot is empty: log must not be full yet.
135        // start dumping from beginning of buffer
136        bufPtr = buffer;
137    }
138
139    do {
140        Record *rec = *bufPtr;
141
142        rec->dump(os);
143
144        if (++bufPtr == wrapRecPtr) {
145            bufPtr = &buffer[0];
146        }
147    } while (bufPtr != nextRecPtr);
148}
149
150PrintfRecord::~PrintfRecord()
151{
152    delete &args;
153}
154
155void
156PrintfRecord::dump(ostream &os)
157{
158    string fmt = "";
159
160    if (!name.empty()) {
161        fmt = "%s: " + fmt;
162        args.prepend(name);
163    }
164
165    if (cycle != (Tick)-1) {
166        fmt = "%7d: " + fmt;
167        args.prepend(cycle);
168    }
169
170    fmt += format;
171
172    args.dump(os, fmt);
173    os.flush();
174}
175
176DataRecord::DataRecord(Tick _cycle, const string &_name,
177                       const void *_data, int _len)
178    : Record(_cycle), name(_name), len(_len)
179{
180    data = new uint8_t[len];
181    memcpy(data, _data, len);
182}
183
184DataRecord::~DataRecord()
185{
186    delete [] data;
187}
188
189void
190DataRecord::dump(ostream &os)
191{
192    int c, i, j;
193
194    for (i = 0; i < len; i += 16) {
195        ccprintf(os, "%d: %s: %08x  ", cycle, name, i);
196        c = len - i;
197        if (c > 16) c = 16;
198
199        for (j = 0; j < c; j++) {
200            ccprintf(os, "%02x ", data[i + j] & 0xff);
201            if ((j & 0xf) == 7 && j > 0)
202                ccprintf(os, " ");
203        }
204
205        for (; j < 16; j++)
206            ccprintf(os, "   ");
207        ccprintf(os, "  ");
208
209        for (j = 0; j < c; j++) {
210            int ch = data[i + j] & 0x7f;
211            ccprintf(os,
212                     "%c", (char)(isprint(ch) ? ch : ' '));
213        }
214
215        ccprintf(os, "\n");
216
217        if (c < 16)
218            break;
219    }
220}
221} // namespace Trace
222
223//
224// Returns the current output stream for debug information.  As a
225// wrapper around Trace::dprintf_stream, this handles cases where debug
226// information is generated in the process of parsing .ini options,
227// before we process the option that sets up the debug output stream
228// itself.
229//
230std::ostream &
231DebugOut()
232{
233    return *Trace::dprintf_stream;
234}
235
236/////////////////////////////////////////////
237//
238// C-linkage functions for invoking from gdb
239//
240/////////////////////////////////////////////
241
242//
243// Dump trace buffer to specified file (cout if NULL)
244//
245extern "C"
246void
247dumpTrace(const char *filename)
248{
249    if (filename != NULL) {
250        ofstream out(filename);
251        Trace::theLog.dump(out);
252        out.close();
253    }
254    else {
255        Trace::theLog.dump(cout);
256    }
257}
258
259
260//
261// Turn on/off trace output to cerr.  Typically used when trace output
262// is only going to circular buffer, but you want to see what's being
263// sent there as you step through some code in gdb.  This uses the
264// same facility as the "trace to file" feature, and will print error
265// messages rather than clobbering an existing ostream pointer.
266//
267extern "C"
268void
269echoTrace(bool on)
270{
271    if (on) {
272        if (Trace::dprintf_stream != NULL) {
273            cerr << "Already echoing trace to a file... go do a 'tail -f'"
274                 << " on that file instead." << endl;
275        } else {
276            Trace::dprintf_stream = &cerr;
277        }
278    } else {
279        if (Trace::dprintf_stream != &cerr) {
280            cerr << "Not echoing trace to cerr." << endl;
281        } else {
282            Trace::dprintf_stream = NULL;
283        }
284    }
285}
286
287extern "C"
288void
289printTraceFlags()
290{
291    using namespace Trace;
292    for (int i = 0; i < numFlagStrings; ++i)
293        if (flags[i])
294            cprintf("%s\n", flagStrings[i]);
295}
296
297void
298tweakTraceFlag(const char *string, bool value)
299{
300    using namespace Trace;
301    std::string str(string);
302
303    for (int i = 0; i < numFlagStrings; ++i) {
304        if (str != flagStrings[i])
305            continue;
306
307        int idx = i;
308
309        if (idx < NumFlags) {
310            flags[idx] = value;
311        } else {
312            idx -= NumFlags;
313            if (idx >= NumCompoundFlags) {
314                ccprintf(cerr, "Invalid compound flag");
315                return;
316            }
317
318            const Flags *flagVec = compoundFlags[idx];
319
320            for (int j = 0; flagVec[j] != -1; ++j) {
321                if (flagVec[j] >= NumFlags) {
322                    ccprintf(cerr, "Invalid compound flag");
323                    return;
324                }
325                flags[flagVec[j]] = value;
326            }
327        }
328
329        cprintf("flag %s was %s\n", string, value ? "set" : "cleared");
330        return;
331    }
332
333    cprintf("could not find flag %s\n", string);
334}
335
336extern "C"
337void
338setTraceFlag(const char *string)
339{
340    tweakTraceFlag(string, true);
341}
342
343extern "C"
344void
345clearTraceFlag(const char *string)
346{
347    tweakTraceFlag(string, false);
348}
349