Skip to content

Instantly share code, notes, and snippets.

@erique
Last active October 28, 2022 22:28
Show Gist options
  • Save erique/8721cf413e5b2771f36d44e72a10294d to your computer and use it in GitHub Desktop.
Save erique/8721cf413e5b2771f36d44e72a10294d to your computer and use it in GitHub Desktop.
Quick hack to monitor hit count, ECLK ticks and rough CPU usage for each interrupt level
// m68k-amigaos-gcc irq_perf.c -o irq_perf -noixemul -O2 -m68020 --omit-frame-pointer
#include <stdio.h>
#include <stdarg.h>
#include <proto/exec.h>
#include <proto/timer.h>
#include <exec/execbase.h>
#include <devices/timer.h>
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#define RawPutChar(___ch) \
LP1NR(516, RawPutChar , BYTE, ___ch, d0,\
, EXEC_BASE_NAME)
void raw_put_char(uint32_t c __asm("d0"), struct ExecBase* SysBase __asm("a3"))
{
RawPutChar(c);
}
void kprintf(const char *format, ...)
{
va_list args;
struct ExecBase* SysBase = *((struct ExecBase **) (4L));
va_start(args, format);
// 115200 bps 8N1
__asm volatile("move.w #(3546895/115200),0xdff032": : : "cc", "memory");
RawDoFmt((STRPTR) format, (APTR) args,
(__fpt) raw_put_char, (APTR) SysBase);
va_end(args);
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
struct Device* TimerBase;
struct TimerData
{
uint32_t hits;
uint32_t ticks;
struct IntVector vector;
};
void perfVector( uint32_t d0 __asm("d0"),
uint32_t req __asm("d1"),
uint32_t custom __asm("a0"),
void* data __asm("a1"),
uint32_t jmp __asm("a5"),
uint32_t exec __asm("a6") )
{
struct TimerData* perf = (struct TimerData*)data;
struct EClockVal sample;
ReadEClock(&sample);
perf->ticks -= sample.ev_lo;
struct IntVector* vector = &perf->vector;
typedef void (*ISR)(uint32_t d0 __asm("d0"),
uint32_t req __asm("d1"),
uint32_t custom __asm("a0"),
void* data __asm("a1"),
uint32_t jmp __asm("a5"),
uint32_t exec __asm("a6"));
ISR code = vector->iv_Code;
data = vector->iv_Data;
code(d0, req, custom, data, jmp, exec);
ReadEClock(&sample);
perf->ticks += sample.ev_lo;
perf->hits++;
}
#define NUM_INT_LEVELS (16)
struct TimerData perfData[NUM_INT_LEVELS] = { 0 };
struct Interrupt* wrappedSetIntVector(ULONG intNumber __asm("d0"),
struct Interrupt* interrupt __asm("a1"),
struct ExecBase* SysBase __asm("a6"))
{
struct IntVector* vector = &perfData[intNumber].vector;
Disable();
struct Interrupt* old = (struct Interrupt*)vector->iv_Node;
if ((vector->iv_Node = &interrupt->is_Node))
{
vector->iv_Data = interrupt->is_Data;
vector->iv_Code = interrupt->is_Code;
}
else
{
vector->iv_Data = (void*)-1;
vector->iv_Code = (void*)-1;
}
Enable();
return old;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int main()
{
struct MsgPort* timerPort = CreateMsgPort();
struct timerequest* timeReq = CreateIORequest(timerPort, sizeof(struct timerequest));
if (OpenDevice((STRPTR)TIMERNAME, UNIT_MICROHZ, (struct IORequest*)timeReq, 0))
{
printf("failed to open timer.device\n");
return -1;
}
TimerBase = timeReq->tr_node.io_Device;
static const uint8_t intLevels = NUM_INT_LEVELS;
const char* vecNames[NUM_INT_LEVELS] =
{
"TBE ",
"DSKBLK",
"SOFT ",
"PORTS ",
"COPER ",
"VERTB ",
"BLIT ",
"AUD0 ",
"AUD1 ",
"AUD2 ",
"AUD3 ",
"RBF ",
"DSKSYN",
"EXTER ",
"INTEN ",
"NMI "
};
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Disable();
for(int16_t i = intLevels-1; i >= 0; --i)
{
struct IntVector* vector = &SysBase->IntVects[i];
struct TimerData* perf = &perfData[i];
perf->vector = *vector;
vector->iv_Data = perf;
vector->iv_Code = perfVector;
vector->iv_Node = 0;
}
void* oldFunc = SetFunction((struct Library*)SysBase, -162, (APTR) wrappedSetIntVector);
Enable();
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
printf("ctrl-c to exit; output via serial (115kbps 8N1)\n");
struct EClockVal lastSample;
ReadEClock(&lastSample);
while(1)
{
timeReq->tr_node.io_Command = TR_ADDREQUEST;
timeReq->tr_time.tv_sec = 1;
timeReq->tr_time.tv_micro = 0;
SendIO((struct IORequest*)timeReq);
uint32_t signals = Wait( (1L << timerPort->mp_SigBit) | SIGBREAKF_CTRL_C);
AbortIO((struct IORequest*)timeReq);
WaitIO((struct IORequest*)timeReq);
if (signals & SIGBREAKF_CTRL_C)
break;
kprintf("\033[2J");
struct TimerData currPerf[NUM_INT_LEVELS] = { 0 };
Disable();
for(int16_t i = intLevels-1; i >= 0; --i)
{
currPerf[i] = perfData[i];
perfData[i].hits = 0;
perfData[i].ticks = 0;
}
Enable();
struct EClockVal currSample;
ReadEClock(&currSample);
uint64_t prev = *((uint64_t*)&lastSample);
uint64_t curr = *((uint64_t*)&currSample);
uint64_t delta = curr - prev;
for(int16_t i = intLevels-1; i >= 0; --i)
{
uint32_t ratio = (uint32_t)100 * (uint32_t)currPerf[i].ticks / (uint32_t)delta;
kprintf(" %s = [%4ld] = %6ld = %3ld%%%\n", vecNames[i], currPerf[i].hits, currPerf[i].ticks, ratio);
}
lastSample = currSample;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Disable();
SetFunction((struct Library*)SysBase, -162, (APTR) oldFunc);
for(int16_t i = intLevels-1; i >= 0; --i)
{
struct IntVector* vector = &SysBase->IntVects[i];
struct TimerData* perf = &perfData[i];
*vector = perf->vector;
}
Enable();
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CloseDevice((struct IORequest*)timeReq);
DeleteIORequest(timeReq);
DeleteMsgPort(timerPort);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment