Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / tools / r4300prof.c
CommitLineData
451ab91e 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - r4300prof.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2008 Richard Goedeken *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26/* Global data */
27unsigned int instr_samples[132];
28char instr_name[][10] =
29{
30 "reserved", "NI", "J", "JAL", "BEQ", "BNE", "BLEZ", "BGTZ",
31 "ADDI", "ADDIU", "SLTI", "SLTIU", "ANDI", "ORI", "XORI", "LUI",
32 "BEQL", "BNEL", "BLEZL", "BGTZL", "DADDI", "DADDIU", "LDL", "LDR",
33 "LB", "LH", "LW", "LWL", "LBU", "LHU", "LWU", "LWR",
34 "SB", "SH", "SW", "SWL", "SWR", "SDL", "SDR", "LWC1",
35 "LDC1", "LD", "LL", "SWC1", "SDC1", "SD", "SC", "BLTZ",
36 "BGEZ", "BLTZL", "BGEZL", "BLTZAL", "BGEZAL", "BLTZALL", "BGEZALL", "SLL",
37 "SRL", "SRA", "SLLV", "SRLV", "SRAV", "JR", "JALR", "SYSCALL",
38 "MFHI", "MTHI", "MFLO", "MTLO", "DSLLV", "DSRLV", "DSRAV", "MULT",
39 "MULTU", "DIV", "DIVU", "DMULT", "DMULTU", "DDIV", "DDIVU", "ADD",
40 "ADDU", "SUB", "SUBU", "AND", "OR", "XOR", "NOR", "SLT",
41 "SLTU", "DADD", "DADDU", "DSUB", "DSUBU", "DSLL", "DSRL", "DSRA",
42 "TEQ", "DSLL32", "DSRL32", "DSRA32", "BC1F", "BC1T", "BC1FL", "BC1TL",
43 "TLBWI", "TLBP", "TLBR", "TLBWR", "ERET", "MFC0", "MTC0", "MFC1",
44 "DMFC1", "CFC1", "MTC1", "DMTC1", "CTC1", "f.CVT", "f.CMP", "f.ADD",
45 "f.SUB", "f.MUL", "f.DIV", "f.SQRT", "f.ABS", "f.MOV", "f.NEG", "f.ROUND",
46 "f.TRUNC", "f.CEIL", "f.FLOOR"
47};
48unsigned int instr_type[131] = { 9, 10, 6, 6, 7, 7, 7, 7, 3, 3, 4, 4, 3, 4, 4, 0,
49 7, 7, 7, 7, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 7,
51 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 6, 6, 10,
52 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3,
53 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
54 8, 4, 4, 4, 7, 7, 7, 7, 10, 10, 10, 10, 8, 2, 2, 2,
55 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5,
56 5, 5, 5 };
57char instr_typename[][20] = { "Load", "Store", "Data move/convert", "32-bit math", "64-bit math", "Float Math",
58 "Jump", "Branch", "Exceptions", "Reserved", "Other" };
59
60/* Global functions */
61int GetInstrType(int opcode);
62int AddrCompare(const void *, const void *);
63int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage);
64
65/* defined types */
66typedef struct __attribute__ ((__packed__))
67{
68 int mipsop;
69 long x86addr;
70} r4300op;
71
72typedef struct
73{
74 long x86addr;
75 int samples;
76} profilehit;
77
78/* static functions */
79static int isSpace(char ch)
80{
81 return (ch == ' ' || ch == '\t' ? 1 : 0);
82}
83
84static int isNum(char ch)
85{
86 return (ch >= '0' && ch <= '9' ? 1 : 0);
87}
88
89static int isFloat(char ch)
90{
91 return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e' ? 1 : 0);
92}
93
94static int isHex(char ch)
95{
96 return ((ch >= '0' && ch <= '9') || ((ch & 0xdf) >= 'A' && (ch & 0xdf) <= 'F') ? 1 : 0);
97}
98
99/* main */
100int main(int argc, void *argv[])
101{
102 long lOpStart, lOpEnd;
103 int flength, oplistlength, totaltime, proflistlength;
104 int samp_unknown, samp_blockend, samp_notcompiled, samp_wrappers, samp_flush;
105 int i, j;
106 FILE *pfIn;
107 r4300op *pOpAddrTable;
108 profilehit *pProfTable;
109 char *pch, *pchSampleData;
110
111 /* check arguments */
112 if (argc < 3)
113 {
114 printf("Usage: r4300prof r4300addr.dat x86profile.txt\n\n");
115 printf("r4300addr.dat - binary table of r4300 opcodes and corresponding x86 starting addresses\n");
116 printf("x86profile.txt - text file containing a list of profile sample counts by x86 address on the heap\n\n");
117 return 1;
118 }
119
120 /* open r4300 opcode/x86 address table generated from emulator run */
121 printf("Loading %s...\n", argv[1]);
122 pfIn = fopen(argv[1], "rb");
123 if (pfIn == NULL)
124 {
125 printf("Couldn't open input file: %s\n", argv[1]);
126 return 2;
127 }
128
129 /* get file length and calculate number of r4300op table entries */
130 fseek(pfIn, 0L, SEEK_END);
131 flength = (int) ftell(pfIn);
132 fseek(pfIn, 0L, SEEK_SET);
133 oplistlength = flength / sizeof(r4300op);
134
135 /* read the file */
136 pOpAddrTable = (r4300op *) malloc(flength);
137 if (pOpAddrTable == NULL)
138 {
139 printf("Failed to allocate %i bytes for OpAddrTable!\n", flength);
140 fclose(pfIn);
141 return 3;
142 }
143 fread(pOpAddrTable, 1, flength, pfIn);
144 fclose(pfIn);
145 printf("%i r4300 instruction locations read.\n", oplistlength);
146
147 /* sort the opcode/address table according to x86addr */
148 qsort(pOpAddrTable, oplistlength, sizeof(r4300op), AddrCompare);
149
150 /* remove any 0-length r4300 instructions */
151 i = 0;
152 j = 0;
153 while (i < oplistlength)
154 {
155 pOpAddrTable[j].mipsop = pOpAddrTable[i].mipsop;
156 pOpAddrTable[j].x86addr = pOpAddrTable[i].x86addr;
157 i++;
158 if (pOpAddrTable[j].x86addr != pOpAddrTable[i].x86addr)
159 j++;
160 }
161 oplistlength = j;
162 printf("%i non-empty MIPS instructions.\n", oplistlength);
163
164 /* convert each r4300 opcode to an instruction type index */
165 for (i = 0; i < oplistlength; i++)
166 if (pOpAddrTable[i].mipsop > 0 || pOpAddrTable[i].mipsop < -16)
167 pOpAddrTable[i].mipsop = GetInstrType(pOpAddrTable[i].mipsop);
168
169 /* open the profiling sample data file */
170 printf("Loading %s...\n", argv[2]);
171 pfIn = fopen(argv[2], "rb");
172 if (pfIn == NULL)
173 {
174 printf("Couldn't open input file: %s\n", argv[2]);
175 free(pOpAddrTable);
176 return 4;
177 }
178
179 /* load it */
180 fseek(pfIn, 0L, SEEK_END);
181 flength = (int) ftell(pfIn);
182 fseek(pfIn, 0L, SEEK_SET);
183 pchSampleData = (char *) malloc(flength + 16);
184 if (pchSampleData == NULL)
185 {
186 printf("Failed to allocate %i bytes for pchSampleData!\n", flength + 16);
187 fclose(pfIn);
188 free(pOpAddrTable);
189 return 5;
190 }
191 fread(pchSampleData, 1, flength, pfIn);
192 pchSampleData[flength] = 0;
193 fclose(pfIn);
194
195 /* count the number of newlines in the ascii-formatted sample data file */
196 proflistlength = 1;
197 pch = pchSampleData;
198 while (pch = strchr(pch, '\n'))
199 {
200 proflistlength++;
201 pch++;
202 }
203 printf("%i lines in sample data file.\n", proflistlength);
204
205 /* extract text data into binary table */
206 pProfTable = (profilehit *) malloc(proflistlength * sizeof(profilehit));
207 if (pProfTable == NULL)
208 {
209 printf("Failed to allocate %i bytes for pProfTable!\n", proflistlength * sizeof(profilehit));
210 free(pOpAddrTable);
211 free(pchSampleData);
212 return 6;
213 }
214 pch = pchSampleData;
215 j = 0;
216 long long llOffset = 0;
217 while (j < proflistlength)
218 {
219 long lAddress;
220 int iSamples;
221 float fPercentage;
222 char *pchNext = strchr(pch, '\n');
223 if (pchNext != NULL) *pchNext++ = 0; // null-terminate this line
224 if (strstr(pch, "range:0x") != NULL) // search for offset change
225 {
226 pch = strstr(pch, "range:0x") + 8; // extract hex value and update our offset
227 char *pch2 = pch;
228 while (isHex(*pch2)) pch2++;
229 *pch2 = 0;
230 llOffset = strtoll(pch, NULL, 16);
231 }
232 else // parse line for sample point
233 {
234 int rval = ParseProfLine(pch, &lAddress, &iSamples, &fPercentage);
235 if (rval != 0)
236 {
237 pProfTable[j].x86addr = (unsigned long) (lAddress + llOffset);
238 pProfTable[j].samples = iSamples;
239 j++;
240 }
241 }
242 pch = pchNext;
243 if (pch == NULL) break;
244 }
245 free(pchSampleData);
246 proflistlength = j;
247 printf("Found %i profile hits.\n", proflistlength);
248
249 /* clear r4300 instruction sample data table */
250 for (i = 0; i < 132; i++)
251 instr_samples[i] = 0;
252
253 /* calculate r4300 instruction profiling data by merging the tables */
254 samp_unknown = 0;
255 samp_blockend = 0;
256 samp_notcompiled = 0;
257 samp_wrappers = 0;
258 samp_flush = 0;
259 i = 0; // i == OpAddrTable index
260 lOpStart = pOpAddrTable[0].x86addr;
261 lOpEnd = pOpAddrTable[1].x86addr;
262 for (j = 0; j < proflistlength; j++) // j == pProfTable index
263 {
264 long lOpx86addr = pProfTable[j].x86addr;
265 if (lOpx86addr >= lOpStart && lOpx86addr <= lOpEnd) /* these profile samples lie within current r4300 instruction */
266 {
267 int instr = pOpAddrTable[i].mipsop;
268 if (instr == -1) printf("%lx sample point lies between %i/%lx and %i/%lx\n", lOpx86addr, instr, lOpStart, pOpAddrTable[i+1].mipsop, lOpEnd);
269
270 if (instr == -1)
271 samp_unknown += pProfTable[j].samples;
272 else if (instr == -2)
273 samp_notcompiled += pProfTable[j].samples;
274 else if (instr == -3)
275 samp_blockend += pProfTable[j].samples;
276 else if (instr == -4)
277 samp_wrappers += pProfTable[j].samples;
278 else if (instr == -5)
279 samp_flush += pProfTable[j].samples;
280 else
281 instr_samples[instr] += pProfTable[j].samples;
282 continue;
283 }
284 if (lOpx86addr < pOpAddrTable[0].x86addr || lOpx86addr >= pOpAddrTable[oplistlength-1].x86addr)
285 { /* outside the range of all recompiled instructions */
286 samp_unknown += pProfTable[j].samples;
287 continue;
288 }
289 if (lOpx86addr < lOpStart) /* discontinuity in profile list, go back to start */
290 {
291 i = 0;
292 lOpStart = pOpAddrTable[0].x86addr;
293 lOpEnd = pOpAddrTable[1].x86addr;
294 j--;
295 continue;
296 }
297 /* this profile point is ahead of current r4300 instruction */
298 do /* race ahead in r4300 opcode list until we hit this profile sample point */
299 {
300 i++;
301 } while (i+1 < oplistlength && lOpx86addr > pOpAddrTable[i+1].x86addr);
302 lOpStart = pOpAddrTable[i].x86addr;
303 lOpEnd = pOpAddrTable[i+1].x86addr;
304 if (lOpx86addr < lOpStart || lOpx86addr > lOpEnd)
305 {
306 printf("Error: lOpx86addr = %lx but lOpStart, lOpEnd = %lx, %lx\n", lOpx86addr, lOpStart, lOpEnd);
307 return 7;
308 }
309 /* we have found the correct r4300 instruction corresponding to this profile point */
310 j--;
311 }
312
313 /* print the results */
314 unsigned int iTypeCount[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
315 printf("\nInstruction time (samples):\n");
316 totaltime = 0;
317 for (i = 0; i < 131; i++)
318 {
319 printf("%8s: %08i ", instr_name[i], instr_samples[i]);
320 if (i % 5 == 4) printf("\n");
321 iTypeCount[instr_type[i]] += instr_samples[i];
322 totaltime += instr_samples[i];
323 }
324 int special = samp_flush + samp_wrappers + samp_notcompiled + samp_blockend;
325 printf("\n\nSpecial code samples:\n");
326 printf(" Regcache flushing: %i\n", samp_flush);
327 printf(" Jump wrappers: %i\n", samp_wrappers);
328 printf(" NOTCOMPILED: %i\n", samp_notcompiled);
329 printf(" block postfix & link samples: %i\n", samp_blockend);
330
331 printf("\nUnaccounted samples: %i\n", samp_unknown);
332 printf("Total accounted instruction samples: %i\n", totaltime + special);
333 for (i = 0; i < 11; i++)
334 {
335 printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / totaltime, iTypeCount[i]);
336 }
337
338 free(pOpAddrTable);
339 free(pProfTable);
340 return 0;
341}
342
343int AddrCompare(const void *p1, const void *p2)
344{
345 const r4300op *pOp1 = (const r4300op *) p1;
346 const r4300op *pOp2 = (const r4300op *) p2;
347
348 if (pOp1->x86addr < pOp2->x86addr)
349 return -1;
350 else if (pOp1->x86addr == pOp2->x86addr)
351 return (int) (pOp1 - pOp2); /* this forces qsort to be stable */
352 else
353 return 1;
354}
355
356int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage)
357{
358 char chVal[128], *pchOut;
359
360 /* skip any initial whitespace */
361 while (isSpace(*pchIn)) pchIn++;
362 if (!isHex(*pchIn)) return 0;
363
364 /* parse hexadecimal address value */
365 pchOut = chVal;
366 while (isHex(*pchIn)) *pchOut++ = *pchIn++;
367 *pchOut = 0;
368 if (!isSpace(*pchIn)) return 0;
369 *plAddress = strtol(chVal, NULL, 16);
370
371 /* skip more whitespace */
372 while (isSpace(*pchIn)) pchIn++;
373 if (!isNum(*pchIn)) return 0;
374
375 /* parse decimal sample count value */
376 pchOut = chVal;
377 while (isNum(*pchIn)) *pchOut++ = *pchIn++;
378 *pchOut = 0;
379 if (!isSpace(*pchIn)) return 0;
380 *piSamples = atoi(chVal);
381
382 /* skip more whitespace */
383 while (isSpace(*pchIn)) pchIn++;
384 if (!isFloat(*pchIn)) return 0;
385
386 /* parse floating-point percentage value */
387 pchOut = chVal;
388 while (isFloat(*pchIn)) *pchOut++ = *pchIn++;
389 *pchOut = 0;
390 if (!isSpace(*pchIn) && *pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;
391 *pfPercentage = atof(chVal);
392
393 /* if this isn't the end of the line, it's not a valid sample point */
394 while (isSpace(*pchIn)) pchIn++;
395 if (*pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;
396
397 return 1;
398}
399
400static int InstrTypeStd[64] =
401{
402 -1, -1, 02, 03, 04, 05, 06, 07, 8, 9, 10, 11, 12, 13, 14, 15,
403 -1, -1, 00, 00, 16, 17, 18, 19, 20, 21, 22, 23, 00, 00, 00, 00,
404 24, 25, 27, 26, 28, 29, 31, 30, 32, 33, 35, 34, 37, 38, 36, 01,
405 42, 39, 00, 00, 01, 40, 00, 41, 46, 43, 00, 00, 01, 44, 00, 45
406};
407
408static int InstrTypeSpecial[64] =
409{
410 55, 00, 56, 57, 58, 00, 59, 60,
411 61, 62, 00, 00, 63, 01, 00, 00,
412 64, 65, 66, 67, 68, 00, 69, 70,
413 71, 72, 73, 74, 75, 76, 77, 78,
414 79, 80, 81, 82, 83, 84, 85, 86,
415 00, 00, 87, 88, 89, 90, 91, 92,
416 01, 01, 01, 01, 96, 00, 01, 00,
417 93, 00, 94, 95, 97, 00, 98, 99
418};
419
420static int InstrTypeRegImm[32] =
421{
422 47, 48, 49, 50, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 01, 00,
423 51, 52, 53, 54, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
424};
425
426static int InstrTypeCop1[32] =
427{
428 111, 112, 113, 00, 114, 115, 116, 00,
429 -1, 00, 00, 00, 00, 00, 00, 00,
430 -1, -1, 00, 00, -1, -1, 00, 00,
431 00, 00, 00, 00, 00, 00, 00, 00
432};
433
434static int InstrTypeCop1Math[64] =
435{
436 119, 120, 121, 122, 123, 124, 125, 126,
437 127, 128, 129, 130, 127, 128, 129, 130,
438 00, 00, 00, 00, 00, 00, 00, 00,
439 00, 00, 00, 00, 00, 00, 00, 00,
440 117, 117, 00, 00, 117, 117, 00, 00,
441 00, 00, 00, 00, 00, 00, 00, 00,
442 118, 118, 118, 118, 118, 118, 118, 118,
443 118, 118, 118, 118, 118, 118, 118, 118
444};
445
446
447int GetInstrType(int opcode)
448{
449 int iType = (opcode >> 26) & 63;
450
451 if (iType == 0)
452 {
453 /* SPECIAL instruction */
454 iType = opcode & 63;
455 return InstrTypeSpecial[iType];
456 }
457 else if (iType == 1)
458 {
459 /* REGIMM instruction */
460 iType = (opcode >> 16) & 31;
461 return InstrTypeRegImm[iType];
462 }
463 else if (iType == 16)
464 {
465 /* COP0 instruction */
466 int iType1 = opcode & 0x01FFFFFF;
467 int iType2 = (opcode >> 21) & 31;
468 if (iType1 == 1)
469 return 106; // TLBR
470 else if (iType1 == 2)
471 return 104; // TLBWI
472 else if (iType1 == 6)
473 return 107; // TLBWR
474 else if (iType1 == 8)
475 return 105; // TLBP
476 else if (iType1 == 24)
477 return 108; // ERET
478 else if ((opcode & 0x7FF) == 0 && iType2 == 0)
479 return 109; // MFC0
480 else if ((opcode & 0x7FF) == 0 && iType2 == 4)
481 return 110; // MTC0
482 else
483 return 0; // reserved
484 }
485 else if (iType == 17)
486 {
487 /* COP1 instruction */
488 int iType1 = (opcode >> 21) & 31;
489 if (iType1 == 8)
490 {
491 /* conditional branch */
492 int iType2 = (opcode >> 16) & 31;
493 if (iType2 == 0)
494 return 100; // BC1F
495 else if (iType2 == 1)
496 return 101; // BC1T
497 else if (iType2 == 2)
498 return 102; // BC1FL
499 else if (iType2 == 3)
500 return 103; // BC1TL
501 else
502 return 0; // reserved
503 }
504 else if (iType1 == 16 || iType1 == 17 || iType1 == 20 || iType1 == 21)
505 {
506 /* Single, Double, Word, Long instructions */
507 int iType2 = opcode & 63;
508 return InstrTypeCop1Math[iType2];
509 }
510 else
511 {
512 /* other Cop1 (move) */
513 return InstrTypeCop1[iType1];
514 }
515 }
516
517 /* standard MIPS instruction */
518 return InstrTypeStd[iType];
519}
520