/* SCCSID = %W% %E% */ /**************************************************************************** * * * Copyright (c) IBM Corporation 1994 - 1997. * * * * The following IBM OS/2 source code is provided to you solely for the * * the purpose of assisting you in your development of OS/2 device drivers. * * You may use this code in accordance with the IBM License Agreement * * provided in the IBM Device Driver Source Kit for OS/2. * * * ****************************************************************************/ /**@internal %W% * Facilities for logging errors, status, and realtime traces. Logged * information can be routed to any combination of display, COM: lines, * system trace buffer (retrieved by TRACEFMT utility), and/or system * error log. * @version %I% * @context 16-bit, Ring 3 or Ring 0, DD Ioctl time context. The BINARYLOG * and PERFLOG methods may be used in Interrupt context. * @notes * * Supported syntax for data formatting (case insensitive) * * >--- % --+-----+--------------- B -+---| * | | | | * +- 0 -+ +----------- Q -+ * | | * +-----+---+- D -+ * | | | | * +- L -+ +- X -+ * * Where * 0 - format with leading 0's * L - long integer * D - format decimal * X - format hexidecimal * B - byte hex (BINARY logs only) * Q - quadword hex (BINARY logs only) * Not supported at this time * S - ASCIIZ data * * @notes bugs * The %b, %q, and %b datatypes are not implemented across all log types. * * @history */ extern "C" { #define INCL_NOPMAPI #define INCL_DOSMISC #include } #include #include // strlen() intrinsic #include "iodelay.h" #include "log.hpp" #include #define CR 0x0d #define LF 0x0a #define LEADING_ZEROES 0x8000 #define SIGNIFICANT_FIELD 0x0007 #define UART_DATA 0x00 // UART Data port #define UART_INT_ENAB 0x01 // UART Interrupt enable #define UART_INT_ID 0x02 // interrupt ID #define UART_LINE_CTRL 0x03 // line control registers #define UART_MODEM_CTRL 0x04 // modem control register #define UART_LINE_STAT 0x05 // line status register #define UART_MODEM_STAT 0x06 // modem status regiser #define UART_DIVISOR_LO 0x00 // divisor latch least sig #define UART_DIVISOR_HI 0x01h // divisor latch most sig // Global logging variables. Values are setup in init.cpp. LOG* pStatus; // Points to the status log. LOG* pError; // Points to the error log. LOG* pSoftError; // Points to the error log. BINARYLOG* pTrace; // Points to the trace log. PERFLOG* pPerfLog; // Points to Performance trace log. // Used to convert binary to ascii static const char ddhextab[]="0123456789ABCDEF"; //----------- ddprintf_DecWordToASCII - PSZ LOG::_DecWordToASCII(PSZ StrPtr, WORD wDecVal, WORD Option) /* ; StrPtr - Output, pointer to location to save resulting ASCII string. ; wDecVal - Input, 16 bit value to convert. ; Option - Input, selects leading zeros. */ { BOOL fNonZero=FALSE; WORD Digit; WORD Power=10000; while (Power) { Digit=0; while (wDecVal >=Power) //Digit=wDecVal/Power; { Digit++; wDecVal-=Power; } if (Digit) fNonZero=TRUE; if (Digit || fNonZero || (Option & LEADING_ZEROES) || ((Power==1) && (fNonZero==FALSE))) { *StrPtr=(char)('0'+Digit); StrPtr++; } if (Power==10000) Power=1000; else if (Power==1000) Power=100; else if (Power==100) Power=10; else if (Power==10) Power=1; else Power=0; } // end while return (StrPtr); } //----------- ddprintf_DecLongToASCII - PSZ LOG::_DecLongToASCII(PSZ StrPtr, DWORD lDecVal,WORD Option) { BOOL fNonZero=FALSE; DWORD Digit; DWORD Power=1000000000; // 1 billion while (Power) { Digit=0; // Digit=lDecVal/Power while (lDecVal >=Power) // replaced with while loop { Digit++; lDecVal-=Power; } if (Digit) fNonZero=TRUE; if (Digit || fNonZero || (Option & LEADING_ZEROES) || ((Power==1) && (fNonZero==FALSE))) { *StrPtr=(char)('0'+Digit); StrPtr++; } if (Power==1000000000) // 1 billion Power=100000000; else if (Power==100000000) Power=10000000; else if (Power==10000000) Power=1000000; else if (Power==1000000) Power=100000; else if (Power==100000) Power=10000; else if (Power==10000) Power=1000; else if (Power==1000) Power=100; else if (Power==100) Power=10; else if (Power==10) Power=1; else Power=0; } return (StrPtr); } //----------- ddprintf_HexWordToASCII - PSZ LOG::_HexWordToASCII(PSZ StrPtr, WORD wHexVal, WORD Option) { BOOL fNonZero=FALSE; WORD Digit; WORD Power=0xF000; WORD ShiftVal=12; while (Power) { Digit=(wHexVal & Power)>>ShiftVal; if (Digit) fNonZero=TRUE; if (Digit || fNonZero || (Option & LEADING_ZEROES) || ((Power==0x0F) && (fNonZero==FALSE))) //*StrPtr++=(char)('0'+Digit); *StrPtr++=ddhextab[Digit]; Power>>=4; ShiftVal-=4; } // end while return (StrPtr); } //----------- ddprintf_HexLongToASCII - PSZ LOG::_HexLongToASCII( PSZ StrPtr, DWORD wHexVal, WORD Option ) { BOOL fNonZero=FALSE; DWORD Digit; DWORD Power=0xF0000000; DWORD ShiftVal=28; while (Power) { Digit=(wHexVal & Power)>>ShiftVal; if (Digit) fNonZero=TRUE; if (Digit || fNonZero || (Option & LEADING_ZEROES) || ((Power==0x0F) && (fNonZero==FALSE))) *StrPtr++=ddhextab[Digit]; if (Power==0xF0000000) // 1 billion Power=0xF000000; else if (Power==0xF000000) Power=0xF00000; else if (Power==0xF00000) Power=0xF0000; else if (Power==0xF0000) Power=0xF000; else if (Power==0xF000) Power=0xF00; else if (Power==0xF00) Power=0xF0; else if (Power==0xF0) Power=0xF; else Power=0; ShiftVal-=4; } // end while return (StrPtr); } USHORT LOG::_ddsprintf ( PSZ pszOutString, PSZ pszFmtString, STACK_ADR Parm ) /* ; ddsprintf - "sprintf" function on a vbl number of arguments. ; ; pszOutString - Output string saved here. ; pszFmtString - Input, Ascii string containing text and formating information. ; Parm - Input, Address of the 1st of the variable number of arguments. ; ;Returns: ; Length of string created. */ { PSZ BuildPtr = pszOutString; PSZ pStr = (PSZ) pszFmtString; PSZ SubStr; WORD wBuildOption; while (*pStr) { // don't overflow target if (BuildPtr >= &pszOutString[ MAX_MsgLen-2 ] ) break; switch (*pStr) { // the top switch case '%': wBuildOption=0; pStr++; if (*pStr=='0') { wBuildOption|=LEADING_ZEROES; pStr++; } switch(*pStr) { // switch on "%" options case 'x': case 'X': BuildPtr=_HexWordToASCII(BuildPtr, *Parm.WordPtr++,wBuildOption); pStr++; continue; case 'd': case 'D': BuildPtr=_DecWordToASCII(BuildPtr, *Parm.WordPtr++,wBuildOption); pStr++; continue; case 's': case 'S': SubStr = (PSZ) *Parm.StringPtr; while (*SubStr) *BuildPtr++ = *SubStr++; Parm.StringPtr++; BuildPtr--; // remove the \0 pStr++; continue; case 'l': case 'L': pStr++; switch (*pStr) { // "%l" options case 'x': BuildPtr=_HexLongToASCII(BuildPtr, *Parm.LongPtr++,wBuildOption); pStr++; continue; case 'd': BuildPtr=_DecLongToASCII(BuildPtr, *Parm.LongPtr++,wBuildOption); pStr++; continue; } // end switch on "%l" options continue; // dunno what he wants case 0: continue; } // end switch on "%" options break; case '\\': pStr++; switch (*pStr) { case 'n': *BuildPtr++=CR; *BuildPtr++=LF; pStr++; continue; case 'r': *BuildPtr++=CR; pStr++; continue; case 0: continue; } // end switch break; // case '\\' case '\n': pStr++; *BuildPtr++=CR; *BuildPtr++=LF; continue; break; } // end top switch *BuildPtr++=*pStr++; } // end while *BuildPtr=0; // cauterize the string return strlen( (char *) pszOutString ); } USHORT _CheckRing(void); #pragma aux _CheckRing = \ "push cs" \ "pop ax " \ "and ax,0003H" \ parm nomemory \ modify [ax]; //----------------------- ddputstring - void LOG::_ddputstring ( USHORT iMsgNum, PSZ pszMsg ) { int iMsgLength = strlen( (char *) pszMsg ); // Sring length of the message. // Need to know whether we're still executing at Ring 3. if ( _bInRing3 ) if (_CheckRing() != 3) // In Ring 3 last time, still there now? _bInRing3 = FALSE; // Nope, and no need to check again. // --- Index through possible places to save this message. for (int destination=0; destination != _nMsgSinks; ++destination) { if ( bMsgSink[destination] ) { switch (destination) { case _Display: // Put message on the display only if we're in Ring 3. if ( _bInRing3 ) { static CHAR szCRLF[] = "\r\n"; static int iCRLF_Len = strlen( (char *) szCRLF ); DosPutMessage( 1, iMsgLength, (PCHAR) pszMsg ); DosPutMessage( 1, iCRLF_Len, (PCHAR) szCRLF ); } break; case _SerialLine: { PCHAR pChar = (PCHAR) pszMsg; while (*pChar) _CharOut(*pChar++); } _CharOut( '\n' ); break; case _ErrorLog: if ( _bInRing3 ) DosSysTrace( _DosTraceMajorCode, iMsgLength, iMsgNum, (PCHAR) pszMsg); else DevHelp_RAS( _DosTraceMajorCode, iMsgNum, iMsgLength, pszMsg); break; case _TraceLog: if ( _bInRing3 ) DosSysTrace( _DosTraceMajorCode, iMsgLength, iMsgNum, (PCHAR) pszMsg); else DevHelp_RAS( _DosTraceMajorCode, iMsgNum, iMsgLength, pszMsg); break; } } } } void LOG::_CharOut(char c) /* Sends a single character to the COM: port. */ { UCHAR inbyte; inbyte = inp((_ComPort + UART_LINE_STAT)); while ((inbyte & 0x20) != 0x20) { iodelay(1); inbyte = inp((_ComPort + UART_LINE_STAT)); } // Send the character outp((_ComPort + UART_DATA), c); } // Create a log. The parms indicate where to stream the messages to. LOG::LOG( USHORT DosTraceMajorCode, PSZ* apszMsgText ) : _DosTraceMajorCode ( DosTraceMajorCode ), _bInRing3 ( _CheckRing() != 0 ), _apszMsgText ( apszMsgText ), _ComPort ( 0x2F8 ) { bMsgSink[ _Display ] = FALSE; bMsgSink[ _SerialLine ] = FALSE; bMsgSink[ _ErrorLog ] = FALSE; bMsgSink[ _TraceLog ] = TRUE; } void LOG::vTrace( USHORT eventNum ) /* Log the specified event. */ { //--- Output the string. _ddputstring( eventNum, _apszMsgText[ eventNum ] ); } void LOG::vLog( USHORT eventNum, ... ) /* Log the specified event, with data. */ { STACK_ADR Arg1Adr; // Points to the 1st of a variable number // of arguments in the stack. PUSHORT pEventNum; //--- Get the stack address of the 1st argument following the error number. pEventNum = (PUSHORT) &eventNum; // Adr of event num ++pEventNum; // Advance ptr past 1st arg. Arg1Adr.VoidPtr = (PVOID) pEventNum; // Adr of 1st after err num. //--- Format the message and any of the data. //### Should have check here for error number out of range. _ddsprintf( _dd_BuildString, _apszMsgText[ eventNum ], Arg1Adr ); //--- Output the string. _ddputstring( eventNum, _dd_BuildString ); } PERFLOG::PERFLOG( USHORT DosTraceMajorCode, PSZ* apszMsgText ) : LOG( DosTraceMajorCode, apszMsgText ) { bMsgSink[ _Display ] = FALSE; bMsgSink[ _SerialLine ] = FALSE; bMsgSink[ _ErrorLog ] = FALSE; bMsgSink[ _TraceLog ] = TRUE; } void PERFLOG::vTrace( USHORT eventNum ) { RDTSC_COUNT rdtsc_result; // Output from Pentium timing instruction. rdtsc( &rdtsc_result ); if ( _CheckRing() ) DosSysTrace( _DosTraceMajorCode, sizeof(RDTSC_COUNT), eventNum, (PCHAR) &rdtsc_result); else DevHelp_RAS( _DosTraceMajorCode, eventNum, sizeof(RDTSC_COUNT), (PSZ) &rdtsc_result); } USHORT BINARYLOG::_ddsprintf ( PSZ pszOutString, PSZ pszFmtString, STACK_ADR Parm ) /* ; ddsprintf - "sprintf" function on a vbl number of arguments. ; ; pszOutString - Output string saved here. ; pszFmtString - Input, Ascii string containing text and formating information. ; Parm - Input, Address of the 1st of the variable number of arguments. ; ;Returns ; USHORT nDataLen - lentgh of the data string that is saved in pszOutString ; ;Notes ; This function needed to reverse the order of the args pushed on the stack. */ { PSZ pStr = (PSZ) pszFmtString; USHORT uStrLen = 0; // Total string length accumulated here. USHORT nBytes; // Length of one data item saved here. BOOL lFlag; /*--- Find out how many bytes of log data have been pushed on * the stack. Do this by walking formatting string and counting * the number of bytes expected by the format control characters. */ while ( *pStr ) { /* ### FUTURE: Want to seek forward to '%' or '0'. Any quick way to do it? */ if (*pStr != '%') { // If not the start of a format directive, ++pStr; // then continue to next byte. continue; } nBytes = 0; // Not 0 indicates a recognized sequence. lFlag = FALSE; // Set TRUE when "L" is present. ++ pStr; // Advance past '%' if (*pStr == '0') // Advance past '0' ++pStr; if (*pStr == 'l' || *pStr == 'L') { ++pStr; // Advance past "L" if present. lFlag = TRUE; } if (*pStr == 'b' || *pStr == 'B') nBytes = 1; else if (*pStr == 'd' || *pStr == 'D' || *pStr == 'x' || *pStr == 'X') { if (lFlag) nBytes = 4; else nBytes = 2; } else if (*pStr == 'q' || *pStr == 'Q') nBytes = 8; /* If we recognize the formatting command, then count the bytes, * otherwise ignore this '%' directive and continue. */ if (nBytes) { _fmemcpy( (PVOID) pszOutString, Parm.VoidPtr, nBytes ); //Rudi: stack is word-wise ! if( nBytes == 1 ) Parm.BytePtr += 2; else Parm.BytePtr += nBytes; pszOutString += nBytes; uStrLen += nBytes; } ++pStr; } return( uStrLen ); } BINARYLOG::BINARYLOG( USHORT DosTraceMajorCode, PSZ* apszMsgText ) : LOG( DosTraceMajorCode, apszMsgText ) { bMsgSink[ _Display ] = FALSE; bMsgSink[ _SerialLine ] = FALSE; bMsgSink[ _ErrorLog ] = FALSE; bMsgSink[ _TraceLog ] = TRUE; } void BINARYLOG::vTrace( USHORT eventNum ) /* Log the specified trace point. */ { //--- Save the event in the system trace log. if ( _CheckRing() ) DosSysTrace( _DosTraceMajorCode, 0, eventNum, (PCHAR) NULL ); else DevHelp_RAS( _DosTraceMajorCode, eventNum, 0, (PSZ) NULL ); } void BINARYLOG::vLog( USHORT eventNum, ... ) /* Log the specified trace point, with data. */ { STACK_ADR pStack; // Points to the 1st of a variable number // of arguments in the stack. USHORT usLength; // Number of bytes to log. //--- Get the stack address of the 1st argument following the event number. pStack.WordPtr = (WORD*) &eventNum; // Adr of event num ++pStack.WordPtr; // Advance ptr to 1st arg after event number. //--- Invoke binary version of _ddsprintf() to get args into the right order. usLength = _ddsprintf( _dd_BuildString, _apszMsgText[ eventNum ], pStack ); //--- Save data bytes in the System trace log and return. if ( _CheckRing() ) DosSysTrace( _DosTraceMajorCode, usLength, eventNum, (PCHAR) _dd_BuildString ); else DevHelp_RAS( _DosTraceMajorCode, eventNum, usLength, (PSZ) _dd_BuildString ); }