Get profiler working

This commit is contained in:
James Lambert 2023-12-09 22:31:55 -07:00
parent e50e0a32ac
commit 7e10fc5d2f
4 changed files with 135 additions and 1 deletions

3
.gitignore vendored
View file

@ -24,3 +24,6 @@ tools/vtf2png
skelatool64/*.zip*
src/controls/controller-data.h
log.txt

View file

@ -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,

View file

@ -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();

124
tools/profile_parser.js Normal file
View file

@ -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));
}