Print Output

"To printf or not to printf , that is the question!" Well, not to butcher Shakespeare, but sometimes the old ASCII text output is an excellent method of tracking down obscure bugs . This is not meant to displace the use of full-blown symbolic debuggers , but sometimes the old fashioned printf is better.

Note that I said sometimes !

This is where things get interesting! (I keep saying that in this book, do I not!) With the C++ cout IOStream class it would have to be overloaded and the output rerouted to your own device. The printf function is a little harder to capture although the standard output could be reassigned. Some platforms such as Win32 do not use printf. Instead they use a string output function such as OutputDebugString(). What I like to do is create a thin abstraction layer (whether it be written in C++ or C) of log functions that route to one or more specified output devices. This is set up for two types of output: Debug, which is not shipped with the game, and Release, which is. So, for example:

 LogWrite() LogRDWrite() 

They are both similar to the functionality of printf() except they do not return a count of bytes written. The LogWrite() function is a debug version that gets stubbed to nothing in a release build. LogRDWrite() is compiled for both a release and debug skew and gets shipped with the code. This helps to simplify the job of customer support when something really bad happens. There are other functions in the log module, but these two are at the core and they in essence would call printf() and/or OutputDebugString(), etc. The idea is that your cross-platform application would call the log functions and be abstracted for the individual platform.

When I develop Win32 applications I frequently route this output to one or all of the following: the function OutputDebugString(), a Notepad window, a text file, a high-speed serial port with hardware handshake, and a TCP/IP communications setup for data collection on another computer. I do something similar for other embedded platforms. With a smart terminal there, I can back scroll through live memory dumps to track behaviors.

One of the methods I frequently use with this type of diagnostics is to set threshold values to enable the trigger, which in effect allows the capture and printing of select data. This helps to minimize being inundated with massive amounts of data that hinders rather than helps detect problems.

As a superset of this functionality, the following functions are generated for even more detailed information on a generic basis.

For platforms that do not offer you the advantage of an IDE or if you need to track problems and history dumps of memory or registers, then some of the following functions should come in handy.

Float Array Print

This function prints a memory dump of a list of single-precision floats (four per line of printed text).

Listing 21-1: ...\chap20\vecBug\vecBug.cpp
image from book
 void vmp_FloatPrint(const float * pf, uint nCnt) {   unsigned int n;   int i;   ASSERT_PTR4(pf);   ASSERT_ZERO(nCnt);       vmp_SIMDEntry();       do {     i = (--nCnt >= 4) ? 3 :  (nCnt & 3);     nCnt -= i;         printf("%8.8x ", pf);         do {       printf("%lf ", (double)(*pf));       pf++;     } while (i-- > 0);         printf("\n");   } while (nCnt);       vmp_SIMDExit(); } 
image from book

Once a float array function is in place, creating other dependent functions such as the following is a snap. Also, by creating a debug versus release version, the debug would be the function type definitions and the release would be empty macros, thus stubbing the code to non-existence similar to the following:

 #ifdef USE_LOG void vmp_FloatPrint(const float * pf, uint nCnt); #else #define vmp_FloatPrint(pf, nCnt)     // stub #endif 

The same would need to be implemented in the logging header file for any other logging function that would need to be stubbed in a release build.

Vector Print

Listing 21-2: \chap20\vecBug\vecBug.cpp
image from book
 void vmp_VecPrint(const vmp3DVector *pVec) {   vmp_FloatPrint((float *)pVec, 3); } 
image from book

Quad Vector Print

Listing 21-3: ...\chap20\vecBug\vecBug.cpp
image from book
 void vmp_QVecPrint(const vmp3DQVector *pVec) {   vmp_FloatPrint((float *)pVec, 4); } 
image from book

Quaternion Print

Listing 21-4: ...\chap20\vecBug\vecBug.cpp
image from book
 void vmp_QuatPrint(const vmpQuat *pVec) {   vmp_FloatPrint((float *)pVec, 4); } 
image from book

Matrix Print

This function dumps a single-precision floating-point matrix. It expands single-precision to double-precision to help minimize any problems of ASCII exponential representations.

Listing 21-5: ...\chap20\vecBug\vecBug.cpp
image from book
 void vmp_MatrixPrint(const vmp3DMatrix Mx) {   ASSERT_PTR4(Mx);       vmp_SIMDEntry();       printf("%8.8x ", Mx);          // Print address   printf("%lf %lf %lf %lf\n",   (double)Mx[0][0],       (double)Mx[0][1], (double)Mx[0][2], (double)Mx[0][3]);   printf("         %lf %lf %lf %lf\n",    (double)Mx[1][0],       (double)Mx[1][1], (double)Mx[1][2], (double)Mx[1][3]);   printf("         %lf %lf %lf %lf\n",    (double)Mx[2][0],       (double)Mx[2][1], (double)Mx[2][2], (double)Mx[2][3]);   printf("         %lf %lf %lf %lf\n\n",  (double)Mx[3][0],       (double)Mx[3][1], (double)Mx[3][2], (double)Mx[3][3]);       vmp_SIMDExit(); } 
image from book

Memory Dump

There is nothing sweeter than a memory dump similar to the old DOS debug.exe program. A combination of address, hex data, and ASCII equivalents is dumped on an up to 16-byte ASCII string. Even when you have a memory dump window within your IDE it can typically only handle one dump at a time. If you are trying to track a nasty little problem or to verify proper functionality, sometimes a print trail is far superior . So with this in mind, the following should be a handy little tool for your development cycle.

Listing 21-6: ...\chap20\vecBug\vecBug.cpp
image from book
 void vmp_LogMem(const void * const vp, uint size) {   uint   x, y, run, tail, col, n;   char *p, buf[256];   byte *mp;       ASSERT_PTR(vp);   ASSERT_ZERO(size);       mp = (byte *) vp;       run = 16;   col = tail = 0;         //   For all lines       for (y = 0; y < size;)   {     if (size-y < run)     {       run = size-y;           // Display only requested!       n = 16 - run;           // trailing places       tail = n * 3;           //  trailing spaces       tail += ((n+3) >> 2);   // column separators     }                               // Print address     p = buf + sprintf(buf, "%8.8x ", mp);           // Hex values         for (x = 0; x < run; x++)     {       p += sprintf(p, " %2.2x", *(mp+x));           if ((++col % 4) == 0)       {         *p++ = ' ';       }     }           //   Any trailing spaces ?         while (tail--)     {       *p++ = ' ';     }            // ASCII chars         for (x = 0; x < run; x++)     {       if ((0x20 <= *mp) && (*mp <= 0x7f))       {         *p++ = *mp++;       }       else       {         *p++ = '.';         mp++;       }     }         *p++ = '\r';     *p++ = '\n';     *p = 0;     printf(buf);         y += run;     run = 16;     tail = col = 0;     }       printf("\r\n"); } 
image from book

32.64-Bit 80X86 Assembly Language Architecture
32/64-Bit 80x86 Assembly Language Architecture
ISBN: 1598220020
EAN: 2147483647
Year: 2003
Pages: 191

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: