/core/src/grlib/g_profiler.c
C | 672 lines | 445 code | 131 blank | 96 comment | 130 complexity | c385cd61d83ef7fbe3341171a2039335 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0
- /*
- * Fenix - Videogame compiler/interpreter
- * Current release : FENIX - PROJECT 1.0
- * Project documentation : http://fenix.divsite.net
- *
- * Copyright Š 2008 José Luis Cebrián Pagüe
- * Copyright Š 2002 Fenix Team
- */
- #include <string.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <math.h>
- #include <limits.h>
- #include <stdio.h>
- #include <support/ticks.h>
- #include <runtime/runtime.h>
- #include <grlib/grlib.h>
- typedef struct
- {
- const char * name;
- int valid;
- int active;
- int called;
- Uint64 start;
- Uint64 accumulator;
- Uint64 children;
- int parent;
- int parent_count;
- }
- PROFILE_SAMPLE;
- typedef struct
- {
- const char * name;
- int valid;
- int calls;
- Uint64 total;
- Uint64 average;
- Uint64 min;
- Uint64 max;
- Uint64 last;
- }
- PROFILE_HISTORY;
- static PROFILE_SAMPLE * samples = NULL;
- static PROFILE_HISTORY * history = NULL;
- static Uint64 gprof_frame_start = 0;
- static Uint64 gprof_frame_count = 0;
- static int gprof_allocated = 0;
- static int gprof_sample_count = 0;
- static int gprof_history_count = 0;
- static int gprof_active = 0;
- static int gprof_activate = 1;
- /** Internal function used to allocate more profile samples. The number
- * of profile entries has no internal limit.
- *
- * @param count New value for gprof_allocated
- **/
- static int gprof_allocate (int count)
- {
- int i;
- assert (count > gprof_allocated);
- /* Alloc more dynamic memory */
- samples = (PROFILE_SAMPLE *) realloc (samples,
- count * sizeof(PROFILE_SAMPLE));
- history = (PROFILE_HISTORY *) realloc (history,
- count * sizeof(PROFILE_HISTORY));
- if (samples == NULL || history == NULL)
- {
- gr_error ("gprof_allocate: sin memoria");
- samples = NULL;
- history = NULL;
- return 0;
- }
- /* Initialize the new slots */
- for (i = gprof_sample_count ; i < gprof_allocated ; i++)
- samples[i].valid = 0;
- for (i = gprof_history_count ; i < gprof_allocated ; i++)
- history[i].valid = 0;
- gprof_allocated = count;
- return 1;
- }
- /** Internal function used to sort the list of sample history.
- * A sample is drawn first if
- * - Both are top-level samples and a < b
- * - Both are children of the same parent and a.average < b.average
- * - The parent of a should be drawn before b or its parent
- *
- * @param a Pointer to a sample number
- * @param b Pointer to a sample number
- * @returns < 1 if sample a should be drawn first
- * 0 if it does not matter
- * > 1 if sample b should be drawn first
- **/
- static int gprof_compare (const int * a, const int * b)
- {
- if (samples[*b].parent == samples[*a].parent)
- {
- int timediff;
- /* Both are top level */
- if (samples[*a].parent == -1)
- return *a - *b ;
-
- /* Children of the same parent */
- timediff = (int)((history[*a].average - history[*b].average) * 100.0);
- if (timediff == 0)
- return strcmp (samples[*a].name, samples[*b].name);
- return -timediff;
- }
- /* Neither are top-level */
- if (samples[*b].parent != -1 && samples[*a].parent != -1)
- {
- return samples[*a].parent - samples[*b].parent;
- }
- /* b is top level, a is not */
- if (samples[*a].parent != -1)
- {
- if (*b == samples[*a].parent)
- return 1;
- return samples[*a].parent - *b;
- }
- /* a is top level, b is not */
- if (samples[*b].parent != -1)
- {
- if (*a == samples[*b].parent)
- return -1;
- return *a - samples[*b].parent;
- }
- assert (!"Not reached");
- return 0;
- }
- /** Internal function used to store a sample into the history
- *
- * @param i Index of the sample object
- * @param total Time elapsed after frame start, in ticks
- **/
- static void gprof_store (int i, Uint64 total)
- {
- PROFILE_SAMPLE * sample = &samples[i];
- Uint64 value = sample->accumulator;
- /* Initialize history */
- if (!history[i].valid)
- {
- history[i].valid = 1;
- history[i].name = sample->name;
- history[i].total = 0;
- }
- /* Update history */
- if (history[i].total == 0)
- {
- history[i].total = value;
- history[i].max = value;
- history[i].min = value;
- }
- else
- {
- history[i].total += value;
- if (history[i].min > value)
- history[i].min = value;
- if (history[i].max < value)
- history[i].max = value;
- }
- history[i].calls = sample->called;
- history[i].last = value;
- history[i].average = history[i].total / gprof_frame_count;
- }
- /** Initialize the profiler. This should be called at program startup once.
- **/
- void gprof_init()
- {
- int i;
- samples = (PROFILE_SAMPLE *) malloc(32 * sizeof(PROFILE_SAMPLE));
- history = (PROFILE_HISTORY *) malloc(32 * sizeof(PROFILE_HISTORY));
- if (samples == NULL || history == NULL)
- {
- gr_error ("gprof_init: sin memoria");
- return;
- }
- gprof_allocated = 32;
- for (i = 0 ; i < gprof_allocated ; i++)
- {
- samples[i].valid = 0;
- history[i].valid = 0;
- }
- }
- /** Starts the timing of a program block
- *
- * @param name Name of the block (it must the same pointer each time,
- * use a constant string or a constant pointer)
- **/
- void gprof_begin (const char * name)
- {
- if (name == 0)
- return;
- static int last_entry = -1;
- int i, available = -1;
- if (!gprof_active) return;
- /* Search for already-existing profile */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].valid)
- {
- if (samples[i].name == name)
- {
- if (samples[i].active)
- gr_error ("gprof_begin: bloque %s ya activo", name);
- samples[i].called++;
- samples[i].active++;
- samples[i].start = get_ticks();
- last_entry = i;
- return;
- }
- }
- else if (available == -1)
- {
- available = i;
- }
- }
- /* No available slot */
- if (available == -1)
- {
- if (gprof_sample_count == gprof_allocated)
- {
- if (!gprof_allocate (gprof_allocated + 32))
- return;
- }
- available = gprof_sample_count++;
- }
- samples[available].name = name;
- samples[available].valid = 1;
- samples[available].parent_count = 0;
- samples[available].children = 0;
- samples[available].start = get_ticks();
- samples[available].active = 1;
- samples[available].parent = -1;
- samples[available].called = 1;
- /* Count parents (any active sample) */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].active && i != available)
- {
- samples[available].parent_count++;
- if (samples[available].parent_count == 1)
- samples[available].parent = i;
- else if (samples[samples[available].parent].accumulator > samples[i].accumulator)
- samples[available].parent = i;
- }
- }
- }
- /** End the timing of a program block
- *
- * @param name Name of the block (same pointer used in gprof_start!)
- **/
- void gprof_end (const char * name)
- {
- if (name == 0)
- return;
- int i;
- Uint64 now;
- Uint64 elapsed;
- if (!gprof_active) return;
- /* Search the profile */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].valid && samples[i].name == name)
- {
- if (samples[i].active < 1)
- {
- gr_error ("gprof_end: bloque %s inactivo", name);
- return;
- }
- break;
- }
- }
- if (i == gprof_sample_count)
- {
- gr_error ("gprof_end: no se encontró el bloque %s", name);
- return;
- }
- /* Stop the timing */
- now = get_ticks();
- elapsed = now - samples[i].start;
- /* TODO: Fix this assert */
- //assert (samples[i].children <= elapsed);
- samples[i].active--;
- samples[i].accumulator = elapsed ; /* - samples[i].children? */
- /* Search for parents */
- if (samples[i].parent != -1)
- {
- int parent = samples[i].parent;
- samples[parent].children += elapsed;
- }
- }
- /** Call this function at frame start time. It will close any opened
- * profile samples and update the profiler history.
- **/
- void gprof_frame()
- {
- int i;
- int count;
- int min_parent_count;
- Uint64 now;
- Uint64 elapsed;
- /* Close any open samples, childs first */
- do
- {
- min_parent_count = -1;
- count = 0;
- /* Count active samples and find those with less parents */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].valid && samples[i].active)
- {
- if (min_parent_count < samples[i].parent_count)
- min_parent_count = samples[i].parent_count;
- count++;
- }
- }
- if (min_parent_count == -1)
- break;
- /* Stop found samples */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].valid && samples[i].active)
- {
- if (min_parent_count == samples[i].parent_count)
- {
- gprof_end (samples[i].name);
- count--;
- }
- }
- }
- }
- while (count > 0);
- /* First frame? */
- if (gprof_frame_count == 0)
- {
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- samples[i].called = 0;
- samples[i].accumulator = 0;
- samples[i].children = 0;
- }
- gprof_frame_start = get_ticks();
- gprof_frame_count++;
- return;
- }
- if (gprof_activate != gprof_active)
- {
- if (gprof_activate != 0)
- {
- gprof_active = 1;
- if (gprof_activate < 0)
- gprof_activate++;
- }
- else gprof_active = 0;
- }
- if (!gprof_active) return;
- /* Update frame information */
- now = get_ticks();
- elapsed = now - gprof_frame_start;
- if (elapsed < 1)
- return;
- gprof_frame_start = now;
- gprof_frame_count++;
- /* Store sample history */
- for (i = 0 ; i < gprof_sample_count ; i++)
- {
- if (samples[i].called)
- {
- gprof_store (i, elapsed);
- samples[i].called = 0;
- samples[i].accumulator = 0;
- samples[i].children = 0;
- }
- }
- }
- /** Dumps profiler history to a text file (in append mode)
- *
- * @param filename Name of the file
- **/
- void gprof_dump (const char * filename)
- {
- int i = 0;
- int n;
- int parents_of = -1;
- FILE * f = fopen (filename, "a");
-
- if (f == NULL) return;
- fputs ("\n", f);
- fputs ("-------------------------------------------------------\n", f);
- fputs (" Avg : Min : Max : Last : Profile name \n", f);
- fputs ("-------------------------------------------------------\n", f);
- for (;;)
- {
- if (i == gprof_sample_count)
- {
- if (parents_of == -1)
- break;
- i = parents_of;
- parents_of = samples[i].parent;
- i++;
- continue;
- }
- if (samples[i].parent != parents_of)
- {
- i++;
- continue;
- }
- /* Write the history information */
- fprintf (f, "%6.02f :%6.02f :%6.02f :%6.02f : ",
- fenix->ticks_to_ms(history[i].average),
- fenix->ticks_to_ms(history[i].min),
- fenix->ticks_to_ms(history[i].max),
- fenix->ticks_to_ms(history[i].last));
-
- for (n = 0 ; n < samples[i].parent_count ; n++)
- fputs (" ", f);
- fputs (samples[i].name, f);
- fputs ("\n", f);
- /* Now, search children */
- parents_of = i;
- i = 0;
- }
- fputs ("-------------------------------------------------------\n", f);
- fclose (f);
- }
- /** Reset the profile history
- **/
- void gprof_reset ()
- {
- int i;
- for (i = 0 ; i < gprof_sample_count ; i++)
- history[i].valid = 0;
- gprof_frame_count = 0;
-
- if (!gprof_active)
- {
- gprof_activate = -2;
- }
- }
- /** Draw the current profile history to a bitmap
- *
- * @param dest Destination graphic
- **/
- #define CHARWIDTH 6
- #define CHARHEIGHT 8
- #define COLUMNS 50
- /* This macro is used to find worthless samples. A worthless sample is one
- * that could be ignored if there is not enough screen space the full list */
- #define ISWORTHLESS(i) (samples[i].parent != -1)
- /** Paints a report of the current profile in the destination graph
- *
- * @param dest Destination graphic (for example, the screen)
- * @param area [output] The bounding box of the resulting report
- * is stored here before the function returns
- **/
- void gprof_draw (GRAPH * dest, REGION * area)
- {
- int x, y;
- int i, c;
- int n;
- char buffer[100];
- int count;
- int count_worthless = 0;
- int show_worthless = 0;
- int * list;
- assert( area != NULL );
- assert( dest != NULL );
- /* Count total history lines */
- for (i = count = count_worthless = 0 ; i < gprof_sample_count ; i++)
- {
- if (history[i].valid)
- {
- count++;
- if (ISWORTHLESS(i)) count_worthless++;
- }
- }
- if (count == 0) return;
- gprof_begin ("Profiler");
- x = (dest->width - COLUMNS*CHARWIDTH)/2 ;
- y = 5;
- area->x = x;
- area->y = y;
- area->x2 = x + COLUMNS*CHARWIDTH;
- /* Sort the list */
- list = (int *)malloc(sizeof(int) * count);
- if (list == NULL) return;
- for (i = c = 0 ; i < gprof_sample_count ; i++)
- {
- if (history[i].valid)
- list[c++] = i;
- }
- assert (c == count);
- qsort (list, count, sizeof(int), ( int(*)(const void*, const void*) )gprof_compare);
- show_worthless = (dest->height/CHARHEIGHT - 7) - (count - count_worthless);
- /* Draw the header */
- gr_sys_color (0xFFFFFF, 0x404040);
- gr_sys_puts (dest, x, y+=CHARHEIGHT, "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02",COLUMNS);
- gr_sys_puts (dest, x, y+=CHARHEIGHT, " Avg Min Max Last # : Profile name ",COLUMNS);
- gr_sys_puts (dest, x, y+=CHARHEIGHT, "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02",COLUMNS);
- /* Draw the profiler information box */
- for (c = 0 ; c < count ; c++)
- {
- i = list[c];
- if (!history[i].valid) continue;
-
- if (show_worthless > 0 || !ISWORTHLESS(i))
- {
- if (ISWORTHLESS(i))
- show_worthless--;
- /* Write the history information */
-
- _snprintf (buffer, 100, "%7.02f%7.02f%7.01f%7.01f%4d : ",
- fenix->ticks_to_ms(history[i].average),
- fenix->ticks_to_ms(history[i].min),
- fenix->ticks_to_ms(history[i].max),
- fenix->ticks_to_ms(history[i].last),
- history[i].calls);
-
- for (n = 0 ; n < samples[i].parent_count ; n++)
- strncat (buffer, " ", 99-strlen(buffer));
-
- strncat (buffer, samples[i].name, 99-strlen(buffer));
- gr_sys_puts (dest, x, y+=CHARHEIGHT, buffer, COLUMNS);
- }
- else if (ISWORTHLESS(i))
- {
- if (show_worthless == 0)
- gr_sys_puts (dest, x, y+=CHARHEIGHT, " - - - - : ....... ",COLUMNS);
- show_worthless--;
- }
- }
- gr_sys_puts (dest, x, y+=CHARHEIGHT, "\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02",COLUMNS);
- area->y2 = y+CHARHEIGHT;
- free (list);
- gprof_end ("Profiler");
- }
- /** Toggles the profiler
- *
- * @param dest Destination graphic
- **/
- void gprof_toggle()
- {
- background_dirty = 1;
- gprof_activate = !gprof_activate;
- }
- int gprof_isactive()
- {
- return gprof_active;
- }