trace.cc revision 2
1/* 2 * Copyright (c) 2003 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 "misc.hh" 37#include "trace.hh" 38#include "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 = NULL; 53 54int dprintf_ignore_size; 55vector<string> dprintf_ignore; 56vector<vector<string> > ignore_tokens; 57vector<int> ignore_size; 58 59bool 60dprintf_ignore_name(const string &name) 61{ 62 vector<string> name_tokens; 63 tokenize(name_tokens, name, '.'); 64 int ntsize = name_tokens.size(); 65 66 for (int i = 0; i < dprintf_ignore_size; ++i) { 67 bool match = true; 68 int jstop = ignore_size[i]; 69 for (int j = 0; j < jstop; ++j) { 70 if (j >= ntsize) 71 break; 72 73 const string &ignore = ignore_tokens[i][j]; 74 if (ignore != "*" && ignore != name_tokens[j]) { 75 match = false; 76 break; 77 } 78 } 79 80 if (match == true) 81 return true; 82 } 83 84 return false; 85} 86 87 88Log theLog; 89 90Log::Log() 91{ 92 size = 0; 93 buffer = NULL; 94} 95 96 97void 98Log::init(int _size) 99{ 100 if (buffer != NULL) { 101 fatal("Trace::Log::init called twice!"); 102 } 103 104 size = _size; 105 106 buffer = new (Record *)[size]; 107 108 for (int i = 0; i < size; ++i) { 109 buffer[i] = NULL; 110 } 111 112 nextRecPtr = &buffer[0]; 113 wrapRecPtr = &buffer[size]; 114} 115 116 117Log::~Log() 118{ 119 for (int i = 0; i < size; ++i) { 120 delete buffer[i]; 121 } 122 123 delete [] buffer; 124} 125 126 127void 128Log::append(Record *rec) 129{ 130 // dump record to output stream if there's one open 131 if (dprintf_stream != NULL) { 132 rec->dump(*dprintf_stream); 133 } 134 135 // no buffering: justget rid of it now 136 if (buffer == NULL) { 137 delete rec; 138 return; 139 } 140 141 Record *oldRec = *nextRecPtr; 142 143 if (oldRec != NULL) { 144 // log has wrapped: overwrite 145 delete oldRec; 146 } 147 148 *nextRecPtr = rec; 149 150 if (++nextRecPtr == wrapRecPtr) { 151 nextRecPtr = &buffer[0]; 152 } 153} 154 155 156void 157Log::dump(ostream &os) 158{ 159 if (buffer == NULL) { 160 return; 161 } 162 163 Record **bufPtr = nextRecPtr; 164 165 if (*bufPtr == NULL) { 166 // next record slot is empty: log must not be full yet. 167 // start dumping from beginning of buffer 168 bufPtr = buffer; 169 } 170 171 do { 172 Record *rec = *bufPtr; 173 174 rec->dump(os); 175 176 if (++bufPtr == wrapRecPtr) { 177 bufPtr = &buffer[0]; 178 } 179 } while (bufPtr != nextRecPtr); 180} 181 182PrintfRecord::~PrintfRecord() 183{ 184 delete &args; 185} 186 187 188void 189PrintfRecord::dump(ostream &os) 190{ 191 string fmt = ""; 192 193 if (!name.empty()) { 194 fmt = "%s: " + fmt; 195 args.prepend(name); 196 } 197 198 if (cycle != (Tick)-1) { 199 fmt = "%7d: " + fmt; 200 args.prepend(cycle); 201 } 202 203 fmt += format; 204 205 args.dump(os, fmt); 206 os.flush(); 207} 208 209 210 211RawDataRecord::RawDataRecord(Tick _cycle, 212 const uint8_t *_data, int _len) 213 : Record(_cycle), len(_len) 214{ 215 data = new uint8_t[len]; 216 memcpy(data, _data, len); 217} 218 219 220RawDataRecord::~RawDataRecord() 221{ 222 delete [] data; 223} 224 225 226void 227RawDataRecord::dump(ostream &os) 228{ 229 int c, i, j; 230 231 for (i = 0; i < len; i += 16) { 232 ccprintf(os, "%08x ", i); 233 c = len - i; 234 if (c > 16) c = 16; 235 236 for (j = 0; j < c; j++) { 237 ccprintf(os, "%02x ", data[i + j] & 0xff); 238 if ((j & 0xf) == 7 && j > 0) 239 ccprintf(os, " "); 240 } 241 242 for (; j < 16; j++) 243 ccprintf(os, " "); 244 ccprintf(os, " "); 245 246 for (j = 0; j < c; j++) { 247 int ch = data[i + j] & 0x7f; 248 ccprintf(os, 249 "%c", (char)(isprint(ch) ? ch : ' ')); 250 } 251 252 ccprintf(os, "\n"); 253 254 if (c < 16) 255 break; 256 } 257} 258} // namespace Trace 259 260// 261// Returns the current output stream for debug information. As a 262// wrapper around Trace::dprintf_stream, this handles cases where debug 263// information is generated in the process of parsing .ini options, 264// before we process the option that sets up the debug output stream 265// itself. 266// 267std::ostream & 268DebugOut() 269{ 270 if (Trace::dprintf_stream) 271 return *Trace::dprintf_stream; 272 else 273 return cerr; 274} 275 276///////////////////////////////////////////// 277// 278// C-linkage functions for invoking from gdb 279// 280///////////////////////////////////////////// 281 282// 283// Dump trace buffer to specified file (cout if NULL) 284// 285extern "C" 286void 287dumpTrace(const char *filename) 288{ 289 if (filename != NULL) { 290 ofstream out(filename); 291 Trace::theLog.dump(out); 292 out.close(); 293 } 294 else { 295 Trace::theLog.dump(cout); 296 } 297} 298 299 300// 301// Turn on/off trace output to cerr. Typically used when trace output 302// is only going to circular buffer, but you want to see what's being 303// sent there as you step through some code in gdb. This uses the 304// same facility as the "trace to file" feature, and will print error 305// messages rather than clobbering an existing ostream pointer. 306// 307extern "C" 308void 309echoTrace(bool on) 310{ 311 if (on) { 312 if (Trace::dprintf_stream != NULL) { 313 cerr << "Already echoing trace to a file... go do a 'tail -f'" 314 << " on that file instead." << endl; 315 } else { 316 Trace::dprintf_stream = &cerr; 317 } 318 } else { 319 if (Trace::dprintf_stream != &cerr) { 320 cerr << "Not echoing trace to cerr." << endl; 321 } else { 322 Trace::dprintf_stream = NULL; 323 } 324 } 325} 326