diff options
Diffstat (limited to 'src/base/trace.cc')
-rw-r--r-- | src/base/trace.cc | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/src/base/trace.cc b/src/base/trace.cc new file mode 100644 index 000000000..90db7f045 --- /dev/null +++ b/src/base/trace.cc @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <ctype.h> +#include <fstream> +#include <iostream> +#include <list> +#include <string> +#include <vector> + +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/str.hh" + +using namespace std; + +namespace Trace { +const string DefaultName("global"); +FlagVec flags(NumFlags, false); + +// +// This variable holds the output stream for debug information. Other +// than setting up/redirecting this stream, do *NOT* reference this +// directly; use DebugOut() (see below) to access this stream for +// output. +// +ostream *dprintf_stream = &cerr; + +ObjectMatch ignore; + +Log theLog; + +Log::Log() +{ + size = 0; + buffer = NULL; +} + + +void +Log::init(int _size) +{ + if (buffer != NULL) { + fatal("Trace::Log::init called twice!"); + } + + size = _size; + + buffer = new Record *[size]; + + for (int i = 0; i < size; ++i) { + buffer[i] = NULL; + } + + nextRecPtr = &buffer[0]; + wrapRecPtr = &buffer[size]; +} + + +Log::~Log() +{ + for (int i = 0; i < size; ++i) { + delete buffer[i]; + } + + delete [] buffer; +} + + +void +Log::append(Record *rec) +{ + // dump record to output stream if there's one open + if (dprintf_stream != NULL) { + rec->dump(*dprintf_stream); + } else { + rec->dump(cout); + } + + // no buffering: justget rid of it now + if (buffer == NULL) { + delete rec; + return; + } + + Record *oldRec = *nextRecPtr; + + if (oldRec != NULL) { + // log has wrapped: overwrite + delete oldRec; + } + + *nextRecPtr = rec; + + if (++nextRecPtr == wrapRecPtr) { + nextRecPtr = &buffer[0]; + } +} + + +void +Log::dump(ostream &os) +{ + if (buffer == NULL) { + return; + } + + Record **bufPtr = nextRecPtr; + + if (*bufPtr == NULL) { + // next record slot is empty: log must not be full yet. + // start dumping from beginning of buffer + bufPtr = buffer; + } + + do { + Record *rec = *bufPtr; + + rec->dump(os); + + if (++bufPtr == wrapRecPtr) { + bufPtr = &buffer[0]; + } + } while (bufPtr != nextRecPtr); +} + +PrintfRecord::~PrintfRecord() +{ + delete &args; +} + +void +PrintfRecord::dump(ostream &os) +{ + string fmt = ""; + + if (!name.empty()) { + fmt = "%s: " + fmt; + args.prepend(name); + } + + if (cycle != (Tick)-1) { + fmt = "%7d: " + fmt; + args.prepend(cycle); + } + + fmt += format; + + args.dump(os, fmt); + os.flush(); +} + +DataRecord::DataRecord(Tick _cycle, const string &_name, + const void *_data, int _len) + : Record(_cycle), name(_name), len(_len) +{ + data = new uint8_t[len]; + memcpy(data, _data, len); +} + +DataRecord::~DataRecord() +{ + delete [] data; +} + +void +DataRecord::dump(ostream &os) +{ + int c, i, j; + + for (i = 0; i < len; i += 16) { + ccprintf(os, "%d: %s: %08x ", cycle, name, i); + c = len - i; + if (c > 16) c = 16; + + for (j = 0; j < c; j++) { + ccprintf(os, "%02x ", data[i + j] & 0xff); + if ((j & 0xf) == 7 && j > 0) + ccprintf(os, " "); + } + + for (; j < 16; j++) + ccprintf(os, " "); + ccprintf(os, " "); + + for (j = 0; j < c; j++) { + int ch = data[i + j] & 0x7f; + ccprintf(os, + "%c", (char)(isprint(ch) ? ch : ' ')); + } + + ccprintf(os, "\n"); + + if (c < 16) + break; + } +} +} // namespace Trace + +// +// Returns the current output stream for debug information. As a +// wrapper around Trace::dprintf_stream, this handles cases where debug +// information is generated in the process of parsing .ini options, +// before we process the option that sets up the debug output stream +// itself. +// +std::ostream & +DebugOut() +{ + return *Trace::dprintf_stream; +} + +///////////////////////////////////////////// +// +// C-linkage functions for invoking from gdb +// +///////////////////////////////////////////// + +// +// Dump trace buffer to specified file (cout if NULL) +// +extern "C" +void +dumpTrace(const char *filename) +{ + if (filename != NULL) { + ofstream out(filename); + Trace::theLog.dump(out); + out.close(); + } + else { + Trace::theLog.dump(cout); + } +} + + +// +// Turn on/off trace output to cerr. Typically used when trace output +// is only going to circular buffer, but you want to see what's being +// sent there as you step through some code in gdb. This uses the +// same facility as the "trace to file" feature, and will print error +// messages rather than clobbering an existing ostream pointer. +// +extern "C" +void +echoTrace(bool on) +{ + if (on) { + if (Trace::dprintf_stream != NULL) { + cerr << "Already echoing trace to a file... go do a 'tail -f'" + << " on that file instead." << endl; + } else { + Trace::dprintf_stream = &cerr; + } + } else { + if (Trace::dprintf_stream != &cerr) { + cerr << "Not echoing trace to cerr." << endl; + } else { + Trace::dprintf_stream = NULL; + } + } +} + +extern "C" +void +printTraceFlags() +{ + using namespace Trace; + for (int i = 0; i < numFlagStrings; ++i) + if (flags[i]) + cprintf("%s\n", flagStrings[i]); +} + +void +tweakTraceFlag(const char *string, bool value) +{ + using namespace Trace; + std::string str(string); + + for (int i = 0; i < numFlagStrings; ++i) { + if (str != flagStrings[i]) + continue; + + int idx = i; + + if (idx < NumFlags) { + flags[idx] = value; + } else { + idx -= NumFlags; + if (idx >= NumCompoundFlags) { + ccprintf(cerr, "Invalid compound flag"); + return; + } + + const Flags *flagVec = compoundFlags[idx]; + + for (int j = 0; flagVec[j] != -1; ++j) { + if (flagVec[j] >= NumFlags) { + ccprintf(cerr, "Invalid compound flag"); + return; + } + flags[flagVec[j]] = value; + } + } + + cprintf("flag %s was %s\n", string, value ? "set" : "cleared"); + return; + } + + cprintf("could not find flag %s\n", string); +} + +extern "C" +void +setTraceFlag(const char *string) +{ + tweakTraceFlag(string, true); +} + +extern "C" +void +clearTraceFlag(const char *string) +{ + tweakTraceFlag(string, false); +} |