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