windows-nt/Source/XPSP1/NT/sdktools/wst/wstune/wsreduce.c
2020-09-26 16:20:57 +08:00

1378 lines
41 KiB
C

/*
* Module Name: WSREDUCE.C
*
* Program: WSREDUCE
*
*
* Description:
*
* Performs data reduction on the function reference data collected
* by WST.DLL. Analyzes the WSP file information, and produces
* a suggested list for the ordering of functions within the tuned
* modules. An ASCII version of the reordered function list is written
* to stdout. In addition, a WSR file for each reduced module is
* produced for subsequent use by WSPDUMP /R.
*
* The reduction algorithm employed by WSREDUCE is described in detail
* in WSINSTR.DOC. Briefly, each function monitored by the working set tuner
* is considered to be a vertex in a graph. There is an edge from vertex
* "A" to vertex "B" if the function reference strings for "A" and "B"
* have any overlapping 1 bits. Likewise, there is an edge from vertex "B"
* to vertex "A". The edges between vertices are weighted depending on
* the relative importance of the ending vertex, and the number of
* overlapping bits between the start and end vertices. The relative
* importance of the end vertices, and the weighted edges between
* vertices, is stored in a decision matrix. A greedy algorithm is run on
* the decision matrix to determine a better ordering for the measured
* functions.
*
*
* Microsoft Confidential
*
* Copyright (c) Microsoft Corporation 1992
*
* All Rights Reserved
*
* Modification History:
*
* Modified for NT June 13, 1992 MarkLea.
* 4-23-98: QFE - Performance unacceptable on high function counts DerrickG (mdg):
* - new WSP file format for large symbol counts (ULONG vs. USHORT)
* - support for long file names (LFN) of input/output files
* - removed buggy reference to WSDIR env. variable
* - removed command-line parsing from wsReduceMain()
* - based .TMI & .WSR file names exclusively on .WSP name for consistency
* - removed limit on symbol name lengths - return allocated name from WsTMIReadRec()
* - removed unused code and symbols
* - Analyzed the code blocked off by OPTIMIZE - it doesn't produce the same
* output as non-OPTIMIZEd code, and is buggy (won't build as is) - removed.
* - Removed multiple module capabilities from code (shell sends one at a time)
* - I addressed memory and performance issues by using a smaller allocation
* for WsDecision (USHORT vs. long), using one value to mark a taken vertex
* (as opposed to half the value space by using -1), and an optional
* progress indicator to reassure users. Modified wsRedScaleWsDecision()
* to maximize the scaled values (using some more float math).
* - Added "pnEdges" and "nEdgeCount" to function structure. If the number
* of set functions is < USHRT_MAX (very likely, even for very large
* projects), allocate as needed a sorted index for WsRedReorder(). This
* cuts dramatically the number of passes through the matrix searching for
* the next edge to consider, and permits some other optimizations. The
* optimized algorithm produces identical results for the important high
* usage high overlap functions, but could diverge in the results for low
* usage (2 or 1 hits) low overlap functions. Differences are not
* significant from a results performance perspective - a better algorithm
* would give marginally better results. The original algorithm is in place
* bracketed with "#ifdef SLOWMO".
*
*
*/
#include "wstune.h"
/*
* Function prototypes.
*/
VOID wsRedInitialization( VOID );
VOID wsRedInitModules( VOID );
VOID wsRedInitFunctions( VOID );
VOID wsRedSetup( VOID );
VOID wsRedSetWsDecision( VOID );
VOID wsRedScaleWsDecision( VOID );
VOID wsRedWeightWsDecision( VOID );
#ifdef SLOWMO
UINT wsRedChooseEdge( UINT );
#else // SLOWMO
UINT wsRedChooseEdgeOpt( UINT ); // mdg 98/4 Alternate optimized edge chooser
INT __cdecl wsRedChooseEdgeOptCmp ( const UINT *, const UINT * );
BOOL wsRedChooseEdgeOptAlloc( UINT uiIndex );
UINT wsRedChooseEdgeOptNextEdge( UINT uiIndex, BOOL bNoSelectOpt );
#endif // SLOWMO
VOID wsRedReorder( VOID );
VOID wsRedOutput( VOID );
VOID wsRedOpenWSR( FILE **);
VOID wsRedExit( UINT, USHORT, UINT, ULONG, PSZ );
VOID wsRedCleanup(VOID);
/*
* Type definitions and structure declarations.
*/
/* Data reduction per module information */
struct wsrmod_s {
FILE *wsrmod_hFileWSR; // module's WSR file pointer
FILE *wsrmod_hFileTMI; // module's TMI file pointer
FILE *wsrmod_hFileWSP; // module's WSP file handle
union {
PCHAR wsrmod_pchModName;// pointer to module base name
PCHAR wsrmod_pchModFile;// pointer to WSP file name
} wsrmod_un;
ULONG wsrmod_ulOffWSP; // offset of first function bitstring
};
typedef struct wsrmod_s wsrmod_t;
/* Data reduction per function information */
struct wsrfxn_s {
PCHAR wsrfxn_pchFxnName; // pointer to function name
ULONG wsrfxn_cbFxn; // Size of function in bytes
BOOL wsrfxn_fCandidate; // Candidate flag
#ifndef SLOWMO
UINT nEdgesLeft; // Count of sorted edges left to consider in WsDecision for this function
UINT nEdgesAlloc; // Number of items allocated in pnEdges
UINT * pnEdges; // Allocated array of sorted edges for this function
#endif // SLOWMO
};
typedef struct wsrfxn_s wsrfxn_t;
/*
* Global variable declaration and initialization.
*/
static char *szFileWSP = NULL; // WSP file name
static char *szFileTMI = NULL; // TMI file name
static char *szFileWSR = NULL; // WSR file name
static ULONG rc = NO_ERROR; // Return code
static ULONG ulTmp; // Temp variable for Dos API returns
static UINT cTmiFxns = 0; // Number of functions in tmi file
static UINT cFxnsTot = 0; // Total number of functions
static UINT cSnapsTot = 0; // Total number of snapshots
static UINT cbBitStr = 0; // Number of bytes per fxn bitstring
#ifdef DEBUG
static BOOL fVerbose = FALSE; // Flag for verbose mode
#endif /* DEBUG */
#ifndef TMIFILEHACK
static BOOL fFxnSizePresent = FALSE; // Flag for function size availability
#endif /* !TMIFILEHACK */
static wsrmod_t WsrMod; // Module information
static wsrmod_t *pWsrMod = &WsrMod; // Pointer for legacy use
static wsrfxn_t *WsrFxn; // Pointer to function information
static ULONG *FxnBits; // Pointer to dword of bitstring
static ULONG *FxnOrder; // Pointer to ordered list of
// function ordinals
typedef USHORT WsDecision_t;
#define WSDECISION_TAKEN USHRT_MAX // Reserve highest value for special code
#define WsDecision_MAX (WSDECISION_TAKEN-1) // Use fullest spread for decision matrix
static WsDecision_t **WsDecision; // Decision matrix for data reduction; mdg 98/4 use small alloc for large symbol counts
static ULONG ulRefHi1 = 0; // Highest diagonal value (for WsRedScaleWsDecision)
static ULONG ulRefHi2 = 0; // Second highest diagonal value (for WsRedScaleWsDecision)
static UINT uiSelected = 0; // Highest function ordinal selected (for WsRedReorder)
static UINT cFxnOrder = 0; // Count of ordered functions
#ifndef SLOWMO
static UINT nFxnToSort; // To pass static value to wsRedChooseEdgeOptCmp()
#endif // SLOWMO
static FILE *hFileWLK = NULL; // Handle to file containing ordered
HGLOBAL hMem[10];
ULONG ulFxnIndex; // Index of original TMI order of function.
#ifdef TMR
ULONG pqwTime0[2];
#endif /* TMR */
/*
* Procedure wsReduceMain
*
*
***
* Effects:
*
* Performs data reduction and analysis on the input modules' function reference
* data.
*
* szBaseName Specifies a module WSP file name
*/
BOOL wsReduceMain( CHAR *szBaseName )
{
size_t i;
char * pSlash;
szFileWSP = malloc( i = strlen( szBaseName ) + 5 );
if (szFileWSP) {
szFileWSP = strcat( strcpy(szFileWSP , szBaseName ), ".WSP" );
} else {
exit(1);
}
szFileTMI = malloc( i );
if (szFileTMI) {
szFileTMI = strcat( strcpy( szFileTMI, szBaseName ), ".TMI" );
} else {
free(szFileWSP);
exit(1);
}
#ifdef DEBUG
fVerbose = fDbgVerbose;
#endif // DEBUG
// Create output file in current directory
if (NULL != (pSlash = strrchr( szBaseName, '\\' ))
|| NULL != (pSlash = strrchr( szBaseName, '/' ))
|| NULL != (pSlash = strrchr( szBaseName, ':' )))
{
++pSlash;
szFileWSR = malloc(strlen( pSlash ) + 5 );
if (szFileWSR) {
szFileWSR = strcat( strcpy(szFileWSR, pSlash ), ".WSR" );
} else {
free(szFileTMI);
free(szFileWSP);
exit(1);
}
} else {
szFileWSR = malloc( i );
if (szFileWSR) {
szFileWSR = strcat( strcpy( szFileWSR, szBaseName ), ".WSR" );
} else {
free(szFileTMI);
free(szFileWSP);
exit(1);
}
}
#ifdef TMR
DosTmrQueryTime((PQWORD)pqwTime0);
printf("Top of Main, 0x%lx:0x%lx\n", pqwTime0[1], pqwTime0[0]);
#endif /* TMR */
pWsrMod->wsrmod_un.wsrmod_pchModFile = szFileWSP;
#ifdef DEBUG
printf("\t%s\n", pWsrMod->wsrmod_un.wsrmod_pchModFile);
#endif /* DEBUG */
// Initialize module and function information structures.
wsRedInitialization();
// Set up weighted decision matrix.
wsRedSetup();
// Perform the function reference data analysis.
wsRedReorder();
// Output the analysis results.
wsRedOutput();
// Cleanup memory allocations.
wsRedCleanup();
free( szFileWSP );
free( szFileWSR );
free( szFileTMI );
return(NO_ERROR);
}
/*
*
***LP wsRedInitialization
*
*
* Effects:
* - Calls wsRedInitModules to:
* o Open and validate each module's WSP file.
* o Open and validate each module's TMI file.
* - Calls wsRedInitFunctions to:
* o Set up WsrFxn[] with per function information.
* o Allocate FxnBits[].
* - Allocates WsDecision[][].
* - Allocates and initializes DiagonalFxn[].
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedInitialization()
{
UINT i; // Loop counter
// Setup module information.
wsRedInitModules();
// Setup function information for each module.
wsRedInitFunctions();
// Allocate the decision matrix, WsDecision[cFxnsTot][cFxnsTot].
WsDecision = (WsDecision_t **) AllocAndLockMem((cFxnsTot * cFxnsTot * sizeof(WsDecision_t)) + (cFxnsTot * sizeof(WsDecision_t *)), &hMem[1]);
if (WsDecision == NULL)
wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM,
(cFxnsTot+1)*cFxnsTot*sizeof(WsDecision_t), "WsDecision[][]");
for (i = 0; i < cFxnsTot; i++)
{
WsDecision[i] = (WsDecision_t *) (WsDecision+cFxnsTot)+(i*cFxnsTot);
}
}
/*
*
***LP wsRedInitModules
*
*
* Effects:
* - Opens and validates each module's WSP file.
* - Opens and validates each module's TMI file.
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedInitModules()
{
wsphdr_t WspHdr; // WSP file header
UINT cFxns = 0; // Number of functions for this module
ULONG ulTimeStamp = 0; // Time stamp
ULONG ulTDFID = 0; // TDF Identifier
/* Open module's input WSP file. Read and validate
* WSP file header.
*/
rc = WsWSPOpen(pWsrMod->wsrmod_un.wsrmod_pchModFile,
&(pWsrMod->wsrmod_hFileWSP), (PFN) wsRedExit,
&WspHdr, ERROR, PRINT_MSG );
if (NULL == (pWsrMod->wsrmod_un.wsrmod_pchModName = malloc( 1 + WspHdr.wsphdr_dtqo.dtqo_cbPathname )))
wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM,
WspHdr.wsphdr_dtqo.dtqo_cbPathname + 1,
pWsrMod->wsrmod_un.wsrmod_pchModFile);
rc = fread( pWsrMod->wsrmod_un.wsrmod_pchModName, WspHdr.wsphdr_dtqo.dtqo_cbPathname,
1, pWsrMod->wsrmod_hFileWSP );
if (rc != 1)
wsRedExit(ERROR, PRINT_MSG, MSG_FILE_BAD_HDR, (ULONG)-1L,
pWsrMod->wsrmod_un.wsrmod_pchModFile);
pWsrMod->wsrmod_un.wsrmod_pchModName[WspHdr.wsphdr_dtqo.dtqo_cbPathname] = '\0';
ulTimeStamp = WspHdr.wsphdr_ulTimeStamp;
cSnapsTot = WspHdr.wsphdr_ulSnaps;
cbBitStr = cSnapsTot * sizeof(ULONG);
pWsrMod->wsrmod_ulOffWSP = WspHdr.wsphdr_ulOffBits;
/*
* Open associated TMI file. Assume it lives in same directory.
* Read and validate TMI header. Increment cFxnsTot.
*/
cTmiFxns = WsTMIOpen(szFileTMI, &(pWsrMod->wsrmod_hFileTMI),
(PFN) wsRedExit,
0, (PCHAR)0);
cFxns = WspHdr.wsphdr_dtqo.dtqo_SymCnt;
#ifdef DEBUG
printf("%s file header: # fxns = %ld, TDF ID = 0x%x\n", szFileTMI,
cFxns, (UINT) WspHdr.wsphdr_dtqo.dtqo_usID);
#endif /* DEBUG */
cFxnsTot = cFxns;
// If no function data to analyze, just exit without error.
if (cFxnsTot == 0)
wsRedExit(NO_ERROR, NO_MSG, NO_MSG, 0, NULL);
}
/*
*
***LP wsRedInitFunctions
*
*
* Effects:
* - Sets up WsrFxn[] with per function information.
* - Allocates FxnBits[].
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedInitFunctions()
{
UINT uiFxn = 0; // Function number
UINT cFxns = 0; // Number of functions for this module
// Allocate memory for per function info, WsrFxn[cFxnsTot].
WsrFxn = (wsrfxn_t *) AllocAndLockMem(cFxnsTot*sizeof(wsrfxn_t), &hMem[3]);
if (WsrFxn == NULL)
wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM,
cFxnsTot * sizeof(wsrfxn_t), "WsrFxn[]");
WsIndicator( WSINDF_NEW, "Load Functions", cFxnsTot );
// Initialize WsrFxn[cFxnsTot].
uiFxn = 0; // loop index init
cFxns = cFxnsTot; // loop invariant
#ifdef DEBUG
if (fVerbose)
{
printf("Initializing WsrFxn[] for %s:\n\tstart/end fxn indices (%d/%d)\n",
pWsrMod->wsrmod_un.wsrmod_pchModName, uiFxn,
cFxns - 1);
printf("TMI file handle: %ld\n",pWsrMod->wsrmod_hFileTMI);
}
#endif /* DEBUG */
for (; uiFxn < cFxns; uiFxn++)
{
WsIndicator( WSINDF_PROGRESS, NULL, uiFxn );
WsrFxn[uiFxn].wsrfxn_cbFxn =
WsTMIReadRec(&(WsrFxn[uiFxn].wsrfxn_pchFxnName),&ulFxnIndex,&ulTmp,pWsrMod->wsrmod_hFileTMI,
(PFN) wsRedExit, (PCHAR)0);
#ifdef DEBUG
if (fVerbose)
printf("\tWsrFxn[%d] %s\n",
uiFxn, WsrFxn[uiFxn].wsrfxn_pchFxnName );
#endif /* DEBUG */
WsrFxn[uiFxn].wsrfxn_fCandidate = TRUE;
}
// Close TMI file.
fclose(pWsrMod->wsrmod_hFileTMI);
WsIndicator( WSINDF_FINISH, NULL, 0 );
// Allocate space to hold 32 snapshots for each function.
FxnBits = (ULONG *) AllocAndLockMem(cFxnsTot*sizeof(ULONG), &hMem[4]);
if (FxnBits == NULL)
wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM,
cFxnsTot * sizeof(ULONG), "FxnBits[]");
}
/*
*
***LP wsRedSetup
*
*
* Effects:
*
* Initializes the data structures used to analyze the function
* reference bitstrings, including the weighted decision matrix.
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedSetup()
{
wsRedSetWsDecision(); // set up initial decision matrix
wsRedScaleWsDecision(); // scale the decision matrix
wsRedWeightWsDecision(); // weight the matrix "edge" entries
}
/*
*
***LP wsRedSetWsDecision
*
*
* Effects:
*
* Initializes and weights the decision matrix, WsDecision[][].
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedSetWsDecision()
{
UINT i = 0, j = 0; // Temporary loop indexes
UINT uiFxn = 0; // Function number
UINT uiFBits = 0; // Loop index for bitstring dwords
UINT clFBits = 0; // Count of fxn bitstring dwords
ULONG ulResult = 0; // Returned from procedure call
FILE *hFile; // File handle
/* For each dword of snapshot bitstrings...*/
clFBits = (cbBitStr + sizeof(ULONG) - 1) / sizeof(ULONG);
WsIndicator( WSINDF_NEW, "Fill In Matrix", clFBits * cFxnsTot );
for (uiFBits = 0; uiFBits < clFBits; uiFBits++)
{
ULONG ulOffWSP;
WsIndicator( WSINDF_PROGRESS, "Reading Snaps ", 0 );
// Fill in FxnBits for this snapshot
#ifdef DEBUG
if (fVerbose)
printf( "Setting up FxnBits snapshot %lu for %s\n",
uiFBits, pWsrMod->wsrmod_un.wsrmod_pchModName );
#endif /* DEBUG */
hFile = pWsrMod->wsrmod_hFileWSP;
ulOffWSP = uiFBits + pWsrMod->wsrmod_ulOffWSP;
for ( uiFxn = 0; uiFxn < cFxnsTot; uiFxn++, ulOffWSP += cbBitStr) // Loop functions
{
// Seek to next dword of function's bitstring.
if ((rc = fseek( hFile, ulOffWSP, SEEK_SET )) != NO_ERROR)
wsRedExit(ERROR, PRINT_MSG, MSG_FILE_OFFSET,rc,
pWsrMod->wsrmod_un.wsrmod_pchModName);
// Read next dword of function's bitstring.
rc = fread( &(FxnBits[uiFxn]), sizeof(ULONG), 1, hFile );
if(rc != 1)
wsRedExit(ERROR, PRINT_MSG, MSG_FILE_READ, rc,
pWsrMod->wsrmod_un.wsrmod_pchModName);
} // for each function
WsIndicator( WSINDF_PROGRESS, "Fill In Matrix", 0 );
hFile = pWsrMod->wsrmod_hFileWSP;
#ifdef DEBUG
if (fVerbose)
printf("Setting up WsDecision[][] for %s:\n\tstart/end fxn indices (%d/%d)\n",
pWsrMod->wsrmod_un.wsrmod_pchModName,
uiFxn, cFxnsTot - 1);
#endif /* DEBUG */
/* For each function... */
for ( uiFxn = 0; uiFxn < cFxnsTot; uiFxn++ )
{
WsIndicator( WSINDF_PROGRESS, NULL, (uiFBits * cFxnsTot) + uiFxn );
// Get the current snapshot
ulTmp = FxnBits[uiFxn];
#ifdef DEBUG
if (fVerbose)
printf("\tFxnBits[%d] = 0x%lx\n", uiFxn, ulTmp);
#endif /* DEBUG */
/* If there are bits set... */
if (ulTmp != 0)
{
/* Sum the "on" bits and add the result
* to WsDecision[uiFxn][uiFxn].
*/
ulResult = 0;
while (ulTmp)
{
++ulResult;
ulTmp &= ulTmp - 1;
}
ulTmp = WsDecision[uiFxn][uiFxn] += (WsDecision_t)ulResult;
if (ulTmp > ulRefHi2) // Set the highest two diagonal values on the last pass
if (ulTmp > ulRefHi1)
{
ulRefHi2 = ulRefHi1;
ulRefHi1 = ulTmp;
uiSelected = uiFxn; // Remember highest value's index
}
else
ulRefHi2 = ulTmp;
/* Sum the overlapping "on" bits for this
* function's dword with each preceding
* function's dword, and add the results to
* WsDecision[][].
*/
for (i = 0; i < uiFxn; i++)
{
ulTmp = FxnBits[i] & FxnBits[uiFxn];
if (ulTmp) // mdg 98/4
{
ulResult = 0;
while (ulTmp)
{
++ulResult;
ulTmp &= ulTmp - 1;
}
WsDecision[uiFxn][i] += (WsDecision_t)ulResult;
WsDecision[i][uiFxn] += (WsDecision_t)ulResult;
}
} /* End For each previous function's dword */
} /* End If there are bits set...*/
} /* End For each function... */
} /* End For each dword of bitstrings */
WsIndicator( WSINDF_FINISH, NULL, 0 );
#ifdef DEBUG
if (fVerbose)
{
printf("\nRAW MATRIX:\n");
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
printf("row %4d:\n", uiFxn);
for (i = 0; i < cFxnsTot; i++)
printf("0x%lx ", (LONG)WsDecision[uiFxn][i]);
printf("\n");
}
}
#endif /* DEBUG */
}
/*
*
***LP wsRedOpenWSR
*
*
* Effects:
* Opens the output WSR files, one per module. If only one module
* is being reduced, also opens a WLK file, setting the WLK file handle
* as a side effect.
*
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedOpenWSR(FILE **phFileWLK)
{
/* Close WSP file, and open module output file. */
fclose(pWsrMod->wsrmod_hFileWSP);
if ((pWsrMod->wsrmod_hFileWSR = fopen(szFileWSR, "w"))
== NULL)
{
wsRedExit(ERROR, PRINT_MSG,MSG_FILE_OPEN,rc, szFileWSR);
}
/* We're only analyzing ONE module. Also open a WLK
* file. This file will contain the function names in their
* reordered sequence. The linker will use this file to
* automatically reorder functions. Note that we reuse szFileWSR
* here.
*/
strcpy(strstr(szFileWSR, ".WSR"), ".PRF");
if ((*phFileWLK = fopen(szFileWSR, "w")) == NULL)
wsRedExit(ERROR, PRINT_MSG,MSG_FILE_OPEN,rc, szFileWSR);
}
/*
*
***LP wsRedScaleWsDecision
*
*
* Effects:
*
* If necessary, scales the diagonal values of the matrix to avoid overflow
* during calculations of the weighted edges (below). Sets up DiagonalFxn[]
* as a side effect. Note that we go through gyrations to set
* DiagonalFxn up backwards, so that qsort() will handle ties a little better.
*
* Returns:
*
* Void.
*/
VOID
wsRedScaleWsDecision()
{
UINT i = 0, j = 0; // Temporary loop indexes
UINT uiFxn = 0; // Function number
double fTmp; // Temporary float variable
WsDecision_t lTmp;
fTmp = (double)ulRefHi1 * (double)ulRefHi2;
if (fTmp > WsDecision_MAX)
{
// Scale down the diagonal. Don't allow rescaled entries
// to be zero if they were non-zero before scaling.
fTmp /= WsDecision_MAX;
printf("%s %s: WARNING -- Scaling back the reduction matrix by %f.\n",
szProgName, pszVersion, fTmp);
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
lTmp = WsDecision[uiFxn][uiFxn];
if (lTmp)
{
lTmp = (WsDecision_t)(lTmp / fTmp); // Discard any remainders to avoid potential overflows
if (lTmp == 0)
WsDecision[uiFxn][uiFxn] = 1;
else
WsDecision[uiFxn][uiFxn] = lTmp;
}
}
#ifdef DEBUG
if (fVerbose)
{
printf("\nSCALED MATRIX:\n");
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
printf("row %4d:\n", uiFxn);
for (i = 0; i < cFxnsTot; i++)
printf("0x%lx ", (LONG)WsDecision[uiFxn][i]);
printf("\n");
}
}
#endif /* DEBUG */
}
#ifdef DEBUG
if (fVerbose)
{
printf("Got ulRefHi1 = %ld, ulRefHi2 = %ld\n",
ulRefHi1, ulRefHi2);
}
#endif /* DEBUG */
}
/*
*
***LP wsRedWeightWsDecision
*
*
* Effects:
*
* Weights the decision matrix edges from start vertex to end vertex,
* depending on the relative importance of the end vertex.
*
* Returns:
*
* Void.
*/
VOID
wsRedWeightWsDecision()
{
UINT i = 0, j = 0; // Temporary loop indexes
UINT uiFxn = 0; // Function number
WsIndicator( WSINDF_NEW, "Weight Matrix ", cFxnsTot );
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
WsIndicator( WSINDF_PROGRESS, NULL, uiFxn );
for (i = 0; i < cFxnsTot; i++)
{
if (uiFxn == i)
continue;
if (WsDecision[uiFxn][i]) // mdg 98/4
WsDecision[uiFxn][i] *= WsDecision[i][i];
}
}
WsIndicator( WSINDF_FINISH, NULL, 0 );
#ifdef DEBUG
if (fVerbose)
{
printf("\nWEIGHTED MATRIX:\n");
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
printf("row %4d:\n", uiFxn);
for (i = 0; i < cFxnsTot; i++)
printf("0x%lx ", (LONG)WsDecision[uiFxn][i]);
printf("\n");
}
}
#endif /* DEBUG */
}
/*
*
***LP wsRedReorder
*
* Requires:
*
* Effects:
*
* A greedy algorithm is used to determine a better ordering for the functions
* whose reference patterns are represented in the decision matrix. The
* algorithm is as follows:
*
* o Select the function whose value on the diagonal is greatest.
* The selected function becomes the current starting vertex,
* and is first on the list of ordered functions. Mark that it
* is no longer a candidate function. Note that this does NOT mean
* that its vertex is removed from the graph.
*
* o While there is more than one function remaining as a candidate:
*
* - Choose the edge of greatest weight leading from the current
* starting vertex. Ties are broken as follows: If one of the
* tied ending vertices is in the selected set and the other is
* not, choose the edge whose ending vertex is already selected
* (because we already know that vertex is "important"); further
* ties are broken by choosing the end vertex whose diagonal value
* is greatest.
*
* - If the ending vertex chosen above is still a candidate (i.e., not
* already selected), then select it for the list of ordered
* functions, and mark that it is no longer a candidate.
*
* - Set the matrix entry for the chosen edge to some invalid value,
* so that edge will never be chosen again.
*
* - Set current starting vertex equal to the ending vertex chosen
* above.
*
* o Select the one remaining function for the list of ordered functions.
*
* mdg 98/4: Added "pnEdges" and "nEdgeCount" to function structure. If the number
* of set functions is < USHRT_MAX (very likely, even for very large
* projects), allocate as needed a sorted index for WsRedReorder(). This
* cuts dramatically the number of passes through the matrix searching for
* the next edge to consider.
*
* Returns:
*
* Void.
*/
VOID
wsRedReorder()
{
UINT uiFxn = 0; // Function number
UINT i = 0; // Temporary loop index
UINT cCandidates = 0; // Count of candidates remaining
UINT uiEdge = 0; // Function ordinal edge selected
/* Reuse FxnBits[] for the ordered list of functions, FxnOrder[]. */
WsIndicator( WSINDF_NEW, "Reorder Matrix", cFxnsTot );
FxnOrder = FxnBits;
memset((PVOID) FxnOrder, 0, cFxnsTot * sizeof(ULONG));
cCandidates = cFxnsTot;
FxnOrder[cFxnOrder++] = uiSelected;
WsrFxn[uiSelected].wsrfxn_fCandidate = FALSE;
--cCandidates;
while (cCandidates > 1)
{
WsIndicator( WSINDF_PROGRESS, NULL, cFxnsTot - cCandidates );
/* Follow highest weighted edge from selected vertex. */
#ifdef SLOWMO
uiEdge = wsRedChooseEdge(uiSelected);
#else // SLOWMO
uiEdge = wsRedChooseEdgeOpt( uiSelected );
#endif // SLOWMO
#ifdef DEBUG
if (fVerbose)
printf("choose edge (%d->%d)\n", uiSelected, uiEdge);
#endif
uiSelected = uiEdge;
if (WsrFxn[uiEdge].wsrfxn_fCandidate)
{
FxnOrder[cFxnOrder++] = uiSelected;
WsrFxn[uiSelected].wsrfxn_fCandidate = FALSE;
--cCandidates;
}
}
WsIndicator( WSINDF_FINISH, NULL, 0 );
if (cCandidates == 1)
{
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
if (WsrFxn[uiFxn].wsrfxn_fCandidate)
{
FxnOrder[cFxnOrder++] = uiFxn;
break;
}
}
}
#ifdef SLOWMO
/*
*
***LP wsRedChooseEdge
*
*
* Effects:
*
* "Selects" a function from the candidate pool, based on weighted
* edge from 'index' function to a candidate function.
*
*
*
* Returns:
*
* Ordinal number of selected function.
*
*/
UINT
wsRedChooseEdge(UINT uiIndex)
{
UINT uiFxn = 0; // Function ordinal number.
WsDecision_t iMaxWt = WSDECISION_TAKEN; // Highest weighted edge encountered.
UINT uiRet = 0; // Return index.
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
if (uiFxn == uiIndex
|| WsDecision[uiIndex][uiFxn] == WSDECISION_TAKEN)
continue;
if (WsDecision[uiIndex][uiFxn] > iMaxWt
|| iMaxWt == WSDECISION_TAKEN )
{
iMaxWt = WsDecision[uiIndex][uiFxn];
uiRet = uiFxn;
}
else if (WsDecision[uiIndex][uiFxn] == iMaxWt)
{
/* Need tiebreak. If 'uiFxn' has already been selected,
* we know it is important, so choose it. Otherwise,
* and in the case where more than one of the tied
* functions has already been selected, choose based
* on the diagonal value.
*/
if ((WsrFxn[uiFxn].wsrfxn_fCandidate == FALSE) &&
(WsrFxn[uiRet].wsrfxn_fCandidate))
/* Choose 'uiFxn', it's been selected before */
uiRet = uiFxn;
else
if (WsDecision[uiFxn][uiFxn] > WsDecision[uiRet][uiRet])
uiRet = uiFxn;
}
}
WsDecision[uiIndex][uiRet] = WsDecision[uiRet][uiIndex] = WSDECISION_TAKEN;
return(uiRet);
}
#else // SLOWMO
/*
*
***LP wsRedChooseEdgeOpt
*
*
* Effects:
*
* "Selects" a function from the candidate pool, based on weighted
* edge from 'index' function to a candidate function. Allocates a sorted
* index (highest to lowest) to each function's edges on demand. Uses the
* current highest value (with a few checks) as the selection. This
* optimized algorithm produces identical results for the important high
* usage high overlap functions, but diverges in the results for low usage
* (2 or 1 hits) low overlap functions. Differences are not significant
* from a performance perspective - a better algorithm would give marginally
* better results.
*
*
*
* Returns:
*
* Ordinal number of selected function.
*
*/
UINT
wsRedChooseEdgeOpt(UINT uiIndex)
{
UINT uiRet;
wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex];
// Allocate and sort edges list if it doesn't already exist for this function
if (wsRedChooseEdgeOptAlloc( uiIndex ))
{
wsRedExit( ERROR, PRINT_MSG, MSG_NO_MEM,
(cFxnsTot - 1) * sizeof(*pWsrFxn->pnEdges), "WsrFxn[].pnEdges" );
}
// Check remaining edges
uiRet = wsRedChooseEdgeOptNextEdge( uiIndex, FALSE );
if (uiRet == cFxnsTot)
// What should we do here? The algorithm we're copying falls through
// and arbitrarily returns 0. It seems we should pick the most overlapped
// non-Candidate, or the heaviest Candidate and restart from there.
{
WsDecision_t iMaxWt;
static UINT nFxnOrdStart = 0; // Remember last value to restart there
static UINT nFxnTotStart = 0; // Remember last value to restart there
UINT nSelIndex;
UINT nFxn;
// Search for most overlapped non-Candidate that's not uiIndex ('uiIndex' should be empty by now)
iMaxWt = WSDECISION_TAKEN;
for (nFxn = nFxnOrdStart; nFxn < cFxnOrder; ++nFxn)
{
UINT nLocalIndex = FxnOrder[nFxn];
UINT nRetCheck;
if (!WsrFxn[nLocalIndex].nEdgesLeft)
{
if (nFxnOrdStart == nFxn) // Haven't found available edge yet?
++nFxnOrdStart; // All non-Candidates already have been allocated, so they can be skipped next time
continue;
}
// Get the first available value remaining
nRetCheck = wsRedChooseEdgeOptNextEdge( nLocalIndex, TRUE );
if (nRetCheck != cFxnsTot
&& nRetCheck != uiIndex)
{
// See if this one's heavier
if (WsDecision[nLocalIndex][nRetCheck] > iMaxWt
|| iMaxWt == WSDECISION_TAKEN)
{
nSelIndex = nLocalIndex;
iMaxWt = WsDecision[nSelIndex][nRetCheck];
uiRet = nRetCheck;
}
else if (WsDecision[nLocalIndex][nRetCheck] == iMaxWt // On tie, use heaviest function
&& WsDecision[nRetCheck][nRetCheck] > WsDecision[uiRet][uiRet]) // Assume uiRet != cFxnsTot by now
{
nSelIndex = nLocalIndex;
uiRet = nRetCheck;
}
}
}
if (uiRet != cFxnsTot) // Found an overlapped non-Candidate?
{
WsDecision[nSelIndex][uiRet] = WsDecision[uiRet][nSelIndex] = WSDECISION_TAKEN;
return uiRet;
}
else // Didn't find an overlapped non-Candidate?
{
// Search for heaviest Candidate - assume at least two are left: see wsRedReorder()
iMaxWt = WSDECISION_TAKEN;
for (nFxn = nFxnTotStart; nFxn < cFxnsTot; ++nFxn)
{
if (!WsrFxn[nFxn].wsrfxn_fCandidate)
{
if (nFxnTotStart == nFxn) // Haven't found unused value yet?
++nFxnTotStart; // If it's not a candidate now, it won't be again either
continue;
}
if (nFxn == uiIndex)
continue;
if (WsDecision[nFxn][nFxn] > iMaxWt
|| iMaxWt == WSDECISION_TAKEN)
{
iMaxWt = WsDecision[nFxn][nFxn];
uiRet = nFxn;
}
}
}
}
WsDecision[uiIndex][uiRet] = WsDecision[uiRet][uiIndex] = WSDECISION_TAKEN;
return uiRet;
}
// Comparison function for qsort - uses external nFxnToSort for static index
INT
__cdecl
wsRedChooseEdgeOptCmp ( const UINT *pn1, const UINT *pn2 )
{
WsDecision_t Val1 = WsDecision[nFxnToSort][*pn1],
Val2 = WsDecision[nFxnToSort][*pn2];
return Val1 > Val2 ? -1 // higher values preferred
: Val1 < Val2 ? 1
// If the same, prefer the highest valued diagonal
: (Val1 = WsDecision[*pn1][*pn1]) > (Val2 = WsDecision[*pn2][*pn2]) ? -1
: Val1 < Val2 ? 1
// Prefer prior function if no other differences
: *pn1 < *pn2 ? -1
: 1;
}
// Allocate and sort edges list for a function if not already allocated
// Return TRUE on failure to allocate, FALSE if successful (even if list is empty)
// Creates sorted index list from all non-zero unused WsDecision entries for this row
// except for the diagonal. Sorts from greatest to lowest: see wsRedChooseEdgeOptCmp().
// If no such entries exist, marks the function edges as allocated, but with none
// left to scan; doesn't actually allocate any memory.
BOOL
wsRedChooseEdgeOptAlloc( UINT uiIndex )
{
wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex];
if (pWsrFxn->nEdgesAlloc == 0
&& pWsrFxn->pnEdges == NULL)
{
UINT nEdgeTot, nFxn;
// Allocate maximum size initially
pWsrFxn->pnEdges = malloc( (cFxnsTot - 1) * sizeof(*pWsrFxn->pnEdges) );
if (pWsrFxn->pnEdges == NULL) // No more memory?
return TRUE;
// Fill in array
for (nEdgeTot = nFxn = 0; nFxn < cFxnsTot; ++nFxn)
{
if (nFxn == uiIndex) // Skip diagonal
continue;
if (WsDecision[uiIndex][nFxn] > 0 // Edge still available? No point in considering 0
&& WsDecision[uiIndex][nFxn] != WSDECISION_TAKEN)
pWsrFxn->pnEdges[nEdgeTot++] = nFxn;
}
if (nEdgeTot > 0) // Edges available?
{
if (nEdgeTot != (cFxnsTot - 1)) // Extra space allocated?
{
// Make it smaller
UINT *pNewAlloc = realloc( pWsrFxn->pnEdges, nEdgeTot * sizeof(*pWsrFxn->pnEdges) );
if (pNewAlloc != NULL)
pWsrFxn->pnEdges = pNewAlloc;
}
// Fill in remaining structure members
pWsrFxn->nEdgesAlloc = pWsrFxn->nEdgesLeft = nEdgeTot;
// Sort highest to lowest
nFxnToSort = uiIndex; // Set static for sort function
qsort( pWsrFxn->pnEdges, nEdgeTot, sizeof(*pWsrFxn->pnEdges),
(int (__cdecl *)(const void *, const void *))wsRedChooseEdgeOptCmp );
}
else // pWsrFxn->nEdgesAlloc == NULL
{
// Set structure members to indicate nothing left
pWsrFxn->nEdgesAlloc = 1; // non-zero indicates some allocation happened
pWsrFxn->nEdgesLeft = 0;
free( pWsrFxn->pnEdges ); // Eliminate allocation - nothing left to check
pWsrFxn->pnEdges = NULL;
}
}
return FALSE;
}
// Get next edge for given function; highest overlap of most-used function
// Returns "cFxnsTot" if no edge exists; otherwise the function index of next edge
// Side-effect: optimizes search for next pass; frees edge index if no longer needed
// Since choosing an edge marks WsDecision entries as used (WSDECISION_TAKEN), we
// must step over any of these entries. Once these entries and the first unused entry
// have been selected, we don't need to consider them anymore. However, if
// 'bNoSelectOpt' is TRUE, only optimize leading skipped entries (not the selected
// entry, since it may not be taken).
UINT
wsRedChooseEdgeOptNextEdge( UINT uiIndex, BOOL bNoSelectOpt )
{
wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex];
UINT uiRet = cFxnsTot;
if (pWsrFxn->nEdgesLeft > 0)
{
UINT nMaxIx,
nNextIx = pWsrFxn->nEdgesAlloc - pWsrFxn->nEdgesLeft;
WsDecision_t iMax, iNext;
UINT nRetCheck;
// Get the first available value remaining
while ((iMax = WsDecision[uiIndex][nRetCheck = pWsrFxn->pnEdges[nMaxIx = nNextIx++]])
== WSDECISION_TAKEN
&& nNextIx < pWsrFxn->nEdgesAlloc);
// Check next available value for equivalence
if (iMax != WSDECISION_TAKEN)
{
UINT nMaxIxNext = nMaxIx; // Save index of next used entry
uiRet = nRetCheck;
for (; nNextIx < pWsrFxn->nEdgesAlloc; ++nNextIx)
{
nRetCheck = pWsrFxn->pnEdges[nNextIx];
iNext = WsDecision[uiIndex][nRetCheck];
if (iNext != WSDECISION_TAKEN)
{
if (iNext != iMax // only need to check for equality since already sorted
|| !WsrFxn[uiRet].wsrfxn_fCandidate) // Already selected - choose this one
break;
else
{
/* Need tiebreak. If 'nRetCheck' has already been selected,
* we know it is important, so choose it. Otherwise,
* and in the case where more than one of the tied
* functions have already been selected, choose based
* on the diagonal value (i.e. keep previous choice since
* sort already accounts for diagonal if equal vertices).
*/
if (!WsrFxn[nRetCheck].wsrfxn_fCandidate)
// Choose 'nRetCheck' - it's been selected before
{
uiRet = nRetCheck;
nMaxIxNext = nMaxIx - 1; // First used entry will be checked again; don't skip it
}
}
}
else if (nMaxIxNext == (nNextIx - 1)) // Skip unavailable values after first used entry only
++nMaxIxNext;
}
if (!bNoSelectOpt && nMaxIxNext != nMaxIx)
nMaxIx = nMaxIxNext; // Skip over first used entry and unused entries after it
}
else if (bNoSelectOpt) // This is the last one, so step over it anyway
++nMaxIx;
// Adjust the optimization indexes
pWsrFxn->nEdgesLeft = pWsrFxn->nEdgesAlloc - nMaxIx - (bNoSelectOpt ? 0 : 1);
if (pWsrFxn->nEdgesLeft == 0)
{
free( pWsrFxn->pnEdges ); // Eliminate allocation - nothing left to check
pWsrFxn->pnEdges = NULL;
}
#ifdef YOUVE_REALLY_GOT_MEMORY_PROBLEMS
else if (pWsrFxn->nEdgesLeft < pWsrFxn->nEdgesAlloc / 2 // Periodically get rid of some unused memory
&& pWsrFxn->nEdgesLeft > 50)
{
// Move edges to lower part of allocation and reallocate
UINT * pNewAlloc;
nNextIx = pWsrFxn->nEdgesAlloc - pWsrFxn->nEdgesLeft;
MoveMemory( pWsrFxn->pnEdges, &pWsrFxn->pnEdges[nNextIx], pWsrFxn->nEdgesLeft * sizeof(*pWsrFxn->pnEdges) );
pNewAlloc = realloc( pWsrFxn->pnEdges, pWsrFxn->nEdgesLeft * sizeof(*pWsrFxn->pnEdges) );
if (pNewAlloc != NULL)
pWsrFxn->pnEdges = pNewAlloc;
pWsrFxn->nEdgesAlloc = pWsrFxn->nEdgesLeft;
}
#endif // YOUVE_REALLY_GOT_MEMORY_PROBLEMS
}
return uiRet;
}
#endif // SLOWMO
/*
*
***LP wsRedOutput
*
*
* Effects:
*
* Prints the reordered list of functions, and writes each module's
* ordered list of function ordinals to the module's associated WSR file.
* If only one module is being processed, then we also write the ordered
* list of function names to a WLK file.
*
* Returns:
*
* Void. If an error is encountered, exits through wsRedExit()
* with ERROR.
*/
VOID
wsRedOutput()
{
UINT uiFxn;
UINT uiFxnOrd;
wsrfxn_t *pWsrFxn;
// fxn names for linker reordering
// Open one WSR file per module. If only one module is reduced,
// then also open a WLK file. Handle to WLK file is set in
// wsRedOpenWSR().
wsRedOpenWSR(&hFileWLK);
WsIndicator( WSINDF_NEW, "Saving Results", cTmiFxns );
for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++)
{
WsIndicator( WSINDF_PROGRESS, NULL, uiFxn );
pWsrFxn = &(WsrFxn[uiFxnOrd = FxnOrder[uiFxn]]);
/* Print the function information. */
#ifdef DEBUG
if (fVerbose)
#ifndef TMIFILEHACK
if (fFxnSizePresent == FALSE)
printf(" %s: %s\n",
pWsrMod->wsrmod_un.wsrmod_pchModName,
pWsrFxn->wsrfxn_pchFxnName);
else
#endif /* !TMIFILEHACK */
printf(" (0x%08lx bytes) %s: %s\n",
pWsrFxn->wsrfxn_cbFxn,
pWsrMod->wsrmod_un.wsrmod_pchModName,
pWsrFxn->wsrfxn_pchFxnName);
#endif // DEBUG
/* Write the function's ordinal number to its
* module's associated WSR output file.
*/
fprintf(pWsrMod->wsrmod_hFileWSR, "%ld\n",
uiFxnOrd);
/* Write the function name to the WLK file, for linker use. */
if (hFileWLK != NULL &&
strcmp("???", pWsrFxn->wsrfxn_pchFxnName) &&
strcmp("_penter", pWsrFxn->wsrfxn_pchFxnName))
fprintf(hFileWLK, "%s\n", pWsrFxn->wsrfxn_pchFxnName);
}
for (uiFxn = cFxnsTot; uiFxn < cTmiFxns; uiFxn++)
{
WsIndicator( WSINDF_PROGRESS, NULL, uiFxn );
pWsrFxn = &(WsrFxn[FxnOrder[0]]);
/* Write the function's ordinal number to its
* module's associated WSR output file.
*/
fprintf(pWsrMod->wsrmod_hFileWSR, "%ld\n",
uiFxn);
}
/* Close the WSR files. */
fclose(pWsrMod->wsrmod_hFileWSR);
pWsrMod->wsrmod_hFileWSR = NULL;
WsIndicator( WSINDF_FINISH, NULL, 0 );
}
/*
*
***LP wsRedExit
*
***
* Requires:
*
*
***
*
* Effects:
*
* Frees up resources (as necessary). Exits with the specified
* exit code, or returns void if exit code is NOEXIT.
*
***
* Returns:
*
* Void, else exits.
*/
VOID
wsRedExit(UINT uiExitCode, USHORT fPrintMsg, UINT uiMsgCode, ULONG ulParam1, PSZ pszParam2)
{
/* Print message, if necessary. */
if (fPrintMsg)
{
printf(pchMsg[uiMsgCode], szProgName, pszVersion, ulParam1, pszParam2);
}
// Special case: do NOT exit if called with NOEXIT.
if (uiExitCode == NOEXIT)
return;
wsRedCleanup(); // mdg 98/4
exit(uiExitCode);
}
VOID wsRedCleanup(VOID)
{
UINT x;
free( pWsrMod->wsrmod_un.wsrmod_pchModName );
pWsrMod->wsrmod_un.wsrmod_pchModName = NULL;
for (x = 0; x < cFxnsTot; x++) {
free( WsrFxn[x].wsrfxn_pchFxnName );
WsrFxn[x].wsrfxn_pchFxnName = NULL;
#ifndef SLOWMO
if (WsrFxn[x].pnEdges != NULL)
{
free( WsrFxn[x].pnEdges );
WsrFxn[x].pnEdges = NULL;
}
#endif // SLOWMO
}
for (x=0;x < 5 ; x++ ) {
UnlockAndFreeMem(hMem[x]);
}
/* Close the WLK file. */
if (NULL != hFileWLK)
fclose(hFileWLK);
}