|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.