Windows Debug Monitor

#ifndef _WinDbgMon_H_
#define _WinDbgMon_H_

#include <windows.h>
#include <functional>
#include <concurrent_queue.h>

class WinDbgMon
{
public:
    typedef std::function<void(int, const char *str)> Callback;

private:
    enum {
        TIMEOUT_WIN_DEBUG = 100,
    };

    struct dbwin_buffer_t
    {
        DWORD dwProcessId;
        char  data[4096 - sizeof(DWORD)];
    };

private:
    HANDLE m_hDBWinMutex = NULL;
    HANDLE m_hDBMonBuffer = NULL;
    HANDLE m_hEventBufferReady = NULL;
    HANDLE m_hEventDataReady = NULL;
    HANDLE m_hMonitorThread = NULL;
    HANDLE m_hLoggingThread = NULL;
    BOOL m_bWinDebugMonStopped = true;
    struct dbwin_buffer_t *m_pDBBuffer = NULL;
    Callback OnDebugMessage;
    concurrency::concurrent_queue<std::string> _output_queue;

private:
    static DWORD WINAPI MonitorThread(void *pData);
    static DWORD WINAPI LoggingThread(void *pData);
    void OutputString(const std::string &str);
    void ProcessData();

public:
    WinDbgMon(Callback onDebugMessage = NULL);
    DWORD Run();
    void Stop();
};

#endif
#include <exception>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <ctime>
#include <tchar.h>
#include "WinDbgMon.h"

// ----------------------------------------------------------------------------

//  PROPERTIES OF OBJECTS

// ----------------------------------------------------------------------------

//  NAME        |   DBWinMutex      DBWIN_BUFFER_READY      DBWIN_DATA_READY

// ----------------------------------------------------------------------------

//  TYPE        |   Mutex           Event                   Event

//  ACCESS      |   All             All                     Sync

//  INIT STATE  |   ?               Signaled                Nonsignaled

//  PROPERTY    |   ?               Auto-Reset              Auto-Reset

// ----------------------------------------------------------------------------


WinDbgMon::WinDbgMon(Callback onDebugMessage)
{
    OnDebugMessage = onDebugMessage;
}

DWORD WinDbgMon::Run()
{
    if (m_hDBWinMutex == NULL) {
        // Mutex: DBWin

        auto DBWIN_MUTEX = _T("DBWinMutex");
        m_hDBWinMutex = ::OpenMutex(SYNCHRONIZE, FALSE, DBWIN_MUTEX);
        if (m_hDBWinMutex == NULL) {
            return -1;
        }

        // Event: buffer ready

        auto DBWIN_BUFFER_READY = _T("DBWIN_BUFFER_READY");
        m_hEventBufferReady = ::OpenEvent(EVENT_ALL_ACCESS, FALSE, DBWIN_BUFFER_READY);
        if (m_hEventBufferReady == NULL) {
            m_hEventBufferReady = ::CreateEvent(
                NULL,
                FALSE,  // auto-reset

                TRUE,   // initial state: signaled

                DBWIN_BUFFER_READY
            );
            if (m_hEventBufferReady == NULL) {
                return -2;
            }
        }

        // Event: data ready

        auto DBWIN_DATA_READY = _T("DBWIN_DATA_READY");
        m_hEventDataReady = ::OpenEvent(SYNCHRONIZE, FALSE, DBWIN_DATA_READY);
        if (m_hEventDataReady == NULL) {
            m_hEventDataReady = ::CreateEvent(
                NULL,
                FALSE,  // auto-reset

                FALSE,  // initial state: nonsignaled

                DBWIN_DATA_READY
            );
            if (m_hEventDataReady == NULL) {
                return -3;
            }
        }

        // Shared memory

        // ---------------------------------------------------------

        auto DBWIN_BUFFER = _T("DBWIN_BUFFER");
        m_hDBMonBuffer = ::OpenFileMapping(FILE_MAP_READ, FALSE, DBWIN_BUFFER);
        if (m_hDBMonBuffer == NULL) {
            m_hDBMonBuffer = ::CreateFileMapping(
                INVALID_HANDLE_VALUE,
                NULL,
                PAGE_READWRITE,
                0,
                sizeof(struct dbwin_buffer_t),
                DBWIN_BUFFER
            );
            if (m_hDBMonBuffer == NULL) {
                return -4;
            }
        }

        m_pDBBuffer = (struct dbwin_buffer_t *)::MapViewOfFile(m_hDBMonBuffer, SECTION_MAP_READ, 0, 0, 0);
        if (m_pDBBuffer == NULL) {
            return -5;
        }

        // Start threads

        m_bWinDebugMonStopped = FALSE;

        // 1) start monitoring thread

        m_hMonitorThread = ::CreateThread(NULL, 0, MonitorThread, this, 0, NULL);
        if (m_hMonitorThread == NULL) {
            return -6;
        }

        // 2) start logging thread

        // ---------------------------------------------------------

        m_hLoggingThread = ::CreateThread(NULL, 0, LoggingThread, this, 0, NULL);
        if (m_hLoggingThread == NULL) {
            return -7;
        }

        // prompt for cancelation

        ::OutputDebugString(_T("Debug monitor is started, press CTRL+C to quit."));

        // wait for termination of threads

        HANDLE threads[] = { m_hMonitorThread, m_hLoggingThread };
        if (::WaitForMultipleObjects(sizeof(threads) / sizeof(HANDLE), threads, TRUE, INFINITE) == WAIT_FAILED) {
            return -8;
        }

        // shutdown

        ::UnmapViewOfFile(m_pDBBuffer);
        CloseHandle(m_hDBMonBuffer);
        CloseHandle(m_hEventBufferReady);
        CloseHandle(m_hEventDataReady);
        CloseHandle(m_hDBWinMutex);
        m_hDBWinMutex = NULL;
    }
    return 0;
}

void WinDbgMon::Stop()
{
    m_bWinDebugMonStopped = TRUE;
}

void WinDbgMon::ProcessData()
{
    // wait for data ready

    if (::WaitForSingleObject(m_hEventDataReady, TIMEOUT_WIN_DEBUG) == WAIT_OBJECT_0) {
        if (OnDebugMessage != NULL) {
            OnDebugMessage(m_pDBBuffer->dwProcessId, m_pDBBuffer->data);
        }
        else {
            std::ostringstream oss;
            oss << m_pDBBuffer->dwProcessId << ": " << m_pDBBuffer->data;
            _output_queue.push(oss.str());
        }
        // signal buffer ready

        SetEvent(m_hEventBufferReady);
    }
}

DWORD WINAPI WinDbgMon::MonitorThread(void *pData)
{
    WinDbgMon *_this = (WinDbgMon *)pData;
    if (_this != NULL) {
        while (!_this->m_bWinDebugMonStopped) {
            _this->ProcessData();
        }
    }
    return 0;
}

DWORD WINAPI WinDbgMon::LoggingThread(void * pData)
{
    WinDbgMon *_this = (WinDbgMon *)pData;
    std::string s;

    while (!_this->m_bWinDebugMonStopped) {
        if (_this->_output_queue.try_pop(s)) {
            _this->OutputString(s);
        }
        else {
            ::Sleep(10);
        }
    }
    return 0;
}

void WinDbgMon::OutputString(const std::string &str)
{
    auto timestamp = []() {
        char buffer[64];
        time_t t = time(NULL);
        tm tm;
        localtime_s(&tm, &t);
        strftime(buffer, sizeof(buffer) / sizeof(char), "%Y-%m-%d %T", &tm);
        return std::string(buffer);
    };

    std::ostringstream oss;
    oss << "[" << timestamp() << "] " << str;
    if (str.length() > 0 && str.back() != '\n') {
        oss << std::endl;
    }
    std::cout << oss.str();
}
#include <vector>
#include <iostream>
#include <signal.h>
#include <conio.h>
#include <tchar.h>
#include "WinDbgMon.h"

using namespace std;

WinDbgMon dbgmon;

void on_user_break(int)
{
    std::cout << "User Break" << std::endl;
    dbgmon.Stop();
}

int main()
{
    TCHAR title[256];
    ::GetConsoleTitle(title, sizeof(title) / sizeof(TCHAR));
    ::SetConsoleTitle(_T("Windows Debug Monitor"));

    std::vector<int> signals = {
        SIGINT,
        SIGILL,
        SIGFPE,
        SIGSEGV,
        SIGTERM,
        SIGBREAK,
        SIGABRT
    };

    for (auto &signum : signals) {
        signal(signum, on_user_break);
    }

    auto err = dbgmon.Run();
    if (err < 0) {
        void *msgbuf;
        int count = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPTSTR)&msgbuf,
            0,
            NULL
        );
        cout << "Faied to initialize: " << (TCHAR *)msgbuf << endl;
    }

    ::SetConsoleTitle(title);
    return 0;
}