Hello,
The attached patch adds Raw, an std::ostream manipulator to print
possibly non-terminated buffers and their labels/sizes.
The Raw manipulator tries to be smart about printing buffers at various
debugging levels: Large buffers are only printed at DBG_DATA by default.
This allows the caller to mix higher-level debug messages with dumping
of potentially large volumes of data. This smartness can be overruled
using an explicit minLevel() method call.
I think this manipulator can be useful in many places. The next patch I
will post illustrates such usage for helper debugging.
Thank you,
Alex.
Added Raw, an std::ostream manipulator to print possibly non-terminated buffers
and their labels/sizes.
The Raw manipulator tries to be smart about printing buffers at various
debugging levels: Large buffers are only printed at DBG_DATA by default.
This allows the caller to mix higher-level debug messages with dumping
of potentially large volumes of data. This smartness can be overruled
using an explicit minLevel() method call.
=== modified file 'src/Debug.h'
--- src/Debug.h 2012-11-12 13:11:36 +0000
+++ src/Debug.h 2012-12-13 23:12:29 +0000
@@ -64,66 +64,68 @@
/* defined debug section limits */
#define MAX_DEBUG_SECTIONS 100
/* defined names for Debug Levels */
#define DBG_CRITICAL 0 /**< critical messages always shown when they occur */
#define DBG_IMPORTANT 1 /**< important messages always shown when their section is being checked */
/* levels 2-8 are still being discussed amongst the developers */
#define DBG_DATA 9 /**< output is a large data dump only necessary for advanced debugging */
#define DBG_PARSE_NOTE(x) (opt_parse_cfg_only?0:(x)) /**< output is always to be displayed on '-k parse' but at level-x normally. */
class Debug
{
public:
static char *debugOptions;
static char *cache_log;
static int rotateNumber;
static int Levels[MAX_DEBUG_SECTIONS];
- static int level;
+ static int level; ///< minimum debugging level required by debugs() call
+ static int sectionLevel; ///< maximum debugging level allowed now
static int override_X;
static int log_stderr;
static bool log_syslog;
static std::ostream &getDebugOut();
static void finishDebug();
static void parseOptions(char const *);
private:
// Hack: replaces global ::xassert() to debug debugging assertions
static void xassert(const char *msg, const char *file, int line);
static std::ostringstream *CurrentDebug;
static int TheDepth; // level of nested debugging calls
};
extern FILE *debug_log;
size_t BuildPrefixInit();
const char * SkipBuildPrefix(const char* path);
/* Debug stream */
#define debugs(SECTION, LEVEL, CONTENT) \
do { \
if ((Debug::level = (LEVEL)) <= Debug::Levels[SECTION]) { \
+ Debug::sectionLevel = Debug::Levels[SECTION]; \
std::ostream &_dbo=Debug::getDebugOut(); \
if (Debug::level > DBG_IMPORTANT) \
_dbo << SkipBuildPrefix(__FILE__)<<"("<<__LINE__<<") "<<__FUNCTION__<<": "; \
_dbo << CONTENT; \
Debug::finishDebug(); \
} \
} while (/*CONSTCOND*/ 0)
/** stream manipulator which does nothing.
* \deprecated Do not add to new code, and remove when editing old code
*
* Its purpose is to inactivate calls made following previous debugs()
* guidelines such as
* debugs(1,2, HERE << "some message");
*
* His former objective is now absorbed in the debugs call itself
*/
inline std::ostream&
HERE(std::ostream& s)
{
@@ -140,21 +142,59 @@
#else
#define MYNAME __FUNCTION__ << " "
#endif
/* some uint8_t do not like streaming control-chars (values 0-31, 127+) */
inline std::ostream& operator <<(std::ostream &os, const uint8_t d)
{
return (os << (int)d);
}
/* Legacy debug style. Still used in some places. needs to die... */
#define do_debug(SECTION, LEVEL) ((Debug::level = (LEVEL)) <= Debug::Levels[SECTION])
#define old_debug(SECTION, LEVEL) if do_debug((SECTION), (LEVEL)) _db_print
/* Legacy debug function definitions */
void _db_init(const char *logfile, const char *options);
void _db_print(const char *,...) PRINTF_FORMAT_ARG1;
void _db_set_syslog(const char *facility);
void _db_rotate_log(void);
+
+/// Prints raw and/or non-terminated data safely, efficiently, and beautifully.
+/// Allows raw data debugging in debugs() statements with low debugging levels
+/// by printing only if higher section debugging levels are configured:
+/// debugs(11, DBG_IMPORTANT, "always printed" << Raw(may be printed...));
+class Raw {
+public:
+ Raw(const char *label, const char *data, const size_t size):
+ level(-1), label_(label), data_(data), size_(size) {}
+
+ /// limit data printing to at least the given debugging level
+ Raw &minLevel(const int aLevel) { level = aLevel; return *this; }
+
+ /// If debugging is prohibited by the current debugs() or section level,
+ /// prints nothing. Otherwise, dumps data using one of these formats:
+ /// " label[size]=data" if label was set and data size is positive
+ /// " label[0]" if label was set and data size is zero
+ /// " data" if label was not set and data size is positive
+ /// "" (i.e., prints nothing) if label was not set and data size is zero
+ std::ostream &print(std::ostream &os) const;
+
+ /// Minimum section debugging level necessary for printing. By default,
+ /// small strings are always printed while large strings are only printed
+ /// if DBG_DATA debugging level is enabled.
+ int level;
+
+private:
+ const char *label_; ///< optional data name or ID; triggers size printing
+ const char *data_; ///< raw data to be printed
+ size_t size_; ///< data length
+};
+
+inline
+std::ostream &operator <<(std::ostream &os, const Raw &raw)
+{
+ return raw.print(os);
+}
+
#endif /* SQUID_DEBUG_H */
=== modified file 'src/debug.cc'
--- src/debug.cc 2012-10-08 05:21:11 +0000
+++ src/debug.cc 2012-12-13 23:10:41 +0000
@@ -28,40 +28,41 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*/
#include "squid.h"
#include "Debug.h"
#include "SquidTime.h"
#include "util.h"
#include "ipc/Kids.h"
/* for shutting_down flag in xassert() */
#include "globals.h"
char *Debug::debugOptions = NULL;
int Debug::override_X = 0;
int Debug::log_stderr = -1;
bool Debug::log_syslog = false;
int Debug::Levels[MAX_DEBUG_SECTIONS];
int Debug::level;
+int Debug::sectionLevel;
char *Debug::cache_log = NULL;
int Debug::rotateNumber = -1;
FILE *debug_log = NULL;
static char *debug_log_file = NULL;
static int Ctx_Lock = 0;
static const char *debugLogTime(void);
static const char *debugLogKid(void);
static void ctx_print(void);
#if HAVE_SYSLOG
#ifdef LOG_LOCAL4
static int syslog_facility = 0;
#endif
static void _db_print_syslog(const char *format, va_list args);
#endif
static void _db_print_stderr(const char *format, va_list args);
static void _db_print_file(const char *format, va_list args);
#if _SQUID_WINDOWS_
extern LPCRITICAL_SECTION dbg_mutex;
typedef BOOL (WINAPI * PFInitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION, DWORD);
@@ -784,20 +785,40 @@
{
// XXX: This must be kept in sync with the actual debug.cc location
const char *ThisFileNameTail = "src/debug.cc";
const char *file=__FILE__;
// Disable heuristic if it does not work.
if (!strstr(file, ThisFileNameTail))
return 0;
return strlen(file)-strlen(ThisFileNameTail);
}
const char*
SkipBuildPrefix(const char* path)
{
static const size_t BuildPrefixLength = BuildPrefixInit();
return path+BuildPrefixLength;
}
+
+std::ostream &
+Raw::print(std::ostream &os) const
+{
+ if (label_)
+ os << ' ' << label_ << '[' << size_ << ']';
+
+ if (!size_)
+ return os;
+
+ // finalize debugging level if no level was set explicitly via minLevel()
+ const int finalLevel = (level >= 0) ? level :
+ (size_ > 40 ? DBG_DATA : Debug::sectionLevel);
+ if (finalLevel <= Debug::sectionLevel) {
+ os << (label_ ? '=' : ' ');
+ os.write(data_, size_);
+ }
+
+ return os;
+}
=== modified file 'src/tests/stub_debug.cc'
--- src/tests/stub_debug.cc 2012-08-08 07:35:10 +0000
+++ src/tests/stub_debug.cc 2012-12-13 23:58:23 +0000
@@ -3,40 +3,41 @@
* For use by test binaries which do not need the full context debugging
*
* Note: it doesn't use the STUB API as the functions defined here must
* not abort the unit test.
*/
#include "squid.h"
#include "Debug.h"
#if HAVE_STDIO_H
#include <stdio.h>
#endif
FILE *debug_log = NULL;
int Debug::TheDepth = 0;
char *Debug::debugOptions;
char *Debug::cache_log= NULL;
int Debug::rotateNumber = 0;
int Debug::Levels[MAX_DEBUG_SECTIONS];
int Debug::level;
+int Debug::sectionLevel;
int Debug::override_X = 0;
int Debug::log_stderr = 1;
bool Debug::log_syslog = false;
Ctx
ctx_enter(const char *descr)
{
return -1;
}
void
ctx_exit(Ctx ctx)
{
}
void
_db_init(const char *logfile, const char *options)
{}
void
@@ -123,20 +124,40 @@
}
void
Debug::xassert(const char *msg, const char *file, int line)
{
if (CurrentDebug) {
*CurrentDebug << "assertion failed: " << file << ":" << line <<
": \"" << msg << "\"";
}
abort();
}
std::ostringstream *Debug::CurrentDebug (NULL);
const char*
SkipBuildPrefix(const char* path)
{
return path;
}
+
+std::ostream &
+Raw::print(std::ostream &os) const
+{
+ if (label_)
+ os << ' ' << label_ << '[' << size_ << ']';
+
+ if (!size_)
+ return os;
+
+ // finalize debugging level if no level was set explicitly via minLevel()
+ const int finalLevel = (level >= 0) ? level :
+ (size_ > 40 ? DBG_DATA : Debug::sectionLevel);
+ if (finalLevel <= Debug::sectionLevel) {
+ os << (label_ ? '=' : ' ');
+ os.write(data_, size_);
+ }
+
+ return os;
+}