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

Reply via email to