base: Reimplement the DPRINTF mechanism in a Logger class

This patch adds a Logger class encapsulating dprintf.  This allows
variants of DPRINTF logging to be constructed and substituted in
place of the default behaviour.

The Logger provides a logMessage(when, name, format, ...) member
function like Trace::dprintf and a getOstream member function to
use a raw ostream for logging.

A class OstreamLogger is provided which generates the customary
debugging output with Trace::OstreamLogger::logMessage being the
old Trace::dprintf.
This commit is contained in:
Andrew Bardsley 2014-10-16 05:49:53 -04:00
parent a2d246b6b8
commit 960935a5bd
3 changed files with 176 additions and 103 deletions

View file

@ -1,4 +1,7 @@
/*
* Copyright (c) 2014 ARM Limited
* All rights reserved
*
* Copyright (c) 2001-2006 The Regents of The University of Michigan
* All rights reserved.
*
@ -27,11 +30,13 @@
*
* Authors: Nathan Binkert
* Steve Reinhardt
* Andrew Bardsley
*/
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "base/misc.hh"
@ -39,89 +44,105 @@
#include "base/str.hh"
#include "base/trace.hh"
using namespace std;
const std::string &name()
{
static const std::string default_name("global");
namespace Trace {
return default_name;
}
namespace Trace
{
const string DefaultName("global");
bool enabled = 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;
ostream &
// This variable holds the output logger for debug information. Other
// than setting up/redirecting this logger, do *NOT* reference this
// directly
Logger *debug_logger = NULL;
Logger *
getDebugLogger()
{
/* Set a default logger to cerr when no other logger is set */
if (!debug_logger)
debug_logger = new OstreamLogger(std::cerr);
return debug_logger;
}
std::ostream &
output()
{
return *dprintf_stream;
return getDebugLogger()->getOstream();
}
void
setOutput(const string &filename)
setDebugLogger(Logger *logger)
{
dprintf_stream = simout.find(filename);
if (!dprintf_stream)
dprintf_stream = simout.create(filename);
if (!logger)
warn("Trying to set debug logger to NULL\n");
else
debug_logger = logger;
}
ObjectMatch ignore;
bool
__dprintf_prologue(Tick when, const std::string &name)
void
Logger::dump(Tick when, const std::string &name, const void *d, int len)
{
if (!name.empty() && ignore.match(name))
return false;
return;
std::ostream &os = *dprintf_stream;
if (when != MaxTick)
ccprintf(os, "%7d: ", when);
if (!name.empty())
os << name << ": ";
return true;
}
void
dump(Tick when, const std::string &name, const void *d, int len)
{
const char *data = static_cast<const char *>(d);
std::ostream &os = *dprintf_stream;
int c, i, j;
for (i = 0; i < len; i += 16) {
if (!__dprintf_prologue(when, name))
return;
std::ostringstream line;
ccprintf(os, "%08x ", i);
ccprintf(line, "%08x ", i);
c = len - i;
if (c > 16) c = 16;
for (j = 0; j < c; j++) {
ccprintf(os, "%02x ", data[i + j] & 0xff);
ccprintf(line, "%02x ", data[i + j] & 0xff);
if ((j & 0xf) == 7 && j > 0)
ccprintf(os, " ");
ccprintf(line, " ");
}
for (; j < 16; j++)
ccprintf(os, " ");
ccprintf(os, " ");
ccprintf(line, " ");
ccprintf(line, " ");
for (j = 0; j < c; j++) {
int ch = data[i + j] & 0x7f;
ccprintf(os, "%c", (char)(isprint(ch) ? ch : ' '));
ccprintf(line, "%c", (char)(isprint(ch) ? ch : ' '));
}
ccprintf(os, "\n");
ccprintf(line, "\n");
logMessage(when, name, line.str());
if (c < 16)
break;
}
}
void
OstreamLogger::logMessage(Tick when, const std::string &name,
const std::string &message)
{
if (!name.empty() && ignore.match(name))
return;
if (when != MaxTick)
ccprintf(stream, "%7d: ", when);
if (!name.empty())
stream << name << ": ";
stream << message;
stream.flush();
}
} // namespace Trace

View file

@ -2,15 +2,6 @@
* Copyright (c) 2014 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2001-2006 The Regents of The University of Michigan
* All rights reserved.
*
@ -39,6 +30,7 @@
*
* Authors: Nathan Binkert
* Steve Reinhardt
* Andrew Bardsley
*/
#ifndef __BASE_TRACE_HH__
@ -54,34 +46,78 @@
namespace Trace {
using Debug::SimpleFlag;
using Debug::CompoundFlag;
std::ostream &output();
void setOutput(const std::string &filename);
extern bool enabled;
bool changeFlag(const char *str, bool value);
void dumpStatus();
extern ObjectMatch ignore;
extern const std::string DefaultName;
bool __dprintf_prologue(Tick when, const std::string &name);
template<typename ...Args> void
dprintf(Tick when, const std::string &name, const char *format,
const Args &...args)
/** Debug logging base class. Handles formatting and outputting
* time/name/message messages */
class Logger
{
if (!__dprintf_prologue(when, name))
return;
protected:
/** Name match for objects to ignore */
ObjectMatch ignore;
std::ostream &os(output());
ccprintf(os, format, args...);
os.flush();
}
public:
/** Log a single message */
template <typename ...Args>
void dprintf(Tick when, const std::string &name, const char *fmt,
const Args &...args)
{
if (!name.empty() && ignore.match(name))
return;
void dump(Tick when, const std::string &name, const void *data, int len);
std::ostringstream line;
ccprintf(line, fmt, args...);
logMessage(when, name, line.str());
}
/** Dump a block of data of length len */
virtual void dump(Tick when, const std::string &name,
const void *d, int len);
/** Log formatted message */
virtual void logMessage(Tick when, const std::string &name,
const std::string &message) = 0;
/** Return an ostream that can be used to send messages to
* the 'same place' as formatted logMessage messages. This
* can be implemented to use a logger's underlying ostream,
* to provide an ostream which formats the output in some
* way, or just set to one of std::cout, std::cerr */
virtual std::ostream &getOstream() = 0;
/** Set objects to ignore */
void setIgnore(ObjectMatch &ignore_) { ignore = ignore_; }
virtual ~Logger() { }
};
/** Logging wrapper for ostreams with the format:
* <when>: <name>: <message-body> */
class OstreamLogger : public Logger
{
protected:
std::ostream &stream;
public:
OstreamLogger(std::ostream &stream_) : stream(stream_)
{ }
void logMessage(Tick when, const std::string &name,
const std::string &message) M5_ATTR_OVERRIDE;
std::ostream &getOstream() M5_ATTR_OVERRIDE { return stream; }
};
/** Get the current global debug logger. This takes ownership of the given
* logger which should be allocated using 'new' */
Logger *getDebugLogger();
/** Get the ostream from the current global logger */
std::ostream &output();
/** Delete the current global logger and assign a new one */
void setDebugLogger(Logger *logger);
/** Enable debug logging */
extern bool enabled;
} // namespace Trace
@ -94,7 +130,9 @@ struct StringWrap
const std::string &operator()() const { return str; }
};
inline const std::string &name() { return Trace::DefaultName; }
// Return the global context name "global". This function gets called when
// the DPRINTF macros are used in a context without a visible name() function
const std::string &name();
// Interface for things with names. (cf. SimObject but without other
// functionality). This is useful when using DPRINTF
@ -124,40 +162,46 @@ class Named
#define DTRACE(x) ((Debug::x) && Trace::enabled)
#define DDUMP(x, data, count) do { \
using namespace Debug; \
if (DTRACE(x)) \
Trace::dump(curTick(), name(), data, count); \
#define DDUMP(x, data, count) do { \
using namespace Debug; \
if (DTRACE(x)) \
Trace::getDebugLogger()->dump(curTick(), name(), data, count); \
} while (0)
#define DPRINTF(x, ...) do { \
using namespace Debug; \
if (DTRACE(x)) \
Trace::dprintf(curTick(), name(), __VA_ARGS__); \
#define DPRINTF(x, ...) do { \
using namespace Debug; \
if (DTRACE(x)) { \
Trace::getDebugLogger()->dprintf(curTick(), name(), \
__VA_ARGS__); \
} \
} while (0)
#define DPRINTFS(x, s, ...) do { \
using namespace Debug; \
if (DTRACE(x)) \
Trace::dprintf(curTick(), s->name(), __VA_ARGS__); \
#define DPRINTFS(x, s, ...) do { \
using namespace Debug; \
if (DTRACE(x)) { \
Trace::getDebugLogger()->dprintf(curTick(), s->name(), \
__VA_ARGS__); \
} \
} while (0)
#define DPRINTFR(x, ...) do { \
using namespace Debug; \
if (DTRACE(x)) \
Trace::dprintf((Tick)-1, std::string(), __VA_ARGS__); \
#define DPRINTFR(x, ...) do { \
using namespace Debug; \
if (DTRACE(x)) { \
Trace::getDebugLogger()->dprintf((Tick)-1, std::string(), \
__VA_ARGS__); \
} \
} while (0)
#define DDUMPN(data, count) do { \
Trace::dump(curTick(), name(), data, count); \
#define DDUMPN(data, count) do { \
Trace::getDebugLogger()->dump(curTick(), name(), data, count); \
} while (0)
#define DPRINTFN(...) do { \
Trace::dprintf(curTick(), name(), __VA_ARGS__); \
#define DPRINTFN(...) do { \
Trace::getDebugLogger()->dprintf(curTick(), name(), __VA_ARGS__); \
} while (0)
#define DPRINTFNR(...) do { \
Trace::dprintf((Tick)-1, string(), __VA_ARGS__); \
#define DPRINTFNR(...) do { \
Trace::getDebugLogger()->dprintf((Tick)-1, string(), __VA_ARGS__); \
} while (0)
#else // !TRACING_ON

View file

@ -33,17 +33,25 @@
%{
#include "base/trace.hh"
#include "base/types.hh"
#include "base/output.hh"
inline void
output(const char *filename)
{
Trace::setOutput(filename);
std::ostream *file_stream = simout.find(filename);
if (!file_stream)
file_stream = simout.create(filename);
Trace::setDebugLogger(new Trace::OstreamLogger(*file_stream));
}
inline void
ignore(const char *expr)
{
Trace::ignore.setExpression(expr);
ObjectMatch ignore(expr);
Trace::getDebugLogger()->setIgnore(ignore);
}
using Trace::enabled;