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