You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1805 lines
47 KiB
1805 lines
47 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
order.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the order tool which reads a call graph output
|
|
by the linker and the performance data from the kernel profile and
|
|
produces a .prf file subsequent input to the linker.
|
|
|
|
Author:
|
|
|
|
David N. Cutler (davec) 24-Feb-1995
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stdlib.h"
|
|
#include "stdio.h"
|
|
#include "string.h"
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
|
|
//
|
|
// Define maximum values for table sizes.
|
|
//
|
|
|
|
#define MAXIMUM_CALLED 75 // maximum number of called functions
|
|
#define MAXIMUM_FUNCTION 5000 // maximum number of program functions
|
|
#define MAXIMUM_TOKEN 100 // maximum character in input token
|
|
#define MAXIMUM_SECTION 20 // maximum number of allocation sections
|
|
#define MAXIMUM_SYNONYM 10 // maximum number of synonym symbols
|
|
|
|
//
|
|
// Define file numbers.
|
|
//
|
|
|
|
#define CALLTREE_FILE 0 // call tree file produced by linker
|
|
#define PROFILE_FILE 1 // profile file produced by kernprof
|
|
#define ORDER_FILE 2 // order file produced by this program
|
|
|
|
//
|
|
// Define back edge node sttucture.
|
|
//
|
|
// Back edge nodes are used to represent back edges in the call graph and
|
|
// are constructed after the function list has been defined.
|
|
//
|
|
//
|
|
|
|
typedef struct _BACK_EDGE_NODE {
|
|
LIST_ENTRY Entry;
|
|
struct _FUNCTION_NODE *Node;
|
|
} BACK_EDGE_NODE, *PBACK_EDGE_NODE;
|
|
|
|
//
|
|
// Define called node structure.
|
|
//
|
|
// Called nodes are used to represent forward edges in the call graph and
|
|
// are constructed when the function list is being defined.
|
|
//
|
|
|
|
#define REFERENCE_NODE 0 // called entry is reference to node
|
|
#define REFERENCE_NAME 1 // called entry is reference to name
|
|
|
|
struct _FUNCTION_NODE;
|
|
|
|
typedef struct _CALLED_NODE {
|
|
union {
|
|
struct _FUNCTION_NODE *Node;
|
|
PCHAR Name;
|
|
} u;
|
|
|
|
ULONG Type;
|
|
} CALLED_NODE, *PCALLED_NODE;
|
|
|
|
//
|
|
// Define section node structure.
|
|
//
|
|
// Section nodes collect allocation information and contain the list of
|
|
// function nodes in the section.
|
|
//
|
|
|
|
typedef struct _SECTION_NODE {
|
|
LIST_ENTRY SectionListHead;
|
|
LIST_ENTRY OrderListHead;
|
|
PCHAR Name;
|
|
ULONG Base;
|
|
ULONG Size;
|
|
ULONG Offset;
|
|
ULONG Number;
|
|
ULONG Threshold;
|
|
} SECTION_NODE, *PSECTION_NODE;
|
|
|
|
//
|
|
// Define symbol node structure.
|
|
//
|
|
// Symbol nodes are associated with function nodes and store synonym names
|
|
// for the functions and their type of allocation.
|
|
//
|
|
|
|
typedef struct _SYMBOL_NODE {
|
|
PCHAR Name;
|
|
LONG Type;
|
|
} SYMBOL_NODE, *PSYMBOL_NODE;
|
|
|
|
//
|
|
// Define function node structure.
|
|
//
|
|
// Function nodes contain information about a paritcular function and its
|
|
// edges in the call graph.
|
|
//
|
|
|
|
typedef struct _FUNCTION_NODE {
|
|
SYMBOL_NODE SynonymList[MAXIMUM_SYNONYM];
|
|
CALLED_NODE CalledList[MAXIMUM_CALLED];
|
|
LIST_ENTRY CallerListHead;
|
|
LIST_ENTRY OrderListEntry;
|
|
LIST_ENTRY SectionListEntry;
|
|
PSECTION_NODE SectionNode;
|
|
ULONG NumberSynonyms;
|
|
ULONG NumberCalled;
|
|
ULONG Rva;
|
|
ULONG Size;
|
|
ULONG HitCount;
|
|
ULONG HitDensity;
|
|
ULONG Offset;
|
|
ULONG Placed;
|
|
ULONG Ordered;
|
|
} FUNCTION_NODE, *PFUNCTION_NODE;
|
|
|
|
//
|
|
// Define forward referenced functions.
|
|
//
|
|
|
|
VOID
|
|
CheckForConflict (
|
|
PFUNCTION_NODE FunctionNode,
|
|
PFUNCTION_NODE ConflictNode,
|
|
ULONG Depth
|
|
);
|
|
|
|
VOID
|
|
DumpInternalTables (
|
|
VOID
|
|
);
|
|
|
|
PFUNCTION_NODE
|
|
FindHighestDensityFunction (
|
|
PFUNCTION_NODE CallerNode
|
|
);
|
|
|
|
LONG
|
|
GetNextToken (
|
|
IN FILE *InputFile,
|
|
OUT PCHAR TokenBuffer
|
|
);
|
|
|
|
PFUNCTION_NODE
|
|
LookupFunctionNode (
|
|
IN PCHAR Name,
|
|
IN ULONG Rva,
|
|
IN ULONG Size,
|
|
IN LONG Type
|
|
);
|
|
|
|
PSECTION_NODE
|
|
LookupSectionNode (
|
|
IN PCHAR Name
|
|
);
|
|
|
|
VOID
|
|
OrderFunctionList (
|
|
VOID
|
|
);
|
|
|
|
ULONG
|
|
ParseCallTreeFile (
|
|
IN FILE *InputFile
|
|
);
|
|
|
|
ULONG
|
|
ParseProfileFile (
|
|
IN FILE *InputFile
|
|
);
|
|
|
|
VOID
|
|
PlaceCallerList (
|
|
IN PFUNCTION_NODE FunctionNode,
|
|
IN ULONG Depth
|
|
);
|
|
|
|
VOID
|
|
SortFunctionList (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
WriteOrderFile (
|
|
IN FILE *OutputFile
|
|
);
|
|
|
|
//
|
|
// Define function list data.
|
|
//
|
|
|
|
ULONG NumberFunctions = 0;
|
|
PFUNCTION_NODE FunctionList[MAXIMUM_FUNCTION];
|
|
|
|
//
|
|
// Define section list data.
|
|
//
|
|
|
|
ULONG NumberSections = 0;
|
|
PSECTION_NODE SectionList[MAXIMUM_SECTION];
|
|
|
|
//
|
|
// Define input and output file name defaults.
|
|
//
|
|
|
|
PCHAR FileName[3] = {"calltree.out", "profile.out", "order.prf"};
|
|
|
|
//
|
|
// Define dump flags.
|
|
//
|
|
|
|
ULONG DumpBackEdges = 0;
|
|
ULONG DumpFunctionList = 0;
|
|
ULONG DumpGoodnessValue = 0;
|
|
ULONG DumpSectionList = 0;
|
|
ULONG TraceAllocation = 0;
|
|
|
|
//
|
|
// Define primary cache mask parameter.
|
|
//
|
|
|
|
ULONG CacheMask = 8192 - 1;
|
|
ULONG CacheSize = 8192;
|
|
|
|
VOID
|
|
__cdecl
|
|
main (
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
FILE *InputFile;
|
|
ULONG Index;
|
|
FILE *OutputFile;
|
|
ULONG Shift;
|
|
ULONG Status;
|
|
PCHAR Switch;
|
|
|
|
//
|
|
// Parse the command parameters.
|
|
//
|
|
|
|
for (Index = 1; Index < (ULONG)argc; Index += 1) {
|
|
Switch = argv[Index];
|
|
if (*Switch++ == '-') {
|
|
if (*Switch == 'b') {
|
|
DumpBackEdges = 1;
|
|
|
|
} else if (*Switch == 'c') {
|
|
Switch += 1;
|
|
if (sscanf(Switch, "%d", &Shift) != 1) {
|
|
fprintf(stderr, "ORDER: Conversion of the shift failed\n");
|
|
exit(1);
|
|
}
|
|
|
|
CacheMask = (1024 << Shift) - 1;
|
|
CacheSize = (1024 << Shift);
|
|
|
|
} else if (*Switch == 'f') {
|
|
DumpFunctionList = 1;
|
|
|
|
} else if (*Switch == 'g') {
|
|
Switch += 1;
|
|
FileName[CALLTREE_FILE] = Switch;
|
|
|
|
} else if (*Switch == 'k') {
|
|
Switch += 1;
|
|
FileName[PROFILE_FILE] = Switch;
|
|
|
|
} else if (*Switch == 's') {
|
|
DumpSectionList = 1;
|
|
|
|
} else if (*Switch == 't') {
|
|
TraceAllocation = 1;
|
|
|
|
} else if (*Switch == 'v') {
|
|
DumpGoodnessValue = 1;
|
|
|
|
} else {
|
|
if (*Switch != '?') {
|
|
fprintf(stderr, "ORDER: Invalid switch %s\n", argv[Index]);
|
|
}
|
|
|
|
fprintf(stderr, "ORDER: Usage order [switch] [output-file]\n");
|
|
fprintf(stderr, " -b = print graph backedges\n");
|
|
fprintf(stderr, " -cn = primary cache size 1024*2**n\n");
|
|
fprintf(stderr, " -f = print function list\n");
|
|
fprintf(stderr, " -gfile = specify graph input file, default calltree.out\n");
|
|
fprintf(stderr, " -kfile = specify profile input file, default profile.out\n");
|
|
fprintf(stderr, " -s = print section list\n");
|
|
fprintf(stderr, " -t = trace allocation\n");
|
|
fprintf(stderr, " -v = print graph placement value\n");
|
|
fprintf(stderr, " -? - print usage\n");
|
|
exit(1);
|
|
}
|
|
|
|
} else {
|
|
FileName[ORDER_FILE] = argv[Index];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open and parse the call tree file.
|
|
//
|
|
|
|
InputFile = fopen(FileName[CALLTREE_FILE], "r");
|
|
if (InputFile == NULL) {
|
|
fprintf(stderr,
|
|
"ORDER: Open of call tree file %s failed\n",
|
|
FileName[CALLTREE_FILE]);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
Status = ParseCallTreeFile(InputFile);
|
|
fclose(InputFile);
|
|
if (Status != 0) {
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Open and parse the profile file.
|
|
//
|
|
|
|
InputFile = fopen(FileName[PROFILE_FILE], "r");
|
|
if (InputFile == NULL) {
|
|
fprintf(stderr,
|
|
"ORDER: Open of profile file %s failed\n",
|
|
FileName[PROFILE_FILE]);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
Status = ParseProfileFile(InputFile);
|
|
fclose(InputFile);
|
|
if (Status != 0) {
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Sort the function list and create the section lists.
|
|
//
|
|
|
|
SortFunctionList();
|
|
|
|
//
|
|
// Order function list.
|
|
//
|
|
|
|
OrderFunctionList();
|
|
|
|
//
|
|
// Open the output file and write the ordered function list.
|
|
//
|
|
|
|
OutputFile = fopen(FileName[ORDER_FILE], "w");
|
|
if (OutputFile == NULL) {
|
|
fprintf(stderr,
|
|
"ORDER: Open of order file %s failed\n",
|
|
FileName[ORDER_FILE]);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
WriteOrderFile(OutputFile);
|
|
fclose(OutputFile);
|
|
if (Status != 0) {
|
|
exit(1);
|
|
}
|
|
|
|
//
|
|
// Dump internal tables as appropriate.
|
|
//
|
|
|
|
DumpInternalTables();
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CheckForConflict (
|
|
PFUNCTION_NODE FunctionNode,
|
|
PFUNCTION_NODE ConflictNode,
|
|
ULONG Depth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks for an allocation conflict .
|
|
|
|
Arguments:
|
|
|
|
FunctionNode - Supplies a pointer to a function node that has been
|
|
placed.
|
|
|
|
ConflictNode - Supplies a pointer to a function node that has not
|
|
been placed.
|
|
|
|
Depth - Supplies the current allocation depth.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Base;
|
|
ULONG Bound;
|
|
ULONG Index;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
ULONG Offset;
|
|
PFUNCTION_NODE PadNode;
|
|
PSECTION_NODE SectionNode;
|
|
ULONG Wrap;
|
|
|
|
//
|
|
// Compute the cache size truncated offset and bound of the placed
|
|
// function.
|
|
//
|
|
|
|
Offset = FunctionNode->Offset & CacheMask;
|
|
Bound = Offset + FunctionNode->Size;
|
|
SectionNode = FunctionNode->SectionNode;
|
|
|
|
//
|
|
// If the section offset conflicts with the placed function,
|
|
// then attempt to allocate a function from the end of the
|
|
// section list that will pad the memory allocation so the
|
|
// conflict function does not overlap with the placed function.
|
|
//
|
|
|
|
Base = SectionNode->Offset & CacheMask;
|
|
Wrap = (Base + ConflictNode->Size) & CacheMask;
|
|
while (((Base >= Offset) && (Base < Bound)) ||
|
|
((Base < Offset) && (Wrap >= Bound)) ||
|
|
((Wrap >= Offset) && (Wrap < Base))) {
|
|
ListHead = &SectionNode->SectionListHead;
|
|
ListEntry = ListHead->Blink;
|
|
while (ListEntry != ListHead) {
|
|
PadNode = CONTAINING_RECORD(ListEntry,
|
|
FUNCTION_NODE,
|
|
SectionListEntry);
|
|
|
|
if ((PadNode->Ordered == 0) &&
|
|
(PadNode->SynonymList[0].Type == 'C')) {
|
|
PadNode->Ordered = 1;
|
|
PadNode->Placed = 1;
|
|
InsertTailList(&SectionNode->OrderListHead,
|
|
&PadNode->OrderListEntry);
|
|
|
|
PadNode->Offset = SectionNode->Offset;
|
|
SectionNode->Offset += PadNode->Size;
|
|
|
|
//
|
|
// If allocation is being trace, then output the
|
|
// allocation and depth information.
|
|
//
|
|
|
|
if (TraceAllocation != 0) {
|
|
fprintf(stdout,
|
|
"pp %6lx %4lx %-8s",
|
|
PadNode->Offset,
|
|
PadNode->Size,
|
|
SectionNode->Name);
|
|
|
|
for (Index = 0; Index < Depth; Index += 1) {
|
|
fprintf(stdout, " ");
|
|
}
|
|
|
|
fprintf(stdout, "%s\n",
|
|
PadNode->SynonymList[0].Name);
|
|
}
|
|
|
|
Base = SectionNode->Offset & CacheMask;
|
|
Wrap = (Base + ConflictNode->Size) & CacheMask;
|
|
break;
|
|
}
|
|
|
|
ListEntry = ListEntry->Blink;
|
|
}
|
|
|
|
if (ListEntry == ListHead) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
DumpInternalTables (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function dumps various internal tables.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Base;
|
|
ULONG Bound;
|
|
PFUNCTION_NODE CalledNode;
|
|
PFUNCTION_NODE CallerNode;
|
|
PFUNCTION_NODE FunctionNode;
|
|
ULONG Index;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
ULONG Loop;
|
|
PCHAR Name;
|
|
ULONG Number;
|
|
ULONG Offset;
|
|
PSECTION_NODE SectionNode;
|
|
ULONG Sum;
|
|
ULONG Total;
|
|
ULONG Wrap;
|
|
|
|
//
|
|
// Scan the function list and dump each function entry.
|
|
//
|
|
|
|
if (DumpFunctionList != 0) {
|
|
fprintf(stdout, "Dump of function list with attributes\n\n");
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
|
|
//
|
|
// Dump the function node.
|
|
//
|
|
|
|
FunctionNode = FunctionList[Index];
|
|
fprintf(stdout,
|
|
"%7d %-36s %c %-8s %6lx %4lx %7d\n",
|
|
FunctionNode->HitDensity,
|
|
FunctionNode->SynonymList[0].Name,
|
|
FunctionNode->SynonymList[0].Type,
|
|
FunctionNode->SectionNode->Name,
|
|
FunctionNode->Rva,
|
|
FunctionNode->Size,
|
|
FunctionNode->HitCount);
|
|
|
|
//
|
|
// Dump the synonym names.
|
|
//
|
|
|
|
for (Loop = 1; Loop < FunctionNode->NumberSynonyms; Loop += 1) {
|
|
fprintf(stdout,
|
|
" syno: %-34s %c\n",
|
|
FunctionNode->SynonymList[Loop].Name,
|
|
FunctionNode->SynonymList[Loop].Type);
|
|
}
|
|
|
|
//
|
|
// Dump the called references.
|
|
//
|
|
|
|
for (Loop = 0; Loop < FunctionNode->NumberCalled; Loop += 1) {
|
|
CalledNode = FunctionNode->CalledList[Loop].u.Node;
|
|
Name = CalledNode->SynonymList[0].Name;
|
|
fprintf(stdout," calls: %-s\n", Name);
|
|
}
|
|
}
|
|
|
|
fprintf(stdout, "\n\n");
|
|
}
|
|
|
|
//
|
|
// Scan the function list and dump the back edges of each function
|
|
// entry.
|
|
//
|
|
|
|
if (DumpBackEdges != 0) {
|
|
fprintf(stdout, "Dump of function list back edges\n\n");
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
FunctionNode = FunctionList[Index];
|
|
fprintf(stdout, "%s\n", FunctionNode->SynonymList[0].Name);
|
|
ListHead = &FunctionNode->CallerListHead;
|
|
ListEntry = ListHead->Flink;
|
|
while (ListEntry != ListHead) {
|
|
CallerNode = CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node;
|
|
fprintf(stdout, " %s\n", CallerNode->SynonymList[0].Name);
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
}
|
|
|
|
fprintf(stdout, "\n\n");
|
|
}
|
|
|
|
//
|
|
// Scan the section list and dump each entry.
|
|
//
|
|
|
|
if (DumpSectionList != 0) {
|
|
fprintf(stdout, "Dump of section list\n\n");
|
|
for (Index = 0; Index < NumberSections; Index += 1) {
|
|
SectionNode = SectionList[Index];
|
|
fprintf(stdout,
|
|
"%-8s %6lx, %6lx, %6lx, %4d %7d\n",
|
|
SectionNode->Name,
|
|
SectionNode->Base,
|
|
SectionNode->Size,
|
|
SectionNode->Offset,
|
|
SectionNode->Number,
|
|
SectionNode->Threshold);
|
|
}
|
|
|
|
fprintf(stdout, "\n\n");
|
|
}
|
|
|
|
//
|
|
// Compute the graph goodness value as the summation of the hit
|
|
// count of all functions whose allocation does not conflict with
|
|
// the functions that call it and whose hit density is great than
|
|
// the section threshold.
|
|
//
|
|
|
|
if (DumpGoodnessValue != 0) {
|
|
Number = 0;
|
|
Sum = 0;
|
|
Total = 0;
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
FunctionNode = FunctionList[Index];
|
|
SectionNode = FunctionNode->SectionNode;
|
|
Total += FunctionNode->Size;
|
|
if ((FunctionNode->HitDensity > SectionNode->Threshold) &&
|
|
(FunctionNode->SynonymList[0].Type == 'C')) {
|
|
Offset = FunctionNode->Offset & CacheMask;
|
|
Bound = (Offset + FunctionNode->Size) & CacheMask;
|
|
Sum += FunctionNode->Size;
|
|
ListHead = &FunctionNode->CallerListHead;
|
|
ListEntry = ListHead->Flink;
|
|
while (ListEntry != ListHead) {
|
|
CallerNode = CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node;
|
|
Base = CallerNode->Offset & CacheMask;
|
|
Wrap = (Base + CallerNode->Size) & CacheMask;
|
|
if ((((Base >= Offset) && (Base < Bound)) ||
|
|
((Base < Offset) && (Wrap >= Bound)) ||
|
|
((Wrap >= Offset) && (Wrap < Base))) &&
|
|
(CallerNode != FunctionNode) &&
|
|
(CallerNode->HitDensity > SectionNode->Threshold)) {
|
|
Number += 1;
|
|
fprintf(stdout,
|
|
"%-36s %6lx %4lx conflicts with\n %-36s %6lx %4lx\n",
|
|
FunctionNode->SynonymList[0].Name,
|
|
FunctionNode->Offset,
|
|
FunctionNode->Size,
|
|
CallerNode->SynonymList[0].Name,
|
|
CallerNode->Offset,
|
|
CallerNode->Size);
|
|
|
|
} else {
|
|
Sum += CallerNode->Size;
|
|
}
|
|
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
}
|
|
}
|
|
|
|
Sum = Sum * 100 / Total;
|
|
fprintf(stdout, "Graph goodness value is %d\n", Sum);
|
|
fprintf(stdout, "%d conflicts out of %d functions\n", Number, NumberFunctions);
|
|
}
|
|
}
|
|
|
|
PFUNCTION_NODE
|
|
FindHighestDensityFunction (
|
|
PFUNCTION_NODE CallerNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function finds the function node that has the highest hit density
|
|
of all the functions called by the caller node.
|
|
|
|
Arguments:
|
|
|
|
CallerNode - Supplies a pointer to a function node whose highest
|
|
hit density called function is to be found.
|
|
|
|
Return Value:
|
|
|
|
The address of the function node for the highest hit density called
|
|
function is returned as the function value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PFUNCTION_NODE CheckNode;
|
|
PFUNCTION_NODE FoundNode;
|
|
ULONG Index;
|
|
|
|
//
|
|
// Scan all the functions called by the specified function and
|
|
// compute the address of the highest hit density called function.
|
|
//
|
|
|
|
FoundNode = NULL;
|
|
for (Index = 0; Index < CallerNode->NumberCalled; Index += 1) {
|
|
if (CallerNode->CalledList[Index].Type == REFERENCE_NODE) {
|
|
CheckNode = CallerNode->CalledList[Index].u.Node;
|
|
if ((FoundNode == NULL) ||
|
|
(CheckNode->HitDensity > FoundNode->HitDensity)) {
|
|
FoundNode = CheckNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FoundNode;
|
|
}
|
|
|
|
LONG
|
|
GetNextToken (
|
|
IN FILE *InputFile,
|
|
OUT PCHAR TokenBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads the next token from the specified input file,
|
|
copies it to the token buffer, zero terminates the token, and
|
|
returns the delimiter character.
|
|
|
|
Arguments:
|
|
|
|
InputFile - Supplies a pointer to the input file descripor.
|
|
|
|
TokenBuffer - Supplies a pointer to the output token buffer.
|
|
|
|
Return Value:
|
|
|
|
The token delimiter character is returned as the function value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
CHAR Character;
|
|
|
|
//
|
|
// Read characters from the input stream and copy them to the token
|
|
// buffer until an EOF or token delimiter is encountered. Terminate
|
|
// the token will a null and return the token delimiter character.
|
|
//
|
|
|
|
do {
|
|
Character = (CHAR)fgetc(InputFile);
|
|
if ((Character != ' ') &&
|
|
(Character != '\t')) {
|
|
break;
|
|
}
|
|
|
|
} while(TRUE);
|
|
|
|
do {
|
|
if ((Character == EOF) ||
|
|
(Character == ' ') ||
|
|
(Character == '\n') ||
|
|
(Character == '\t')) {
|
|
break;
|
|
}
|
|
|
|
*TokenBuffer++ = Character;
|
|
Character = (CHAR)fgetc(InputFile);
|
|
} while(TRUE);
|
|
|
|
*TokenBuffer = '\0';
|
|
return Character;
|
|
}
|
|
|
|
PFUNCTION_NODE
|
|
LookupFunctionNode (
|
|
IN PCHAR Name,
|
|
IN ULONG Rva,
|
|
IN ULONG Size,
|
|
IN LONG Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches the function list for a matching entry.
|
|
|
|
Arguments:
|
|
|
|
Name - Supplies a pointer to the name of the function.
|
|
|
|
Rva - Supplies the revlative virtual address of the function.
|
|
|
|
Size - Supplies the size of the function.
|
|
|
|
Type - specified the type of the function (0, N, or C).
|
|
|
|
Return Value:
|
|
|
|
If a matching entry is found, then the function node address is
|
|
returned as the function value. Otherwise, NULL is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Index;
|
|
ULONG Loop;
|
|
PFUNCTION_NODE Node;
|
|
ULONG Number;
|
|
|
|
//
|
|
// Search the function list for a matching function.
|
|
//
|
|
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
Node = FunctionList[Index];
|
|
|
|
//
|
|
// Search the synonym list for the specified function name.
|
|
//
|
|
|
|
for (Loop = 0; Loop < Node->NumberSynonyms; Loop += 1) {
|
|
if (strcmp(Name, Node->SynonymList[Loop].Name) == 0) {
|
|
if (Type != 0) {
|
|
fprintf(stderr,
|
|
"ORDER: Warning - duplicate function name %s\n",
|
|
Name);
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the type is nonzero, then a function definition is being
|
|
// looked up and the RVA/size must be checked for a synonym. If
|
|
// the RVA and size of the entry are equal to the RVA and size
|
|
// of the reference, then the function name is added to the node
|
|
// as a synonym.
|
|
//
|
|
|
|
if (Type != 0) {
|
|
if ((Node->Rva == Rva) && (Node->Size == Size)) {
|
|
Number = Node->NumberSynonyms;
|
|
if (Number >= MAXIMUM_SYNONYM) {
|
|
fprintf(stderr,
|
|
"ORDER: Warning - Too many synonyms %s\n",
|
|
Name);
|
|
|
|
} else {
|
|
if (Type == 'C') {
|
|
Node->SynonymList[Number].Name = Node->SynonymList[0].Name;
|
|
Node->SynonymList[Number].Type = Node->SynonymList[0].Type;
|
|
Number = 0;
|
|
}
|
|
|
|
Node->SynonymList[Number].Name = Name;
|
|
Node->SynonymList[Number].Type = Type;
|
|
Node->NumberSynonyms += 1;
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PSECTION_NODE
|
|
LookupSectionNode (
|
|
IN PCHAR Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function searches the section list for a matching entry.
|
|
|
|
Arguments:
|
|
|
|
Name - Supplies a pointer to the name of the section.
|
|
|
|
Return Value:
|
|
|
|
If a matching entry is found, then the section node address is
|
|
returned as the function value. Otherwise, NULL is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Index;
|
|
PSECTION_NODE SectionNode;
|
|
|
|
//
|
|
// Search the function list for a matching function.
|
|
//
|
|
|
|
for (Index = 0; Index < NumberSections; Index += 1) {
|
|
SectionNode = SectionList[Index];
|
|
if (strcmp(Name, SectionNode->Name) == 0) {
|
|
return SectionNode;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
PlaceCallerList (
|
|
IN PFUNCTION_NODE FunctionNode,
|
|
ULONG Depth
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function recursively places all the functions in the caller list
|
|
for the specified function.
|
|
|
|
Arguments:
|
|
|
|
FunctionNode - Supplies a pointer to a function node.
|
|
|
|
Depth - Supplies the depth of the function in the caller tree.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PFUNCTION_NODE CallerNode;
|
|
ULONG Index;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
PFUNCTION_NODE PadNode;
|
|
PSECTION_NODE SectionNode;
|
|
|
|
//
|
|
// Scan the caller list and process each function that has not been
|
|
// placed.
|
|
//
|
|
//
|
|
|
|
Depth += 1;
|
|
SectionNode = FunctionNode->SectionNode;
|
|
ListHead = &FunctionNode->CallerListHead;
|
|
ListEntry = ListHead->Flink;
|
|
while (ListHead != ListEntry) {
|
|
CallerNode = CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node;
|
|
|
|
//
|
|
// If the caller is in the same section, has not been placed, is
|
|
// placeable, has a hit density above the section threshold, has
|
|
// not been placed, and the current function is the highest density
|
|
// called function of the caller, then insert the function in the
|
|
// section order list and compute it's offset and bound.
|
|
//
|
|
|
|
if ((SectionNode == CallerNode->SectionNode) &&
|
|
(CallerNode->Placed == 0) &&
|
|
(CallerNode->Ordered == 0) &&
|
|
(CallerNode->SynonymList[0].Type == 'C') &&
|
|
(CallerNode->HitDensity > SectionNode->Threshold) &&
|
|
(FindHighestDensityFunction(CallerNode) == FunctionNode)) {
|
|
CallerNode->Placed = 1;
|
|
CallerNode->Ordered = 1;
|
|
|
|
//
|
|
// Resolve any allocation conflict, insert function in the
|
|
// section order list, and place the fucntion.
|
|
//
|
|
|
|
CheckForConflict(FunctionNode, CallerNode, Depth);
|
|
InsertTailList(&SectionNode->OrderListHead,
|
|
&CallerNode->OrderListEntry);
|
|
|
|
CallerNode->Offset = SectionNode->Offset;
|
|
SectionNode->Offset += CallerNode->Size;
|
|
|
|
//
|
|
// If allocation is being trace, then output the allocation and
|
|
// depth information.
|
|
//
|
|
|
|
if (TraceAllocation != 0) {
|
|
fprintf(stdout,
|
|
"%2d %6lx %4lx %-8s",
|
|
Depth,
|
|
CallerNode->Offset,
|
|
CallerNode->Size,
|
|
SectionNode->Name);
|
|
|
|
for (Index = 0; Index < Depth; Index += 1) {
|
|
fprintf(stdout, " ");
|
|
}
|
|
|
|
fprintf(stdout, "%s\n",
|
|
CallerNode->SynonymList[0].Name);
|
|
}
|
|
|
|
PlaceCallerList(CallerNode, Depth);
|
|
}
|
|
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
OrderFunctionList (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function computes the link order for based on the information
|
|
in the function list.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Base;
|
|
ULONG Bound;
|
|
PFUNCTION_NODE CallerNode;
|
|
FUNCTION_NODE DummyNode;
|
|
PFUNCTION_NODE FunctionNode;
|
|
ULONG High;
|
|
ULONG Index;
|
|
ULONG Limit;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
ULONG Low;
|
|
ULONG Offset;
|
|
PFUNCTION_NODE PadNode;
|
|
PSECTION_NODE SectionNode;
|
|
ULONG Span;
|
|
|
|
//
|
|
// Scan forward through the function list and compute the link order.
|
|
//
|
|
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
FunctionNode = FunctionList[Index];
|
|
|
|
//
|
|
// If the function has not been placed, then place the function.
|
|
//
|
|
|
|
if ((FunctionNode->Placed == 0) &&
|
|
(FunctionNode->SynonymList[0].Type == 'C')) {
|
|
FunctionNode->Ordered = 1;
|
|
FunctionNode->Placed = 1;
|
|
SectionNode = FunctionNode->SectionNode;
|
|
|
|
//
|
|
// Attempt to find the highest hit density caller than has
|
|
// already been placed and compute the total bounds for all
|
|
// placed caller functions.
|
|
//
|
|
|
|
Bound = 0;
|
|
Offset = CacheMask;
|
|
ListHead = &FunctionNode->CallerListHead;
|
|
ListEntry = ListHead->Flink;
|
|
while (ListEntry != ListHead) {
|
|
CallerNode = CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node;
|
|
if ((SectionNode == CallerNode->SectionNode) &&
|
|
(CallerNode->Placed != 0) &&
|
|
(CallerNode->Ordered != 0) &&
|
|
(CallerNode->SynonymList[0].Type == 'C') &&
|
|
(CallerNode->HitDensity > SectionNode->Threshold)) {
|
|
Base = CallerNode->Offset & CacheMask;
|
|
Limit = Base + CallerNode->Size;
|
|
Low = min(Offset, Base);
|
|
High = max(Bound, Limit);
|
|
Span = High - Low;
|
|
if ((Span < CacheSize) &&
|
|
((CacheSize - Span) > FunctionNode->Size)) {
|
|
Offset = Low;
|
|
Bound = High;
|
|
}
|
|
}
|
|
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
//
|
|
// If a caller has already been placed and the hit density is
|
|
// above the section threshold, then resolve any allocation
|
|
// conflict before inserting the function in the section order
|
|
// list and placing it in memory.
|
|
//
|
|
|
|
if (Bound != 0) {
|
|
Span = Bound - Offset;
|
|
if ((Span < CacheSize) &&
|
|
((CacheSize - Span) > FunctionNode->Size)) {
|
|
DummyNode.SectionNode = SectionNode;
|
|
DummyNode.Offset = Offset;
|
|
DummyNode.Size = Span;
|
|
CheckForConflict(&DummyNode, FunctionNode, 1);
|
|
}
|
|
}
|
|
|
|
InsertTailList(&SectionNode->OrderListHead,
|
|
&FunctionNode->OrderListEntry);
|
|
|
|
FunctionNode->Offset = SectionNode->Offset;
|
|
SectionNode->Offset += FunctionNode->Size;
|
|
|
|
//
|
|
// If allocation is being trace, then output the allocation and
|
|
// depth information.
|
|
//
|
|
|
|
if (TraceAllocation != 0) {
|
|
fprintf(stdout,
|
|
"%2d %6lx %4lx %-8s %s\n",
|
|
1,
|
|
FunctionNode->Offset,
|
|
FunctionNode->Size,
|
|
SectionNode->Name,
|
|
FunctionNode->SynonymList[0].Name);
|
|
}
|
|
|
|
PlaceCallerList(FunctionNode, 1);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ULONG
|
|
ParseCallTreeFile (
|
|
IN FILE *InputFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads the call tree data and produces the initial call
|
|
graph.
|
|
|
|
Arguments:
|
|
|
|
InputFile - Supplies a pointer to the input file stream.
|
|
|
|
Return Value:
|
|
|
|
A value of zero is returned if the call tree is successfully parsed.
|
|
Otherwise, a nonzero value is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCHAR Buffer;
|
|
PFUNCTION_NODE CalledNode;
|
|
PBACK_EDGE_NODE CallerNode;
|
|
LONG Delimiter;
|
|
ULONG HitCount;
|
|
ULONG Index;
|
|
ULONG Loop;
|
|
PCHAR Name;
|
|
PFUNCTION_NODE Node;
|
|
ULONG Number;
|
|
ULONG Rva;
|
|
PSECTION_NODE SectionNode;
|
|
ULONG Size;
|
|
CHAR TokenBuffer[MAXIMUM_TOKEN];
|
|
LONG Type;
|
|
|
|
//
|
|
// Process the call tree file.
|
|
//
|
|
|
|
Buffer = &TokenBuffer[0];
|
|
do {
|
|
|
|
//
|
|
// Get the relative virtual address of the next function.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
break;
|
|
}
|
|
|
|
if (sscanf(Buffer, "%lx", &Rva) != 1) {
|
|
fprintf(stderr, "ORDER: Conversion of the RVA failed\n");
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Get the function type.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file at function type\n");
|
|
return 1;
|
|
}
|
|
|
|
Type = *Buffer;
|
|
|
|
//
|
|
// Get the section name.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file at section name\n");
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// If the specfied section is not already in the section list, then
|
|
// allocate and initialize a new section list entry.
|
|
//
|
|
|
|
SectionNode = LookupSectionNode(Buffer);
|
|
if (SectionNode == NULL) {
|
|
|
|
//
|
|
// Allocate a section node and zero.
|
|
//
|
|
|
|
if (NumberSections >= MAXIMUM_SECTION) {
|
|
fprintf(stderr, "ORDER: Maximum number of sections exceeded\n");
|
|
return 1;
|
|
}
|
|
|
|
SectionNode = (PSECTION_NODE)malloc(sizeof(SECTION_NODE));
|
|
if (SectionNode == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate section node\n");
|
|
return 1;
|
|
}
|
|
|
|
memset((PCHAR)SectionNode, 0, sizeof(SECTION_NODE));
|
|
SectionList[NumberSections] = SectionNode;
|
|
NumberSections += 1;
|
|
|
|
//
|
|
// Initialize section node.
|
|
//
|
|
|
|
InitializeListHead(&SectionNode->OrderListHead);
|
|
InitializeListHead(&SectionNode->SectionListHead);
|
|
Name = (PCHAR)malloc(strlen(Buffer) + 1);
|
|
if (Name == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate section name\n");
|
|
return 1;
|
|
}
|
|
|
|
strcpy(Name, Buffer);
|
|
SectionNode->Name = Name;
|
|
}
|
|
|
|
//
|
|
// Get the function size.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file at function size\n");
|
|
return 1;
|
|
}
|
|
|
|
if (sscanf(Buffer, "%lx", &Size) != 1) {
|
|
fprintf(stderr, "ORDER: Conversion of the function size failed\n");
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Get the function name.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file at function name\n");
|
|
return 1;
|
|
}
|
|
|
|
Name = (PCHAR)malloc(strlen(Buffer) + 1);
|
|
if (Name == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate function name\n");
|
|
return 1;
|
|
}
|
|
|
|
strcpy(Name, Buffer);
|
|
|
|
//
|
|
// If the specified function is not already in the function list,
|
|
// then allocate and initialize a new function list entry.
|
|
//
|
|
|
|
Node = LookupFunctionNode(Name, Rva, Size, Type);
|
|
if (Node == NULL) {
|
|
|
|
//
|
|
// Allocate a function node and zero.
|
|
//
|
|
|
|
if (NumberFunctions >= MAXIMUM_FUNCTION) {
|
|
fprintf(stderr, "ORDER: Maximum number of functions exceeded\n");
|
|
return 1;
|
|
}
|
|
|
|
Node = (PFUNCTION_NODE)malloc(sizeof(FUNCTION_NODE));
|
|
if (Node == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate function node\n");
|
|
return 1;
|
|
}
|
|
|
|
memset((PCHAR)Node, 0, sizeof(FUNCTION_NODE));
|
|
FunctionList[NumberFunctions] = Node;
|
|
NumberFunctions += 1;
|
|
|
|
//
|
|
// Initialize function node.
|
|
//
|
|
|
|
InitializeListHead(&Node->CallerListHead);
|
|
Node->SynonymList[0].Name = Name;
|
|
Node->SynonymList[0].Type = Type;
|
|
Node->NumberSynonyms = 1;
|
|
Node->SectionNode = SectionNode;
|
|
|
|
//
|
|
// Initialize relative virtual address and function size.
|
|
//
|
|
|
|
Node->Rva = Rva;
|
|
if (Size == 0) {
|
|
Size = 4;
|
|
}
|
|
|
|
Node->Size = Size;
|
|
}
|
|
|
|
//
|
|
// Parse the called forward edges and add them to the current node.
|
|
//
|
|
|
|
if (Delimiter != '\n') {
|
|
do {
|
|
|
|
//
|
|
// Get next function reference.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file called scan\n");
|
|
return 1;
|
|
}
|
|
|
|
Number = Node->NumberCalled;
|
|
if (Number >= MAXIMUM_CALLED) {
|
|
fprintf(stderr,
|
|
"ORDER: Too many called references %s\n",
|
|
Buffer);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Lookup the specified function in the function list. If the
|
|
// specified function is found, then store the address of the
|
|
// function node in the called list. Otherwise, allocate a name
|
|
// buffer, copy the function name to the buffer, and store the
|
|
// address of the name buffer in the called list.
|
|
//
|
|
|
|
CalledNode = LookupFunctionNode(Buffer, 0, 0, 0);
|
|
if (CalledNode == NULL) {
|
|
Name = (PCHAR)malloc(strlen(Buffer) + 1);
|
|
if (Name == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate reference name\n");
|
|
return 1;
|
|
}
|
|
|
|
strcpy(Name, Buffer);
|
|
Node->CalledList[Number].u.Name = Name;
|
|
Node->CalledList[Number].Type = REFERENCE_NAME;
|
|
|
|
} else {
|
|
Node->CalledList[Number].u.Node = CalledNode;
|
|
Node->CalledList[Number].Type = REFERENCE_NODE;
|
|
}
|
|
|
|
Node->NumberCalled += 1;
|
|
} while (Delimiter != '\n');
|
|
}
|
|
|
|
} while(TRUE);
|
|
|
|
//
|
|
// Scan the function table and do the final resolution for all called
|
|
// functions names that were unresolved when the individual functions
|
|
// were defined.
|
|
//
|
|
|
|
for (Index = 0; Index < NumberFunctions; Index += 1) {
|
|
Node = FunctionList[Index];
|
|
for (Loop = 0; Loop < Node->NumberCalled; Loop += 1) {
|
|
if (Node->CalledList[Loop].Type == REFERENCE_NAME) {
|
|
CalledNode =
|
|
LookupFunctionNode(Node->CalledList[Loop].u.Name,
|
|
0,
|
|
0,
|
|
0);
|
|
|
|
if (CalledNode == NULL) {
|
|
fprintf(stderr,
|
|
"ORDER: Unresolved reference name %s\n",
|
|
Node->CalledList[Loop].u.Name);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
Node->CalledList[Loop].Type = REFERENCE_NODE;
|
|
Node->CalledList[Loop].u.Node = CalledNode;
|
|
}
|
|
|
|
} else {
|
|
CalledNode = Node->CalledList[Loop].u.Node;
|
|
}
|
|
|
|
//
|
|
// Allocate a back edge node and place the node in the caller
|
|
// list of called function.
|
|
//
|
|
|
|
CallerNode = (PBACK_EDGE_NODE)malloc(sizeof(BACK_EDGE_NODE));
|
|
if (CallerNode == NULL) {
|
|
fprintf(stderr, "ORDER: Failed to allocate caller node\n");
|
|
return 1;
|
|
}
|
|
|
|
CallerNode->Node = Node;
|
|
InsertTailList(&CalledNode->CallerListHead, &CallerNode->Entry);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ULONG
|
|
ParseProfileFile (
|
|
IN FILE *InputFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads the profile data and computes the hit density
|
|
for each funtion.
|
|
|
|
Arguments:
|
|
|
|
InputFile - Supplies a pointer to the input file stream.
|
|
|
|
Return Value:
|
|
|
|
A value of zero is returned if the call tree is successfully parsed.
|
|
Otherwise, a nonzero value is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCHAR Buffer;
|
|
ULONG HitCount;
|
|
LONG Delimiter;
|
|
PFUNCTION_NODE FunctionNode;
|
|
CHAR TokenBuffer[MAXIMUM_TOKEN];
|
|
|
|
//
|
|
// Process the profile file.
|
|
//
|
|
|
|
Buffer = &TokenBuffer[0];
|
|
do {
|
|
|
|
//
|
|
// Get the bucket hit count.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
break;
|
|
}
|
|
|
|
if (sscanf(Buffer, "%d", &HitCount) != 1) {
|
|
fprintf(stderr, "ORDER: Conversion of bucket hit failed\n");
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Get the function name.
|
|
//
|
|
|
|
Delimiter = GetNextToken(InputFile, Buffer);
|
|
if (Delimiter == EOF) {
|
|
fprintf(stderr, "ORDER: Premature end of file at profile name\n");
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// Lookup the function name in the function table and update the
|
|
// hit count.
|
|
//
|
|
|
|
FunctionNode = LookupFunctionNode(Buffer, 0, 0, 0);
|
|
if (FunctionNode == NULL) {
|
|
fprintf(stderr, "ORDER: Warning function name %s undefined\n", Buffer);
|
|
|
|
} else {
|
|
FunctionNode->HitCount += HitCount;
|
|
// FunctionNode->HitDensity = FunctionNode->HitCount;
|
|
FunctionNode->HitDensity =
|
|
(FunctionNode->HitCount * 100) / FunctionNode->Size;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
SortFunctionList (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sorts the function list by hit density and creates
|
|
the section list ordered by hit density.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PFUNCTION_NODE CallerList[MAXIMUM_FUNCTION];
|
|
PFUNCTION_NODE CallerNode;
|
|
PFUNCTION_NODE FunctionNode;
|
|
LONG i;
|
|
LONG j;
|
|
LONG k;
|
|
PSECTION_NODE InitNode;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
ULONG NumberCallers;
|
|
PSECTION_NODE SectionNode;
|
|
|
|
//
|
|
// All functions that are in the INIT section or cannot be placed are
|
|
// forced to have a hit density of zero.
|
|
//
|
|
|
|
InitNode = LookupSectionNode("INIT");
|
|
if (InitNode == NULL) {
|
|
fprintf(stderr, "ORDER: Warning - unable to find INIT section\n");
|
|
}
|
|
|
|
for (i = 0; i < (LONG)NumberFunctions; i += 1) {
|
|
FunctionNode = FunctionList[i];
|
|
SectionNode = FunctionNode->SectionNode;
|
|
if ((SectionNode == InitNode) ||
|
|
(FunctionNode->SynonymList[0].Type != 'C')) {
|
|
FunctionNode->HitDensity = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform a bubble sort on the function list hit density.
|
|
//
|
|
|
|
if (NumberFunctions > 1) {
|
|
i = 0;
|
|
do {
|
|
for (j = i; j >= 0; j -= 1) {
|
|
if (FunctionList[j]->HitDensity >= FunctionList[j + 1]->HitDensity) {
|
|
if (FunctionList[j]->HitDensity > FunctionList[j + 1]->HitDensity) {
|
|
break;
|
|
|
|
} else if (FunctionList[j]->Size >= FunctionList[j + 1]->Size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
FunctionNode = FunctionList[j];
|
|
FunctionList[j] = FunctionList[j + 1];
|
|
FunctionList[j + 1] = FunctionNode;
|
|
}
|
|
|
|
i += 1;
|
|
} while (i < (LONG)(NumberFunctions - 1));
|
|
}
|
|
|
|
//
|
|
// Perform a bubble sort on the caller list of each function.
|
|
//
|
|
|
|
for (k = 0; k < (LONG)NumberFunctions; k += 1) {
|
|
FunctionNode = FunctionList[i];
|
|
ListHead = &FunctionNode->CallerListHead;
|
|
ListEntry = ListHead->Flink;
|
|
i = 0;
|
|
while (ListEntry != ListHead) {
|
|
CallerList[i] = CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node;
|
|
i += 1;
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
|
|
if (i > 1) {
|
|
NumberCallers = i;
|
|
i = 0;
|
|
do {
|
|
for (j = i; j >= 0; j -= 1) {
|
|
if (CallerList[j]->HitDensity >= CallerList[j + 1]->HitDensity) {
|
|
if (CallerList[j]->HitDensity > CallerList[j + 1]->HitDensity) {
|
|
break;
|
|
|
|
} else if (CallerList[j]->Size >= CallerList[j + 1]->Size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
CallerNode = CallerList[j];
|
|
CallerList[j] = CallerList[j + 1];
|
|
CallerList[j + 1] = CallerNode;
|
|
}
|
|
|
|
i += 1;
|
|
} while (i < (LONG)(NumberCallers - 1));
|
|
|
|
ListEntry = FunctionNode->CallerListHead.Flink;
|
|
for (i = 0; i < (LONG)NumberCallers; i += 1) {
|
|
CONTAINING_RECORD(ListEntry, BACK_EDGE_NODE, Entry)->Node = CallerList[i];
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Compute the size of each section and create the section lists ordered
|
|
// by hit density.
|
|
//
|
|
|
|
for (i = 0; i < (LONG)NumberFunctions; i += 1) {
|
|
FunctionNode = FunctionList[i];
|
|
SectionNode = FunctionNode->SectionNode;
|
|
SectionNode->Size += FunctionNode->Size;
|
|
SectionNode->Number += 1;
|
|
InsertTailList(&SectionNode->SectionListHead,
|
|
&FunctionNode->SectionListEntry);
|
|
}
|
|
|
|
//
|
|
// Set the hit density threshold to zero.
|
|
//
|
|
|
|
|
|
for (i = 0; i < (LONG)NumberSections; i += 1) {
|
|
SectionList[i]->Threshold = 0;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
WriteOrderFile (
|
|
IN FILE *OutputFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function scans the section list and writes the link order file.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG Index;
|
|
PFUNCTION_NODE FunctionNode;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListHead;
|
|
PSECTION_NODE SectionNode;
|
|
|
|
//
|
|
// Scan the section list and write the link order list.
|
|
//
|
|
|
|
for (Index = 0; Index < NumberSections; Index += 1) {
|
|
SectionNode = SectionList[Index];
|
|
ListHead = &SectionNode->OrderListHead;
|
|
ListEntry = ListHead->Flink;
|
|
while (ListHead != ListEntry) {
|
|
FunctionNode = CONTAINING_RECORD(ListEntry,
|
|
FUNCTION_NODE,
|
|
OrderListEntry);
|
|
|
|
fprintf(OutputFile, "%s\n", FunctionNode->SynonymList[0].Name);
|
|
ListEntry = ListEntry->Flink;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|