--- hatari/src/debug/profilecpu.c 2019/04/09 08:53:05 1.1 +++ hatari/src/debug/profilecpu.c 2019/04/09 08:54:23 1.1.1.2 @@ -25,6 +25,15 @@ const char Profilecpu_fileid[] = "Hatari #include "stMemory.h" #include "symbols.h" #include "tos.h" +#include "screen.h" +#include "video.h" + + +/* cartridge area */ +#define CART_START 0xFA0000 +#define CART_END 0xFC0000 +#define CART_SIZE (CART_END - CART_START) + /* if non-zero, output (more) warnings on suspicious: * - cycle/instruction counts @@ -66,9 +75,12 @@ static struct { profile_area_t tos; /* ROM TOS stats */ int active; /* number of active data items in all areas */ Uint32 *sort_arr; /* data indexes used for sorting */ + int prev_family; /* previous instruction opcode family */ Uint32 prev_cycles; /* previous instruction cycles counter */ Uint32 prev_pc; /* previous instruction address */ - int prev_family; /* previous instruction opcode family */ + Uint32 loop_start; /* address of last loop start */ + Uint32 loop_end; /* address of last loop end */ + Uint32 loop_count; /* how many times it was looped */ Uint32 disasm_addr; /* 'addresses' command start address */ bool processed; /* true when data is already processed */ bool enabled; /* true when profiling enabled */ @@ -92,25 +104,31 @@ static inline Uint32 address2index(Uint3 DebugUI(REASON_CPU_EXCEPTION); #endif } - if (pc >= TosAddress && pc < TosAddress + TosSize) { + if (pc < STRamEnd) { + /* most likely case, use RAM address as-is */ + + } else if (pc >= TosAddress && pc < TosAddress + TosSize) { /* TOS, put it after RAM data */ pc = pc - TosAddress + STRamEnd; - - } else if (pc >= 0xFA0000 && pc < 0xFC0000) { - /* ROM, put it after RAM & TOS data */ - pc = pc - 0xFA0000 + STRamEnd + TosSize; - + if (TosAddress >= CART_END) { + /* and after cartridge data as it's higher */ + pc += CART_SIZE; + } + } else if (pc >= CART_START && pc < CART_END) { + /* ROM, put it after RAM data */ + pc = pc - CART_START + STRamEnd; + if (TosAddress < CART_START) { + /* and after TOS as it's higher */ + pc += TosSize; + } } else { - /* if in RAM, use as-is */ - if (unlikely(pc >= STRamEnd)) { - fprintf(stderr, "WARNING: 'invalid' CPU PC profile instruction address 0x%x!\n", pc); - /* extra entry at end is reserved for invalid PC values */ - pc = STRamEnd + TosSize + 0x20000; + fprintf(stderr, "WARNING: 'invalid' CPU PC profile instruction address 0x%x!\n", pc); + /* extra entry at end is reserved for invalid PC values */ + pc = STRamEnd + TosSize + 0x20000; #if DEBUG - skip_assert = true; - DebugUI(REASON_CPU_EXCEPTION); + skip_assert = true; + DebugUI(REASON_CPU_EXCEPTION); #endif - } } /* CPU instructions are at even addresses, save space by halving */ return (pc >> 1); @@ -126,13 +144,25 @@ static Uint32 index2address(Uint32 idx) if (idx < STRamEnd) { return idx; } - /* TOS */ idx -= STRamEnd; - if (idx < TosSize) { + /* TOS before cartridge area? */ + if (TosAddress < CART_START) { + /* TOS */ + if (idx < TosSize) { + return idx + TosAddress; + } + idx -= TosSize; + /* ROM */ + return idx + CART_START; + } else { + /* ROM */ + if (idx < CART_SIZE) { + return idx + CART_START; + } + idx -= CART_SIZE; + /* TOS */ return idx + TosAddress; } - /* ROM */ - return idx - TosSize + 0xFA0000; } /* ------------------ CPU profile results ----------------- */ @@ -205,7 +235,7 @@ void Profile_CpuShowStats(void) fprintf(stderr, "ROM TOS (0x%X-0x%X):\n", TosAddress, TosAddress + TosSize); show_cpu_area_stats(&cpu_profile.tos); - fprintf(stderr, "Cartridge ROM (0xFA0000-0xFC0000):\n"); + fprintf(stderr, "Cartridge ROM (0x%X-%X):\n", CART_START, CART_END); show_cpu_area_stats(&cpu_profile.rom); fprintf(stderr, "\n= %.5fs\n", @@ -550,8 +580,8 @@ void Profile_CpuSave(FILE *out) if (text < TosAddress) { fprintf(out, "PROGRAM_TEXT:\t0x%06x-0x%06x\n", text, DebugInfo_GetTEXTEnd()); } - fprintf(out, "CARTRIDGE:\t0xfa0000-0xfc0000\n"); - Profile_CpuShowAddresses(0, 0xFC0000-2, out); + fprintf(out, "CARTRIDGE:\t0x%06x-0x%06x\n", CART_START, CART_END); + Profile_CpuShowAddresses(0, CART_END-2, out); Profile_CpuShowCallers(out); } @@ -605,6 +635,11 @@ bool Profile_CpuStart(void) cpu_profile.prev_family = OpcodeFamily; cpu_profile.prev_pc = M68000_GetPC() & 0xffffff; + cpu_profile.loop_start = PC_UNDEFINED; + cpu_profile.loop_end = PC_UNDEFINED; + cpu_profile.loop_count = 0; + Profile_LoopReset(); + cpu_profile.disasm_addr = 0; cpu_profile.processed = false; cpu_profile.enabled = true; @@ -692,8 +727,8 @@ static void collect_calls(Uint32 pc, cou if (unlikely(pc == cpu_callinfo.return_pc) && likely(cpu_callinfo.depth)) { flag = cpu_opcode_type(family, prev_pc, pc); - /* previous address can be exception return (RTE) if exception - * occurred right after returning from subroutine call (RTS) + /* previous address can be exception return (e.g. RTE) instead of RTS, + * if exception occurred right after returning from subroutine call. */ if (likely(flag == CALL_SUBRETURN || flag == CALL_EXCRETURN)) { caller_pc = Profile_CallEnd(&cpu_callinfo, counters); @@ -703,11 +738,11 @@ static void collect_calls(Uint32 pc, cou * e.g. because there was a jsr or jump to return address */ Uint32 nextpc; - fprintf(stderr, "WARNING: subroutine call returned 0x%x -> 0x%x, not though RTS!\n", prev_pc, pc); + fprintf(stderr, "WARNING: subroutine call returned 0x%x -> 0x%x, not through RTS!\n", prev_pc, pc); Disasm(stderr, prev_pc, &nextpc, 1); #endif } - /* next address might be another function, so need to fall through */ + /* next address might be another symbol, so need to fall through */ } /* address is one which we're tracking? */ @@ -715,7 +750,7 @@ static void collect_calls(Uint32 pc, cou if (unlikely(idx >= 0)) { flag = cpu_opcode_type(family, prev_pc, pc); - if (flag == CALL_SUBROUTINE) { + if (flag == CALL_SUBROUTINE || flag == CALL_EXCEPTION) { /* special HACK for for EmuTOS AES switcher which * changes stack content to remove itself from call * stack and uses RTS for subroutine *calls*, not @@ -743,7 +778,7 @@ static void collect_calls(Uint32 pc, cou cpu_callinfo.return_pc = Disasm_GetNextPC(prev_pc); } } else if (caller_pc != PC_UNDEFINED) { - /* returned from function to first instrction of another symbol: + /* returned from function to first instruction of another symbol: * 0xf384 jsr some_function * other_symbol: * 0f3x8a some_instruction @@ -759,6 +794,18 @@ static void collect_calls(Uint32 pc, cou } /** + * log last loop info, if there's suitable data for one + */ +static void log_last_loop(void) +{ + unsigned len = cpu_profile.loop_end - cpu_profile.loop_start; + if (cpu_profile.loop_count > 1 && (len < profile_loop.cpu_limit || !profile_loop.cpu_limit)) { + fprintf(profile_loop.fp, "CPU %d 0x%06x %d %d\n", nVBLs, + cpu_profile.loop_start, len, cpu_profile.loop_count); + } +} + +/** * Update CPU cycle and count statistics for PC address. * * This gets called after instruction has executed and PC @@ -776,6 +823,24 @@ void Profile_CpuUpdate(void) */ cpu_profile.prev_pc = pc = M68000_GetPC() & 0xffffff; + if (unlikely(profile_loop.fp)) { + if (pc < prev_pc) { + if (pc == cpu_profile.loop_start && prev_pc == cpu_profile.loop_end) { + cpu_profile.loop_count++; + } else { + cpu_profile.loop_start = pc; + cpu_profile.loop_end = prev_pc; + cpu_profile.loop_count = 1; + } + } else { + if (pc > cpu_profile.loop_end) { + log_last_loop(); + cpu_profile.loop_end = 0xffffffff; + cpu_profile.loop_count = 0; + } + } + } + idx = address2index(prev_pc); assert(idx <= cpu_profile.size); prev = cpu_profile.data + idx; @@ -924,6 +989,12 @@ void Profile_CpuStop(void) if (cpu_profile.processed || !cpu_profile.enabled) { return; } + + log_last_loop(); + if (profile_loop.fp) { + fflush(profile_loop.fp); + } + /* user didn't change RAM or TOS size in the meanwhile? */ assert(cpu_profile.size == (STRamEnd + 0x20000 + TosSize) / 2);