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