From aa77480fb57d74b0009f181b40d861314df5b4d0 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 27 Feb 2023 17:21:00 -0500 Subject: [PATCH] Making progress on util::dprintf --- luprex/cpp/core/drivenengine.cpp | 1 + luprex/cpp/core/drivenengine.hpp | 1 - luprex/cpp/core/enginewrapper.hpp | 17 +++++++++ luprex/cpp/core/util.cpp | 57 +++++++++++++++++++++++++++++++ luprex/cpp/core/util.hpp | 16 +++++++++ luprex/cpp/drv/driver-common.cpp | 6 ++++ 6 files changed, 97 insertions(+), 1 deletion(-) diff --git a/luprex/cpp/core/drivenengine.cpp b/luprex/cpp/core/drivenengine.cpp index 10d57634..8a2ab6e5 100644 --- a/luprex/cpp/core/drivenengine.cpp +++ b/luprex/cpp/core/drivenengine.cpp @@ -1016,6 +1016,7 @@ static void init_engine_wrapper_helper(EngineWrapper *w) { w->replay_initialize = replaycore_initialize; w->replay_step = replaycore_step; + w->hook_dprintf = util::hook_dprintf; w->release = release; }; diff --git a/luprex/cpp/core/drivenengine.hpp b/luprex/cpp/core/drivenengine.hpp index 42f2cc8b..1c8e1f32 100644 --- a/luprex/cpp/core/drivenengine.hpp +++ b/luprex/cpp/core/drivenengine.hpp @@ -318,7 +318,6 @@ private: friend class Channel; }; - ////////////////////////////////////////////////////////////////////////////////// diff --git a/luprex/cpp/core/enginewrapper.hpp b/luprex/cpp/core/enginewrapper.hpp index b7d9d4a4..783d24c9 100644 --- a/luprex/cpp/core/enginewrapper.hpp +++ b/luprex/cpp/core/enginewrapper.hpp @@ -227,6 +227,23 @@ struct EngineWrapper { ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + // Hook dprintf + // + // The engine provides a function 'util::dprintf' to print debugging + // messages. It does not use stderr or std::cerr, because in windows, those + // go to the bit-bucket. + // + // This routine hooks dprintf to change where the output goes. Ideally, + // the intent is to send the output somewhere easily accessible, like the + // visual studio debug output log, or the unreal editor output log, or + // something like that. Or, better yet, to all those places at once. + // + // The hook function will get passed one line of output at a time. The line + // will only contain printable characters. The line will not contain a + // newline - the newline is implied. + // + void (*hook_dprintf)(void (*func)(const char *oneline)); + // Restore the wrapper to its initial blank state. // // Note that the wrapper must have already been initialized using diff --git a/luprex/cpp/core/util.cpp b/luprex/cpp/core/util.cpp index 5b942bd1..887282ca 100644 --- a/luprex/cpp/core/util.cpp +++ b/luprex/cpp/core/util.cpp @@ -720,6 +720,63 @@ eng::string XYZ::debug_string() const { return oss.str(); } + +void (*dprintf_hook)(const char *oneline); + +void hook_dprintf(void (*func)(const char *oneline)) { + dprintf_hook = func; +} + +static void chop_up_dprintf(char *buffer, int size) { + // Drop the final newline, if any. We're going + // to automatically end with a newline and we don't + // want to double up. + if ((size > 0) && (buffer[size-1] == '\n')) { + size -= 1; + } + + // Chop it up into lines and call the hook one line + // at a time. Replace control characters as we go. + const char *base = buffer; + for (int i = 0; i <= size; i++) { + if (buffer[i] < ' ') { + if ((buffer[i] == '\n') || (buffer[i] == 0)) { + buffer[i] = 0; + if (dprintf_hook == nullptr) { + fprintf(stderr, "%s\n", base); + } else { + dprintf_hook(base); + } + base = buffer + i + 1; + } else { + buffer[i] = ' '; + } + } + } + + // If we're sending to stderr, flush. If not, then + // the hook routine is responsible for flushing its own + // output. + if (dprintf_hook == nullptr) { + fflush(stderr); + } +} + +void dprintf(const char *format, ...) { + char buffer[256]; + va_list args; + va_start (args, format); + int n = vsnprintf(buffer, 256, format, args); + if (n <= 255) { + chop_up_dprintf(buffer, n); + } else { + char *lbuffer = (char *)malloc(n + 1); + vsnprintf(lbuffer, n+1, format, args); + chop_up_dprintf(lbuffer, n); + free(lbuffer); + } +} + } // namespace util diff --git a/luprex/cpp/core/util.hpp b/luprex/cpp/core/util.hpp index ab0c6d74..a8fb5a7c 100644 --- a/luprex/cpp/core/util.hpp +++ b/luprex/cpp/core/util.hpp @@ -357,6 +357,22 @@ inline eng::string ss(const ARGS & ... args) { return oss.str(); } + + +// dprintf +// +// Send a debugging message to somewhere that it can be seen. This routine +// initially just sends output to stderr. But it can be hooked to send output +// somewhere else, like to a debug output window. +// +// The hook function must be a function that accepts a single line of text. The +// hook function will always be passed one line, consisting of printable +// characters only. There will be no control characters. The newline is +// implied. +// +void dprintf(const char *format, ...); +void hook_dprintf(void (*func)(const char *oneline)); + // A better API than std::setfill, std::hex, std::setw, std::setprecision // // Usage examples: diff --git a/luprex/cpp/drv/driver-common.cpp b/luprex/cpp/drv/driver-common.cpp index 2228ca9f..934d094f 100644 --- a/luprex/cpp/drv/driver-common.cpp +++ b/luprex/cpp/drv/driver-common.cpp @@ -9,6 +9,11 @@ static void if_error_print_and_exit(const std::string_view str) { } } +static void dprintf_callback(const char *oneline) { + fprintf(stderr, "DPRINTF: %s\n", oneline); + fflush(stderr); +} + class Driver { public: enum ChanState { @@ -536,6 +541,7 @@ class Driver { // Load the DLL and gain access to its functions. call_init_engine_wrapper(&engw); engw.replay_cb_sent_outgoing = replay_cb_sent_outgoing; + engw.hook_dprintf(dprintf_callback); // If argv contains "replay ", do a replay, // and then skip everything else.