trace.cc revision 2632:1bb2f91485ea
1/*
2 * Copyright (c) 2001-2005 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    } else {
102        rec->dump(cout);
103    }
104
105    // no buffering: justget rid of it now
106    if (buffer == NULL) {
107        delete rec;
108        return;
109    }
110
111    Record *oldRec = *nextRecPtr;
112
113    if (oldRec != NULL) {
114        // log has wrapped: overwrite
115        delete oldRec;
116    }
117
118    *nextRecPtr = rec;
119
120    if (++nextRecPtr == wrapRecPtr) {
121        nextRecPtr = &buffer[0];
122    }
123}
124
125
126void
127Log::dump(ostream &os)
128{
129    if (buffer == NULL) {
130        return;
131    }
132
133    Record **bufPtr = nextRecPtr;
134
135    if (*bufPtr == NULL) {
136        // next record slot is empty: log must not be full yet.
137        // start dumping from beginning of buffer
138        bufPtr = buffer;
139    }
140
141    do {
142        Record *rec = *bufPtr;
143
144        rec->dump(os);
145
146        if (++bufPtr == wrapRecPtr) {
147            bufPtr = &buffer[0];
148        }
149    } while (bufPtr != nextRecPtr);
150}
151
152PrintfRecord::~PrintfRecord()
153{
154    delete &args;
155}
156
157void
158PrintfRecord::dump(ostream &os)
159{
160    string fmt = "";
161
162    if (!name.empty()) {
163        fmt = "%s: " + fmt;
164        args.prepend(name);
165    }
166
167    if (cycle != (Tick)-1) {
168        fmt = "%7d: " + fmt;
169        args.prepend(cycle);
170    }
171
172    fmt += format;
173
174    args.dump(os, fmt);
175    os.flush();
176}
177
178DataRecord::DataRecord(Tick _cycle, const string &_name,
179                       const void *_data, int _len)
180    : Record(_cycle), name(_name), len(_len)
181{
182    data = new uint8_t[len];
183    memcpy(data, _data, len);
184}
185
186DataRecord::~DataRecord()
187{
188    delete [] data;
189}
190
191void
192DataRecord::dump(ostream &os)
193{
194    int c, i, j;
195
196    for (i = 0; i < len; i += 16) {
197        ccprintf(os, "%d: %s: %08x  ", cycle, name, i);
198        c = len - i;
199        if (c > 16) c = 16;
200
201        for (j = 0; j < c; j++) {
202            ccprintf(os, "%02x ", data[i + j] & 0xff);
203            if ((j & 0xf) == 7 && j > 0)
204                ccprintf(os, " ");
205        }
206
207        for (; j < 16; j++)
208            ccprintf(os, "   ");
209        ccprintf(os, "  ");
210
211        for (j = 0; j < c; j++) {
212            int ch = data[i + j] & 0x7f;
213            ccprintf(os,
214                     "%c", (char)(isprint(ch) ? ch : ' '));
215        }
216
217        ccprintf(os, "\n");
218
219        if (c < 16)
220            break;
221    }
222}
223} // namespace Trace
224
225//
226// Returns the current output stream for debug information.  As a
227// wrapper around Trace::dprintf_stream, this handles cases where debug
228// information is generated in the process of parsing .ini options,
229// before we process the option that sets up the debug output stream
230// itself.
231//
232std::ostream &
233DebugOut()
234{
235    return *Trace::dprintf_stream;
236}
237
238/////////////////////////////////////////////
239//
240// C-linkage functions for invoking from gdb
241//
242/////////////////////////////////////////////
243
244//
245// Dump trace buffer to specified file (cout if NULL)
246//
247extern "C"
248void
249dumpTrace(const char *filename)
250{
251    if (filename != NULL) {
252        ofstream out(filename);
253        Trace::theLog.dump(out);
254        out.close();
255    }
256    else {
257        Trace::theLog.dump(cout);
258    }
259}
260
261
262//
263// Turn on/off trace output to cerr.  Typically used when trace output
264// is only going to circular buffer, but you want to see what's being
265// sent there as you step through some code in gdb.  This uses the
266// same facility as the "trace to file" feature, and will print error
267// messages rather than clobbering an existing ostream pointer.
268//
269extern "C"
270void
271echoTrace(bool on)
272{
273    if (on) {
274        if (Trace::dprintf_stream != NULL) {
275            cerr << "Already echoing trace to a file... go do a 'tail -f'"
276                 << " on that file instead." << endl;
277        } else {
278            Trace::dprintf_stream = &cerr;
279        }
280    } else {
281        if (Trace::dprintf_stream != &cerr) {
282            cerr << "Not echoing trace to cerr." << endl;
283        } else {
284            Trace::dprintf_stream = NULL;
285        }
286    }
287}
288
289extern "C"
290void
291printTraceFlags()
292{
293    using namespace Trace;
294    for (int i = 0; i < numFlagStrings; ++i)
295        if (flags[i])
296            cprintf("%s\n", flagStrings[i]);
297}
298
299void
300tweakTraceFlag(const char *string, bool value)
301{
302    using namespace Trace;
303    std::string str(string);
304
305    for (int i = 0; i < numFlagStrings; ++i) {
306        if (str != flagStrings[i])
307            continue;
308
309        int idx = i;
310
311        if (idx < NumFlags) {
312            flags[idx] = value;
313        } else {
314            idx -= NumFlags;
315            if (idx >= NumCompoundFlags) {
316                ccprintf(cerr, "Invalid compound flag");
317                return;
318            }
319
320            const Flags *flagVec = compoundFlags[idx];
321
322            for (int j = 0; flagVec[j] != -1; ++j) {
323                if (flagVec[j] >= NumFlags) {
324                    ccprintf(cerr, "Invalid compound flag");
325                    return;
326                }
327                flags[flagVec[j]] = value;
328            }
329        }
330
331        cprintf("flag %s was %s\n", string, value ? "set" : "cleared");
332        return;
333    }
334
335    cprintf("could not find flag %s\n", string);
336}
337
338extern "C"
339void
340setTraceFlag(const char *string)
341{
342    tweakTraceFlag(string, true);
343}
344
345extern "C"
346void
347clearTraceFlag(const char *string)
348{
349    tweakTraceFlag(string, false);
350}
351