trace.cc revision 1354
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