Path: blob/master/libmupen64plus/mupen64plus-core/tools/r4300prof.c
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus - r4300prof.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2008 Richard Goedeken *4* *5* This program is free software; you can redistribute it and/or modify *6* it under the terms of the GNU General Public License as published by *7* the Free Software Foundation; either version 2 of the License, or *8* (at your option) any later version. *9* *10* This program is distributed in the hope that it will be useful, *11* but WITHOUT ANY WARRANTY; without even the implied warranty of *12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *13* GNU General Public License for more details. *14* *15* You should have received a copy of the GNU General Public License *16* along with this program; if not, write to the *17* Free Software Foundation, Inc., *18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *19* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2021#include <stdio.h>22#include <stdlib.h>23#include <string.h>2425/* Global data */26unsigned int instr_samples[132];27char instr_name[][10] =28{29"reserved", "NI", "J", "JAL", "BEQ", "BNE", "BLEZ", "BGTZ",30"ADDI", "ADDIU", "SLTI", "SLTIU", "ANDI", "ORI", "XORI", "LUI",31"BEQL", "BNEL", "BLEZL", "BGTZL", "DADDI", "DADDIU", "LDL", "LDR",32"LB", "LH", "LW", "LWL", "LBU", "LHU", "LWU", "LWR",33"SB", "SH", "SW", "SWL", "SWR", "SDL", "SDR", "LWC1",34"LDC1", "LD", "LL", "SWC1", "SDC1", "SD", "SC", "BLTZ",35"BGEZ", "BLTZL", "BGEZL", "BLTZAL", "BGEZAL", "BLTZALL", "BGEZALL", "SLL",36"SRL", "SRA", "SLLV", "SRLV", "SRAV", "JR", "JALR", "SYSCALL",37"MFHI", "MTHI", "MFLO", "MTLO", "DSLLV", "DSRLV", "DSRAV", "MULT",38"MULTU", "DIV", "DIVU", "DMULT", "DMULTU", "DDIV", "DDIVU", "ADD",39"ADDU", "SUB", "SUBU", "AND", "OR", "XOR", "NOR", "SLT",40"SLTU", "DADD", "DADDU", "DSUB", "DSUBU", "DSLL", "DSRL", "DSRA",41"TEQ", "DSLL32", "DSRL32", "DSRA32", "BC1F", "BC1T", "BC1FL", "BC1TL",42"TLBWI", "TLBP", "TLBR", "TLBWR", "ERET", "MFC0", "MTC0", "MFC1",43"DMFC1", "CFC1", "MTC1", "DMTC1", "CTC1", "f.CVT", "f.CMP", "f.ADD",44"f.SUB", "f.MUL", "f.DIV", "f.SQRT", "f.ABS", "f.MOV", "f.NEG", "f.ROUND",45"f.TRUNC", "f.CEIL", "f.FLOOR"46};47unsigned int instr_type[131] = { 9, 10, 6, 6, 7, 7, 7, 7, 3, 3, 4, 4, 3, 4, 4, 0,487, 7, 7, 7, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,491, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 7,507, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 6, 6, 10,512, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3,523, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,538, 4, 4, 4, 7, 7, 7, 7, 10, 10, 10, 10, 8, 2, 2, 2,542, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5,555, 5, 5 };56char instr_typename[][20] = { "Load", "Store", "Data move/convert", "32-bit math", "64-bit math", "Float Math",57"Jump", "Branch", "Exceptions", "Reserved", "Other" };5859/* Global functions */60int GetInstrType(int opcode);61int AddrCompare(const void *, const void *);62int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage);6364/* defined types */65typedef struct __attribute__ ((__packed__))66{67int mipsop;68long x86addr;69} r4300op;7071typedef struct72{73long x86addr;74int samples;75} profilehit;7677/* static functions */78static int isSpace(char ch)79{80return (ch == ' ' || ch == '\t' ? 1 : 0);81}8283static int isNum(char ch)84{85return (ch >= '0' && ch <= '9' ? 1 : 0);86}8788static int isFloat(char ch)89{90return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e' ? 1 : 0);91}9293static int isHex(char ch)94{95return ((ch >= '0' && ch <= '9') || ((ch & 0xdf) >= 'A' && (ch & 0xdf) <= 'F') ? 1 : 0);96}9798/* main */99int main(int argc, void *argv[])100{101long lOpStart, lOpEnd;102int flength, oplistlength, totaltime, proflistlength;103int samp_unknown, samp_blockend, samp_notcompiled, samp_wrappers, samp_flush;104int i, j;105FILE *pfIn;106r4300op *pOpAddrTable;107profilehit *pProfTable;108char *pch, *pchSampleData;109110/* check arguments */111if (argc < 3)112{113printf("Usage: r4300prof r4300addr.dat x86profile.txt\n\n");114printf("r4300addr.dat - binary table of r4300 opcodes and corresponding x86 starting addresses\n");115printf("x86profile.txt - text file containing a list of profile sample counts by x86 address on the heap\n\n");116return 1;117}118119/* open r4300 opcode/x86 address table generated from emulator run */120printf("Loading %s...\n", argv[1]);121pfIn = fopen(argv[1], "rb");122if (pfIn == NULL)123{124printf("Couldn't open input file: %s\n", argv[1]);125return 2;126}127128/* get file length and calculate number of r4300op table entries */129fseek(pfIn, 0L, SEEK_END);130flength = (int) ftell(pfIn);131fseek(pfIn, 0L, SEEK_SET);132oplistlength = flength / sizeof(r4300op);133134/* read the file */135pOpAddrTable = (r4300op *) malloc(flength);136if (pOpAddrTable == NULL)137{138printf("Failed to allocate %i bytes for OpAddrTable!\n", flength);139fclose(pfIn);140return 3;141}142fread(pOpAddrTable, 1, flength, pfIn);143fclose(pfIn);144printf("%i r4300 instruction locations read.\n", oplistlength);145146/* sort the opcode/address table according to x86addr */147qsort(pOpAddrTable, oplistlength, sizeof(r4300op), AddrCompare);148149/* remove any 0-length r4300 instructions */150i = 0;151j = 0;152while (i < oplistlength)153{154pOpAddrTable[j].mipsop = pOpAddrTable[i].mipsop;155pOpAddrTable[j].x86addr = pOpAddrTable[i].x86addr;156i++;157if (pOpAddrTable[j].x86addr != pOpAddrTable[i].x86addr)158j++;159}160oplistlength = j;161printf("%i non-empty MIPS instructions.\n", oplistlength);162163/* convert each r4300 opcode to an instruction type index */164for (i = 0; i < oplistlength; i++)165if (pOpAddrTable[i].mipsop > 0 || pOpAddrTable[i].mipsop < -16)166pOpAddrTable[i].mipsop = GetInstrType(pOpAddrTable[i].mipsop);167168/* open the profiling sample data file */169printf("Loading %s...\n", argv[2]);170pfIn = fopen(argv[2], "rb");171if (pfIn == NULL)172{173printf("Couldn't open input file: %s\n", argv[2]);174free(pOpAddrTable);175return 4;176}177178/* load it */179fseek(pfIn, 0L, SEEK_END);180flength = (int) ftell(pfIn);181fseek(pfIn, 0L, SEEK_SET);182pchSampleData = (char *) malloc(flength + 16);183if (pchSampleData == NULL)184{185printf("Failed to allocate %i bytes for pchSampleData!\n", flength + 16);186fclose(pfIn);187free(pOpAddrTable);188return 5;189}190fread(pchSampleData, 1, flength, pfIn);191pchSampleData[flength] = 0;192fclose(pfIn);193194/* count the number of newlines in the ascii-formatted sample data file */195proflistlength = 1;196pch = pchSampleData;197while (pch = strchr(pch, '\n'))198{199proflistlength++;200pch++;201}202printf("%i lines in sample data file.\n", proflistlength);203204/* extract text data into binary table */205pProfTable = (profilehit *) malloc(proflistlength * sizeof(profilehit));206if (pProfTable == NULL)207{208printf("Failed to allocate %i bytes for pProfTable!\n", proflistlength * sizeof(profilehit));209free(pOpAddrTable);210free(pchSampleData);211return 6;212}213pch = pchSampleData;214j = 0;215long long llOffset = 0;216while (j < proflistlength)217{218long lAddress;219int iSamples;220float fPercentage;221char *pchNext = strchr(pch, '\n');222if (pchNext != NULL) *pchNext++ = 0; // null-terminate this line223if (strstr(pch, "range:0x") != NULL) // search for offset change224{225pch = strstr(pch, "range:0x") + 8; // extract hex value and update our offset226char *pch2 = pch;227while (isHex(*pch2)) pch2++;228*pch2 = 0;229llOffset = strtoll(pch, NULL, 16);230}231else // parse line for sample point232{233int rval = ParseProfLine(pch, &lAddress, &iSamples, &fPercentage);234if (rval != 0)235{236pProfTable[j].x86addr = (unsigned long) (lAddress + llOffset);237pProfTable[j].samples = iSamples;238j++;239}240}241pch = pchNext;242if (pch == NULL) break;243}244free(pchSampleData);245proflistlength = j;246printf("Found %i profile hits.\n", proflistlength);247248/* clear r4300 instruction sample data table */249for (i = 0; i < 132; i++)250instr_samples[i] = 0;251252/* calculate r4300 instruction profiling data by merging the tables */253samp_unknown = 0;254samp_blockend = 0;255samp_notcompiled = 0;256samp_wrappers = 0;257samp_flush = 0;258i = 0; // i == OpAddrTable index259lOpStart = pOpAddrTable[0].x86addr;260lOpEnd = pOpAddrTable[1].x86addr;261for (j = 0; j < proflistlength; j++) // j == pProfTable index262{263long lOpx86addr = pProfTable[j].x86addr;264if (lOpx86addr >= lOpStart && lOpx86addr <= lOpEnd) /* these profile samples lie within current r4300 instruction */265{266int instr = pOpAddrTable[i].mipsop;267if (instr == -1) printf("%lx sample point lies between %i/%lx and %i/%lx\n", lOpx86addr, instr, lOpStart, pOpAddrTable[i+1].mipsop, lOpEnd);268269if (instr == -1)270samp_unknown += pProfTable[j].samples;271else if (instr == -2)272samp_notcompiled += pProfTable[j].samples;273else if (instr == -3)274samp_blockend += pProfTable[j].samples;275else if (instr == -4)276samp_wrappers += pProfTable[j].samples;277else if (instr == -5)278samp_flush += pProfTable[j].samples;279else280instr_samples[instr] += pProfTable[j].samples;281continue;282}283if (lOpx86addr < pOpAddrTable[0].x86addr || lOpx86addr >= pOpAddrTable[oplistlength-1].x86addr)284{ /* outside the range of all recompiled instructions */285samp_unknown += pProfTable[j].samples;286continue;287}288if (lOpx86addr < lOpStart) /* discontinuity in profile list, go back to start */289{290i = 0;291lOpStart = pOpAddrTable[0].x86addr;292lOpEnd = pOpAddrTable[1].x86addr;293j--;294continue;295}296/* this profile point is ahead of current r4300 instruction */297do /* race ahead in r4300 opcode list until we hit this profile sample point */298{299i++;300} while (i+1 < oplistlength && lOpx86addr > pOpAddrTable[i+1].x86addr);301lOpStart = pOpAddrTable[i].x86addr;302lOpEnd = pOpAddrTable[i+1].x86addr;303if (lOpx86addr < lOpStart || lOpx86addr > lOpEnd)304{305printf("Error: lOpx86addr = %lx but lOpStart, lOpEnd = %lx, %lx\n", lOpx86addr, lOpStart, lOpEnd);306return 7;307}308/* we have found the correct r4300 instruction corresponding to this profile point */309j--;310}311312/* print the results */313unsigned int iTypeCount[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};314printf("\nInstruction time (samples):\n");315totaltime = 0;316for (i = 0; i < 131; i++)317{318printf("%8s: %08i ", instr_name[i], instr_samples[i]);319if (i % 5 == 4) printf("\n");320iTypeCount[instr_type[i]] += instr_samples[i];321totaltime += instr_samples[i];322}323int special = samp_flush + samp_wrappers + samp_notcompiled + samp_blockend;324printf("\n\nSpecial code samples:\n");325printf(" Regcache flushing: %i\n", samp_flush);326printf(" Jump wrappers: %i\n", samp_wrappers);327printf(" NOTCOMPILED: %i\n", samp_notcompiled);328printf(" block postfix & link samples: %i\n", samp_blockend);329330printf("\nUnaccounted samples: %i\n", samp_unknown);331printf("Total accounted instruction samples: %i\n", totaltime + special);332for (i = 0; i < 11; i++)333{334printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / totaltime, iTypeCount[i]);335}336337free(pOpAddrTable);338free(pProfTable);339return 0;340}341342int AddrCompare(const void *p1, const void *p2)343{344const r4300op *pOp1 = (const r4300op *) p1;345const r4300op *pOp2 = (const r4300op *) p2;346347if (pOp1->x86addr < pOp2->x86addr)348return -1;349else if (pOp1->x86addr == pOp2->x86addr)350return (int) (pOp1 - pOp2); /* this forces qsort to be stable */351else352return 1;353}354355int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage)356{357char chVal[128], *pchOut;358359/* skip any initial whitespace */360while (isSpace(*pchIn)) pchIn++;361if (!isHex(*pchIn)) return 0;362363/* parse hexadecimal address value */364pchOut = chVal;365while (isHex(*pchIn)) *pchOut++ = *pchIn++;366*pchOut = 0;367if (!isSpace(*pchIn)) return 0;368*plAddress = strtol(chVal, NULL, 16);369370/* skip more whitespace */371while (isSpace(*pchIn)) pchIn++;372if (!isNum(*pchIn)) return 0;373374/* parse decimal sample count value */375pchOut = chVal;376while (isNum(*pchIn)) *pchOut++ = *pchIn++;377*pchOut = 0;378if (!isSpace(*pchIn)) return 0;379*piSamples = atoi(chVal);380381/* skip more whitespace */382while (isSpace(*pchIn)) pchIn++;383if (!isFloat(*pchIn)) return 0;384385/* parse floating-point percentage value */386pchOut = chVal;387while (isFloat(*pchIn)) *pchOut++ = *pchIn++;388*pchOut = 0;389if (!isSpace(*pchIn) && *pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;390*pfPercentage = atof(chVal);391392/* if this isn't the end of the line, it's not a valid sample point */393while (isSpace(*pchIn)) pchIn++;394if (*pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0;395396return 1;397}398399static int InstrTypeStd[64] =400{401-1, -1, 02, 03, 04, 05, 06, 07, 8, 9, 10, 11, 12, 13, 14, 15,402-1, -1, 00, 00, 16, 17, 18, 19, 20, 21, 22, 23, 00, 00, 00, 00,40324, 25, 27, 26, 28, 29, 31, 30, 32, 33, 35, 34, 37, 38, 36, 01,40442, 39, 00, 00, 01, 40, 00, 41, 46, 43, 00, 00, 01, 44, 00, 45405};406407static int InstrTypeSpecial[64] =408{40955, 00, 56, 57, 58, 00, 59, 60,41061, 62, 00, 00, 63, 01, 00, 00,41164, 65, 66, 67, 68, 00, 69, 70,41271, 72, 73, 74, 75, 76, 77, 78,41379, 80, 81, 82, 83, 84, 85, 86,41400, 00, 87, 88, 89, 90, 91, 92,41501, 01, 01, 01, 96, 00, 01, 00,41693, 00, 94, 95, 97, 00, 98, 99417};418419static int InstrTypeRegImm[32] =420{42147, 48, 49, 50, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 01, 00,42251, 52, 53, 54, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00423};424425static int InstrTypeCop1[32] =426{427111, 112, 113, 00, 114, 115, 116, 00,428-1, 00, 00, 00, 00, 00, 00, 00,429-1, -1, 00, 00, -1, -1, 00, 00,43000, 00, 00, 00, 00, 00, 00, 00431};432433static int InstrTypeCop1Math[64] =434{435119, 120, 121, 122, 123, 124, 125, 126,436127, 128, 129, 130, 127, 128, 129, 130,43700, 00, 00, 00, 00, 00, 00, 00,43800, 00, 00, 00, 00, 00, 00, 00,439117, 117, 00, 00, 117, 117, 00, 00,44000, 00, 00, 00, 00, 00, 00, 00,441118, 118, 118, 118, 118, 118, 118, 118,442118, 118, 118, 118, 118, 118, 118, 118443};444445446int GetInstrType(int opcode)447{448int iType = (opcode >> 26) & 63;449450if (iType == 0)451{452/* SPECIAL instruction */453iType = opcode & 63;454return InstrTypeSpecial[iType];455}456else if (iType == 1)457{458/* REGIMM instruction */459iType = (opcode >> 16) & 31;460return InstrTypeRegImm[iType];461}462else if (iType == 16)463{464/* COP0 instruction */465int iType1 = opcode & 0x01FFFFFF;466int iType2 = (opcode >> 21) & 31;467if (iType1 == 1)468return 106; // TLBR469else if (iType1 == 2)470return 104; // TLBWI471else if (iType1 == 6)472return 107; // TLBWR473else if (iType1 == 8)474return 105; // TLBP475else if (iType1 == 24)476return 108; // ERET477else if ((opcode & 0x7FF) == 0 && iType2 == 0)478return 109; // MFC0479else if ((opcode & 0x7FF) == 0 && iType2 == 4)480return 110; // MTC0481else482return 0; // reserved483}484else if (iType == 17)485{486/* COP1 instruction */487int iType1 = (opcode >> 21) & 31;488if (iType1 == 8)489{490/* conditional branch */491int iType2 = (opcode >> 16) & 31;492if (iType2 == 0)493return 100; // BC1F494else if (iType2 == 1)495return 101; // BC1T496else if (iType2 == 2)497return 102; // BC1FL498else if (iType2 == 3)499return 103; // BC1TL500else501return 0; // reserved502}503else if (iType1 == 16 || iType1 == 17 || iType1 == 20 || iType1 == 21)504{505/* Single, Double, Word, Long instructions */506int iType2 = opcode & 63;507return InstrTypeCop1Math[iType2];508}509else510{511/* other Cop1 (move) */512return InstrTypeCop1[iType1];513}514}515516/* standard MIPS instruction */517return InstrTypeStd[iType];518}519520521522