From 7e10fc5d2fde441d44043be9c699f2f72ef289ea Mon Sep 17 00:00:00 2001 From: James Lambert Date: Sat, 9 Dec 2023 22:31:55 -0700 Subject: [PATCH] Get profiler working --- .gitignore | 3 + src/graphics/profile_task.c | 5 ++ src/main.c | 4 +- tools/profile_parser.js | 124 ++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tools/profile_parser.js diff --git a/.gitignore b/.gitignore index 8f3e5b8..eac14e9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ tools/vtf2png skelatool64/*.zip* src/controls/controller-data.h + + +log.txt \ No newline at end of file diff --git a/src/graphics/profile_task.c b/src/graphics/profile_task.c index 0f6dfe7..a26806e 100644 --- a/src/graphics/profile_task.c +++ b/src/graphics/profile_task.c @@ -75,8 +75,13 @@ OSTime profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task) { u64 us = OS_CYCLES_TO_NSEC(result); + // wait for DP to be available + while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID)); + copyGfx(tmp, curr, 3); + osWritebackDCacheAll(); + char message[64]; int messageLen = sprintf( message, diff --git a/src/main.c b/src/main.c index f5cb379..36870b4 100644 --- a/src/main.c +++ b/src/main.c @@ -330,7 +330,9 @@ static void gameProc(void* arg) { } if (controllerGetButtonDown(0, R_JPAD)) { - profileTask(&scheduler, &gameThread, &gGraphicsTasks[drawBufferIndex ^ 1].task.list); + struct GraphicsTask* task = &gGraphicsTasks[drawBufferIndex]; + zeroMemory(task->framebuffer, sizeof(u16) * SCREEN_WD * SCREEN_HT); + profileTask(&scheduler, &gameThread, &task->task.list); } timeUpdateDelta(); soundPlayerUpdate(); diff --git a/tools/profile_parser.js b/tools/profile_parser.js new file mode 100644 index 0000000..862ffbd --- /dev/null +++ b/tools/profile_parser.js @@ -0,0 +1,124 @@ +const fs = require('fs'); + +const lines = fs.readFileSync(process.argv[2], 'utf-8').split('\n'); + +const symbolMapLines = fs.readFileSync(process.argv[3], 'utf-8').split('\n'); + +const lineParserRegexp = /(\d+)\/\d+ 0x([a-f0-9]{2})([a-f0-9]{6})([a-f0-9]{8}) ms (\d+\.\d+)/ + +const symbolParserRegexp = /0x([a-f0-9]{16})\s+(\w+)/ + +function parseLine(line) { + const match = lineParserRegexp.exec(line); + + if (!match) { + return null; + } + + return { + index: parseInt(match[1]), + command: match[2], + w0: match[3], + w1: match[4], + startTime: parseFloat(match[5]), + } +} + +function parseSymbolLine(line) { + const match = symbolParserRegexp.exec(line); + + if (!match) { + return null; + } + + return { + address: match[1].substring(8), + name: match[2], + }; +} + +const linesParsed = lines.map(parseLine).filter(Boolean); + +const symbolAddressMapping = new Map(); + +for (const symbolLine of symbolMapLines) { + const parsedLine = parseSymbolLine(symbolLine); + + if (!parsedLine) { + continue; + } + + if (symbolAddressMapping.has(parsedLine.address)) { + symbolAddressMapping.set(parsedLine.address, symbolAddressMapping.get(parsedLine.address) + ',' + parsedLine.name); + } else { + symbolAddressMapping.set(parsedLine.address, parsedLine.name); + } +} + +const combinedCommands = []; + +for (const parsedLine of linesParsed) { + const existing = combinedCommands[parsedLine.index]; + + if (existing) { + existing.startTime += parsedLine.startTime; + existing.total += 1; + } else { + combinedCommands[parsedLine.index] = { + ...parsedLine, + total: 1, + }; + } +} + +for (let i = 0; i < combinedCommands.length; ++i) { + const current = combinedCommands[i]; + + if (current) { + current.startTime /= current.total; + } +} + +for (let i = 0; i + 1 < combinedCommands.length; ++i) { + const current = combinedCommands[i]; + const next = combinedCommands[i + 1]; + + current.elapsedTime = next.startTime - current.startTime; +} + +// the last command is always a pipe sync we dont care about +combinedCommands.pop(); + +combinedCommands.sort((a, b) => b.elapsedTime - a.elapsedTime); + +function formatAddress(address) { + return symbolAddressMapping.get(address) || `0x${address}`; +} + +function formatCommandName(command) { + switch (command.command) { + case 'db': + { + const segment = parseInt(command.w0.substring(4), 16) / 4; + return `gsSPSegment(0x${segment}, 0x${command.w1})`; + } + case 'de': + return `gsSPDisplayList(${formatAddress(command.w1)})`; + case 'f6': + return `gsDPFillRectangle`; + case 'd8': + return `gsSPPopMatrix`; + case 'da': + return `gsSPMatrix`; + default: + return `unknown 0x${command.command} 0x${command.w0}${command.w1}`; + } +} + +function formatCommand(command) { + return `${command.elapsedTime} ${formatCommandName(command)}`; +} + +for (const command of combinedCommands) { + console.log(formatCommand(command)); +} \ No newline at end of file