X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fmupen64plus-core%2Ftools%2Fr4300prof.c;fp=source%2Fmupen64plus-core%2Ftools%2Fr4300prof.c;h=7578d3b0ad821a443acc66bac1b808807d530842;hb=451ab91e3827a6384981b3300e2a7000d2eaba58;hp=0000000000000000000000000000000000000000;hpb=a2ab25365b5b0dddbee476d695d8a31151407581;p=mupen64plus-pandora.git diff --git a/source/mupen64plus-core/tools/r4300prof.c b/source/mupen64plus-core/tools/r4300prof.c new file mode 100644 index 0000000..7578d3b --- /dev/null +++ b/source/mupen64plus-core/tools/r4300prof.c @@ -0,0 +1,520 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - r4300prof.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2008 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +/* Global data */ +unsigned int instr_samples[132]; +char instr_name[][10] = +{ + "reserved", "NI", "J", "JAL", "BEQ", "BNE", "BLEZ", "BGTZ", + "ADDI", "ADDIU", "SLTI", "SLTIU", "ANDI", "ORI", "XORI", "LUI", + "BEQL", "BNEL", "BLEZL", "BGTZL", "DADDI", "DADDIU", "LDL", "LDR", + "LB", "LH", "LW", "LWL", "LBU", "LHU", "LWU", "LWR", + "SB", "SH", "SW", "SWL", "SWR", "SDL", "SDR", "LWC1", + "LDC1", "LD", "LL", "SWC1", "SDC1", "SD", "SC", "BLTZ", + "BGEZ", "BLTZL", "BGEZL", "BLTZAL", "BGEZAL", "BLTZALL", "BGEZALL", "SLL", + "SRL", "SRA", "SLLV", "SRLV", "SRAV", "JR", "JALR", "SYSCALL", + "MFHI", "MTHI", "MFLO", "MTLO", "DSLLV", "DSRLV", "DSRAV", "MULT", + "MULTU", "DIV", "DIVU", "DMULT", "DMULTU", "DDIV", "DDIVU", "ADD", + "ADDU", "SUB", "SUBU", "AND", "OR", "XOR", "NOR", "SLT", + "SLTU", "DADD", "DADDU", "DSUB", "DSUBU", "DSLL", "DSRL", "DSRA", + "TEQ", "DSLL32", "DSRL32", "DSRA32", "BC1F", "BC1T", "BC1FL", "BC1TL", + "TLBWI", "TLBP", "TLBR", "TLBWR", "ERET", "MFC0", "MTC0", "MFC1", + "DMFC1", "CFC1", "MTC1", "DMTC1", "CTC1", "f.CVT", "f.CMP", "f.ADD", + "f.SUB", "f.MUL", "f.DIV", "f.SQRT", "f.ABS", "f.MOV", "f.NEG", "f.ROUND", + "f.TRUNC", "f.CEIL", "f.FLOOR" +}; +unsigned int instr_type[131] = { 9, 10, 6, 6, 7, 7, 7, 7, 3, 3, 4, 4, 3, 4, 4, 0, + 7, 7, 7, 7, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 7, + 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 6, 6, 10, + 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 4, 4, 4, 7, 7, 7, 7, 10, 10, 10, 10, 8, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, + 5, 5, 5 }; +char instr_typename[][20] = { "Load", "Store", "Data move/convert", "32-bit math", "64-bit math", "Float Math", + "Jump", "Branch", "Exceptions", "Reserved", "Other" }; + +/* Global functions */ +int GetInstrType(int opcode); +int AddrCompare(const void *, const void *); +int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage); + +/* defined types */ +typedef struct __attribute__ ((__packed__)) +{ + int mipsop; + long x86addr; +} r4300op; + +typedef struct +{ + long x86addr; + int samples; +} profilehit; + +/* static functions */ +static int isSpace(char ch) +{ + return (ch == ' ' || ch == '\t' ? 1 : 0); +} + +static int isNum(char ch) +{ + return (ch >= '0' && ch <= '9' ? 1 : 0); +} + +static int isFloat(char ch) +{ + return ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || ch == '-' || ch == 'e' ? 1 : 0); +} + +static int isHex(char ch) +{ + return ((ch >= '0' && ch <= '9') || ((ch & 0xdf) >= 'A' && (ch & 0xdf) <= 'F') ? 1 : 0); +} + +/* main */ +int main(int argc, void *argv[]) +{ + long lOpStart, lOpEnd; + int flength, oplistlength, totaltime, proflistlength; + int samp_unknown, samp_blockend, samp_notcompiled, samp_wrappers, samp_flush; + int i, j; + FILE *pfIn; + r4300op *pOpAddrTable; + profilehit *pProfTable; + char *pch, *pchSampleData; + + /* check arguments */ + if (argc < 3) + { + printf("Usage: r4300prof r4300addr.dat x86profile.txt\n\n"); + printf("r4300addr.dat - binary table of r4300 opcodes and corresponding x86 starting addresses\n"); + printf("x86profile.txt - text file containing a list of profile sample counts by x86 address on the heap\n\n"); + return 1; + } + + /* open r4300 opcode/x86 address table generated from emulator run */ + printf("Loading %s...\n", argv[1]); + pfIn = fopen(argv[1], "rb"); + if (pfIn == NULL) + { + printf("Couldn't open input file: %s\n", argv[1]); + return 2; + } + + /* get file length and calculate number of r4300op table entries */ + fseek(pfIn, 0L, SEEK_END); + flength = (int) ftell(pfIn); + fseek(pfIn, 0L, SEEK_SET); + oplistlength = flength / sizeof(r4300op); + + /* read the file */ + pOpAddrTable = (r4300op *) malloc(flength); + if (pOpAddrTable == NULL) + { + printf("Failed to allocate %i bytes for OpAddrTable!\n", flength); + fclose(pfIn); + return 3; + } + fread(pOpAddrTable, 1, flength, pfIn); + fclose(pfIn); + printf("%i r4300 instruction locations read.\n", oplistlength); + + /* sort the opcode/address table according to x86addr */ + qsort(pOpAddrTable, oplistlength, sizeof(r4300op), AddrCompare); + + /* remove any 0-length r4300 instructions */ + i = 0; + j = 0; + while (i < oplistlength) + { + pOpAddrTable[j].mipsop = pOpAddrTable[i].mipsop; + pOpAddrTable[j].x86addr = pOpAddrTable[i].x86addr; + i++; + if (pOpAddrTable[j].x86addr != pOpAddrTable[i].x86addr) + j++; + } + oplistlength = j; + printf("%i non-empty MIPS instructions.\n", oplistlength); + + /* convert each r4300 opcode to an instruction type index */ + for (i = 0; i < oplistlength; i++) + if (pOpAddrTable[i].mipsop > 0 || pOpAddrTable[i].mipsop < -16) + pOpAddrTable[i].mipsop = GetInstrType(pOpAddrTable[i].mipsop); + + /* open the profiling sample data file */ + printf("Loading %s...\n", argv[2]); + pfIn = fopen(argv[2], "rb"); + if (pfIn == NULL) + { + printf("Couldn't open input file: %s\n", argv[2]); + free(pOpAddrTable); + return 4; + } + + /* load it */ + fseek(pfIn, 0L, SEEK_END); + flength = (int) ftell(pfIn); + fseek(pfIn, 0L, SEEK_SET); + pchSampleData = (char *) malloc(flength + 16); + if (pchSampleData == NULL) + { + printf("Failed to allocate %i bytes for pchSampleData!\n", flength + 16); + fclose(pfIn); + free(pOpAddrTable); + return 5; + } + fread(pchSampleData, 1, flength, pfIn); + pchSampleData[flength] = 0; + fclose(pfIn); + + /* count the number of newlines in the ascii-formatted sample data file */ + proflistlength = 1; + pch = pchSampleData; + while (pch = strchr(pch, '\n')) + { + proflistlength++; + pch++; + } + printf("%i lines in sample data file.\n", proflistlength); + + /* extract text data into binary table */ + pProfTable = (profilehit *) malloc(proflistlength * sizeof(profilehit)); + if (pProfTable == NULL) + { + printf("Failed to allocate %i bytes for pProfTable!\n", proflistlength * sizeof(profilehit)); + free(pOpAddrTable); + free(pchSampleData); + return 6; + } + pch = pchSampleData; + j = 0; + long long llOffset = 0; + while (j < proflistlength) + { + long lAddress; + int iSamples; + float fPercentage; + char *pchNext = strchr(pch, '\n'); + if (pchNext != NULL) *pchNext++ = 0; // null-terminate this line + if (strstr(pch, "range:0x") != NULL) // search for offset change + { + pch = strstr(pch, "range:0x") + 8; // extract hex value and update our offset + char *pch2 = pch; + while (isHex(*pch2)) pch2++; + *pch2 = 0; + llOffset = strtoll(pch, NULL, 16); + } + else // parse line for sample point + { + int rval = ParseProfLine(pch, &lAddress, &iSamples, &fPercentage); + if (rval != 0) + { + pProfTable[j].x86addr = (unsigned long) (lAddress + llOffset); + pProfTable[j].samples = iSamples; + j++; + } + } + pch = pchNext; + if (pch == NULL) break; + } + free(pchSampleData); + proflistlength = j; + printf("Found %i profile hits.\n", proflistlength); + + /* clear r4300 instruction sample data table */ + for (i = 0; i < 132; i++) + instr_samples[i] = 0; + + /* calculate r4300 instruction profiling data by merging the tables */ + samp_unknown = 0; + samp_blockend = 0; + samp_notcompiled = 0; + samp_wrappers = 0; + samp_flush = 0; + i = 0; // i == OpAddrTable index + lOpStart = pOpAddrTable[0].x86addr; + lOpEnd = pOpAddrTable[1].x86addr; + for (j = 0; j < proflistlength; j++) // j == pProfTable index + { + long lOpx86addr = pProfTable[j].x86addr; + if (lOpx86addr >= lOpStart && lOpx86addr <= lOpEnd) /* these profile samples lie within current r4300 instruction */ + { + int instr = pOpAddrTable[i].mipsop; + if (instr == -1) printf("%lx sample point lies between %i/%lx and %i/%lx\n", lOpx86addr, instr, lOpStart, pOpAddrTable[i+1].mipsop, lOpEnd); + + if (instr == -1) + samp_unknown += pProfTable[j].samples; + else if (instr == -2) + samp_notcompiled += pProfTable[j].samples; + else if (instr == -3) + samp_blockend += pProfTable[j].samples; + else if (instr == -4) + samp_wrappers += pProfTable[j].samples; + else if (instr == -5) + samp_flush += pProfTable[j].samples; + else + instr_samples[instr] += pProfTable[j].samples; + continue; + } + if (lOpx86addr < pOpAddrTable[0].x86addr || lOpx86addr >= pOpAddrTable[oplistlength-1].x86addr) + { /* outside the range of all recompiled instructions */ + samp_unknown += pProfTable[j].samples; + continue; + } + if (lOpx86addr < lOpStart) /* discontinuity in profile list, go back to start */ + { + i = 0; + lOpStart = pOpAddrTable[0].x86addr; + lOpEnd = pOpAddrTable[1].x86addr; + j--; + continue; + } + /* this profile point is ahead of current r4300 instruction */ + do /* race ahead in r4300 opcode list until we hit this profile sample point */ + { + i++; + } while (i+1 < oplistlength && lOpx86addr > pOpAddrTable[i+1].x86addr); + lOpStart = pOpAddrTable[i].x86addr; + lOpEnd = pOpAddrTable[i+1].x86addr; + if (lOpx86addr < lOpStart || lOpx86addr > lOpEnd) + { + printf("Error: lOpx86addr = %lx but lOpStart, lOpEnd = %lx, %lx\n", lOpx86addr, lOpStart, lOpEnd); + return 7; + } + /* we have found the correct r4300 instruction corresponding to this profile point */ + j--; + } + + /* print the results */ + unsigned int iTypeCount[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + printf("\nInstruction time (samples):\n"); + totaltime = 0; + for (i = 0; i < 131; i++) + { + printf("%8s: %08i ", instr_name[i], instr_samples[i]); + if (i % 5 == 4) printf("\n"); + iTypeCount[instr_type[i]] += instr_samples[i]; + totaltime += instr_samples[i]; + } + int special = samp_flush + samp_wrappers + samp_notcompiled + samp_blockend; + printf("\n\nSpecial code samples:\n"); + printf(" Regcache flushing: %i\n", samp_flush); + printf(" Jump wrappers: %i\n", samp_wrappers); + printf(" NOTCOMPILED: %i\n", samp_notcompiled); + printf(" block postfix & link samples: %i\n", samp_blockend); + + printf("\nUnaccounted samples: %i\n", samp_unknown); + printf("Total accounted instruction samples: %i\n", totaltime + special); + for (i = 0; i < 11; i++) + { + printf("%20s: %04.1f%% (%i)\n", instr_typename[i], (float) iTypeCount[i] * 100.0 / totaltime, iTypeCount[i]); + } + + free(pOpAddrTable); + free(pProfTable); + return 0; +} + +int AddrCompare(const void *p1, const void *p2) +{ + const r4300op *pOp1 = (const r4300op *) p1; + const r4300op *pOp2 = (const r4300op *) p2; + + if (pOp1->x86addr < pOp2->x86addr) + return -1; + else if (pOp1->x86addr == pOp2->x86addr) + return (int) (pOp1 - pOp2); /* this forces qsort to be stable */ + else + return 1; +} + +int ParseProfLine(const char *pchIn, long *plAddress, int *piSamples, float *pfPercentage) +{ + char chVal[128], *pchOut; + + /* skip any initial whitespace */ + while (isSpace(*pchIn)) pchIn++; + if (!isHex(*pchIn)) return 0; + + /* parse hexadecimal address value */ + pchOut = chVal; + while (isHex(*pchIn)) *pchOut++ = *pchIn++; + *pchOut = 0; + if (!isSpace(*pchIn)) return 0; + *plAddress = strtol(chVal, NULL, 16); + + /* skip more whitespace */ + while (isSpace(*pchIn)) pchIn++; + if (!isNum(*pchIn)) return 0; + + /* parse decimal sample count value */ + pchOut = chVal; + while (isNum(*pchIn)) *pchOut++ = *pchIn++; + *pchOut = 0; + if (!isSpace(*pchIn)) return 0; + *piSamples = atoi(chVal); + + /* skip more whitespace */ + while (isSpace(*pchIn)) pchIn++; + if (!isFloat(*pchIn)) return 0; + + /* parse floating-point percentage value */ + pchOut = chVal; + while (isFloat(*pchIn)) *pchOut++ = *pchIn++; + *pchOut = 0; + if (!isSpace(*pchIn) && *pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0; + *pfPercentage = atof(chVal); + + /* if this isn't the end of the line, it's not a valid sample point */ + while (isSpace(*pchIn)) pchIn++; + if (*pchIn != '\r' && *pchIn != '\n' && *pchIn != 0) return 0; + + return 1; +} + +static int InstrTypeStd[64] = +{ + -1, -1, 02, 03, 04, 05, 06, 07, 8, 9, 10, 11, 12, 13, 14, 15, + -1, -1, 00, 00, 16, 17, 18, 19, 20, 21, 22, 23, 00, 00, 00, 00, + 24, 25, 27, 26, 28, 29, 31, 30, 32, 33, 35, 34, 37, 38, 36, 01, + 42, 39, 00, 00, 01, 40, 00, 41, 46, 43, 00, 00, 01, 44, 00, 45 +}; + +static int InstrTypeSpecial[64] = +{ + 55, 00, 56, 57, 58, 00, 59, 60, + 61, 62, 00, 00, 63, 01, 00, 00, + 64, 65, 66, 67, 68, 00, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, + 00, 00, 87, 88, 89, 90, 91, 92, + 01, 01, 01, 01, 96, 00, 01, 00, + 93, 00, 94, 95, 97, 00, 98, 99 +}; + +static int InstrTypeRegImm[32] = +{ + 47, 48, 49, 50, 00, 00, 00, 00, 01, 01, 01, 01, 01, 00, 01, 00, + 51, 52, 53, 54, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 +}; + +static int InstrTypeCop1[32] = +{ + 111, 112, 113, 00, 114, 115, 116, 00, + -1, 00, 00, 00, 00, 00, 00, 00, + -1, -1, 00, 00, -1, -1, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00 +}; + +static int InstrTypeCop1Math[64] = +{ + 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 127, 128, 129, 130, + 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, + 117, 117, 00, 00, 117, 117, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, + 118, 118, 118, 118, 118, 118, 118, 118, + 118, 118, 118, 118, 118, 118, 118, 118 +}; + + +int GetInstrType(int opcode) +{ + int iType = (opcode >> 26) & 63; + + if (iType == 0) + { + /* SPECIAL instruction */ + iType = opcode & 63; + return InstrTypeSpecial[iType]; + } + else if (iType == 1) + { + /* REGIMM instruction */ + iType = (opcode >> 16) & 31; + return InstrTypeRegImm[iType]; + } + else if (iType == 16) + { + /* COP0 instruction */ + int iType1 = opcode & 0x01FFFFFF; + int iType2 = (opcode >> 21) & 31; + if (iType1 == 1) + return 106; // TLBR + else if (iType1 == 2) + return 104; // TLBWI + else if (iType1 == 6) + return 107; // TLBWR + else if (iType1 == 8) + return 105; // TLBP + else if (iType1 == 24) + return 108; // ERET + else if ((opcode & 0x7FF) == 0 && iType2 == 0) + return 109; // MFC0 + else if ((opcode & 0x7FF) == 0 && iType2 == 4) + return 110; // MTC0 + else + return 0; // reserved + } + else if (iType == 17) + { + /* COP1 instruction */ + int iType1 = (opcode >> 21) & 31; + if (iType1 == 8) + { + /* conditional branch */ + int iType2 = (opcode >> 16) & 31; + if (iType2 == 0) + return 100; // BC1F + else if (iType2 == 1) + return 101; // BC1T + else if (iType2 == 2) + return 102; // BC1FL + else if (iType2 == 3) + return 103; // BC1TL + else + return 0; // reserved + } + else if (iType1 == 16 || iType1 == 17 || iType1 == 20 || iType1 == 21) + { + /* Single, Double, Word, Long instructions */ + int iType2 = opcode & 63; + return InstrTypeCop1Math[iType2]; + } + else + { + /* other Cop1 (move) */ + return InstrTypeCop1[iType1]; + } + } + + /* standard MIPS instruction */ + return InstrTypeStd[iType]; +} +