/* ** Command & Conquer Red Alert(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Library profiler * * * * File Name : PROFILE.CPP * * * * Programmer : Steve Tall * * * * Start Date : 11/17/95 * * * * Last Update : November 20th 1995 [ST] * * * *---------------------------------------------------------------------------------------------* * Overview: * * Uses a map file to match addresses of functions in the sample file with their names * * * * * *---------------------------------------------------------------------------------------------* * * * Functions: * * Start_Profiler -- initialises the profiler data and starts gathering data * * Stop_Profiler -- stops the timer and writes the profile data to disk * * * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include #include #include #include #include #include #include #define bool int #define true 1 #define false 0 #define NAME_TABLE_SIZE 1000000 //Storage space for function names #define SAMPLE_START 1 //Offset (in dwords) of sample data in sample file /* ** Function prototypes */ void Print_My_Name(void); void Print_Usage(void); int Load_File(char *file_name , unsigned *file_ptr , unsigned mode); bool Extract_Function_Addresses(void); unsigned Get_Hex (char string[] , int length); char *Search_For_Char (char character , char buffer_ptr[] , int buffer_length); char *Search_For_String (char *string , char *buffer_ptr , int buffer_length); void Map_Profiler_Hits (void); void Sort_Functions(void); void Sort_Functions_Again(void); void Output_Profile(void); char *SampleFile; //Ptr to sample file name char *MapFile; //Ptr to map file name unsigned *SampleFileBuffer; //Ptr to buffer that sample file is loaded in to char *MapFileBuffer; //Ptr to buffer that map file is loaded in to unsigned SampleFileLength; //Length of sample file unsigned MapFileLength; //Length of map file char FunctionNames[NAME_TABLE_SIZE]; //Buffer to store function names in char *FunctionNamePtr=&FunctionNames[0]; //Ptr to end of last function name in buffer int TotalFunctions; //Total number of functions extracted from map file int SampleRate; //Number of samples/sec that data was collected at unsigned EndCodeSegment; //Length of the sampled programs code segments /* ** Structure for collating function data */ typedef struct tFunction { unsigned FunctionAddress; //Address of function relative to start of code seg char *FunctionName; //Ptr to name of function in FunctionNames buffer int Hits; //Number of times function was 'hit' when sampling } Function; Function FunctionList[10000]; //max 10,000 functions in map file. /*********************************************************************************************** * main -- program entry point * * * * * * * * INPUT: argc , argv * * * * OUTPUT: 0 * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:21PM ST : Created * *=============================================================================================*/ int main(int argc, char *argv[]) { Print_My_Name(); // print the programs name /* ** If the arguments dont make sense then print the usage */ if (argc!=3 || !strcmpi(argv[1],"/?") || !strcmpi(argv[1],"/h") || !strcmpi(argv[1],"/help") || !strcmpi(argv[1],"-?") || !strcmpi(argv[1],"-h") || !strcmpi(argv[1],"-help") || !strcmpi(argv[1],"?") || !strcmpi(argv[1],"h") || !strcmpi(argv[1],"help")){ Print_Usage(); return(0); } /* ** Get the names of the files to load */ SampleFile=argv[1]; MapFile=argv[2]; /* ** Load the profile sample file */ SampleFileLength = Load_File (SampleFile , (unsigned*)&SampleFileBuffer , O_BINARY); if (!SampleFileLength) return(0); /* ** The sample rate is the 1st dword in the file */ SampleRate=*SampleFileBuffer; /* ** Load the .map file */ MapFileLength = Load_File (MapFile , (unsigned*)&MapFileBuffer , O_BINARY); if (!MapFileLength){ free (SampleFileBuffer); return(0); } /* ** Get the function names from the map file */ cprintf ("Extracting function data from map file.\n"); if (!Extract_Function_Addresses()){ cprintf ("Error parsing .MAP file - aborting\n\n"); return (0); } /* ** Sort the functions into address order to make it easier to map the functions */ cprintf ("Sorting function list by address"); Sort_Functions(); /* ** Map the addresses in the sample file to the function addresses */ cprintf ("\nMapping profiler hits to functions"); Map_Profiler_Hits(); /* ** Sort the functions into order of usage for output */ cprintf ("\nSorting function list by activity"); Sort_Functions_Again(); cprintf ("\n\n"); /* ** Print the function usage statistics */ Output_Profile(); /* ** Cleanup and out */ free (SampleFileBuffer); free (MapFileBuffer); return(0); } /*********************************************************************************************** * Print_My_Name -- print the programs name and version * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:25PM ST : Created * *=============================================================================================*/ void Print_My_Name(void) { cprintf("Westwood profile data analyzer.\n"); cprintf("V 1.0 - 11/17/95\n"); cprintf("Programmer - Steve Tall.\n\n"); } /*********************************************************************************************** * Print_Usage -- print the instructions * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:26PM ST : Created * *=============================================================================================*/ void Print_Usage (void) { cprintf("Usage: PROFILE =0 ; j--){ if (FunctionList[j].FunctionAddress < function_hit){ FunctionList[j].Hits++; break; } } } } /*********************************************************************************************** * Sort_Functions -- hideous bubble sort of functions into address order * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:29PM ST : Created * *=============================================================================================*/ void Sort_Functions (void) { Function address_swap; if (TotalFunctions>1){ for (int outer=0 ; outer FunctionList[inner+1].FunctionAddress ){ memcpy (&address_swap , &FunctionList[inner] , sizeof(Function)); memcpy (&FunctionList[inner] , &FunctionList[inner+1] , sizeof(Function)); memcpy (&FunctionList[inner+1] , &address_swap , sizeof(Function)); } } if (!address_swap.FunctionAddress) break; } } } /*********************************************************************************************** * Sort_Functions -- hideous bubble sort of functions into usage order * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:29PM ST : Created * *=============================================================================================*/ void Sort_Functions_Again (void) { Function address_swap; if (TotalFunctions>1){ for (int outer=0 ; outer EndCodeSegment){ EndCodeSegment = code_segment_start+code_segment_size; } chars_left = end_str_ptr - segment_ptr; segment_ptr = Search_For_Char ( 13 , segment_ptr , chars_left ); chars_left = end_str_ptr - segment_ptr; } while (chars_left > 0); chars_left=MapFileLength; /* ** Search for the 'Memory Map' segment of the map file */ map_ptr = Search_For_String ("Memory Map" , MapFileBuffer , chars_left); if (!map_ptr){ return (false); } chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); /* ** Get the length of the memory map segment by searching for the start of the next segment */ end_str_ptr = Search_For_String ("+-----" , map_ptr , chars_left); if (end_str_ptr){ MapFileLength = ((unsigned)MapFileBuffer + MapFileLength) - (unsigned)end_str_ptr; } chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); /* ** Reset the total number of functions found */ TotalFunctions = 0; /* ** ** Find each occurrence of 0001: as all the functions we want are in the 1st segment ** */ do { /* ** Find '0001:' */ map_ptr = Search_For_String ("0001:" , map_ptr , chars_left); if (!map_ptr){ break; } chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); /* ** Skip the '0001:' portion of the address and get the hext offset of the function */ map_ptr+=5; FunctionList[TotalFunctions].FunctionAddress=Get_Hex(map_ptr,8); /* ** Skip to the function name and get its length by searching for the end of the line */ map_ptr+=10; chars_left = MapFileLength - ( (unsigned)map_ptr - (unsigned)MapFileBuffer ); end_str_ptr = Search_For_Char (13 , map_ptr , chars_left); if (!end_str_ptr){ break; } function_name_length = (unsigned)end_str_ptr - (unsigned)map_ptr; /* ** Copy the function name into the name list and keep a pointer to it */ memcpy (FunctionNamePtr , map_ptr , function_name_length); FunctionList[TotalFunctions].FunctionName = FunctionNamePtr; FunctionNamePtr += function_name_length+1; //Leave an extra 0 on the end as a terminator FunctionList[TotalFunctions].Hits = 0; //We dont yet know how many times we hit it TotalFunctions++; } while (1); /* ** Add in a dummy function at the highest address to represent unknown code hits */ FunctionList[TotalFunctions].FunctionAddress = EndCodeSegment; memcpy (FunctionNamePtr , &unknown , sizeof (unknown)); FunctionList[TotalFunctions].FunctionName = FunctionNamePtr; FunctionNamePtr += sizeof (unknown); FunctionList[TotalFunctions].Hits = 0; TotalFunctions++; return (true); } /*********************************************************************************************** * Get_Hex -- nasty function to convert an ascii hex number to an unsigned int * * I'm sure there must be a lovely 'c' way of doing this but I dont know what it is * * * * * * INPUT: ptr to ascii hex string , number of digits in string * * * * OUTPUT: value of hex string * * * * WARNINGS: None * * * * HISTORY: * * 11/20/95 5:39PM ST : Created * *=============================================================================================*/ unsigned Get_Hex (char string[] , int length) { unsigned hex_val=0; int multiplier=1; char hex_char; for (int i=0 ; i