Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1804 lines
45 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
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 = fgetc(InputFile);
if ((Character != ' ') &&
(Character != '\t')) {
break;
}
} while(TRUE);
do {
if ((Character == EOF) ||
(Character == ' ') ||
(Character == '\n') ||
(Character == '\t')) {
break;
}
*TokenBuffer++ = Character;
Character = 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;
}