═══ 1. Introduction ═══ Welcome to C-Scout. This tool has been specially developed to help developers who are writing complex applications for OS/2. The authors of C-Scout have been using a similar tool internally for their own development work and have now released version 2 of C-Scout. Many improvements have been added to this version in order to improve its usability and performance. The tool is particularly suited for debugging and maintaining multi-process communications systems or complex PM programs where conventional tools or tracing can not be used because of the way they distort the system behaviour or because no other tools are available. C-Scout is however just as much at home in a single-threaded VIO program. About a priori Supported Environments ═══ 1.1. About a priori ═══ a priori computer solutions GmbH is a software house situated in Frankfurt, Germany. It develops software products and bespoke software for several international companies situated throughout Germany. The main platforms it supports are OS/2, UNIX and Windows. a priori can be contacted at: a priori computer solutions GmbH MБnchener Str. 13 60329 Frankfurt Tel. +49-69-239462 Fax. +49-69-236426 EMAIL 100137.2315@compuserve.com ═══ 1.2. Improvements ═══ Many improvements have been added to this version. They include: o Presentation Manager console user interface. o Ability to trace multiple processes in one view. o Ability to trace a DLL. o Ability to trace several views simultaneously. o Support for disjoint trace levels. o No need to initialise tracing in the program to be traced. o Added synchronous tracing capability. o Added ability to register a user-written function to be called in the case of an error detected by the TX macro. o Improved error information. o Added exception handling to datt.dll. o Presentation Manager dialog box for critical errors. o Ability to display active processes known to C-Scout. o Improved support for various compilers. o Several bug fixes and minor improvements. ═══ 1.3. Supported Environments ═══ The following compilers have been tested with C-Scout: o IBM CSet/2 V1.0 and higher. o IBM C/C++ Tools V2.0 and higher. o WATCOM C/C++ V10. o Borland C++ for OS/2 V1.0 and higher. The following restrictions apply when not using the IBM C/C++ compilers: o When tracing, the name of the function is not available because only C-SET provides this information with a compiler macro. o The C library error information in text format, which is displayed when the TX macro has detected an error, is not available. This is because C-Scout has been written using C-SET and therefore can not access the C libraries used by the other compilers. No other restrictions are known. Operating System Known Restrictions ═══ 1.3.1. Operating System ═══ In order to run C-Scout, OS/2 Version 2.1 or higher is required. ═══ 1.3.2. Known Restrictions ═══ The following restrictions are known: o a maximum of 32 views can be active at any one time. The number of views that can be defined is only limited by the resources available. Of these, 32 can be in active trace mode. o a maximum length of 2000 bytes is available to the Tracef function. This limits the size of the formatted buffer to 2000 bytes. o the trace levels must be between 0 and 999999. o a problem occurs when trying to use the trace facility in an exception handler when the synchronous option has been requested. This only occurs when PMScout is being used. The problem is under examination. ═══ 2. Installation ═══ C-Scout is installed by simply executing the install.exe program provided on the diskette. This program uses the Software Installer from IBM to install the C-Scout product in the directories the user specifies. The installer will update the machine's Config.Sys with the necessary changes so that a re-boot is required for the changes to become effective. Installation with a response file or using CID is also possible. Examples are provided with the product. ═══ 3. License Agreement ═══ A license is required for each machine which uses any or all of the following files: PMSCOUT.EXE PM Console. DATT.LIB library used for linking the trace tool. DATT.H include file required to compile a program. A license is not required for the other files which are provided. In particular the production environment console - VIOSCOUT.EXE and the DLLs can be freely distributed. The read.me contains information about the license agreement made when you use C-Scout. Please read this file if you have not already done so. If you do not agree with these terms please return all copies of C-Scout immediately. ═══ 4. How to use C-Scout ═══ In order to use the features of C-Scout, the developers are required to code the API calls in their program and compile and link it together with the supplied library and include file. At run-time, the DLLs will be loaded and provide the execution environment for the tracing functions. Exactly what should be traced at runtime is specified by using a console. There are 2 consoles available for C-Scout: PMSCOUT.EXE a PM Console VIOSCOUT.EXE a VIO Console Both consoles use the concept of a view for tracing. A view is the object which will be traced. It contains all the necessary information about the processes to be traced and the relevant trace levels which are to be activated for these processes. Additionally, information about the log filename and other information is stored with the view object. For more information about each setting, please refer to the on-line help provided with PMSCOUT.EXE. Whenever the user wishes to activate tracing for a particular program or group of programs, he must first define a view containing the information about these programs. Once this has been done, the view can be started, which means that tracing has been activated for this view. Thereafter, whenever an API call is executed, a check is made to determine if the current program is being traced and if so, which trace levels have been set. If a match is found, then a message is sent to the console and the information logged (if logging has been activated) and displayed (if display has been requested). Tracing can be stopped at any time or the settings for the view can be changed during tracing. The trace results can be displayed on-line in a listbox and/or be written to a log-file for later analysis. The logfile can be viewed from the PM console after tracing has been stopped for the view in question. Because the developer knows most about the internals of the system he has developed, the PM Console was developed with the developer in mind. This is where the views are defined and the trace logs examined. From the PM Console it is also possible to create SCT files to be used by the VIO Console in the production environment. An SCT file contains all the information defined for a view and allows an end-user to activate tracing without having to know all about which levels and processes are involved. The VIO Console does not display any trace information to the screen, but writes solely to a logfile. After tracing has been terminated, the logfile can be sent to the developers for analysis. The logfile must first be formatted using the format function in the PM console. Therafter the resulting logfile can be viewed. How to set the trace levels How to compile the program ═══ 4.1. How to set the trace levels ═══ To get an idea of how to set the trace levels for a particular view, consider the case that you want to trace two processes, say Foo.exe and Bar.exe, which make use of interprocess communications. You might have defined four trace levels in a file included by both Foo.c and Bar.c, say in FooBar.h: #define TL_FOO_SEND 10 #define TL_FOO_RCV 11 #define TL_BAR_SEND 12 #define TL_BAR_RCV 13 Each time Foo sends something to Bar, you could code Tracef( TL_FOO_SEND, "Foo sent something" ); in Foo.c and consequently, Tracef( TL_BAR_RCV, "Bar received something" ); in Bar.c each time Bar receives something. To trace Foo and Bar at the same time, you would create a new view and add the two processes Foo.exe and Bar.exe. For Foo, you would set the trace level to 10/10 (i.e. all levels from 10 to 10), and for Bar you would set it to 13/13. Once tracing for your view is started (and, of course, Foo and Bar are started as well), you can watch Foo and Bar sending and receiving. Later on, you might decide that you are not only interested in what Foo receives, but in what Foo also sends. Provided you have coded the appropriate Tracef calls in Foo.c, you would open your view's settings and change the trace levels for Foo.exe from 10/10 to 10/11. Note that it is not neccessary to use different trace levels for Foo and Bar for sending and receiving (though we did here), as C-Scout can distinguish between the same trace levels but from different processes. That is, you could have coded #define TL_SENDING 100 #define TL_RECEIVING 200 in FooBar.h, Tracef( TL_SENDING, "Foo sent something" ); in Foo.c and Tracef( TL_SENDING, "Bar sent something" ); in Bar.c, as well, and set the trace levels for Foo.exe to 100/200 to watch Foo sending and receiving, and for Bar.exe to 100/100 to watch Bar.exe only sending. Now consider the trace levels you are interested in are not consecutive, as in the following case: #define TL_SENDING 100 #define TL_XVAL 150 #define TL_RECEIVING 200 where you might use a statement like Tracef( TL_XVAL, "The value of x is %i", x ); to inspect the value of some integer x in your program. If you now would set the trace levels for Foo to 100/200 to watch Foo sending and receiving, you would see the value of x as well. To avoid this, you would add Foo.exe to the view's list of processes twice: One instance with the level set to 100/100, and the other instance with the level set to 200/200. ═══ 4.2. Compiling your program ═══ The program can be compiled with debug options set or with optimisation set. The program may be dynamically linked or statically linked to the C-Runtime Library of your favourite compiler. The DATT.H include file must be included to use the API functions and macros. Note that it is neccessary to have the file OS2.H included before DATT.H, since DATT.H uses some of the types defined in OS2DEF.H. The program must be linked with the DATT.LIB and the option to respect the case for the names of external functions should be active. This is usually the /NOI option. ═══ Trace Levels ═══ It is important for a development team to carefully plan which levels are to be used by which program and for what purpose. This makes debugging in the production environment a lot easier because the developer is able to pinpoint exactly which information he requires using trace levels. This is particularly true when debugging a DLL. In this case all Exe programs which use the specified levels will be traced. It is strongly recommended that levels for a particular DLL are not used elsewhere. ═══ 5. Developer's Toolkit Functions ═══ This section presents the help for the developer kit functions. They are presented in alphabetical order. ═══ 5.1. Tracef ═══ Select an item: Function Syntax Description Notes Example ═══ Syntax - Tracef ═══ /************************************************/ /* Tracef is used to send an ASCII text string */ /* to the console for display and/or logging. */ /************************************************/ #include VOID APIENTRY Tracef( const ULONG ulLevel, const PSZ pszFormat, argument list ); ULONG ulLevel PSZ pszFormat ... argument list ═══ ulLevel ═══ The level is used to identify the particular part of a system to which this piece of code belongs. The levels should be carefully thought out beforehand so that no two programs are using the same level as this leads to the tracing of information which is not required. ═══ pszFormat ═══ This string corresponds to the format string as used in the standard C function printf. ═══ argument list ═══ The argument list corresponds to the argument list as used in the standard C function printf. ═══ Description - Tracef ═══ Tracef determines if tracing has been activated for this process and if so, will build a string using the format string specified by pszFormat and the argument list in the same way as the standard C function printf. The resulting string will be sent to the console for display and/or logging. ═══ Notes ═══ In case of an error using this function a popup will be displayed with error information. ═══ Example ═══ This example shows a program using the Tracef function to track the number of threads which the user requested to be started. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include case IDD_TESTDLG: { SHORT sNumThreads; TX( WinDlgBox ( HWND_DESKTOP, hwndClient, dlgpTest, 0, IDD_TESTDLG, &sNumThreads ) == DID_ERROR ); /* trace the number entered by the user */ Tracef( TM_INFO, "Number of threads to be started %d", sNumThreads ); break; } ═══ 5.2. HexTrace ═══ Select an item: Function Syntax Description Notes Example ═══ Syntax - HexTrace ═══ /************************************************/ /* Hextrace is used to send the contents of a */ /* buffer to the console without any formatting */ /* taking place. The contents will be logged */ /* and/or displayed as required. */ /************************************************/ #include VOID APIENTRY HexTrace( const ULONG ulLevel, const PVOID pvData, const ULONG ulDataLen ); ULONG ulLevel PVOID pvData ULONG ulDataLen ═══ ulLevel ═══ The level is used to identify the particular part of a system to which this piece of code belongs. ═══ pvData ═══ This is the address of the buffer whose contents are to be displayed without any formatting performed on the buffer. The contents will be displayed as hexadecimal values with their ASCII equivalent next to them. ═══ ulDataLen ═══ This value specifies the number of bytes in the buffer which are to be sent to the console for display and/or logging. This value should not exceed the total length of the buffer. ═══ Description ═══ HexTrace determines if tracing has been activated for this process and if so informs the console to dump the number of bytes specified by ulDataLen beginning at the address specified by pvData to the required output medium. ═══ Notes ═══ In case of an error using this function a popup will be displayed with error information. ═══ Example ═══ This example shows a program dumping the contents of a buffer. In this example buf is an array of char. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include /* dump the contents of buf */ HexTrace( TM_DUMP, buf, sizeof(buf) ); ═══ 5.3. SetErrorInfo ═══ Select an item: Function Syntax Description Notes Example ═══ Syntax - SetErrorInfo ═══ /************************************************/ /* SetErrorInfo is used to register an error */ /* handler which will be called if the TX macro */ /* detects an error condition. */ /************************************************/ #include VOID APIENTRY SetErrorInfo( const PFNCH pfnConditionHandler ); const PFNCH pfnConditionHandler ═══ PFNCH ═══ typedef FNCONHANDLER * PFNCH; ═══ FNCONHANDLER ═══ typedef BOOL APIENTRY FNCONHANDLER( PTXMI ptxBuffer ); ═══ pfnConditionHandler ═══ This is the address of the user function to be called if an error condition is detected. ═══ Description ═══ SetErrorInfo registers the user function on a process basis. The function is valid for all the threads in the process. The function will be called if a TX macro detects an error condition. The function must be of the type FNCONHANDLER and return either TRUE or FALSE. If the registered function returns TRUE, the default error handler is called after the registered handler. If the registered function returns FALSE, the default handler is not called and the process for which the function was registered continues. Thus, it is the developer's responsibility to terminate the process. ═══ Notes ═══ In case of an error using this function a popup will be displayed with error information. ═══ Example ═══ This example shows how a program can register its own critical error function to be called when a TX macro detects an error. ConditionHandler is the function which is to be registered. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include /*-------------------------------*/ /* My own critical error handler */ /*-------------------------------*/ BOOL APIENTRY ConditionHandler( PTXMI ptxBuffer ) { /* if error in second thread */ if ( ptxBuffer->tid == 2 ) { /* continue process */ return FALSE; } else { /* call default error handler */ return TRUE; } } /*----------------------------------------*/ /* Register my own critical error handler */ /*----------------------------------------*/ SetErrorInfo ( ConditionHandler ); ═══ PTXMI. ═══ Pointer to a structure of type TXmacroInfo which is detailed below: typedef struct __TXmacroInfo { UCHAR szFunction[FN_SIZE]; /* function name */ UCHAR szModuleName[CCHMAXPATH]; /* name of module */ ULONG ulLineNo; /* current line number */ UCHAR szSourceLine[SL_SIZE]; /* current source line */ APIRET apiretRc; /* result of expression in TX macro */ INT iErrNo; /* c runtime errno */ INT iDosErrNo; /* c runtime dos error code */ UCHAR szErrBuff1 [EM_SIZE]; /* PM return code text */ UCHAR szErrBuff2 [EM_SIZE]; /* Kernel return code text */ UCHAR szErrBuff3 [EM_SIZE]; /* CRT return code text */ TID tid; /* TID */ PID pid; /* PID */ } _TXmacroInfo; ═══ 6. Developer's Toolkit Macros ═══ As well as the APIs there are macros available which make using C-Scout easier. The macros are documented below: ═══ 6.1. TX ═══ The TX macro (Test eXpression) allows the programmer to encapsulate an expression in the macro and then if at runtime the expression evaluates to TRUE an error screen will be displayed with relevant information. Additionally a log-file is written containing this information for later analysis. A total of 50 such log-files will be written before wrapping to the first log-file again. If tracing is active, this information will also be written to the logfile or screen by the console. The macro uses a feature of the IBM C-Set compiler which defines a macro for the function name. This is only available when using the C-Set compiler or another compiler which provides this information. The macro can still be used for compilers without this feature but the function name is not available. Once the error panel is displayed the user can decide to abort the program or to continue. Normally the user would abort the program at this stage. This macro should be used to protect the program from the effects of an unrecoverable error which would anyway lead to an abnormal end. The expression can contain any Operating System call and so would normally be used to check PM or Kernel calls for example which would not normally produce an error but if not checked would lead to unforeseen errors occurring. Example ═══ TX. ═══ This example shows how a program can check the return code of a PM call by using the TX macro. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include TX(! WinRegisterClass( habThis, TITLE, wpClient, CS_SIZEREDRAW, 0 ) ); ═══ 6.2. FncEntry ═══ The FncEntry macro (Function Entry) is used to trace the entry point of a function. This should be the first statement used after declaring the local variables. The macro uses a feature of the IBM C-Set compiler which defines a macro for the function name. This is only available when using the C-Set compiler or another compiler which provides this information. The macro can still be used for compilers without this feature but the function name is not available. Example ═══ FncEntry. ═══ This example shows how to use the FncEntry and FncExitVoid macros in a function. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include static VOID fnThread ( PVOID lfn ) { INT i; PSZ pszString; /* trace function entry */ FncEntry(); /* main body of function */ /* trace function exit */ FncExitVoid(); } ═══ 6.3. FncExit ═══ The FncExit macro (Function Exit) is the counterpart of the FncEntry macro. This statement should be the last statement in the function before the terminating bracket. The macro generates a return statement with the specified expression used as the parameter for the return statement. If the function is of Type void i.e. no return code then the FncExitVoid macro should be used. The macro documents the function exit and return code by generating an appropriate trace statement. A feature of the IBM C-Set compiler is used for the function name. This is only available when using the C-Set compiler or another compiler which provides this information. The macro can still be used for compilers without this feature but the function name is not available. Example ═══ FncExit. ═══ This example shows how to use the FncExit macro in a function. /*---------------------------------*/ /* include the C-Scout definitions */ /*---------------------------------*/ #include static BOOL MyFunction( VOID ) { INT i; BOOL fRc = TRUE; /* trace function entry */ FncEntry(); /* main body of function */ /* trace function exit and return code */ FncExit( fRc ); } ═══ 7. Errors ═══ If an internal error is detected, C-Scout will produce a popup screen containing error information. If the error persists, please contact a priori for support. The following error messages may occur: o DATT Init-Error - Not Initialised o DATT Alloc-Error o DATT SendError (DosAllocShar..) o DATT SendError (DosOpenQueue) o DATT SendError (DosGiveShar.) o DATT SendError (DosWriteQueue) o DATT SendError (DosOpenEventSem) o DATT SendError (DosWaitSem) ═══ 8. Obtaining Support ═══ Support for C-Scout can be obtained in one of the following ways: o Telephone Call +49-69-239462 and ask for the Technical Support for C-Scout. o Fax The fax number is +49-69-236426. o EMAIL send a message to 100137.2315@compuserve.com. In each case please quote the version information about C-Scout which is available from the PMSCOUT menu by selecting Help and then Product Information.