/******************************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 <wmdisp32.h>
#include <wcuricon.h>
#include <wucomm.h>


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;
#ifdef FE_IME
        WORD    winnls;
#endif // FE_IME
#ifdef FE_SB
        WORD    wifeman;
#endif // FE_SB
} 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 defined(FE_SB)
  #if defined(FE_IME)
    if (iFun < pto->winnls)
        return MOD_COMMDLG;
    if (iFun < pto->wifeman)
        return MOD_WINNLS;
    if (iFun < cAPIThunks)
        return MOD_WIFEMAN;
  #else
    if (iFun < pto->wifeman)
        return MOD_COMMDLG;
    if (iFun < cAPIThunks)
        return MOD_WIFEMAN;
  #endif
#elif defined(FE_IME)
    if (iFun < pto->winnls)
        return MOD_COMMDLG;
    if (iFun < cAPIThunks)
        return MOD_WINNLS;
#else
    if (iFun < cAPIThunks)
        return MOD_COMMDLG;
#endif

    return -1;
}



PSZ apszModNames[] = { "Kernel",
                       "User",
                       "Gdi",
                       "Keyboard",
                       "Sound",
                       "Shell",
                       "Winsock",
                       "Toolhelp",
                       "MMedia",
                       "Commdlg"
#ifdef FE_IME
                       ,"WinNLS"
#endif
#ifdef FE_SB
                       ,"WifeMan"
#endif
                       };

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);

#ifdef FE_IME
    case MOD_WINNLS:
        return pto->winnls;
#endif

#ifdef FE_SB
    case MOD_WIFEMAN:
        return pto->wifeman;
#endif

    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 #                      MSG Name";
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();
    ASSERT_CHECKED_WOW_PRESENT;

    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();
    ASSERT_CHECKED_WOW_PRESENT;

    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();
    ASSERT_CHECKED_WOW_PRESENT;

    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();
    ASSERT_CHECKED_WOW_PRESENT;

    GETEXPRVALUE(iMsgMax, "wow32!iMsgMax", INT);
    GETEXPRVALUE(paw32Msg, "wow32!paw32Msg", PM32);

    if( WDGetMSGProfArgs(lpArgumentString, szMsg, &iMsgNum) ) {
        helpMsgProfDmp();
        return;
    }

    PRINTF("%s  %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("0x%-4X %28s  %7ld  %15ld  %15ld\n", 
                       iMsg,
                       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("0x%-4X %28s  %7ld  %15ld  %15ld\n", 
                       iMsgNum, 
                       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();
    ASSERT_CHECKED_WOW_PRESENT;

    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;
}