5. How can I get debug information from my program in the field ?

A little history: I've been working in this industry since CP/M days, and have noticed that it seems an inescapable fact that a certain small percentage of bugs just don't show up in testing, they only ever show in the field with real users hitting on the system. This is especially true of real-time systems, which is the field I started in (embedded military stuff).

Once you accept this fact of life, you realize that for this class of bugs all the ASSERTs in the world aren't gonna help you because you're working with release builds, and you need something you can use in the field.

So, to take an example: I have a telephony system which is my current large project. the system is around a million lines, of which maybe 1/4 are mine. For the main call handling code I have bits embedded in the code which do something like this :

if (g_bDebugEnabled)
{
   wsprintf (m_szDebug, "Mainfrm_LC: max WS=%d max units=%d",
             wMaxCallWS, byNoOfUnits);
   DebugMessage (m_szDebug) ;
}


where
g_bDebugEnabled is a global flag set up from the registry and m_szDebug is a suitably large char array.

DebugMessage is the debug handler, which can be as simple or complex as you like, but would typically look something like this:

BOOL CMainFrame::DebugMessage (const char * pszString)
{
   static char szLocalDebug [MAX_DEBUG_LENGTH+12] ;

   LRESULT litem ;
   time_t TimeNow ;
   struct tm * pTime ;

   if (!m_bStoppedData)
   {
      memset (szLocalDebug, 0, sizeof(szLocalDebug)) ;

      TimeNow = time (NULL) ;
      pTime = localtime (&TimeNow) ;
      strftime (szLocalDebug, 12, "%H:%M:%S >", pTime) ;
      strcat (szLocalDebug, pszString) ;

      SendDlgItemMessage (IDC_DEBUG_MSGLIST,
                          LB_ADDSTRING,
                          0,
                          (LPARAM) (LPSTR) szLocalDebug) ;

      if (m_bCopyToDbwin)
      {
         strcat (szLocalDebug, " <UserInf>\r\n");
         OutputDebugString (szLocalDebug) ;
      }

      litem = SendDlgItemMessage (IDC_DEBUG_MSGLIST,
                                  LB_GETCOUNT,0,0L);

      if (litem > MAX_DEBUG_MESSAGES)
         litem = SendDlgItemMessage (IDC_DEBUG_MSGLIST,
                                     LB_DELETESTRING,0,0L);

      SendDlgItemMessage (IDC_DEBUG_MSGLIST,
                          LB_SETCURSEL,
                          (WORD)(litem-1),0L);

      if (*pszString == '*') // allow automatic "hold"
         Hold (TRUE) ;

      return TRUE ;
   }

   return FALSE ;
}

If you already have a suitable dialog hanging around, you only have to add a listbox, and a mechanism for holding and clearing it (my diaog is normally hidden, made visible by a weird keystroke sequence). Having the ability to tell the debug code to copy its output to OutputDebugString helps, because I can then use a debug output catcher application like DBWIN32, which provides the ability to save to a file, and also allows me to catch the debug output from multiple apps, all neatly serialised. I developed a multi-application serial comms server some time ago, and could never have met the deadline without this technique to catch timing-related problems.

You pay a little price in code fat for the debug function and the wsprintf code, but if all you're doing is stuff like tellbacks (i.e. "called this function", "exited this function") then even that isn't much, and when debug is disabled you're only paying the performance penalty of a boolean check, which is not much.

Once you have the basic technique down pat, you can then add extra features, like adding an OnCommand handler which calls DebugMessage to give you a record of all the users actions, so you can tell exactly what they did, like this one:

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
   TCHAR szLabel [32] ;
   HMENU hMenu = 0;

   if (g_bDebugEnabled)
   {
      hMenu = ::GetMenu (g_hUIMainWnd);

      if (hMenu)
      {
         if (::GetMenuString (hMenu,
                              LOWORD(wParam),
                              szLabel,
                              sizeof(szLabel)-1,
                              MF_BYCOMMAND))
         {
            wsprintf (m_szDebug, "Menu: User selected '%s'", szLabel);
            DebugMessage (m_szDebug);
         }
      }
   }
   return CFrameWnd::OnCommand(wParam, lParam);
}


You can add similar handlers to dialogs to capture button pushes etc. It's up to you how far you want to take it.



Debuginfo Link Page

Microsoft
Sun (Java)
Linux Online

Further References

Debuginfo Fax Site (English) English Fax Site
Debuginfo Mirror Site (English) English Mirror Site

Debuginfo Fax Site (German) German Fax Site
Debuginfo Mirror Site (German) German Mirror Site

Abi 1999 Online Version
Finland Local Areas
Norway Local Areas

ISDN Technical Overview
ISDN Technical Overview (Mirror Site)

[A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O]

For a full list of links, please visit our official Web site www.actfax.com. If you do not get redirected to that site automatically, please click on one of the links above to get redirected to that site.

Author Matt J. Scully

All rights reserved - Copyright (c) 1995-2000 [005].