Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / tools / r4300prof.c
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 */
79 static int isSpace(char ch)
80 {
81   return (ch == ' ' || ch == '\t' ? 1 : 0);
82 }
83
84 static int isNum(char ch)
85 {
86   return (ch >= '0' && ch <= '9' ? 1 : 0);
87 }
88
89 static int isFloat(char ch)
90 {
91   return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e'  ? 1 : 0);
92 }
93
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 */
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       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
343 int 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
356 int 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
400 static 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
408 static 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                         
420 static 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
426 static 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
434 static 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             
447 int 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