VS-Tipps&Tricks: Einfache Debug-Ausgabe mit TRACE auch in der Release Version

Wer wollte nicht schon immer mal gerne TRACE (Debug)-Ausgaben in seinem Release Programm haben ohne dafür überall OutputDebugString reinschreiben zu müssen.

Die nachfolgene kleine Klasse macht es möglich, den gewohnten Syntax des MFC TRACE Makros zu verwenden und direkt auf die Debugausgabe umzuleiten:

//    CTraceToOutputDebugString
//        Is a nice replacment class for TRACE
//        Easy to use with:
//            #undef TRACE
//            #define TRACE    CTraceToOutputDebugString()

class CTraceToOutputDebugString
{
public:
    // Non Unicode output helper
    void operator()(PCSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

    // Unicode output helper
    void operator()(PCWSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

private:
    // Non Unicode output helper
    void TraceV(PCSTR pszFormat, va_list args)
    {
        // Format the output buffer
        char szBuffer[1024];
        _vsnprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringA(szBuffer);
    }

    // Unicode output helper
    void TraceV(PCWSTR pszFormat, va_list args)
    {
        wchar_t szBuffer[1024];
        _vsnwprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringW(szBuffer);
    }
};

Durch den obenstehenden Code kann man auch in einer Release Version Trace Ausgaben erzeugen und z.B. mit DebugView.exe (Sysinternals) sichtbar machen, ohne evtl. weitere Anpassungen vornehmen zu müssen:

// Activate my special tracer
#undef TRACE
#define TRACE    CTraceToOutputDebugString()

void Foo()
{
     // Sometime usefull to see the output in a release version too
     TRACE(__FUNCTION__ " called at %d\n", GetTickCount());
}

9 Gedanken zu „VS-Tipps&Tricks: Einfache Debug-Ausgabe mit TRACE auch in der Release Version“

  1. Jep. Vor Jahren schon geschrieben und läuft tadellos…
    Zweiter Tipp, man kann es auch noch erweitern und gleich in ein Logfile ausgeben. Dazu nutze ich es dann auch noch, dazu dann noch verschlüsselt und in einem eigenen Format, damit ich es dann später mit einem Programm mir einfach anzeigen und auch nach Nachrichten filtern kann.

    Das ganze läuft in einem extra Thread bei mir. Aus den eigentlichen Arbeitsthreads kommen alle Trace Sachen in meiner Log.dll an. Diese sammelt die Nachrichten und wenn Zeit ist, schreibt sie sie parallel mit raus. Damit verliert man recht wenig Zeit.

    Im Laufe der Jahre hat mich allerdings auch ein was immer mehr genervt.
    Nämlich das man es nicht wie einen Stream beschreiben kann und damit einfacher und C++ konformer in sein Programm mit einbauen kann.

    Irgendwann muss ich mir die Arbeit wohl mal noch machen und das alles noch einmal dahin mit umschreiben, so das man beides kann.

  2. Ja Martin, nettes Beispiel.
    Das würde ich aber trotzdem so nicht benutzen wollen.
    _vsnprintf() frisst zuviel(unnötige) performance bei abgeschaltetem debug output.

    Da würde sich ein vorheriger Blick auf „DBWinMutex“ lohnen. 😉

  3. Nun Torsten, es wird ja erst in OutputDebugString() festgestellt,
    ob überhaupt ein Trace gewünscht wird.
    Die Codesequenz davor wird also immer ausgeführt, ob es notwendig ist oder nicht.

  4. Das seh ich nich so edgar, das liese sich recht schnell per ifdef komplett aus dem Programm entfernen, und ein leeres define auf Trace wird wegoptimiert. Alternativ gänge auch ein zur Laufzeit gesetztes Bool, wenn man es ein-/ausschaltbar haben möchte.

  5. Richtig Torsten. Dein Alternativvorschlag kommt der Intention meines ersten posts schon sehr nahe.
    Ich meinte einfach: Es fehlt noch etwas Optimierung. 😉

  6. Dein DBWinMutex Vorschlag fand ich auch nicht so schlecht, allerdings bin ich eher der Typ der das dann lieber selber schreibt als irgendwas fertiges zu nehmen.
    Das auch der Grund weshalb mein Zeug nach 10 Jahren auch so stark angewachsen ist. 🙂

  7. Ach wenn wir grad an kleinen Hilfsmakros sind 😉

    //---------------------------------------------------------------------------------------------
    // FIXMEs / TODOs / NOTE macros
    //---------------------------------------------------------------------------------------------
    #define _QUOTE(x) # x
    #define QUOTE(x) _QUOTE(x)
    #define __FILE__LINE__ __FILE__ "(" QUOTE(__LINE__) ") : "
    
    #define NOTE( x )  message( x )
    #define FILE_LINE  message( __FILE__LINE__ )
    
    #define TODO( x )  message( __FILE__LINE__ "\n"           \
      " ------------------------------------------------\n" \
      "|  TODO :   " #x "\n" \
      " -------------------------------------------------\n" )
    #define FIXME( x )  message(  __FILE__LINE__ "\n"           \
      " ------------------------------------------------\n" \
      "|  FIXME :  " #x "\n" \
      " -------------------------------------------------\n" )
    #define todo( x )  message( __FILE__LINE__ " TODO :   " #x "\n" ) 
    #define fixme( x ) message( __FILE__LINE__ " FIXME:   " #x "\n" ) 
    

    Beispiel:

    #pragma todo(„sonderbehandlung ja/nein“)

    ergibt
    1>DlgFz.cpp
    1>.\dlgs\DlgFz.cpp(604) : TODO : „sonderbehandlung ja/nein“

    PS: falls ich code zeug falsch gemacht hab, bitte nacharbeiten 😉

  8. Man kann den Code optimieren, indem man eine zusätzliche statische Variable mit diesem Code lädt und die entsprechenden Ausabefunktionen nur zulässt wenn die entsprechende statische Variable true ist.

    bool IsDebuggerViewWaiting()
    {
      if (IsDebuggerPresent())
        return true;
      else
      {
        HANDLE hFileMap = ::OpenFileMapping(FILE_MAP_READ, FALSE, _T("DBWIN_BUFFER"));
        if (hFileMap)
        {
          ::CloseHandle(hFileMap);
          return true;
        }
        return false;
      }
    }
    

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.