--- hatari/src/debug/profile.c 2019/04/09 08:53:06 1.1.1.3 +++ hatari/src/debug/profile.c 2019/04/09 08:58:05 1.1.1.7 @@ -1,7 +1,7 @@ /* * Hatari - profile.c * - * Copyright (C) 2010-2013 by Eero Tamminen + * Copyright (C) 2010-2015 by Eero Tamminen * * This file is distributed under the GNU General Public License, version 2 * or at your option any later version. Read the file gpl.txt for details. @@ -20,9 +20,11 @@ const char Profile_fileid[] = "Hatari pr #include "configuration.h" #include "clocks_timings.h" #include "evaluate.h" +#include "symbols.h" #include "profile.h" #include "profile_priv.h" -#include "symbols.h" + +profile_loop_t profile_loop; /* ------------------ CPU/DSP caller information handling ----------------- */ @@ -70,9 +72,10 @@ static bool output_counter_info(FILE *fp */ fprintf(fp, " %"PRIu64"/%"PRIu64"/%"PRIu64"", counter->calls, counter->count, counter->cycles); - if (counter->misses) { + if (counter->i_misses) { /* these are only with specific WinUAE CPU core */ - fprintf(fp, "/%"PRIu64"", counter->misses); + fprintf(fp, "/%"PRIu64"/%"PRIu64"", + counter->i_misses, counter->d_hits); } return true; } @@ -88,7 +91,7 @@ static void output_caller_info(FILE *fp, if (info->flags) { /* calltypes supported? */ fputc(' ', fp); typecount = 0; - for (k = 0; k < ARRAYSIZE(flaginfo); k++) { + for (k = 0; k < ARRAY_SIZE(flaginfo); k++) { if (info->flags & flaginfo[k].bit) { fputc(flaginfo[k].chr, fp); typecount++; @@ -125,10 +128,10 @@ void Profile_ShowCallers(FILE *fp, int s /* legend */ fputs("# : = [ [ ]], ..., ", fp); fputs("\n# types: ", fp); - for (i = 0; i < ARRAYSIZE(flaginfo); i++) { + for (i = 0; i < ARRAY_SIZE(flaginfo); i++) { fprintf(fp, "%c = %s, ", flaginfo[i].chr, flaginfo[i].info); } - fputs("\n# totals: calls/instructions/cycles/misses\n", fp); + fputs("\n# totals: calls/instructions/cycles/i-misses/d-hits\n", fp); countdiff = 0; countissues = 0; @@ -187,7 +190,8 @@ static void add_counter_costs(counters_t dst->calls += src->calls; dst->count += src->count; dst->cycles += src->cycles; - dst->misses += src->misses; + dst->i_misses += src->i_misses; + dst->d_hits += src->d_hits; } /** @@ -198,7 +202,8 @@ static void set_counter_diff(counters_t dst->calls = ref->calls - dst->calls; dst->count = ref->count - dst->count; dst->cycles = ref->cycles - dst->cycles; - dst->misses = ref->misses - dst->misses; + dst->i_misses = ref->i_misses - dst->i_misses; + dst->d_hits = ref->d_hits - dst->d_hits; } /** @@ -400,7 +405,7 @@ Uint32 Profile_CallEnd(callinfo_t *calli /** * Add costs to all functions still in call stack */ -void Profile_FinalizeCalls(callinfo_t *callinfo, counters_t *totalcost, const char* (*get_symbol)(Uint32 addr)) +void Profile_FinalizeCalls(callinfo_t *callinfo, counters_t *totalcost, const char* (*get_symbol)(Uint32, symtype_t)) { Uint32 addr; if (!callinfo->depth) { @@ -410,7 +415,8 @@ void Profile_FinalizeCalls(callinfo_t *c while (callinfo->depth > 0) { Profile_CallEnd(callinfo, totalcost); addr = callinfo->stack[callinfo->depth].callee_addr; - fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", addr, get_symbol(addr), + fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", + addr, get_symbol(addr, SYMTYPE_TEXT), callinfo->stack[callinfo->depth].ret_addr); } } @@ -423,7 +429,7 @@ static void Profile_ShowStack(bool forDs int i; Uint32 addr; callinfo_t *callinfo; - const char* (*get_symbol)(Uint32 addr); + const char* (*get_symbol)(Uint32, symtype_t); if (forDsp) { Profile_DspGetCallinfo(&callinfo, &get_symbol); @@ -437,8 +443,9 @@ static void Profile_ShowStack(bool forDs for (i = 0; i < callinfo->depth; i++) { addr = callinfo->stack[i].callee_addr; - fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", addr, - get_symbol(addr), callinfo->stack[i].ret_addr); + fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", + addr, get_symbol(addr, SYMTYPE_TEXT), + callinfo->stack[i].ret_addr); } } @@ -494,35 +501,41 @@ void Profile_FreeCallinfo(callinfo_t *ca char *Profile_Match(const char *text, int state) { static const char *names[] = { - "addresses", "callers", "counts", "cycles", "misses", - "off", "on", "save", "stack", "stats", "symbols" + "addresses", "callers", "caches", "counts", "cycles", "d-hits", "i-misses", + "loops", "off", "on", "save", "stack", "stats", "symbols" }; - static int i, len; - - if (!state) - { - /* first match */ - i = 0; - len = strlen(text); - } - /* next match */ - while (i < ARRAYSIZE(names)) { - if (strncasecmp(names[i++], text, len) == 0) - return (strdup(names[i-1])); - } - return NULL; + return DebugUI_MatchHelper(names, ARRAY_SIZE(names), text, state); } const char Profile_Description[] = - " [count|address|file]\n" + " [parameter]\n" + "\n" + "\tSubcommands:\n" + "\t- on\n" + "\t- off\n" + "\t- counts [count]\n" + "\t- cycles [count]\n" + "\t- i-misses [count]\n" + "\t- d-hits [count]\n" + "\t- symbols [count]\n" + "\t- addresses [address]\n" + "\t- callers\n" + "\t- caches\n" + "\t- stack\n" + "\t- stats\n" + "\t- save \n" + "\t- loops [CPU limit] [DSP limit]\n" + "\n" "\t'on' & 'off' enable and disable profiling. Data is collected\n" "\tuntil debugger is entered again at which point you get profiling\n" "\tstatistics ('stats') summary.\n" "\n" "\tThen you can ask for list of the PC addresses, sorted either by\n" - "\texecution 'counts', used 'cycles' or cache 'misses'. First can\n" - "\tbe limited just to named addresses with 'symbols'. Optional\n" - "\tcount will limit how many items will be shown.\n" + "\texecution 'counts', used 'cycles', i-cache misses or d-cache hits.\n" + "\tFirst can be limited just to named addresses with 'symbols'.\n" + "\tOptional count will limit how many items will be shown.\n" + "\n" + "\t'caches' shows histogram of CPU cache usage.\n" "\n" "\t'addresses' lists the profiled addresses in order, with the\n" "\tinstructions (currently) residing at them. By default this\n" @@ -530,10 +543,16 @@ const char Profile_Description[] = "\tspecify the starting address.\n" "\n" "\t'callers' shows (raw) caller information for addresses which\n" - "\thad symbol(s) associated with them. 'stack' shows the currect\n" - "\tprofile stack, this is useful only with :noinit breakpoints.\n" + "\thad symbol(s) associated with them. 'stack' shows the current\n" + "\tprofile stack (this is useful only with :noinit breakpoints).\n" + "\n" + "\tProfile address and callers information can be saved with\n" + "\t'save' command.\n" "\n" - "\tProfile information can be saved with 'save'."; + "\tDetailed (spin) looping information can be collected by\n" + "\tspecifying to which file it should be saved, with optional\n" + "\tlimit(s) on how many bytes first and last instruction\n" + "\taddress of the loop can differ (0 = no limit)."; /** @@ -553,7 +572,7 @@ static bool Profile_Save(const char *fna freq = MachineClocks.DSP_Freq; proc = "DSP"; } else { - freq = MachineClocks.CPU_Freq; + freq = MachineClocks.CPU_Freq_Emul; proc = "CPU"; } #if ENABLE_WINUAE_CPU @@ -573,6 +592,67 @@ static bool Profile_Save(const char *fna } /** + * function CPU & DSP profiling functionality can call to + * reset loop information log by truncating it. Only portable + * way to do that is re-opening it again. + */ +bool Profile_LoopReset(void) +{ + if (!profile_loop.filename) { + return false; + } + if (profile_loop.fp) { + fclose(profile_loop.fp); + } + profile_loop.fp = fopen(profile_loop.filename, "w"); + if (!profile_loop.fp) { + return false; + } + fprintf(profile_loop.fp, "#
\n"); + return true; +} + +/** + * Open file common to both CPU and DSP profiling. + */ +static bool Profile_Loops(int nArgc, char *psArgs[]) +{ + if (nArgc > 2) { + /* check that the given file can be opened for writing */ + if (profile_loop.filename) { + free(profile_loop.filename); + } + profile_loop.filename = strdup(psArgs[2]); + if (Profile_LoopReset()) { + if (nArgc > 3) { + profile_loop.cpu_limit = atoi(psArgs[3]); + if (nArgc > 4) { + profile_loop.dsp_limit = atoi(psArgs[4]); + } + } + fprintf(stderr, "Additional max %d (CPU) & %d (DSP) byte loop profiling enabled to:\n\t%s\n", + profile_loop.cpu_limit, profile_loop.cpu_limit, psArgs[2]); + } else { + free(profile_loop.filename); + profile_loop.filename = NULL; + perror("ERROR: opening profile loop output file failed, disabling!"); + return false; + } + } else { + if (profile_loop.fp) { + fprintf(stderr, "Disabling loop profiling.\n"); + free(profile_loop.filename); + profile_loop.filename = NULL; + fclose(profile_loop.fp); + profile_loop.fp = NULL; + } else { + fprintf(stderr, "ERROR: no file name for saving the loop profiling information.\n"); + } + } + return true; +} + +/** * Command: CPU/DSP profiling enabling, exec stats, cycle and call stats. * Returns DEBUGGER_CMDDONE or DEBUGGER_CMDCONT. */ @@ -584,7 +664,7 @@ int Profile_Command(int nArgc, char *psA if (nArgc > 2) { show = atoi(psArgs[2]); - } + } if (bForDsp) { Profile_DspGetPointers(&enabled, &disasm_addr); } else { @@ -602,9 +682,9 @@ int Profile_Command(int nArgc, char *psA lower = *disasm_addr; } if (bForDsp) { - *disasm_addr = Profile_DspShowAddresses(lower, upper, stdout); + *disasm_addr = Profile_DspShowAddresses(lower, upper, stdout, PAGING_ENABLED); } else { - *disasm_addr = Profile_CpuShowAddresses(lower, upper, stdout); + *disasm_addr = Profile_CpuShowAddresses(lower, upper, stdout, PAGING_ENABLED); } return DEBUGGER_CMDCONT; @@ -622,11 +702,23 @@ int Profile_Command(int nArgc, char *psA } else { Profile_CpuShowStats(); } - } else if (strcmp(psArgs[1], "misses") == 0) { + } else if (strcmp(psArgs[1], "i-misses") == 0) { + if (bForDsp) { + fprintf(stderr, "Cache information is recorded only for CPU, not DSP.\n"); + } else { + Profile_CpuShowInstrMisses(show); + } + } else if (strcmp(psArgs[1], "d-hits") == 0) { if (bForDsp) { - fprintf(stderr, "Cache misses are recorded only for CPU, not DSP.\n"); + fprintf(stderr, "Cache information is recorded only for CPU, not DSP.\n"); } else { - Profile_CpuShowMisses(show); + Profile_CpuShowDataHits(show); + } + } else if (strcmp(psArgs[1], "caches") == 0) { + if (bForDsp) { + fprintf(stderr, "Cache information is recorded only for CPU, not DSP.\n"); + } else { + Profile_CpuShowCaches(); } } else if (strcmp(psArgs[1], "cycles") == 0) { if (bForDsp) { @@ -654,8 +746,13 @@ int Profile_Command(int nArgc, char *psA } } else if (strcmp(psArgs[1], "stack") == 0) { Profile_ShowStack(bForDsp); + } else if (strcmp(psArgs[1], "save") == 0) { Profile_Save(psArgs[2], bForDsp); + + } else if (strcmp(psArgs[1], "loops") == 0) { + Profile_Loops(nArgc, psArgs); + } else { DebugUI_PrintCmdHelp(psArgs[0]); }