--- hatari/src/debug/profile.c 2019/04/09 08:53:06 1.1.1.3 +++ hatari/src/debug/profile.c 2019/04/09 08:54:24 1.1.1.4 @@ -24,6 +24,8 @@ const char Profile_fileid[] = "Hatari pr #include "profile_priv.h" #include "symbols.h" +profile_loop_t profile_loop; + /* ------------------ CPU/DSP caller information handling ----------------- */ @@ -494,27 +496,29 @@ void Profile_FreeCallinfo(callinfo_t *ca char *Profile_Match(const char *text, int state) { static const char *names[] = { - "addresses", "callers", "counts", "cycles", "misses", + "addresses", "callers", "counts", "cycles", "loops", "misses", "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, ARRAYSIZE(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- misses [count]\n" + "\t- symbols [count]\n" + "\t- addresses [address]\n" + "\t- callers\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" @@ -530,10 +534,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 information can be saved with 'save'."; + "\tProfile address and callers information can be saved with\n" + "\t'save' command.\n" + "\n" + "\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)."; /** @@ -573,6 +583,65 @@ 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; + } + } + return true; +} + +/** * Command: CPU/DSP profiling enabling, exec stats, cycle and call stats. * Returns DEBUGGER_CMDDONE or DEBUGGER_CMDCONT. */ @@ -584,7 +653,7 @@ int Profile_Command(int nArgc, char *psA if (nArgc > 2) { show = atoi(psArgs[2]); - } + } if (bForDsp) { Profile_DspGetPointers(&enabled, &disasm_addr); } else { @@ -654,8 +723,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]); }