/******************************Module*Header*******************************\ * Module Name: debug.c * * This file is for debugging tools and extensions. * * Created: 24-Jan-1992 * Author: John Colleran * * History: * Feb 17 92 Matt Felton (mattfe) lots of additional exentions for filtering * Jul 13 92 (v-cjones) Added API & MSG profiling debugger extensions, fixed * other extensions to handle segment motion correctly, * & cleaned up the file in general * Jan 3 96 Neil Sandlin (neilsa) integrated this routine into vdmexts * * Copyright (c) 1992 Microsoft Corporation \**************************************************************************/ #include "precomp.h" #pragma hdrstop #include #define DEBUG_OR_WOWPROFILE 1 #include #include #include #include INT cAPIThunks; // // WARNING: The following code was copied from WOWTBL.C // typedef struct { WORD kernel; WORD dkernel; WORD user; WORD duser; WORD gdi; WORD dgdi; WORD keyboard; WORD sound; WORD shell; WORD winsock; WORD toolhelp; WORD mmedia; WORD commdlg; } TABLEOFFSETS; typedef TABLEOFFSETS UNALIGNED *PTABLEOFFSETS; TABLEOFFSETS tableoffsets; INT TableOffsetFromName(PSZ szTab); INT ModFromCallID(INT iFun) { PTABLEOFFSETS pto = &tableoffsets; LPVOID lpAddress; GETEXPRADDR(lpAddress, "wow32!tableoffsets"); READMEM(lpAddress, &tableoffsets, sizeof(TABLEOFFSETS)); if (iFun < pto->user) return MOD_KERNEL; if (iFun < pto->gdi) return MOD_USER; if (iFun < pto->keyboard) return MOD_GDI; if (iFun < pto->sound) return MOD_KEYBOARD; if (iFun < pto->shell) return MOD_SOUND; if (iFun < pto->winsock) return MOD_SHELL; if (iFun < pto->toolhelp) return MOD_WINSOCK; if (iFun < pto->mmedia) return MOD_TOOLHELP; if (iFun < pto->commdlg) { return(MOD_MMEDIA); } if (iFun < cAPIThunks) return MOD_COMMDLG; return -1; } PSZ apszModNames[] = { "Kernel", "User", "Gdi", "Keyboard", "Sound", "Shell", "Winsock", "Toolhelp", "MMedia", "Commdlg" }; INT nModNames = NUMEL(apszModNames); PSZ GetModName(INT iFun) { INT nMod; nMod = ModFromCallID(iFun); if (nMod == -1) { return "BOGUS!!"; } nMod = nMod >> 12; // get the value into the low byte return apszModNames[nMod]; } INT GetOrdinal(INT iFun) { INT nMod; nMod = ModFromCallID(iFun); if (nMod == -1) { return 0; } return (iFun - TableOffsetFromName(apszModNames[nMod >> 12])); } INT TableOffsetFromName(PSZ szTab) { INT i; PTABLEOFFSETS pto = &tableoffsets; for (i = 0; i < NUMEL(apszModNames); i++) { if (!strcmp(szTab, apszModNames[i])) break; } if (i >= NUMEL(apszModNames)) return 0; switch (i << 12) { case MOD_KERNEL: return pto->kernel; case MOD_USER: return pto->user; case MOD_DGDI: return pto->gdi; case MOD_KEYBOARD: return pto->keyboard; case MOD_SOUND: return pto->sound; case MOD_SHELL: return pto->shell; case MOD_WINSOCK: return pto->winsock; case MOD_TOOLHELP: return pto->toolhelp; case MOD_MMEDIA: return pto->mmedia; case MOD_COMMDLG: return(pto->commdlg); default: return(-1); } } /********* local functions for WOWPROFILE support *********/ /******* function protoypes for local functions for WOWPROFILE support *******/ BOOL WDGetAPIProfArgs(LPSZ lpszArgStr, INT iThkTblMax, PPA32 ppaThkTbls, LPSZ lpszTab, BOOL *bTblAll, LPSZ lpszFun, int *iFunInd); BOOL WDGetMSGProfArgs(LPSZ lpszArgStr, LPSZ lpszMsg, int *iMsgNum); INT WDParseArgStr(LPSZ lpszArgStr, CHAR **argv, INT iMax); BOOL WDGetAPIProfArgs(LPSZ lpszArgStr, INT iThkTblMax, PPA32 ppaThkTbls, LPSZ lpszTab, BOOL *bTblAll, LPSZ lpszFun, int *iFunInd) { /* * Decomposes & interprets argument string to apiprofdmp extension. * INPUT: * lpszArgStr - ptr to input arg string * iThkTblMax - # tables in the thunk tables * ppaThkTbls - ptr to the thunk tables * OUTPUT: * lpszTab - ptr to table name * bTblAll - 0 => dump specific table, 1 => dump all tables * lpszFun - ptr to API name * iFunInd - -1 => dump specific API name * 0 => dump all API entires in table * >0 => dump specific API number (decimal) * RETURN: 0 => OK, 1 => input error (show Usage) * * legal forms: !wow32.apiprofdmp * !wow32.apiprofdmp user * !wow32.apiprofdmp user createwindow * !wow32.apiprofdmp user 41 * !wow32.apiprofdmp createwindow * !wow32.apiprofdmp 41 */ INT i, nArgs; CHAR *argv[2]; nArgs = WDParseArgStr(lpszArgStr, argv, 2); /* if no arguments dump all entries in all tables */ if( nArgs == 0 ) { *iFunInd = 0; // specify dump all API entires in the table *bTblAll = 1; // specify dump all tables return(0); } /* see if 1st arg is a table name */ *bTblAll = 1; // specify dump all tables for (i = 0; i < nModNames; i++) { if (!lstrcmpi(apszModNames[i], argv[0])) { lstrcpy(lpszTab, apszModNames[i]); *bTblAll = 0; // specify dump specific table /* if we got a table name match & only one arg, we're done */ if( nArgs == 1 ) { *iFunInd = 0; // specify dump all API entries in the table return(0); } break; } } #if 0 for(i = 0; i < iThkTblMax; i++) { CHAR temp[40], *TblEnt[2], szTabName[40]; PA32 awThkTbl; /* get table name string from thunk tables */ READMEM_XRETV(awThkTbl, &ppaThkTbls[i], 0); READMEM_XRETV(szTabName, awThkTbl.lpszW32, 0); /* get rid of trailing spaces from table name string */ lstrcpy(temp, szTabName); WDParseArgStr(temp, TblEnt, 1); /* if we found a table name that matches the 1st arg...*/ if( !lstrcmpi(argv[0], TblEnt[0]) ) { lstrcpy(lpszTab, szTabName); *bTblAll = 0; // specify dump specific table /* if we got a table name match & only one arg, we're done */ if( nArgs == 1 ) { *iFunInd = 0; // specify dump all API entries in the table return(0); } break; } } #endif /* if 2 args && the 1st doesn't match a table name above => bad input */ if( (nArgs > 1) && (*bTblAll) ) { return(1); } /* set index to API spec */ nArgs--; /* try to convert API spec to a number */ *iFunInd = atoi(argv[nArgs]); lstrcpy(lpszFun, argv[nArgs]); /* if API spec is not a number => it's a name */ if( *iFunInd == 0 ) { *iFunInd = -1; // specify API search by name } /* else if API number is bogus -- complain */ else if( *iFunInd < 0 ) { return(1); } return(0); } BOOL WDGetMSGProfArgs(LPSZ lpszArgStr, LPSZ lpszMsg, int *iMsgNum) { /* * Decomposes & interprets argument string to msgprofdmp extension. * INPUT: * lpszArgStr - ptr to input arg string * OUTPUT: * lpszMsg - ptr to message name * iMsgNum - -1 => dump all message entries in the table * -2 => lpszMsg contains specific MSG name * >=0 => dump specific message number * RETURN: 0 => OK, 1 => input error (show Usage) */ INT nArgs; CHAR *argv[2]; nArgs = WDParseArgStr(lpszArgStr, argv, 1); /* if no arguments dump all entries in all tables */ if( nArgs == 0 ) { *iMsgNum = -1; // specify dump all MSG entires in the table return(0); } if( !lstrcmpi(argv[0], "HELP") ) return(1); /* try to convert MSG spec to a number */ *iMsgNum = atoi(argv[0]); lstrcpy(lpszMsg, argv[0]); /* if MSG spec is not a number => it's a name */ if( *iMsgNum == 0 ) { *iMsgNum = -2; // specify lpszMsg contains name to search for } /* else if MSG number is bogus -- complain */ else if( *iMsgNum < 0 ) { return(1); } return(0); } /******* API profiler table functions ********/ /* init some common strings */ CHAR szAPI[] = "API# API Name"; CHAR szMSG[] = "MSG Name - MSG #"; CHAR szTITLES[] = "# Calls Tot. tics tics/call"; CHAR szDASHES[] = "----------------------------------- ------- --------------- ---------------"; CHAR szTOOBIG[] = "too large for table."; CHAR szNOTUSED[] = "Unused table index."; CHAR szRNDTRIP[] = "Round trip message profiling"; CHAR szCLEAR[] = "Remember to clear the message profile tables."; VOID apiprofclr( CMD_ARGLIST ) { int iTab, iFun, iEntries; INT iThkTblMax; W32 awAPIEntry; PW32 pawAPIEntryTbl; PA32 awThkTbl; PPA32 ppaThkTbls; CHAR szTable[20]; CMD_INIT(); GETEXPRVALUE(iThkTblMax, "wow32!iThunkTableMax", INT); GETEXPRVALUE(ppaThkTbls, "wow32!pawThunkTables", PPA32); PRINTF("Clearing:"); for(iTab = 0; iTab < iThkTblMax; iTab++) { READMEM_XRET(awThkTbl, &ppaThkTbls[iTab]); READMEM_XRET(szTable, awThkTbl.lpszW32); PRINTF(" %s", szTable); pawAPIEntryTbl = awThkTbl.lpfnA32; READMEM_XRET(iEntries, awThkTbl.lpiFunMax); for(iFun = 0; iFun < iEntries; iFun++) { READMEM_XRET(awAPIEntry, &pawAPIEntryTbl[iFun]); awAPIEntry.cCalls = 0L; awAPIEntry.cTics = 0L; WRITEMEM_XRET(&pawAPIEntryTbl[iFun], awAPIEntry); } } PRINTF("\n"); return; } VOID apiprofdmp( CMD_ARGLIST ) { BOOL bTblAll, bFound; int i, iFun, iFunInd; INT iThkTblMax; W32 awAPIEntry; PW32 pawAPIEntryTbl; PA32 awThkTbl; PPA32 ppaThkTbls; CHAR szTab[20], szFun[40], szTable[20], szFunName[40]; CMD_INIT(); GETEXPRVALUE(iThkTblMax, "wow32!iThunkTableMax", INT); GETEXPRVALUE(ppaThkTbls, "wow32!pawThunkTables", PPA32); GETEXPRVALUE(cAPIThunks, "wow32!cAPIThunks", INT); GETEXPRVALUE(i, "wow32!nModNames", INT); if (i != nModNames) { PRINTF("Error: mismatch of nModNames in apiprofdmp.\nExtension is out of date\n"); return; } if( WDGetAPIProfArgs(lpArgumentString, iThkTblMax, ppaThkTbls, szTab, &bTblAll, szFun, &iFunInd) ) { helpAPIProfDmp(); return; } bFound = FALSE; #if 0 for(iTab = 0; iTab < iThkTblMax; iTab++) { READMEM_XRET(awThkTbl, &ppaThkTbls[iTab]); READMEM_XRET(szTable, awThkTbl.lpszW32); /* if dump all tables || dump this specific table */ if( bTblAll || !lstrcmp(szTab, szTable) ) { pawAPIEntryTbl = awThkTbl.lpfnA32; #endif for (i = 0; i < nModNames; i++) { READMEM_XRET(awThkTbl, &ppaThkTbls[0]); lstrcpy(szTable, apszModNames[i]); /* if dump all tables || dump this specific table */ if (bTblAll || !lstrcmpi(szTab, apszModNames[i])) { INT nFirst, nLast; nFirst = TableOffsetFromName(apszModNames[i]); if (i < nModNames - 1) nLast = TableOffsetFromName(apszModNames[i+1]) - 1; else nLast = cAPIThunks - 1; pawAPIEntryTbl = awThkTbl.lpfnA32; /* if dump a specific API number */ if( iFunInd > 0 ) { PRINTF("\n>>>> %s\n", szTable); PRINTF("%s %s\n%s\n", szAPI, szTITLES, szDASHES); //if( iFunInd >= *(awThkTbl.lpiFunMax) ) { if( iFunInd > nLast - nFirst ) { PRINTF("Index #%d %s.\n", GetOrdinal(iFunInd), szTOOBIG); } else { bFound = TRUE; // READMEM_XRET(awAPIEntry, &pawAPIEntryTbl[iFunInd]); READMEM_XRET(awAPIEntry, &pawAPIEntryTbl[nFirst + iFunInd]); READMEM_XRET(szFunName, awAPIEntry.lpszW32); if( szFunName[0] ) { PRINTF("%4d %30s ", GetOrdinal(iFunInd), szFunName); } else { PRINTF("%4d %30s ", GetOrdinal(iFunInd), szNOTUSED); } PRINTF("%7ld %15ld ", awAPIEntry.cCalls, awAPIEntry.cTics); if(awAPIEntry.cCalls) { PRINTF("%15ld\n", awAPIEntry.cTics/awAPIEntry.cCalls); } else { PRINTF("%15ld\n", 0L); } } } /* else if dump an API by name */ else if ( iFunInd == -1 ) { // READMEM_XRET(iEntries, awThkTbl.lpiFunMax); // for(iFun = 0; iFun < iEntries; iFun++) { for(iFun = nFirst; iFun <= nLast; iFun++) { READMEM_XRET(awAPIEntry, &pawAPIEntryTbl[iFun]); READMEM_XRET(szFunName, awAPIEntry.lpszW32); if ( !lstrcmpi(szFun, szFunName) ) { PRINTF("\n>>>> %s\n", szTable); PRINTF("%s %s\n%s\n", szAPI, szTITLES, szDASHES); PRINTF("%4d %30s %7ld %15ld ", GetOrdinal(iFun), szFunName, awAPIEntry.cCalls, awAPIEntry.cTics); if(awAPIEntry.cCalls) { PRINTF("%15ld\n", awAPIEntry.cTics/awAPIEntry.cCalls); } else { PRINTF("%15ld\n", 0L); } return; } } } /* else dump all the API's in the table */ else { PRINTF("\n>>>> %s\n", szTable); PRINTF("%s %s\n%s\n", szAPI, szTITLES, szDASHES); bFound = TRUE; // READMEM_XRET(iEntries, awThkTbl.lpiFunMax); // for(iFun = 0; iFun < iEntries; iFun++) { for(iFun = nFirst; iFun <= nLast; iFun++) { READMEM_XRET(awAPIEntry, &pawAPIEntryTbl[iFun]); READMEM_XRET(szFunName, awAPIEntry.lpszW32); if(awAPIEntry.cCalls) { PRINTF("%4d %30s %7ld %15ld %15ld\n", GetOrdinal(iFun), szFunName, awAPIEntry.cCalls, awAPIEntry.cTics, awAPIEntry.cTics/awAPIEntry.cCalls); } } if( !bTblAll ) { return; } } } } if( !bFound ) { PRINTF("\nCould not find "); if( !bTblAll ) { PRINTF("%s ", szTab); } PRINTF("API: %s\n", szFun); helpAPIProfDmp(); } return; } /******* MSG profiler table functions ********/ VOID msgprofclr( CMD_ARGLIST ) { int iMsg; INT iMsgMax; M32 awM32; PM32 paw32Msg; CMD_INIT(); GETEXPRVALUE(iMsgMax, "wow32!iMsgMax", INT); GETEXPRVALUE(paw32Msg, "wow32!paw32Msg", PM32); PRINTF("Clearing Message profile table"); for(iMsg = 0; iMsg < iMsgMax; iMsg++) { READMEM_XRET(awM32, &paw32Msg[iMsg]); awM32.cCalls = 0L; awM32.cTics = 0L; WRITEMEM_XRET(&paw32Msg[iMsg], awM32); } PRINTF("\n"); return; } VOID msgprofdmp( CMD_ARGLIST ) { int iMsg, iMsgNum; INT iMsgMax; BOOL bFound; M32 aw32Msg; PM32 paw32Msg; CHAR szMsg[40], *argv[2], szMsg32[40], szMsgName[40]; CMD_INIT(); GETEXPRVALUE(iMsgMax, "wow32!iMsgMax", INT); GETEXPRVALUE(paw32Msg, "wow32!paw32Msg", PM32); if( WDGetMSGProfArgs(lpArgumentString, szMsg, &iMsgNum) ) { helpMsgProfDmp(); return; } PRINTF("%35s %s\n%s\n", szMSG, szTITLES, szDASHES); if( iMsgNum > iMsgMax ) { PRINTF("MSG #%4d %s.\n", iMsgNum, szTOOBIG); return; } bFound = 0; for(iMsg = 0; iMsg < iMsgMax; iMsg++) { READMEM_XRET(aw32Msg, &paw32Msg[iMsg]); READMEM_XRET(szMsgName, aw32Msg.lpszW32); /* if specific msg name, parse name from "WM_MSGNAME 0x00XX" format */ if( iMsgNum == -2 ) { lstrcpy(szMsg32, szMsgName); WDParseArgStr(szMsg32, argv, 1); } /* if 'all' msgs || specific msg # || specific msg name */ if( (iMsgNum == -1) || (iMsg == iMsgNum) || ( (iMsgNum == -2) && (!lstrcmp(szMsg, argv[0])) ) ) { bFound = 1; if(aw32Msg.cCalls) { PRINTF("%35s %7ld %15ld %15ld\n", szMsgName, aw32Msg.cCalls, aw32Msg.cTics, aw32Msg.cTics/aw32Msg.cCalls); } /* else if MSG wasn't sent & we're not dumping the whole table */ else if( iMsgNum != -1 ) { PRINTF("%35s %7ld %15ld %15ld\n", szMsgName, 0L, 0L, 0L); } /* if we're not dumping the whole table, we're done */ if( iMsgNum != -1 ) { return; } } } if( !bFound ) { PRINTF("\nCould not find MSG: %s\n", szMsg); helpMsgProfDmp(); } return; } void msgprofrt( CMD_ARGLIST ) { INT fWMsgProfRT; LPVOID lpAddress; CMD_INIT(); GETEXPRADDR(lpAddress, "wow32!fWMsgProfRT"); READMEM_XRET(fWMsgProfRT, lpAddress); fWMsgProfRT = 1 - fWMsgProfRT; WRITEMEM_XRET(lpAddress, fWMsgProfRT); if( fWMsgProfRT ) { PRINTF("\n%s ENABLED.\n%s\n\n", szRNDTRIP, szCLEAR); } else { PRINTF("\n%s DISABLED.\n%s\n\n", szRNDTRIP, szCLEAR); } return; }