#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 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 1000Hz
timer interrupt.

Do a 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 = SysMAlloc(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)
{ // Sort Profiler function pointer array by least-->greatest.
    return i1 - i2;
}

U0 ProfSort(I64 *array, I64 count)
{
    I64 v1, v2, i1, i2, start_i1, start_i2, end_i1, end_i2, j;

    while (TRUE)
    {
        start_i1 = i1 = 0;
profsort_walk:
        v1 = array[i1];
        while (v1 == array[i1])
        {
            end_i1 = i1; 
            start_i2 = i2 = ++i1;
        }
        if (i1 > count) break; // bail if first search went out of bounds
        v2 = array[i2];
        while (v2 == array[i2])
            end_i2 = i2++; 
        if (i2 > count) break; // bail if second search went out of bounds
        if (end_i1 - start_i1 > end_i2 - start_i2)
        {
            for (j = start_i1; j <= start_i1 + end_i2 - start_i2; j++)
                array[j] = v2;
            for (j = start_i1 + end_i2 - start_i2 + 1; j <= end_i2 - start_i2 + end_i1 + 1; j++)
                array[j] = v1;
        }
        else
        {
            start_i1 = i1 = start_i2;
            goto profsort_walk;
        }
    }
}

public U0 ProfRep(I64 filter_count=1, Bool leave_it=OFF)
{//Profiler report. Call 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);
        ProfSort(pf_array, pf_buf_in_ptr);
        *last_buf = 0;
        "$LTCYAN$ %%Time Hits:    &Function+Offset$FG$\n";
        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;
        "$LTCYAN$ %%Time Hits:    &Function+Offset$FG$\n";
        "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;
    }
}