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