#help_index "Debugging/Profiler;Profiler;Cmd Line (Typically)/Profiler"
#help_file "::/Doc/Profiler"

#define PF_ARRAY_COUNT 0x100000
I64  pf_jiffy_start, pf_jiffy_end;
I64 *pf_array = NULL;
I64  pf_cpu = 0;
I64  pf_buf_in_ptr = 0, pf_depth;
I64  pf_prof_active = 0;

U0 ProfTimerInt(CTask *task)
{//See $LK,"profiler_timer_irq",A="FF:::/Kernel/KInterrupts.CC,profiler_timer_irq"$.
	I64 i, k;

	if (Bt(&pf_prof_active, 0))
		for (k = 0; k <= pf_depth; k++)
		{
			if (task == Gs->idle_task)
				i = SYS_IDLE_PT;
			else
				i = TaskCaller(task, k, TRUE);
			if (pf_buf_in_ptr < PF_ARRAY_COUNT)
			{
				pf_array[pf_buf_in_ptr++] = i;
				pf_jiffy_end = counts.jiffies;
			}
		}
}

public U0 Prof(I64 depth=0, I64 cpu_num=0)
{/*Start collecting profiler statistics.
Profilers report where time is spent
by sampling RIP during the $TX,"1000Hz",D="DD_JIFFY_HZ"$
timer interrupt.

Do a $LK,"ProfRep",A="MN:ProfRep"$(), (profiler report)
after you have collected data.
*/
	if (!(0 <= cpu_num < mp_count))
		ST_ERR_ST "Invalid CPU\n";
	else
	{
		cpu_structs[pf_cpu].profiler_timer_irq = NULL;
		pf_cpu = cpu_num;

		pf_depth = depth;
		pf_buf_in_ptr = 0;
		if (!pf_array)
			pf_array = ZMAlloc(sizeof(I64) * PF_ARRAY_COUNT);
		pf_jiffy_end = pf_jiffy_start = counts.jiffies;
		LBts(&pf_prof_active, 0);
		cpu_structs[pf_cpu].profiler_timer_irq = &ProfTimerInt;
	}
}

I64 ProfCompare(U8 *i1, U8 *i2)
{
	return i1 - i2;
}

public U0 ProfRep(I64 filter_count=1, Bool leave_it=OFF)
{//Profiler report. Call $LK,"Prof",A="MN:Prof"$() first and collect data.
	I64 i, hits, rip, last_rip = 0, routine_total = 0;
	F64 total_time;
	U8  buf[256], buf2[256], last_buf[256];

	if (!LBtr(&pf_prof_active, 0))
		"Profiler Not Active\n";
	if (!pf_buf_in_ptr)
		"No Profiler Statistic\n";
	else
	{
		if (!(total_time = pf_jiffy_end - pf_jiffy_start))
			total_time = 1;
		QuickSortI64(pf_array, pf_buf_in_ptr, &ProfCompare);
		*last_buf = 0;
		for (i = 0; i < pf_buf_in_ptr; i += hits)
		{
			rip = pf_array[i];
			hits = 0;
			do hits++;
			while (i + hits < pf_buf_in_ptr && pf_array[i + hits] == rip);

			StrPrint(buf, "%p", rip);
			StrFirstRemove(buf, "+", buf2);
			if (StrCompare(buf2, last_buf))
			{
				if (*last_buf && routine_total >= filter_count)
					"$$GREEN$$%6.2f %08X:%s\n$$FG$$", 100 * routine_total / total_time, routine_total, last_buf;
				StrCopy(last_buf, buf2);
				routine_total = 0;
			}
			routine_total += hits;
			if (hits >= filter_count)
			{
				"%6.2f %08X:%P\n", 100 * hits / total_time, hits, rip;
				last_rip=rip;
			}
		}
		if (*last_buf && routine_total >= filter_count)
			"$$GREEN$$%6.2f %08X:%s\n$$FG$$", 100 * routine_total / total_time, routine_total, last_buf;
		"Total Time:%0.6fs\n", total_time / JIFFY_FREQ;
		if (leave_it)
		{
			cpu_structs[pf_cpu].profiler_timer_irq = &ProfTimerInt;
			LBts(&pf_prof_active, 0);
		}
		else
			cpu_structs[pf_cpu].profiler_timer_irq = NULL;
	}
}
