|
|
/*++
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; }
|