Annotation of hatari/src/debug/profile.c, revision 1.1.1.4

1.1       root        1: /*
                      2:  * Hatari - profile.c
                      3:  * 
1.1.1.3   root        4:  * Copyright (C) 2010-2013 by Eero Tamminen
1.1       root        5:  *
1.1.1.3   root        6:  * This file is distributed under the GNU General Public License, version 2
                      7:  * or at your option any later version. Read the file gpl.txt for details.
1.1       root        8:  *
1.1.1.3   root        9:  * profile.c - profile caller info handling and debugger parsing functions
1.1       root       10:  */
                     11: const char Profile_fileid[] = "Hatari profile.c : " __DATE__ " " __TIME__;
                     12: 
                     13: #include <stdio.h>
1.1.1.3   root       14: #include <assert.h>
                     15: #include <inttypes.h>
1.1       root       16: #include "main.h"
1.1.1.3   root       17: #include "version.h"
                     18: #include "debugui.h"
1.1       root       19: #include "debug_priv.h"
1.1.1.3   root       20: #include "configuration.h"
                     21: #include "clocks_timings.h"
                     22: #include "evaluate.h"
1.1       root       23: #include "profile.h"
1.1.1.3   root       24: #include "profile_priv.h"
1.1       root       25: #include "symbols.h"
                     26: 
1.1.1.4 ! root       27: profile_loop_t profile_loop;
        !            28: 
1.1       root       29: 
1.1.1.3   root       30: /* ------------------ CPU/DSP caller information handling ----------------- */
1.1       root       31: 
1.1.1.3   root       32: static const struct {
                     33:        char chr;
                     34:        calltype_t bit;
                     35:        const char *info;
                     36: } flaginfo[] = {
                     37:        { 'u', CALL_UNKNOWN,    "unknown PC change" },
                     38:        { 'n', CALL_NEXT,       "PC moved to next instruction", },
                     39:        { 'b', CALL_BRANCH,     "branch/jump" },
                     40:        { 's', CALL_SUBROUTINE, "subroutine call" },
                     41:        { 'r', CALL_SUBRETURN,  "return from subroutine" },
                     42:        { 'e', CALL_EXCEPTION,  "exception" },
                     43:        { 'x', CALL_EXCRETURN,  "return from exception" }
                     44: };
                     45: 
                     46: /**
                     47:  * compare function for qsort() to sort caller data by calls
                     48:  */
                     49: static int cmp_callers(const void *c1, const void *c2)
                     50: {
                     51:        Uint32 calls1 = ((const caller_t*)c1)->calls;
                     52:        Uint32 calls2 = ((const caller_t*)c2)->calls;
                     53:        if (calls1 > calls2) {
                     54:                return -1;
1.1       root       55:        }
1.1.1.3   root       56:        if (calls1 < calls2) {
                     57:                return 1;
1.1       root       58:        }
1.1.1.3   root       59:        return 0;
1.1       root       60: }
                     61: 
                     62: /**
1.1.1.3   root       63:  * output caller counter information
1.1       root       64:  */
1.1.1.3   root       65: static bool output_counter_info(FILE *fp, counters_t *counter)
1.1       root       66: {
1.1.1.3   root       67:        if (!counter->count) {
1.1       root       68:                return false;
                     69:        }
1.1.1.3   root       70:        /* number of calls needs to be first and rest must be in the same order as
                     71:         * they're in the profile disassembly (count of instructions, etc...).
                     72:         */
                     73:        fprintf(fp, " %"PRIu64"/%"PRIu64"/%"PRIu64"",
                     74:                counter->calls, counter->count, counter->cycles);
                     75:        if (counter->misses) {
                     76:                /* these are only with specific WinUAE CPU core */
                     77:                fprintf(fp, "/%"PRIu64"", counter->misses);
                     78:        }
                     79:        return true;
1.1       root       80: }
                     81: 
                     82: /**
1.1.1.3   root       83:  * output caller call counts, call type(s) and costs
1.1       root       84:  */
1.1.1.3   root       85: static void output_caller_info(FILE *fp, caller_t *info, Uint32 *typeaddr)
1.1       root       86: {
1.1.1.3   root       87:        int k, typecount;
                     88: 
                     89:        fprintf(fp, "0x%x = %d", info->addr, info->calls);
                     90:        if (info->flags) {      /* calltypes supported? */
                     91:                fputc(' ', fp);
                     92:                typecount = 0;
                     93:                for (k = 0; k < ARRAYSIZE(flaginfo); k++) {
                     94:                        if (info->flags & flaginfo[k].bit) {
                     95:                                fputc(flaginfo[k].chr, fp);
                     96:                                typecount++;
                     97:                        }
                     98:                }
                     99:                if (typecount > 1) {
                    100:                        *typeaddr = info->addr;
                    101:                }
1.1       root      102:        }
1.1.1.3   root      103:        if (output_counter_info(fp, &(info->all))) {
                    104:                output_counter_info(fp, &(info->own));
                    105:                if (info->calls != info->own.calls) {
                    106:                        fprintf(stderr, "WARNING: mismatch between function 0x%x call count %d and own call cost %"PRIu64"!\n",
                    107:                               info->addr, info->calls, info->own.calls);
                    108:                }
1.1       root      109:        }
1.1.1.3   root      110:        fputs(", ", fp);
1.1       root      111: }
                    112: 
1.1.1.3   root      113: /*
                    114:  * Show collected CPU/DSP callee/caller information.
                    115:  *
                    116:  * Hint: As caller info list is based on number of loaded symbols,
                    117:  * load only text symbols to save memory & make things faster...
1.1       root      118:  */
1.1.1.3   root      119: void Profile_ShowCallers(FILE *fp, int sites, callee_t *callsite, const char * (*addr2name)(Uint32, Uint64 *))
1.1       root      120: {
1.1.1.3   root      121:        int i, j, countissues, countdiff;
                    122:        const char *name;
                    123:        caller_t *info;
                    124:        Uint64 total;
                    125:        Uint32 addr, typeaddr;
                    126: 
                    127:        /* legend */
                    128:        fputs("# <callee>: <caller1> = <calls> <types>[ <inclusive/totals>[ <exclusive/totals>]], <caller2> ..., <callee name>", fp);
                    129:        fputs("\n# types: ", fp);
                    130:        for (i = 0; i < ARRAYSIZE(flaginfo); i++) {
                    131:                fprintf(fp, "%c = %s, ", flaginfo[i].chr, flaginfo[i].info);
                    132:        }
                    133:        fputs("\n# totals: calls/instructions/cycles/misses\n", fp);
                    134: 
                    135:        countdiff = 0;
                    136:        countissues = 0;
                    137:        for (i = 0; i < sites; i++, callsite++) {
                    138:                addr = callsite->addr;
                    139:                if (!addr) {
                    140:                        continue;
                    141:                }
                    142:                name = addr2name(addr, &total);
                    143:                fprintf(fp, "0x%x: ", callsite->addr);
                    144: 
                    145:                typeaddr = 0;
                    146:                info = callsite->callers;
                    147:                qsort(info, callsite->count, sizeof(*info), cmp_callers);
                    148:                for (j = 0; j < callsite->count; j++, info++) {
                    149:                        if (!info->calls) {
                    150:                                break;
                    151:                        }
                    152:                        total -= info->calls;
                    153:                        output_caller_info(fp, info, &typeaddr);
                    154:                }
                    155:                if (name) {
                    156:                        fprintf(fp, "%s", name);
                    157:                }
                    158:                fputs("\n", fp);
                    159:                if (total) {
                    160: #if DEBUG
                    161:                        fprintf(stderr, "WARNING: %llu differences in call and instruction counts for '%s'!\n", total, name);
                    162: #endif
                    163:                        countdiff += total;
                    164:                        countissues++;
                    165:                }
                    166:                if (typeaddr) {
                    167:                        fprintf(stderr, "WARNING: different types of calls (at least) from 0x%x (to 0x%x),\n\t has its codechanged during profiling?\n",
                    168:                                typeaddr, callsite->addr);
                    169:                }
                    170:        }
                    171:        if (countissues) {
                    172:                if (countdiff <= 2 && countissues == countdiff) {
                    173:                        fprintf(stderr, "WARNING: callcount mismatches (%d calls) with address instruction\n\t counts in %d cases, most likely profile start & end.\n",
                    174:                                countdiff, countissues);
                    175:                } else {
                    176:                        /* profiler bug: some (address?) mismatch in recording instruction counts and call counts */
                    177:                        fprintf(stderr, "ERROR: callcount mismatches with address instruction counts\n\t(%d in total) detected in %d cases!\n",
                    178:                                countdiff, countissues);
                    179:                }
1.1       root      180:        }
                    181: }
                    182: 
                    183: 
                    184: /**
1.1.1.3   root      185:  * add second counter values to first counters
1.1       root      186:  */
1.1.1.3   root      187: static void add_counter_costs(counters_t *dst, counters_t *src)
1.1       root      188: {
1.1.1.3   root      189:        dst->calls += src->calls;
                    190:        dst->count += src->count;
                    191:        dst->cycles += src->cycles;
                    192:        dst->misses += src->misses;
1.1       root      193: }
                    194: 
                    195: /**
1.1.1.3   root      196:  * set first counter values to their difference from a reference value
1.1       root      197:  */
1.1.1.3   root      198: static void set_counter_diff(counters_t *dst, counters_t *ref)
1.1       root      199: {
1.1.1.3   root      200:        dst->calls = ref->calls - dst->calls;
                    201:        dst->count = ref->count - dst->count;
                    202:        dst->cycles = ref->cycles - dst->cycles;
                    203:        dst->misses = ref->misses - dst->misses;
1.1       root      204: }
                    205: 
                    206: /**
1.1.1.3   root      207:  * add called (callee) function costs to caller information
1.1       root      208:  */
1.1.1.3   root      209: static void add_callee_cost(callee_t *callsite, callstack_t *stack)
1.1       root      210: {
1.1.1.3   root      211:        caller_t *info = callsite->callers;
                    212:        counters_t owncost;
                    213:        int i;
1.1       root      214: 
1.1.1.3   root      215:        for (i = 0; i < callsite->count; i++, info++) {
                    216:                if (info->addr == stack->caller_addr) {
                    217:                        /* own cost for callee is its child (out) costs
                    218:                         * deducted from full (all) costs
                    219:                         */
                    220:                        owncost = stack->out;
                    221:                        set_counter_diff(&owncost, &(stack->all));
                    222:                        add_counter_costs(&(info->own), &owncost);
                    223:                        add_counter_costs(&(info->all), &(stack->all));
                    224:                        return;
                    225:                }
1.1       root      226:        }
1.1.1.3   root      227:        /* cost is only added for updated callers,
                    228:         * so they should always exist
                    229:         */
                    230:        fprintf(stderr, "ERROR: trying to add costs to non-existing 0x%x caller of 0x%x!\n",
                    231:                stack->caller_addr, callsite->addr);
                    232:        assert(0);
1.1       root      233: }
                    234: 
                    235: 
1.1.1.3   root      236: static void add_caller(callee_t *callsite, Uint32 pc, Uint32 prev_pc, calltype_t flag)
1.1       root      237: {
1.1.1.3   root      238:        caller_t *info;
                    239:        int i, count;
1.1       root      240: 
1.1.1.3   root      241:        /* need to store real call addresses as symbols can change
                    242:         * after profiling has been stopped
                    243:         */
                    244:        info = callsite->callers;
                    245:        if (!info) {
                    246:                info = calloc(1, sizeof(*info));
                    247:                if (!info) {
                    248:                        fprintf(stderr, "ERROR: caller info alloc failed!\n");
                    249:                        return;
1.1       root      250:                }
1.1.1.3   root      251:                /* first call to this address, save address */
                    252:                callsite->addr = pc;
                    253:                callsite->callers = info;
                    254:                callsite->count = 1;
1.1       root      255:        }
1.1.1.3   root      256:        /* how many caller slots are currently allocated? */
                    257:        count = callsite->count;
                    258:        for (;;) {
                    259:                for (i = 0; i < count; i++, info++) {
                    260:                        if (info->addr == prev_pc) {
                    261:                                info->flags |= flag;
                    262:                                info->calls++;
                    263:                                return;
                    264:                        }
                    265:                        if (!info->addr) {
                    266:                                /* empty slot */
                    267:                                info->addr = prev_pc;
                    268:                                info->flags |= flag;
                    269:                                info->calls = 1;
                    270:                                return;
                    271:                        }
1.1       root      272:                }
1.1.1.3   root      273:                /* not enough, double caller slots */
                    274:                count *= 2;
                    275:                info = realloc(callsite->callers, count * sizeof(*info));
                    276:                if (!info) {
                    277:                        fprintf(stderr, "ERROR: caller info alloc failed!\n");
                    278:                        return;
1.1       root      279:                }
1.1.1.3   root      280:                memset(info + callsite->count, 0, callsite->count * sizeof(*info));
                    281:                callsite->callers = info;
                    282:                callsite->count = count;
1.1       root      283:        }
                    284: }
                    285: 
                    286: /**
1.1.1.3   root      287:  * Add information about called symbol, and if it was subroutine
                    288:  * call, add it to stack of functions which total costs are tracked.
                    289:  * callinfo.return_pc needs to be set before invoking this if the call
                    290:  * is of type CALL_SUBROUTINE.
1.1       root      291:  */
1.1.1.3   root      292: void Profile_CallStart(int idx, callinfo_t *callinfo, Uint32 prev_pc, calltype_t flag, Uint32 pc, counters_t *totalcost)
1.1       root      293: {
1.1.1.3   root      294:        callstack_t *stack;
                    295:        int count;
1.1       root      296: 
1.1.1.3   root      297:        if (unlikely(idx >= callinfo->sites)) {
                    298:                fprintf(stderr, "ERROR: number of symbols increased during profiling (%d > %d)!\n", idx, callinfo->sites);
1.1       root      299:                return;
                    300:        }
                    301: 
1.1.1.3   root      302:        add_caller(callinfo->site + idx, pc, prev_pc, flag);
1.1       root      303: 
1.1.1.3   root      304:        /* subroutine call which will return? */
                    305:        if (flag != CALL_SUBROUTINE) {
                    306:                /* no, some other call type */
1.1       root      307:                return;
                    308:        }
1.1.1.3   root      309:        /* yes, add it to call stack */
1.1       root      310: 
1.1.1.3   root      311:        if (unlikely(!callinfo->count)) {
                    312:                /* initial stack alloc, can be a bit larger */
                    313:                count = 8;
                    314:                stack = calloc(count, sizeof(*stack));
                    315:                if (!stack) {
                    316:                        fputs("ERROR: callstack alloc failed!\n", stderr);
                    317:                        return;
1.1       root      318:                }
1.1.1.3   root      319:                callinfo->stack = stack;
                    320:                callinfo->count = count;
1.1       root      321: 
1.1.1.3   root      322:        } else if (unlikely(callinfo->depth+1 >= callinfo->count)) {
                    323:                /* need to alloc more stack space for new call? */
                    324:                count = callinfo->count * 2;
                    325:                stack = realloc(callinfo->stack, count * sizeof(*stack));
                    326:                if (!stack) {
                    327:                        fputs("ERROR: callstack alloc failed!\n", stderr);
                    328:                        return;
1.1       root      329:                }
1.1.1.3   root      330:                memset(stack + callinfo->count, 0, callinfo->count * sizeof(*stack));
                    331:                callinfo->stack = stack;
                    332:                callinfo->count = count;
1.1       root      333:        }
                    334: 
1.1.1.3   root      335:        /* only first instruction can be undefined */
                    336:        assert(callinfo->return_pc != PC_UNDEFINED || !callinfo->depth);
1.1       root      337: 
1.1.1.3   root      338:        /* called function */
                    339:        stack = &(callinfo->stack[callinfo->depth++]);
1.1       root      340: 
1.1.1.3   root      341:        /* store current running totals & zero subcall costs */
                    342:        stack->all = *totalcost;
                    343:        memset(&(stack->out), 0, sizeof(stack->out));
1.1       root      344: 
1.1.1.3   root      345:        /* set subroutine call information */
                    346:        stack->ret_addr = callinfo->return_pc;
                    347:        stack->callee_idx = idx;
                    348:        stack->caller_addr = prev_pc;
                    349:        stack->callee_addr = pc;
1.1       root      350: 
1.1.1.3   root      351:        /* record call to this into costs... */
                    352:        totalcost->calls++;
1.1       root      353: }
                    354: 
                    355: /**
1.1.1.3   root      356:  * If it really was subcall (function) return, store returned function
                    357:  * costs and update callinfo->return_pc value.  Return address of
                    358:  * the instruction which did the returned call.
1.1       root      359:  */
1.1.1.3   root      360: Uint32 Profile_CallEnd(callinfo_t *callinfo, counters_t *totalcost)
1.1       root      361: {
1.1.1.3   root      362:        callstack_t *stack;
1.1       root      363: 
1.1.1.3   root      364:        assert(callinfo->depth);
1.1       root      365: 
1.1.1.3   root      366:        /* remove call info from stack */
                    367:        callinfo->depth--;
1.1       root      368: 
1.1.1.3   root      369:        /* callinfo->depth points now to to-be removed item */
                    370:        stack = &(callinfo->stack[callinfo->depth]);
1.1       root      371: 
1.1.1.3   root      372:        if (unlikely(stack->caller_addr == PC_UNDEFINED)) {
                    373:                /* return address can be undefined only for
                    374:                 * first profiled instruction, i.e. only for
                    375:                 * function at top of stack
                    376:                 */
                    377:                assert(!callinfo->depth);
                    378:        } else {
                    379:                /* full cost is original global cost (in ->all)
                    380:                 * deducted from current global (total) cost
                    381:                 */
                    382:                set_counter_diff(&(stack->all), totalcost);
                    383:                add_callee_cost(callinfo->site + stack->callee_idx, stack);
1.1       root      384:        }
                    385: 
1.1.1.3   root      386:        /* if current function had a parent:
                    387:         * - start tracking that
                    388:         * - add full cost of current function to parent's outside costs
                    389:         */
                    390:        if (callinfo->depth) {
                    391:                callstack_t *parent = stack - 1;
                    392:                callinfo->return_pc = parent->ret_addr;
                    393:                add_counter_costs(&(parent->out), &(stack->all));
                    394:        } else {
                    395:                callinfo->return_pc = PC_UNDEFINED;
1.1       root      396:        }
                    397: 
1.1.1.3   root      398:        /* where the returned function was called from */
                    399:        return stack->caller_addr;
                    400: }
1.1       root      401: 
                    402: /**
1.1.1.3   root      403:  * Add costs to all functions still in call stack
1.1       root      404:  */
1.1.1.3   root      405: void Profile_FinalizeCalls(callinfo_t *callinfo, counters_t *totalcost, const char* (*get_symbol)(Uint32 addr))
1.1       root      406: {
1.1.1.3   root      407:        Uint32 addr;
                    408:        if (!callinfo->depth) {
1.1       root      409:                return;
                    410:        }
1.1.1.3   root      411:        fprintf(stderr, "Finalizing costs for %d non-returned functions:\n", callinfo->depth);
                    412:        while (callinfo->depth > 0) {
                    413:                Profile_CallEnd(callinfo, totalcost);
                    414:                addr = callinfo->stack[callinfo->depth].callee_addr;
                    415:                fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", addr, get_symbol(addr),
                    416:                        callinfo->stack[callinfo->depth].ret_addr);
1.1       root      417:        }
                    418: }
                    419: 
                    420: /**
1.1.1.3   root      421:  * Show current profile stack
1.1       root      422:  */
1.1.1.3   root      423: static void Profile_ShowStack(bool forDsp)
1.1       root      424: {
1.1.1.3   root      425:        int i;
                    426:        Uint32 addr;
                    427:        callinfo_t *callinfo;
                    428:        const char* (*get_symbol)(Uint32 addr);
                    429: 
                    430:        if (forDsp) {
                    431:                Profile_DspGetCallinfo(&callinfo, &get_symbol);
                    432:        } else {
                    433:                Profile_CpuGetCallinfo(&callinfo, &get_symbol);
1.1       root      434:        }
1.1.1.3   root      435:        if (!callinfo->depth) {
                    436:                fprintf(stderr, "Empty stack.\n");
                    437:                return;
1.1       root      438:        }
                    439: 
1.1.1.3   root      440:        for (i = 0; i < callinfo->depth; i++) {
                    441:                addr = callinfo->stack[i].callee_addr;
                    442:                fprintf(stderr, "- 0x%x: %s (return = 0x%x)\n", addr,
                    443:                        get_symbol(addr), callinfo->stack[i].ret_addr);
1.1       root      444:        }
                    445: }
                    446: 
                    447: /**
1.1.1.3   root      448:  * Allocate & set initial callinfo structure information
1.1       root      449:  */
1.1.1.3   root      450: int Profile_AllocCallinfo(callinfo_t *callinfo, int count, const char *name)
1.1       root      451: {
1.1.1.3   root      452:        callinfo->sites = count;
                    453:        if (count) {
                    454:                /* alloc & clear new data */
                    455:                callinfo->site = calloc(count, sizeof(callee_t));
                    456:                if (callinfo->site) {
                    457:                        printf("Allocated %s profile callsite buffer for %d symbols.\n", name, count);
                    458:                        callinfo->prev_pc = callinfo->return_pc = PC_UNDEFINED;
                    459:                } else {
                    460:                        fprintf(stderr, "ERROR: callesite buffer alloc failed!\n");
                    461:                        callinfo->sites = 0;
                    462:                }
1.1       root      463:        }
1.1.1.3   root      464:        return callinfo->sites;
1.1       root      465: }
                    466: 
                    467: /**
1.1.1.3   root      468:  * Free all callinfo structure information
1.1       root      469:  */
1.1.1.3   root      470: void Profile_FreeCallinfo(callinfo_t *callinfo)
1.1       root      471: {
1.1.1.3   root      472:        int i;
                    473:        if (callinfo->sites) {
                    474:                callee_t *site = callinfo->site;
                    475:                for (i = 0; i < callinfo->sites; i++, site++) {
                    476:                        if (site->callers) {
                    477:                                free(site->callers);
                    478:                        }
1.1       root      479:                }
1.1.1.3   root      480:                free(callinfo->site);
                    481:                if (callinfo->stack) {
                    482:                        free(callinfo->stack);
                    483:                }
                    484:                memset(callinfo, 0, sizeof(*callinfo));
1.1       root      485:        }
                    486: }
                    487: 
                    488: 
                    489: /* ------------------- command parsing ---------------------- */
                    490: 
                    491: /**
                    492:  * Readline match callback to list profile subcommand names.
                    493:  * STATE = 0 -> different text from previous one.
                    494:  * Return next match or NULL if no matches.
                    495:  */
                    496: char *Profile_Match(const char *text, int state)
                    497: {
                    498:        static const char *names[] = {
1.1.1.4 ! root      499:                "addresses", "callers", "counts", "cycles", "loops", "misses",
1.1.1.3   root      500:                "off", "on", "save", "stack", "stats", "symbols"
1.1       root      501:        };
1.1.1.4 ! root      502:        return DebugUI_MatchHelper(names, ARRAYSIZE(names), text, state);
1.1       root      503: }
                    504: 
                    505: const char Profile_Description[] =
1.1.1.4 ! root      506:        "<subcommand> [parameter]\n"
        !           507:        "\n"
        !           508:        "\tSubcommands:\n"
        !           509:        "\t- on\n"
        !           510:        "\t- off\n"
        !           511:        "\t- counts [count]\n"
        !           512:        "\t- cycles [count]\n"
        !           513:        "\t- misses [count]\n"
        !           514:        "\t- symbols [count]\n"
        !           515:        "\t- addresses [address]\n"
        !           516:        "\t- callers\n"
        !           517:        "\t- stack\n"
        !           518:        "\t- stats\n"
        !           519:        "\t- save <file>\n"
        !           520:        "\t- loops <file> [CPU limit] [DSP limit]\n"
        !           521:        "\n"
1.1.1.3   root      522:        "\t'on' & 'off' enable and disable profiling.  Data is collected\n"
                    523:        "\tuntil debugger is entered again at which point you get profiling\n"
                    524:        "\tstatistics ('stats') summary.\n"
                    525:        "\n"
                    526:        "\tThen you can ask for list of the PC addresses, sorted either by\n"
                    527:        "\texecution 'counts', used 'cycles' or cache 'misses'. First can\n"
                    528:        "\tbe limited just to named addresses with 'symbols'.  Optional\n"
                    529:        "\tcount will limit how many items will be shown.\n"
                    530:        "\n"
                    531:        "\t'addresses' lists the profiled addresses in order, with the\n"
                    532:        "\tinstructions (currently) residing at them.  By default this\n"
                    533:        "\tstarts from the first executed instruction, or you can\n"
                    534:        "\tspecify the starting address.\n"
                    535:        "\n"
                    536:        "\t'callers' shows (raw) caller information for addresses which\n"
1.1.1.4 ! root      537:        "\thad symbol(s) associated with them.  'stack' shows the current\n"
        !           538:        "\tprofile stack (this is useful only with :noinit breakpoints).\n"
1.1.1.3   root      539:        "\n"
1.1.1.4 ! root      540:        "\tProfile address and callers information can be saved with\n"
        !           541:        "\t'save' command.\n"
        !           542:        "\n"
        !           543:        "\tDetailed (spin) looping information can be collected by\n"
        !           544:        "\tspecifying to which file it should be saved, with optional\n"
        !           545:        "\tlimit(s) on how many bytes first and last instruction\n"
        !           546:        "\taddress of the loop can differ (0 = no limit).";
1.1       root      547: 
                    548: 
                    549: /**
1.1.1.3   root      550:  * Save profiling information for CPU or DSP.
                    551:  */
                    552: static bool Profile_Save(const char *fname, bool bForDsp)
                    553: {
                    554:        FILE *out;
                    555:        Uint32 freq;
                    556:        const char *proc, *core;
                    557:        if (!(out = fopen(fname, "w"))) {
                    558:                fprintf(stderr, "ERROR: opening '%s' for writing failed!\n", fname);
                    559:                perror(NULL);
                    560:                return false;
                    561:        }
                    562:        if (bForDsp) {
                    563:                freq = MachineClocks.DSP_Freq;
                    564:                proc = "DSP";
                    565:        } else {
                    566:                freq = MachineClocks.CPU_Freq;
                    567:                proc = "CPU";
                    568:        }
                    569: #if ENABLE_WINUAE_CPU
                    570:        core = "WinUAE";
                    571: #else
                    572:        core = "OldUAE";
                    573: #endif
                    574:        fprintf(out, "Hatari %s profile (%s, %s CPU core)\n", proc, PROG_NAME, core);
                    575:        fprintf(out, "Cycles/second:\t%u\n", freq);
                    576:        if (bForDsp) {
                    577:                Profile_DspSave(out);
                    578:        } else {
                    579:                Profile_CpuSave(out);
                    580:        }
                    581:        fclose(out);
                    582:        return true;
                    583: }
                    584: 
                    585: /**
1.1.1.4 ! root      586:  * function CPU & DSP profiling functionality can call to
        !           587:  * reset loop information log by truncating it.  Only portable
        !           588:  * way to do that is re-opening it again.
        !           589:  */
        !           590: bool Profile_LoopReset(void)
        !           591: {
        !           592:        if (!profile_loop.filename) {
        !           593:                return false;
        !           594:        }
        !           595:        if (profile_loop.fp) {
        !           596:                fclose(profile_loop.fp);
        !           597:        }
        !           598:        profile_loop.fp = fopen(profile_loop.filename, "w");
        !           599:        if (!profile_loop.fp) {
        !           600:                return false;
        !           601:        }
        !           602:        fprintf(profile_loop.fp, "# <processor> <VBLs from boot> <address> <size> <loops>\n");
        !           603:        return true;
        !           604: }
        !           605: 
        !           606: /**
        !           607:  * Open file common to both CPU and DSP profiling.
        !           608:  */
        !           609: static bool Profile_Loops(int nArgc, char *psArgs[])
        !           610: {
        !           611:        if (nArgc > 2) {
        !           612:                /* check that the given file can be opened for writing */
        !           613:                if (profile_loop.filename) {
        !           614:                        free(profile_loop.filename);
        !           615:                }
        !           616:                profile_loop.filename = strdup(psArgs[2]);
        !           617:                if (Profile_LoopReset()) {
        !           618:                        if (nArgc > 3) {
        !           619:                                profile_loop.cpu_limit = atoi(psArgs[3]);
        !           620:                                if (nArgc > 4) {
        !           621:                                        profile_loop.dsp_limit = atoi(psArgs[4]);
        !           622:                                }
        !           623:                        }
        !           624:                        fprintf(stderr, "Additional max %d (CPU) & %d (DSP) byte loop profiling enabled to:\n\t%s\n",
        !           625:                                profile_loop.cpu_limit, profile_loop.cpu_limit, psArgs[2]);
        !           626:                } else {
        !           627:                        free(profile_loop.filename);
        !           628:                        profile_loop.filename = NULL;
        !           629:                        perror("ERROR: opening profile loop output file failed, disabling!");
        !           630:                        return false;
        !           631:                }
        !           632:        } else {
        !           633:                if (profile_loop.fp) {
        !           634:                        fprintf(stderr, "Disabling loop profiling.\n");
        !           635:                        free(profile_loop.filename);
        !           636:                        profile_loop.filename = NULL;
        !           637:                        fclose(profile_loop.fp);
        !           638:                        profile_loop.fp = NULL;
        !           639:                }
        !           640:        }
        !           641:        return true;
        !           642: }
        !           643: 
        !           644: /**
1.1       root      645:  * Command: CPU/DSP profiling enabling, exec stats, cycle and call stats.
1.1.1.3   root      646:  * Returns DEBUGGER_CMDDONE or DEBUGGER_CMDCONT.
1.1       root      647:  */
1.1.1.3   root      648: int Profile_Command(int nArgc, char *psArgs[], bool bForDsp)
1.1       root      649: {
                    650:        static int show = 16;
1.1.1.3   root      651:        Uint32 *disasm_addr;
1.1       root      652:        bool *enabled;
1.1.1.3   root      653: 
1.1       root      654:        if (nArgc > 2) {
                    655:                show = atoi(psArgs[2]);
1.1.1.4 ! root      656:        }
1.1       root      657:        if (bForDsp) {
1.1.1.3   root      658:                Profile_DspGetPointers(&enabled, &disasm_addr);
1.1       root      659:        } else {
1.1.1.3   root      660:                Profile_CpuGetPointers(&enabled, &disasm_addr);
1.1       root      661:        }
1.1.1.3   root      662: 
                    663:        /* continue or explicit addresses command? */
                    664:        if (nArgc < 2 || strcmp(psArgs[1], "addresses") == 0) {
                    665:                Uint32 lower, upper = 0;
                    666:                if (nArgc > 2) {
                    667:                        if (Eval_Range(psArgs[2], &lower, &upper, false) < 0) {
                    668:                                return DEBUGGER_CMDDONE;
                    669:                        }
                    670:                } else {
                    671:                        lower = *disasm_addr;
                    672:                }
                    673:                if (bForDsp) {
                    674:                        *disasm_addr = Profile_DspShowAddresses(lower, upper, stdout);
                    675:                } else {
                    676:                        *disasm_addr = Profile_CpuShowAddresses(lower, upper, stdout);
                    677:                }
                    678:                return DEBUGGER_CMDCONT;
                    679: 
                    680:        } else if (strcmp(psArgs[1], "on") == 0) {
1.1       root      681:                *enabled = true;
                    682:                fprintf(stderr, "Profiling enabled.\n");
1.1.1.3   root      683: 
                    684:        } else if (strcmp(psArgs[1], "off") == 0) {
1.1       root      685:                *enabled = false;
                    686:                fprintf(stderr, "Profiling disabled.\n");
                    687:        
1.1.1.3   root      688:        } else if (strcmp(psArgs[1], "stats") == 0) {
1.1       root      689:                if (bForDsp) {
                    690:                        Profile_DspShowStats();
                    691:                } else {
                    692:                        Profile_CpuShowStats();
                    693:                }
1.1.1.3   root      694:        } else if (strcmp(psArgs[1], "misses") == 0) {
                    695:                if (bForDsp) {
                    696:                        fprintf(stderr, "Cache misses are recorded only for CPU, not DSP.\n");
                    697:                } else {
                    698:                        Profile_CpuShowMisses(show);
                    699:                }
1.1       root      700:        } else if (strcmp(psArgs[1], "cycles") == 0) {
                    701:                if (bForDsp) {
                    702:                        Profile_DspShowCycles(show);
                    703:                } else {
                    704:                        Profile_CpuShowCycles(show);
                    705:                }
                    706:        } else if (strcmp(psArgs[1], "counts") == 0) {
                    707:                if (bForDsp) {
                    708:                        Profile_DspShowCounts(show, false);
                    709:                } else {
                    710:                        Profile_CpuShowCounts(show, false);
                    711:                }
1.1.1.3   root      712:        } else if (strcmp(psArgs[1], "symbols") == 0) {
1.1       root      713:                if (bForDsp) {
                    714:                        Profile_DspShowCounts(show, true);
                    715:                } else {
                    716:                        Profile_CpuShowCounts(show, true);
                    717:                }
1.1.1.3   root      718:        } else if (strcmp(psArgs[1], "callers") == 0) {
                    719:                if (bForDsp) {
                    720:                        Profile_DspShowCallers(stdout);
                    721:                } else {
                    722:                        Profile_CpuShowCallers(stdout);
                    723:                }
                    724:        } else if (strcmp(psArgs[1], "stack") == 0) {
                    725:                Profile_ShowStack(bForDsp);
1.1.1.4 ! root      726: 
1.1.1.3   root      727:        } else if (strcmp(psArgs[1], "save") == 0) {
                    728:                Profile_Save(psArgs[2], bForDsp);
1.1.1.4 ! root      729: 
        !           730:        } else if (strcmp(psArgs[1], "loops") == 0) {
        !           731:                Profile_Loops(nArgc, psArgs);
        !           732: 
1.1       root      733:        } else {
                    734:                DebugUI_PrintCmdHelp(psArgs[0]);
                    735:        }
1.1.1.3   root      736:        return DEBUGGER_CMDDONE;
1.1       root      737: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.