/*
** 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