1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - r4300prof.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2008 Richard Goedeken *
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. *
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. *
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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27 unsigned int instr_samples[132];
28 char instr_name[][10] =
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"
48 unsigned 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,
57 char instr_typename[][20] = { "Load", "Store", "Data move/convert", "32-bit math", "64-bit math", "Float Math",
58 "Jump", "Branch", "Exceptions", "Reserved", "Other" };
60 /* Global functions */
61 int GetInstrType(int opcode);
62 int AddrCompare(const void *, const void *);
63 int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage);
66 typedef struct __attribute__ ((__packed__))
78 /* static functions */
79 static int isSpace(char ch)
81 return (ch == ' ' || ch == '\t' ? 1 : 0);
84 static int isNum(char ch)
86 return (ch >= '0' && ch <= '9' ? 1 : 0);
89 static int isFloat(char ch)
91 return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e' ? 1 : 0);
94 static int isHex(char ch)
96 return ((ch >= '0' && ch <= '9') || ((ch & 0xdf) >= 'A' && (ch & 0xdf) <= 'F') ? 1 : 0);
100 int main(int argc, void *argv[])
102 long lOpStart, lOpEnd;
103 int flength, oplistlength, totaltime, proflistlength;
104 int samp_unknown, samp_blockend, samp_notcompiled, samp_wrappers, samp_flush;
107 r4300op *pOpAddrTable;
108 profilehit *pProfTable;
109 char *pch, *pchSampleData;
111 /* check arguments */
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");
120 /* open r4300 opcode/x86 address table generated from emulator run */
121 printf("Loading %s...\n", argv[1]);
122 pfIn = fopen(argv[1], "rb");
125 printf("Couldn't open input file: %s\n", argv[1]);
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);
136 pOpAddrTable = (r4300op *) malloc(flength);
137 if (pOpAddrTable == NULL)
139 printf("Failed to allocate %i bytes for OpAddrTable!\n", flength);
143 fread(pOpAddrTable, 1, flength, pfIn);
145 printf("%i r4300 instruction locations read.\n", oplistlength);
147 /* sort the opcode/address table according to x86addr */
148 qsort(pOpAddrTable, oplistlength, sizeof(r4300op), AddrCompare);
150 /* remove any 0-length r4300 instructions */
153 while (i < oplistlength)
155 pOpAddrTable[j].mipsop = pOpAddrTable[i].mipsop;
156 pOpAddrTable[j].x86addr = pOpAddrTable[i].x86addr;
158 if (pOpAddrTable[j].x86addr != pOpAddrTable[i].x86addr)
162 printf("%i non-empty MIPS instructions.\n", oplistlength);
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);
169 /* open the profiling sample data file */
170 printf("Loading %s...\n", argv[2]);
171 pfIn = fopen(argv[2], "rb");
174 printf("Couldn't open input file: %s\n", argv[2]);
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)
186 printf("Failed to allocate %i bytes for pchSampleData!\n", flength + 16);
191 fread(pchSampleData, 1, flength, pfIn);
192 pchSampleData[flength] = 0;
195 /* count the number of newlines in the ascii-formatted sample data file */
198 while (pch = strchr(pch, '\n'))
203 printf("%i lines in sample data file.\n", proflistlength);
205 /* extract text data into binary table */
206 pProfTable = (profilehit *) malloc(proflistlength * sizeof(profilehit));
207 if (pProfTable == NULL)
209 printf("Failed to allocate %i bytes for pProfTable!\n", proflistlength * sizeof(profilehit));
216 long long llOffset = 0;
217 while (j < proflistlength)
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
226 pch = strstr(pch, "range:0x") + 8; // extract hex value and update our offset
228 while (isHex(*pch2)) pch2++;
230 llOffset = strtoll(pch, NULL, 16);
232 else // parse line for sample point
234 int rval = ParseProfLine(pch, &lAddress, &iSamples, &fPercentage);
237 pProfTable[j].x86addr = (unsigned long) (lAddress + llOffset);
238 pProfTable[j].samples = iSamples;
243 if (pch == NULL) break;
247 printf("Found %i profile hits.\n", proflistlength);
249 /* clear r4300 instruction sample data table */
250 for (i = 0; i < 132; i++)
251 instr_samples[i] = 0;
253 /* calculate r4300 instruction profiling data by merging the tables */
256 samp_notcompiled = 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
264 long lOpx86addr = pProfTable[j].x86addr;
265 if (lOpx86addr >= lOpStart && lOpx86addr <= lOpEnd) /* these profile samples lie within current r4300 instruction */
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);
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;
281 instr_samples[instr] += pProfTable[j].samples;
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;
289 if (lOpx86addr < lOpStart) /* discontinuity in profile list, go back to start */
292 lOpStart = pOpAddrTable[0].x86addr;
293 lOpEnd = pOpAddrTable[1].x86addr;
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 */
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)
306 printf("Error: lOpx86addr = %lx but lOpStart, lOpEnd = %lx, %lx\n", lOpx86addr, lOpStart, lOpEnd);
309 /* we have found the correct r4300 instruction corresponding to this profile point */
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");
317 for (i = 0; i < 131; i++)
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];
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);
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++)
335 printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / totaltime, iTypeCount[i]);
343 int AddrCompare(const void *p1, const void *p2)
345 const r4300op *pOp1 = (const r4300op *) p1;
346 const r4300op *pOp2 = (const r4300op *) p2;
348 if (pOp1->x86addr < pOp2->x86addr)
350 else if (pOp1->x86addr == pOp2->x86addr)
351 return (int) (pOp1 - pOp2); /* this forces qsort to be stable */
356 int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage)
358 char chVal[128], *pchOut;
360 /* skip any initial whitespace */
361 while (isSpace(*pchIn)) pchIn++;
362 if (!isHex(*pchIn)) return 0;
364 /* parse hexadecimal address value */
366 while (isHex(*pchIn)) *pchOut++ = *pchIn++;
368 if (!isSpace(*pchIn)) return 0;
369 *plAddress = strtol(chVal, NULL, 16);
371 /* skip more whitespace */
372 while (isSpace(*pchIn)) pchIn++;
373 if (!isNum(*pchIn)) return 0;
375 /* parse decimal sample count value */
377 while (isNum(*pchIn)) *pchOut++ = *pchIn++;
379 if (!isSpace(*pchIn)) return 0;
380 *piSamples = atoi(chVal);
382 /* skip more whitespace */
383 while (isSpace(*pchIn)) pchIn++;
384 if (!isFloat(*pchIn)) return 0;
386 /* parse floating-point percentage value */
388 while (isFloat(*pchIn)) *pchOut++ = *pchIn++;
390 if (!isSpace(*pchIn) && *pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;
391 *pfPercentage = atof(chVal);
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;
400 static int InstrTypeStd[64] =
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
408 static int InstrTypeSpecial[64] =
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
420 static int InstrTypeRegImm[32] =
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
426 static int InstrTypeCop1[32] =
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
434 static int InstrTypeCop1Math[64] =
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
447 int GetInstrType(int opcode)
449 int iType = (opcode >> 26) & 63;
453 /* SPECIAL instruction */
455 return InstrTypeSpecial[iType];
459 /* REGIMM instruction */
460 iType = (opcode >> 16) & 31;
461 return InstrTypeRegImm[iType];
463 else if (iType == 16)
465 /* COP0 instruction */
466 int iType1 = opcode & 0x01FFFFFF;
467 int iType2 = (opcode >> 21) & 31;
470 else if (iType1 == 2)
472 else if (iType1 == 6)
474 else if (iType1 == 8)
476 else if (iType1 == 24)
478 else if ((opcode & 0x7FF) == 0 && iType2 == 0)
480 else if ((opcode & 0x7FF) == 0 && iType2 == 4)
483 return 0; // reserved
485 else if (iType == 17)
487 /* COP1 instruction */
488 int iType1 = (opcode >> 21) & 31;
491 /* conditional branch */
492 int iType2 = (opcode >> 16) & 31;
495 else if (iType2 == 1)
497 else if (iType2 == 2)
499 else if (iType2 == 3)
502 return 0; // reserved
504 else if (iType1 == 16 || iType1 == 17 || iType1 == 20 || iType1 == 21)
506 /* Single, Double, Word, Long instructions */
507 int iType2 = opcode & 63;
508 return InstrTypeCop1Math[iType2];
512 /* other Cop1 (move) */
513 return InstrTypeCop1[iType1];
517 /* standard MIPS instruction */
518 return InstrTypeStd[iType];