mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-01-15 07:21:07 +00:00
301 lines
6.8 KiB
C
301 lines
6.8 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
char *name;
|
||
|
int offset;
|
||
|
int count;
|
||
|
float percent;
|
||
|
}
|
||
|
Profile_Entry;
|
||
|
|
||
|
static const char *profile_filename;
|
||
|
static const char *map_filename;
|
||
|
static int granularity;
|
||
|
static int *profile;
|
||
|
static int profile_len;
|
||
|
static Profile_Entry *functionTable;
|
||
|
static int numFunctions;
|
||
|
static int maxFunctions;
|
||
|
|
||
|
static void read_args(int argc, const char *argv[])
|
||
|
{
|
||
|
if (argc < 3)
|
||
|
{
|
||
|
fprintf(stderr, "annotate <profile file> <map file>\n");
|
||
|
fclose(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
profile_filename = argv[1];
|
||
|
map_filename = argv[2];
|
||
|
}
|
||
|
|
||
|
static void read_profile()
|
||
|
{
|
||
|
FILE *in = fopen(profile_filename, "rb");
|
||
|
|
||
|
if (in == NULL)
|
||
|
{
|
||
|
fprintf(stderr, "Failed to open profile file '%s'\n",
|
||
|
profile_filename);
|
||
|
fclose(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
fseek(in, 0, SEEK_END);
|
||
|
profile_len = (int)ftell(in)-8;
|
||
|
fseek(in, 0, SEEK_SET);
|
||
|
|
||
|
if ((fgetc(in) != 'P') ||
|
||
|
(fgetc(in) != 'R') ||
|
||
|
(fgetc(in) != '0') ||
|
||
|
(fgetc(in) != 'F'))
|
||
|
{
|
||
|
fclose(in);
|
||
|
fprintf(stderr, "'%s' is not a profile file\n",
|
||
|
profile_filename);
|
||
|
fclose(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
fread(&granularity, 4, 1, in);
|
||
|
profile = malloc(profile_len);
|
||
|
if (profile == NULL)
|
||
|
{
|
||
|
fclose(in);
|
||
|
fprintf(stderr, "Out of memory reading profile\n");
|
||
|
fclose(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
fread(profile, 4, profile_len>>2, in);
|
||
|
fclose(in);
|
||
|
}
|
||
|
|
||
|
static void addFn(const char *text, int offset)
|
||
|
{
|
||
|
if (numFunctions == maxFunctions)
|
||
|
{
|
||
|
int newSize = maxFunctions*2;
|
||
|
|
||
|
if (newSize == 0)
|
||
|
newSize = 128;
|
||
|
|
||
|
functionTable = realloc(functionTable,
|
||
|
newSize*sizeof(Profile_Entry));
|
||
|
if (functionTable == NULL)
|
||
|
{
|
||
|
fprintf(stderr, "Out of memory reading mapfile\n");
|
||
|
fflush(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
maxFunctions = newSize;
|
||
|
}
|
||
|
|
||
|
functionTable[numFunctions].name = malloc(strlen(text)+1);
|
||
|
strcpy(functionTable[numFunctions].name, text);
|
||
|
functionTable[numFunctions].offset = offset;
|
||
|
functionTable[numFunctions].count = 0;
|
||
|
functionTable[numFunctions].percent = 0.0;
|
||
|
//fprintf(stdout, "%s %x\n", functionTable[numFunctions].name,
|
||
|
// functionTable[numFunctions].offset);
|
||
|
numFunctions++;
|
||
|
}
|
||
|
|
||
|
static void read_map()
|
||
|
{
|
||
|
FILE *in = fopen(map_filename, "rb");
|
||
|
char text[2048];
|
||
|
|
||
|
if (in == NULL)
|
||
|
{
|
||
|
fprintf(stderr, "Failed to open map file '%s'\n",
|
||
|
map_filename);
|
||
|
fclose(stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
functionTable = NULL;
|
||
|
numFunctions = 0;
|
||
|
maxFunctions = 0;
|
||
|
|
||
|
addFn("Address 0", 0);
|
||
|
|
||
|
while (!feof(in))
|
||
|
{
|
||
|
int offset;
|
||
|
char c;
|
||
|
char *t;
|
||
|
|
||
|
/* Skip over any whitespace */
|
||
|
do
|
||
|
{
|
||
|
c = fgetc(in);
|
||
|
}
|
||
|
while (((c == 32) || (c == 9)) && (!feof(in)));
|
||
|
ungetc(c, in);
|
||
|
|
||
|
/* Try to read an offset */
|
||
|
if (fscanf(in, "0x%x", &offset) != 1)
|
||
|
{
|
||
|
goto over;
|
||
|
}
|
||
|
/* Skip over any whitespace */
|
||
|
do
|
||
|
{
|
||
|
c = fgetc(in);
|
||
|
}
|
||
|
while ((c == 32) && (!feof(in)));
|
||
|
ungetc(c, in);
|
||
|
|
||
|
/* Names never start with . or (*/
|
||
|
if ((c != '_') &&
|
||
|
((c < 'a') || (c > 'z')) &&
|
||
|
((c < 'A') || (c > 'Z')))
|
||
|
goto over;
|
||
|
|
||
|
/* Read the name */
|
||
|
t = text;
|
||
|
do
|
||
|
{
|
||
|
c = fgetc(in);
|
||
|
*t++ = c;
|
||
|
}
|
||
|
while (c > 32);
|
||
|
t[-1] = 0;
|
||
|
|
||
|
/* Now there should be nothing left on this line */
|
||
|
if ((c != 10) && (c != 13))
|
||
|
goto over;
|
||
|
|
||
|
/* And put the return back */
|
||
|
ungetc(c, in);
|
||
|
|
||
|
if (t != text)
|
||
|
{
|
||
|
addFn(text, offset);
|
||
|
}
|
||
|
|
||
|
over:
|
||
|
/* Skip to the end of the line */
|
||
|
do
|
||
|
{
|
||
|
c = fgetc(in);
|
||
|
}
|
||
|
while ((c >= 32) && (!feof(in)));
|
||
|
|
||
|
/* Skip over any newlines */
|
||
|
while (((c == 10) || (c == 13)) && (!feof(in)))
|
||
|
{
|
||
|
c = fgetc(in);
|
||
|
}
|
||
|
|
||
|
/* And put the first non whitespace/non return char back */
|
||
|
ungetc(c, in);
|
||
|
}
|
||
|
|
||
|
fclose(in);
|
||
|
}
|
||
|
|
||
|
static void show_profile()
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i < numFunctions; i++)
|
||
|
{
|
||
|
fprintf(stdout, "%08x (%6.2f%%: %6d) %s\n",
|
||
|
functionTable[i].offset,
|
||
|
functionTable[i].percent,
|
||
|
functionTable[i].count,
|
||
|
functionTable[i].name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int byAddress(const void *_e1, const void *_e2)
|
||
|
{
|
||
|
const Profile_Entry *e1 = (const Profile_Entry *)_e1;
|
||
|
const Profile_Entry *e2 = (const Profile_Entry *)_e2;
|
||
|
|
||
|
return e1->offset - e2->offset;
|
||
|
}
|
||
|
|
||
|
int byTime(const void *_e1, const void *_e2)
|
||
|
{
|
||
|
const Profile_Entry *e1 = (const Profile_Entry *)_e1;
|
||
|
const Profile_Entry *e2 = (const Profile_Entry *)_e2;
|
||
|
|
||
|
return e2->count - e1->count;
|
||
|
}
|
||
|
|
||
|
static void process_profile()
|
||
|
{
|
||
|
int next;
|
||
|
int fn;
|
||
|
int idx;
|
||
|
int max;
|
||
|
int total;
|
||
|
|
||
|
/* Sort into address order */
|
||
|
qsort(functionTable,
|
||
|
numFunctions,
|
||
|
sizeof(Profile_Entry),
|
||
|
byAddress);
|
||
|
|
||
|
/* Run through the profile adding it to the appropriate function */
|
||
|
fn = -1; /* Which function are we looking at */
|
||
|
next = -1; /* At what address should we move to the next function */
|
||
|
idx = 0; /* Where are we in the profile */
|
||
|
max = profile_len>>2;
|
||
|
total = 0;
|
||
|
for (idx = 0; idx < max; idx++)
|
||
|
{
|
||
|
while ((idx<<(granularity+2)) >= next)
|
||
|
{
|
||
|
/* Move to the next function */
|
||
|
fn++;
|
||
|
//fprintf(stdout, "Will be on fn %s until we pass %x\n",
|
||
|
// functionTable[fn].name, functionTable[fn+1].offset);
|
||
|
next = 0x7FFFFFFF;
|
||
|
if (fn+1 < numFunctions)
|
||
|
{
|
||
|
next = functionTable[fn+1].offset;
|
||
|
}
|
||
|
}
|
||
|
//fprintf(stdout, "fn=%d count=%d idx=%d next=%x\n",
|
||
|
// fn, functionTable[fn].count, idx, next);
|
||
|
functionTable[fn].count += profile[idx];
|
||
|
total += profile[idx];
|
||
|
}
|
||
|
|
||
|
for (fn = 0; fn < numFunctions; fn++)
|
||
|
{
|
||
|
functionTable[fn].percent = 100.0*functionTable[fn].count/total;
|
||
|
}
|
||
|
|
||
|
fprintf(stdout, "Profile by Address\n");
|
||
|
show_profile();
|
||
|
|
||
|
/* Sort into time order */
|
||
|
qsort(functionTable,
|
||
|
numFunctions,
|
||
|
sizeof(Profile_Entry),
|
||
|
byTime);
|
||
|
|
||
|
fprintf(stdout, "\n\n");
|
||
|
fprintf(stdout, "Profile by Time\n");
|
||
|
show_profile();
|
||
|
}
|
||
|
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
read_args(argc, argv);
|
||
|
|
||
|
read_profile();
|
||
|
read_map();
|
||
|
|
||
|
process_profile();
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|