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 */
27 unsigned int instr_samples[132];
28 char 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 };
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,
56 5, 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" };
59
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);
64
65 /* defined types */
66 typedef struct __attribute__ ((__packed__))
67 {
68 int mipsop;
69 long x86addr;
70 } r4300op;
71
72 typedef struct
73 {
74 long x86addr;
75 int samples;
76 } profilehit;
77
78 /* static functions */
isSpace(char ch)79 static int isSpace(char ch)
80 {
81 return (ch == ' ' || ch == '\t' ? 1 : 0);
82 }
83
isNum(char ch)84 static int isNum(char ch)
85 {
86 return (ch >= '0' && ch <= '9' ? 1 : 0);
87 }
88
isFloat(char ch)89 static int isFloat(char ch)
90 {
91 return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e' ? 1 : 0);
92 }
93
isHex(char ch)94 static int isHex(char ch)
95 {
96 return ((ch >= '0' && ch <= '9') || ((ch & 0xdf) >= 'A' && (ch & 0xdf) <= 'F') ? 1 : 0);
97 }
98
99 /* main */
main(int argc,void * argv[])100 int 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 free(pOpAddrTable);
308 free(pProfTable);
309 return 7;
310 }
311 /* we have found the correct r4300 instruction corresponding to this profile point */
312 j--;
313 }
314
315 /* print the results */
316 unsigned int iTypeCount[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
317 printf("\nInstruction time (samples):\n");
318 totaltime = 0;
319 for (i = 0; i < 131; i++)
320 {
321 printf("%8s: %08i ", instr_name[i], instr_samples[i]);
322 if (i % 5 == 4) printf("\n");
323 iTypeCount[instr_type[i]] += instr_samples[i];
324 totaltime += instr_samples[i];
325 }
326 int special = samp_flush + samp_wrappers + samp_notcompiled + samp_blockend;
327 printf("\n\nSpecial code samples:\n");
328 printf(" Regcache flushing: %i\n", samp_flush);
329 printf(" Jump wrappers: %i\n", samp_wrappers);
330 printf(" NOTCOMPILED: %i\n", samp_notcompiled);
331 printf(" block postfix & link samples: %i\n", samp_blockend);
332
333 printf("\nUnaccounted samples: %i\n", samp_unknown);
334 printf("Total accounted instruction samples: %i\n", totaltime + special);
335 for (i = 0; i < 11; i++)
336 {
337 printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / totaltime, iTypeCount[i]);
338 }
339
340 free(pOpAddrTable);
341 free(pProfTable);
342 return 0;
343 }
344
AddrCompare(const void * p1,const void * p2)345 int AddrCompare(const void *p1, const void *p2)
346 {
347 const r4300op *pOp1 = (const r4300op *) p1;
348 const r4300op *pOp2 = (const r4300op *) p2;
349
350 if (pOp1->x86addr < pOp2->x86addr)
351 return -1;
352 else if (pOp1->x86addr == pOp2->x86addr)
353 return (int) (pOp1 - pOp2); /* this forces qsort to be stable */
354 else
355 return 1;
356 }
357
ParseProfLine(const char * pchIn,long * plAddress,int * piSamples,float * pfPercentage)358 int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage)
359 {
360 char chVal[128], *pchOut;
361
362 /* skip any initial whitespace */
363 while (isSpace(*pchIn)) pchIn++;
364 if (!isHex(*pchIn)) return 0;
365
366 /* parse hexadecimal address value */
367 pchOut = chVal;
368 while (isHex(*pchIn)) *pchOut++ = *pchIn++;
369 *pchOut = 0;
370 if (!isSpace(*pchIn)) return 0;
371 *plAddress = strtol(chVal, NULL, 16);
372
373 /* skip more whitespace */
374 while (isSpace(*pchIn)) pchIn++;
375 if (!isNum(*pchIn)) return 0;
376
377 /* parse decimal sample count value */
378 pchOut = chVal;
379 while (isNum(*pchIn)) *pchOut++ = *pchIn++;
380 *pchOut = 0;
381 if (!isSpace(*pchIn)) return 0;
382 *piSamples = atoi(chVal);
383
384 /* skip more whitespace */
385 while (isSpace(*pchIn)) pchIn++;
386 if (!isFloat(*pchIn)) return 0;
387
388 /* parse floating-point percentage value */
389 pchOut = chVal;
390 while (isFloat(*pchIn)) *pchOut++ = *pchIn++;
391 *pchOut = 0;
392 if (!isSpace(*pchIn) && *pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;
393 *pfPercentage = atof(chVal);
394
395 /* if this isn't the end of the line, it's not a valid sample point */
396 while (isSpace(*pchIn)) pchIn++;
397 if (*pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;
398
399 return 1;
400 }
401
402 static int InstrTypeStd[64] =
403 {
404 -1, -1, 02, 03, 04, 05, 06, 07, 8, 9, 10, 11, 12, 13, 14, 15,
405 -1, -1, 00, 00, 16, 17, 18, 19, 20, 21, 22, 23, 00, 00, 00, 00,
406 24, 25, 27, 26, 28, 29, 31, 30, 32, 33, 35, 34, 37, 38, 36, 01,
407 42, 39, 00, 00, 01, 40, 00, 41, 46, 43, 00, 00, 01, 44, 00, 45
408 };
409
410 static int InstrTypeSpecial[64] =
411 {
412 55, 00, 56, 57, 58, 00, 59, 60,
413 61, 62, 00, 00, 63, 01, 00, 00,
414 64, 65, 66, 67, 68, 00, 69, 70,
415 71, 72, 73, 74, 75, 76, 77, 78,
416 79, 80, 81, 82, 83, 84, 85, 86,
417 00, 00, 87, 88, 89, 90, 91, 92,
418 01, 01, 01, 01, 96, 00, 01, 00,
419 93, 00, 94, 95, 97, 00, 98, 99
420 };
421
422 static int InstrTypeRegImm[32] =
423 {
424 47, 48, 49, 50, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 01, 00,
425 51, 52, 53, 54, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
426 };
427
428 static int InstrTypeCop1[32] =
429 {
430 111, 112, 113, 00, 114, 115, 116, 00,
431 -1, 00, 00, 00, 00, 00, 00, 00,
432 -1, -1, 00, 00, -1, -1, 00, 00,
433 00, 00, 00, 00, 00, 00, 00, 00
434 };
435
436 static int InstrTypeCop1Math[64] =
437 {
438 119, 120, 121, 122, 123, 124, 125, 126,
439 127, 128, 129, 130, 127, 128, 129, 130,
440 00, 00, 00, 00, 00, 00, 00, 00,
441 00, 00, 00, 00, 00, 00, 00, 00,
442 117, 117, 00, 00, 117, 117, 00, 00,
443 00, 00, 00, 00, 00, 00, 00, 00,
444 118, 118, 118, 118, 118, 118, 118, 118,
445 118, 118, 118, 118, 118, 118, 118, 118
446 };
447
448
GetInstrType(int opcode)449 int GetInstrType(int opcode)
450 {
451 int iType = (opcode >> 26) & 63;
452
453 if (iType == 0)
454 {
455 /* SPECIAL instruction */
456 iType = opcode & 63;
457 return InstrTypeSpecial[iType];
458 }
459 else if (iType == 1)
460 {
461 /* REGIMM instruction */
462 iType = (opcode >> 16) & 31;
463 return InstrTypeRegImm[iType];
464 }
465 else if (iType == 16)
466 {
467 /* COP0 instruction */
468 int iType1 = opcode & 0x01FFFFFF;
469 int iType2 = (opcode >> 21) & 31;
470 if (iType1 == 1)
471 return 106; // TLBR
472 else if (iType1 == 2)
473 return 104; // TLBWI
474 else if (iType1 == 6)
475 return 107; // TLBWR
476 else if (iType1 == 8)
477 return 105; // TLBP
478 else if (iType1 == 24)
479 return 108; // ERET
480 else if ((opcode & 0x7FF) == 0 && iType2 == 0)
481 return 109; // MFC0
482 else if ((opcode & 0x7FF) == 0 && iType2 == 4)
483 return 110; // MTC0
484 else
485 return 0; // reserved
486 }
487 else if (iType == 17)
488 {
489 /* COP1 instruction */
490 int iType1 = (opcode >> 21) & 31;
491 if (iType1 == 8)
492 {
493 /* conditional branch */
494 int iType2 = (opcode >> 16) & 31;
495 if (iType2 == 0)
496 return 100; // BC1F
497 else if (iType2 == 1)
498 return 101; // BC1T
499 else if (iType2 == 2)
500 return 102; // BC1FL
501 else if (iType2 == 3)
502 return 103; // BC1TL
503 else
504 return 0; // reserved
505 }
506 else if (iType1 == 16 || iType1 == 17 || iType1 == 20 || iType1 == 21)
507 {
508 /* Single, Double, Word, Long instructions */
509 int iType2 = opcode & 63;
510 return InstrTypeCop1Math[iType2];
511 }
512 else
513 {
514 /* other Cop1 (move) */
515 return InstrTypeCop1[iType1];
516 }
517 }
518
519 /* standard MIPS instruction */
520 return InstrTypeStd[iType];
521 }
522
523