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 #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; +}