From 7491c8ae76e6dc761299617ad67afb12af2882c4 Mon Sep 17 00:00:00 2001
From: Sam Yates <halfflat@gmail.com>
Date: Tue, 5 Jul 2016 00:29:02 +0200
Subject: [PATCH] Add basic debugging trace capability.

Defines a TRACE(...) macro that, when enabled at
compile time with -DWITH_TRACE, prints to std::cerr
the location in the source, a time stamp, and the
values of any variables given to the macro.

Uses a mutex to protect against simultaneous writes
to std::cerr when TBB is enabled; it is not
optimized for speed.
---
 CMakeLists.txt      |  6 +++++
 src/util/debug.cpp  | 39 +++++++++++++++++++++++++++++++-
 src/util/debug.hpp  | 55 +++++++++++++++++++++++++++++++++++++++++++--
 src/util/ioutil.hpp | 28 +++++++++++++++++++++++
 4 files changed, 125 insertions(+), 3 deletions(-)
 create mode 100644 src/util/ioutil.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a432f4e8..8ba5a855 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,6 +25,12 @@ if(WITH_ASSERTIONS)
     add_definitions("-DWITH_ASSERTIONS")
 endif()
 
+# enable traces?
+set(WITH_TRACE OFF CACHE BOOL "enable TRACE() macros in code")
+if(WITH_TRACE)
+    add_definitions("-DWITH_TRACE")
+endif()
+
 # TBB support
 set(WITH_TBB OFF CACHE BOOL "use TBB for on-node threading" )
 if(WITH_TBB)
diff --git a/src/util/debug.cpp b/src/util/debug.cpp
index 2dd83f56..029fae92 100644
--- a/src/util/debug.cpp
+++ b/src/util/debug.cpp
@@ -1,9 +1,22 @@
+#include <chrono>
 #include <cstdlib>
+#include <cstring>
+#include <iomanip>
 #include <iostream>
+#include <thread>
 
 #include "util/debug.hpp"
+#include "util/ioutil.hpp"
 
-bool nest::mc::util::failed_assertion(const char *assertion, const char *file,
+namespace nest {
+namespace mc {
+namespace util {
+
+#ifdef WITH_TBB
+std::mutex global_debug_cerr_mutex;
+#endif
+
+bool failed_assertion(const char *assertion, const char *file,
                                       int line, const char *func)
 {
     // Explicit flush, as we can't assume default buffering semantics on stderr/cerr,
@@ -14,3 +27,27 @@ bool nest::mc::util::failed_assertion(const char *assertion, const char *file,
     std::abort();
     return false;
 }
+
+std::ostream& debug_emit_trace_leader(std::ostream &out, const char* file, int line, const char* varlist) {
+    iosfmt_guard _(out);
+
+    const char* leaf = std::strrchr(file, '/');
+    out << (leaf?leaf+1:file) << ':' << line << " ";
+
+    using namespace std::chrono;
+    auto tstamp = system_clock::now().time_since_epoch();
+    auto tstamp_usec = duration_cast<microseconds>(tstamp).count();
+
+    out << std::right << '[';
+    out << std::setw(11) << std::setfill('0') << (tstamp_usec/1000000) << '.';
+    out << std::setw(6)  << std::setfill('0') << (tstamp_usec%1000000) << ']';
+
+    if (varlist && *varlist) {
+        out << ' ' << varlist << ": ";
+    }
+    return out;
+}
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/src/util/debug.hpp b/src/util/debug.hpp
index 148b3c44..6739c4ff 100644
--- a/src/util/debug.hpp
+++ b/src/util/debug.hpp
@@ -1,15 +1,66 @@
 #pragma once
 
+#include <iostream>
+#include <sstream>
+#include <mutex>
+
 namespace nest {
 namespace mc {
 namespace util {
 
-bool failed_assertion(const char *assertion, const char *file, int line, const char *func);
+bool failed_assertion(const char* assertion, const char* file, int line, const char* func);
+std::ostream &debug_emit_trace_leader(std::ostream& out, const char* file, int line, const char* varlist);
+
+inline void debug_emit(std::ostream& out) {
+    out << "\n";
+}
 
-} 
+template <typename Head, typename... Tail>
+void debug_emit(std::ostream& out, const Head& head, const Tail&... tail) {
+    out << head;
+    if (sizeof...(tail)) {
+        out << ", ";
+    }
+    debug_emit(out, tail...);
 }
+
+#ifdef WITH_TBB
+extern std::mutex global_debug_cerr_mutex;
+#endif
+
+template <typename... Args>
+void debug_emit_trace(const char *file, int line, const char *varlist, const Args&... args) {
+#ifdef WITH_TBB
+    std::stringstream out;
+#else
+    auto &out = std::cerr;
+#endif
+
+    debug_emit_trace_leader(out, file, line, varlist);
+    debug_emit(out, args...);
+
+#ifdef WITH_TBB
+    std::lock_guard<std::mutex> _(global_debug_cerr_mutex);
+    std::cerr << out.rdbuf();
+#else
+    out.flush();
+#endif
 }
 
+} // namespace util
+} // namespace mc
+} // namespace nest
+
+#ifdef WITH_TRACE
+
+#define TRACE(vars...) nest::mc::util::debug_emit_trace(__FILE__, __LINE__, #vars, ##vars)
+
+#else
+
+#define TRACE(...)
+
+#endif
+
 
 #ifdef WITH_ASSERTIONS
 
diff --git a/src/util/ioutil.hpp b/src/util/ioutil.hpp
new file mode 100644
index 00000000..b6670072
--- /dev/null
+++ b/src/util/ioutil.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <ios>
+
+namespace nest {
+namespace mc {
+namespace util {
+
+class iosfmt_guard {
+public:
+    explicit iosfmt_guard(std::ios &stream): save_(nullptr), stream_(stream) {
+        save_.copyfmt(stream_);
+    }
+
+    ~iosfmt_guard() {
+        stream_.copyfmt(save_);
+    }
+
+private:
+    std::ios save_;
+    std::ios& stream_;
+};
+
+
+} // namespace util
+} // namespace mc
+} // namespace nest
+
-- 
GitLab