1 #include <math.h>
2 #include <assert.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "prof.h"
7 #include "prof_internal.h"
8 
9 // whether zone-self-data is kept to allow the history graph
10 #define Prof_ZONE_HISTORY
11 
12 // whether full detailed (and large)
13 #define Prof_CALL_HISTORY
14 
15 // number of frames of history to keep
16 #define NUM_FRAME_SLOTS                    128
17 
18 
19 // number of unique zones allowed in the entire application
20 // @TODO: remove MAX_PROFILING_ZONES and make it dynamic
21 #define MAX_PROFILING_ZONES                512
22 
23 ////////////////////////////////////////////////////////////////////////
24 
25 // the number of moving averages
26 #define NUM_PROFILE_TRACKER_HISTORY_SLOTS  3
27 
28 // the number of frames to ignore before starting the moving averages
29 #define NUM_THROWAWAY_UPDATES              3
30 
31 // threshhold for a moving average of an integer to be at zero
32 #define INT_ZERO_THRESHHOLD                0.25
33 
34 Prof_Zone *Prof_zones[MAX_PROFILING_ZONES];
35 
36 #ifdef Prof_ZONE_HISTORY
37 static float zone_history[MAX_PROFILING_ZONES][NUM_FRAME_SLOTS]; // 256K
38 #endif
39 
40 // these structures are used solely to track data over time
41 typedef struct
42 {
43    double values[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
44    double variances[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
45 #ifdef Prof_CALL_HISTORY
46    float  history[NUM_FRAME_SLOTS];
47 #endif
48 } History_Scalar;
49 
50 typedef struct
51 {
52    History_Scalar self_time;
53    History_Scalar hierarchical_time;
54    History_Scalar entry_count;
55    int max_recursion;
56 } Profile_Tracker_Data_Record;
57 
58 static History_Scalar frame_time;
59 
60 static double  times_to_reach_90_percent[NUM_PROFILE_TRACKER_HISTORY_SLOTS];
61 static double  precomputed_factors      [NUM_PROFILE_TRACKER_HISTORY_SLOTS];
62 
63 static int        num_active_zones;
64 static int        update_index;     // 2^31 at 100fps = 280 days
65 static double     last_update_time;
66 static Prof_Report_Mode displayed_quantity;
67 
68 #define FRAME_TIME_INITIAL          0.001
69 
70 static int history_index;
71 static int display_frame;
72 static int slot = 1;
73 
clear(History_Scalar * s)74 static void clear(History_Scalar *s)
75 {
76    int i;
77    for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
78       s->values[i] = 0;
79       s->variances[i] = 0;
80    }
81 }
82 
update(History_Scalar * s,double new_value,double * k_array)83 static void update(History_Scalar *s, double new_value, double *k_array)
84 {
85    int i;
86 
87    double new_variance = new_value * new_value;
88 
89    for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
90       double k = k_array[i];
91       s->values[i] = s->values[i] * k + new_value * (1 - k);
92       s->variances[i] = s->variances[i] * k + new_variance * (1 - k);
93    }
94 #ifdef Prof_CALL_HISTORY
95    s->history[history_index] = (float) new_value;
96 #endif
97 }
98 
eternity_set(History_Scalar * s,double new_value)99 static void eternity_set(History_Scalar *s, double new_value)
100 {
101    double new_variance = new_value * new_value;
102 
103    int i;
104    for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
105       s->values[i] = new_value;
106       s->variances[i] = new_variance;
107    }
108 #ifdef Prof_CALL_HISTORY
109    s->history[history_index] = (float) new_value;
110 #endif
111 }
112 
get_value(History_Scalar * s)113 static double get_value(History_Scalar *s)
114 {
115 #ifdef Prof_CALL_HISTORY
116    if (display_frame) {
117       return s->history[(history_index - display_frame + NUM_FRAME_SLOTS) % NUM_FRAME_SLOTS];
118    }
119 #endif
120    return s->values[slot];
121 }
122 
Prof_init_highlevel()123 void Prof_init_highlevel()
124 {
125    int j;
126 
127    update_index = 0;
128    last_update_time = 0;
129 
130    times_to_reach_90_percent[0] = 0.1f;
131    times_to_reach_90_percent[1] = 0.8f;
132    times_to_reach_90_percent[2] = 2.5f;
133 
134    displayed_quantity = Prof_SELF_TIME;
135 
136    clear(&frame_time);
137 
138    for (j = 0; j < NUM_PROFILE_TRACKER_HISTORY_SLOTS; j++) {
139       frame_time.values[j] = FRAME_TIME_INITIAL;
140    }
141 }
142 
143 
144 #ifdef Prof_ENABLED
145 static Prof_Zone *expand = &Prof_region__global;
146 #else
147 static Prof_Zone *expand = NULL;
148 #endif
149 
Prof_set_report_mode(Prof_Report_Mode desired)150 Prof_extern_C void Prof_set_report_mode(Prof_Report_Mode desired)
151 {
152    displayed_quantity = desired;
153 }
154 
155 // visit all Prof_Zone_Stacks
156 extern void Prof_traverse(void (*func)(Prof_Zone_Stack *c));
157 
propogate_stack(Prof_Zone_Stack * c)158 static void propogate_stack(Prof_Zone_Stack *c)
159 {
160    Prof_Zone_Stack *p = c;
161 
162    // propogate times up the stack for hierarchical
163    // times, but watch out for recursion
164 
165    while (p->zone) {
166       if (!p->zone->visited) {
167          p->total_hier_ticks += c->total_self_ticks;
168          p->zone->visited = 1;
169       }
170       p = p->parent;
171    }
172    p = c;
173    while (p->zone) {
174       p->zone->visited = 0;
175       p = p->parent;
176    }
177 }
178 
clear_stack(Prof_Zone_Stack * c)179 static void clear_stack(Prof_Zone_Stack *c)
180 {
181    c->total_hier_ticks = 0;
182    c->total_self_ticks = 0;
183    c->total_entry_count = 0;
184 }
185 
186 static double sum;
sum_times(Prof_Zone_Stack * c)187 static void sum_times(Prof_Zone_Stack *c)
188 {
189    sum += c->total_self_ticks;
190 }
191 
192 static double timestamps_to_seconds;
update_history(Prof_Zone_Stack * c)193 static void update_history(Prof_Zone_Stack *c)
194 {
195    double self_time, hier_time, entry_count;
196 
197    Profile_Tracker_Data_Record *record = (Profile_Tracker_Data_Record *) c->highlevel;
198    Prof_Zone *z = c->zone;
199 
200    if (record == NULL) {
201       record = (Profile_Tracker_Data_Record *) malloc(sizeof(*record));
202       c->highlevel = (void *) record;
203       clear(&record->entry_count);
204       clear(&record->self_time);
205       clear(&record->hierarchical_time);
206       record->max_recursion = 0;
207    }
208 
209    if (c->recursion_depth > record->max_recursion)
210       record->max_recursion = c->recursion_depth;
211 
212    self_time = c->total_self_ticks * timestamps_to_seconds;
213    hier_time = c->total_hier_ticks * timestamps_to_seconds;
214    entry_count = c->total_entry_count;
215 
216    if (update_index < NUM_THROWAWAY_UPDATES) {
217       eternity_set(&record->entry_count, entry_count);
218       eternity_set(&record->self_time, self_time);
219       eternity_set(&record->hierarchical_time, hier_time);
220    } else {
221       update(&record->self_time, self_time, precomputed_factors);
222       update(&record->hierarchical_time, hier_time, precomputed_factors);
223       update(&record->entry_count, entry_count, precomputed_factors);
224    }
225 
226 #ifdef Prof_ZONE_HISTORY
227    * ((float *) z->highlevel) += (float) self_time;
228 #endif
229 }
230 
231 const double SPEEDSTEP_DETECTION_RATIO = 0.08;
232 static int speedstep_warning;
233 
Prof_update(int record_data)234 Prof_extern_C void Prof_update(int record_data)
235 {
236 #ifdef Prof_ENABLED
237    Prof_Begin(iprof_update)
238 
239    static History_Scalar integer_timestamps_per_second;
240    static Prof_Int64 last_integer_timestamp;
241    static Prof_Int64 current_integer_timestamp;
242 
243    int i;
244    double now, dt;
245    Prof_Int64 timestamp_delta;
246    double timestamps_per_second;
247 
248    assert(Prof_num_zones <= MAX_PROFILING_ZONES);
249 
250    Prof_traverse(propogate_stack);
251 
252    // Precompute the time factors
253 
254    now = Prof_get_time();
255 
256    if (update_index == 0) {
257       dt = FRAME_TIME_INITIAL;
258    } else {
259       dt = now - last_update_time;
260       if (dt == 0) dt = FRAME_TIME_INITIAL;
261    }
262 
263    last_update_time = now;
264 
265    for (i = 0; i < NUM_PROFILE_TRACKER_HISTORY_SLOTS; i++) {
266       precomputed_factors[i] = pow(0.1f, dt / times_to_reach_90_percent[i]);
267    }
268 
269    precomputed_factors[0] = 0; // instantaneous.
270 
271    Prof_get_timestamp(&current_integer_timestamp);
272    if (update_index == 0) {
273       sum = 0;
274       Prof_traverse(sum_times);
275       if (sum == 0) sum = 1;
276       timestamp_delta = (Prof_Int64) sum;
277    } else {
278       timestamp_delta = current_integer_timestamp - last_integer_timestamp;
279       if (timestamp_delta == 0) timestamp_delta = 1;
280    }
281 
282    last_integer_timestamp = current_integer_timestamp;
283    timestamps_per_second = (double) timestamp_delta / dt;
284 
285    if (update_index < NUM_THROWAWAY_UPDATES) {
286       eternity_set(&integer_timestamps_per_second, timestamps_per_second);
287    } else {
288       update(&integer_timestamps_per_second, timestamps_per_second, precomputed_factors);
289    }
290 
291    {
292       const int ss_slot = 1;
293       double ss_val, ss_variance, ss_stdev, ss_ratio;
294 
295       ss_val = integer_timestamps_per_second.values[ss_slot];
296       ss_variance = integer_timestamps_per_second.variances[ss_slot] - ss_val*ss_val;
297       ss_stdev = sqrt(fabs(ss_variance));
298       ss_ratio;
299       if (ss_val) {
300          ss_ratio = ss_stdev / fabs(ss_val);
301       } else {
302          ss_ratio = 0;
303       }
304 
305       speedstep_warning = (ss_ratio > SPEEDSTEP_DETECTION_RATIO);
306    }
307 
308    if (!record_data) {
309       Prof_traverse(clear_stack);
310       Prof_End
311       return;
312    }
313 
314    if (timestamps_per_second) {
315       timestamps_to_seconds = 1.0 / timestamps_per_second;
316    } else {
317       timestamps_to_seconds = 0;
318    }
319 
320 #ifdef Prof_ZONE_HISTORY
321    for (i=0; i < Prof_num_zones; ++i) {
322       Prof_zones[i]->highlevel = (void *) &zone_history[i][history_index];
323       zone_history[i][history_index] = 0;
324    }
325 #endif
326 
327    Prof_traverse(update_history);
328 
329    update(&frame_time, dt, precomputed_factors);
330 
331    ++update_index;
332    history_index = (history_index + 1) % NUM_FRAME_SLOTS;
333 
334    Prof_traverse(clear_stack);
335 
336    Prof_End
337 #endif // Prof_ENABLED
338 }
339 
allocate_buffer(int n)340 static Prof_Report *allocate_buffer(int n)
341 {
342    int i;
343    Prof_Report *pob = (Prof_Report *) malloc(sizeof(*pob));
344    pob->num_record = n;
345    pob->record = (Prof_Report_Record *) malloc(sizeof(*pob->record) * pob->num_record);
346    pob->title[0] = pob->title[1] = NULL;
347    for (i=0; i < NUM_TITLE; ++i)
348       pob->title[i] = NULL;
349    for (i=0; i < NUM_HEADER; ++i)
350       pob->header[i] = NULL;
351    for (i=0; i < n; ++i) {
352       pob->record[i].values[0] = 0;
353       pob->record[i].values[1] = 0;
354       pob->record[i].values[2] = 0;
355       pob->record[i].values[3] = 0;
356       pob->record[i].value_flag = 0;
357       pob->record[i].heat = 0;
358       pob->record[i].indent = 0;
359       pob->record[i].number = 0;
360    }
361    return pob;
362 }
363 
364 static int uncounted;
365 static Prof_Recursion_Mode recurse = Prof_FLATTEN_RECURSION;
366 
propogate_to_zone(Prof_Zone_Stack * c)367 static void propogate_to_zone(Prof_Zone_Stack *c)
368 {
369    Prof_Zone *z = c->zone;
370    Profile_Tracker_Data_Record *d = (Profile_Tracker_Data_Record *) c->highlevel;
371    Prof_Report_Record *r;
372 
373 #if 1
374    r = (Prof_Report_Record *) z->highlevel;
375 #else
376    if (recurse == Prof_FLATTEN_RECURSION)
377       r = (Prof_Report_Record *) z->highlevel;
378    else
379       r = ((Prof_Report_Record **) z->highlevel)[c->recursion_depth];
380 #endif
381 
382    if (d) {
383       double t;
384 
385       r->values[0] += 1000 * get_value(&d->self_time);
386       r->values[1] += 1000 * get_value(&d->hierarchical_time);
387       r->values[2] += get_value(&d->entry_count);
388 
389       // arbitrary determination for how low a moving average
390       // has to go to reach 0
391       if (get_value(&d->entry_count) > INT_ZERO_THRESHHOLD) {
392          if (d->max_recursion > r->number)
393             r->number = d->max_recursion;
394          if (c->parent->zone)
395             ((Prof_Report_Record *) c->parent->zone->highlevel)->prefix = '+';
396       }
397 
398 #ifdef Prof_CALL_HISTORY
399       if (display_frame) return;  // no variances when examining history
400 #endif
401       if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
402          t = d->hierarchical_time.variances[slot];
403       } else {
404          t = d->self_time.variances[slot];
405       }
406 
407       t = 1000 * 1000 * t;
408 
409       if (r->heat == 0)
410          r->heat = t;
411       else
412          r->heat = r->heat + t + 2 * sqrt(r->heat * t);
413    } else {
414       ++uncounted;
415    }
416 }
417 
propogate_expanded(Prof_Zone_Stack * c)418 static void propogate_expanded(Prof_Zone_Stack *c)
419 {
420    Profile_Tracker_Data_Record *d = (Profile_Tracker_Data_Record *) c->highlevel;
421    if (d == NULL) {
422       ++uncounted;
423       return;
424    }
425    if (c->parent->zone && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD) {
426       ((Prof_Report_Record *) c->parent->zone->highlevel)[0].prefix = '+';
427       ((Prof_Report_Record *) c->parent->zone->highlevel)[1].prefix = '+';
428       ((Prof_Report_Record *) c->parent->zone->highlevel)[2].prefix = '+';
429    }
430 
431    if (c->zone == expand) {
432       Prof_Report_Record *r = (Prof_Report_Record *) expand->highlevel;
433       // accumulate this time to ourselves
434       r[2].values[0] += 1000 * get_value(&d->self_time);
435       r[2].values[1] += 1000 * get_value(&d->hierarchical_time);
436       r[2].values[2] += get_value(&d->entry_count);
437       if (d->max_recursion > r[2].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
438          r[2].number = d->max_recursion;
439       // propogate it to the parents
440       if (c->parent->zone) {
441          r = (Prof_Report_Record *) c->parent->zone->highlevel;
442          r[1].values[0] += 1000 * get_value(&d->self_time);
443          r[1].values[1] += 1000 * get_value(&d->hierarchical_time);
444          r[1].values[2] += get_value(&d->entry_count);
445          d = (Profile_Tracker_Data_Record *) c->parent->highlevel;
446          if (d->max_recursion > r[1].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
447             r[1].number = d->max_recursion;
448       }
449    }
450 
451    if (c->parent->zone == expand) {
452       Prof_Report_Record *r = (Prof_Report_Record *) c->zone->highlevel;
453       r[0].values[0] += 1000 * get_value(&d->self_time);
454       r[0].values[1] += 1000 * get_value(&d->hierarchical_time);
455       r[0].values[2] += get_value(&d->entry_count);
456       if (d->max_recursion > r[0].number && get_value(&d->entry_count) > INT_ZERO_THRESHHOLD)
457          r[0].number = d->max_recursion;
458    }
459 }
460 
compute_heat(double variance,double value)461 static double compute_heat(double variance, double value)
462 {
463    double factor, stdev;
464    double fabs_value = fabs(value);
465    const float VARIANCE_TOLERANCE_FACTOR = 0.5f;
466 
467    variance = variance - value*value;
468    if (variance < 0) variance = 0;
469    stdev = sqrt(variance);
470 
471    if (fabs_value < 0.000001) {
472       return 0;
473    } else {
474       factor = (stdev / fabs_value) * (1.0f / VARIANCE_TOLERANCE_FACTOR);
475    }
476 
477    if (factor < 0) return 0;
478    if (factor > 1) return 1;
479    return factor;
480 }
481 
pob_compare(const void * p,const void * q)482 static int pob_compare(const void *p, const void *q)
483 {
484    double a = ((Prof_Report_Record *) p)->values[0];
485    double b = ((Prof_Report_Record *) q)->values[0];
486 
487    return (b < a) ? -1 : (b > a);
488 }
489 
pob_expand_compare(const void * p,const void * q)490 static int pob_expand_compare(const void *p, const void *q)
491 {
492    Prof_Report_Record * a = (Prof_Report_Record *) p;
493    Prof_Report_Record * b = (Prof_Report_Record *) q;
494 
495    if (a->indent != b->indent) {
496       if (a->indent == 5) return -1;
497       if (b->indent == 5) return 1;
498       if (a->indent == 3) return 1;
499       if (b->indent == 3) return -1;
500       return 0;
501    }
502    if (a->values[1] == b->values[1])
503       return 0;
504 
505    if (a->values[1] < b->values[1]) {
506       if (a->indent == 5) return -1;
507       return 1;
508    }
509 
510    if (a->indent == 5) return 1;
511    return -1;
512 }
513 
514 static int cursor;
515 static int update_cursor;
516 
Prof_create_report(void)517 Prof_Report *Prof_create_report(void)
518 {
519    double avg_frame_time,fps;
520    char *displayed_quantity_name;
521    int i,s;
522    Prof_Report *pob;
523 
524    if (displayed_quantity == Prof_CALL_GRAPH)
525       s = 3;
526    else
527       s = 1;
528 
529    pob = allocate_buffer(Prof_num_zones * s);
530    for (i=0; i < Prof_num_zones; ++i) {
531       Prof_Zone *z = Prof_zones[i];
532       Prof_Report_Record *r = &pob->record[i*s];
533       z->highlevel = (void *) r;
534       if (displayed_quantity == Prof_CALL_GRAPH) {
535          r[0].name = r[1].name = r[2].name = z->name;
536          r[0].value_flag = 1 | 2 | 4;
537          r[1].value_flag = 1 | 2 | 4;
538          r[2].value_flag = 1 | 2 | 4;
539          r[0].indent = 3;
540          r[1].indent = 5;
541          r[2].indent = 0;
542          r[0].zone = r[1].zone = r[2].zone = (void *) z;
543          r[0].prefix = r[1].prefix = r[2].prefix = 0;
544       } else {
545          r->value_flag = 1 | 2 | 4;
546          r->name = z->name;
547          r->zone = (void *) z;
548          r->indent = 0;
549          r->prefix = 0;
550       }
551    }
552 
553    avg_frame_time = frame_time.values[slot];
554    if (avg_frame_time == 0) avg_frame_time = 0.01f;
555    fps = 1.0f / avg_frame_time;
556 
557    displayed_quantity_name = "*error*";
558    switch (displayed_quantity) {
559       case Prof_SELF_TIME:
560          displayed_quantity_name = "sort self";
561          break;
562       case Prof_HIERARCHICAL_TIME:
563          displayed_quantity_name = "sort hier";
564          break;
565       case Prof_CALL_GRAPH:
566          displayed_quantity_name = "sort hier";
567          break;
568    }
569 
570    pob->title[0] = (char *)  malloc(BUFSIZ);
571    sprintf(pob->title[0],
572           "%3.3lf ms/frame (fps: %3.2lf)  %s",
573            avg_frame_time * 1000, fps, displayed_quantity_name);
574 
575 #ifdef Prof_CALL_HISTORY
576    if (display_frame) {
577       sprintf(pob->title[0] + strlen(pob->title[0]), " - %d frame%s ago",
578           display_frame, display_frame == 1 ? "" : "s");
579    } else {
580       strcat(pob->title[0], " - current frame");
581    }
582 #endif
583 
584    if (speedstep_warning)
585       pob->title[1] = _strdup("WARNING: SpeedStep-like timer inconsistencies detected.  Results are unreliable!");
586 
587    if (displayed_quantity == Prof_CALL_GRAPH) {
588       Prof_Report_Record *r = (Prof_Report_Record *) expand->highlevel;
589       int j=0;
590 
591       Prof_traverse(propogate_expanded);
592 
593       r[2].prefix = '-';
594 
595       for (i=0; i < pob->num_record; ++i) {
596          if (pob->record[i].values[0] || pob->record[i].values[1] || pob->record[i].values[2]) {
597             pob->record[j] = pob->record[i];
598             ++j;
599          }
600       }
601       pob->num_record = j;
602 
603       qsort(pob->record, pob->num_record, sizeof(pob->record[0]), pob_expand_compare);
604 
605       for (i=0; i < pob->num_record; ++i)
606          if (pob->record[i].indent == 5)
607              pob->record[i].indent = 3;
608    } else {
609 
610       uncounted = 0;
611       Prof_traverse(propogate_to_zone);
612 
613       for (i=0; i < Prof_num_zones; ++i) {
614          if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
615             double t = pob->record[i].values[0];
616             pob->record[i].values[0] = pob->record[i].values[1];
617             pob->record[i].values[1] = t;
618          }
619 
620          pob->record[i].heat = compute_heat(pob->record[i].heat, pob->record[i].values[0]);
621       }
622 
623       qsort(pob->record, pob->num_record, sizeof(pob->record[0]), pob_compare);
624 
625    }
626 
627    if (update_cursor) {
628       for (i=0; i < pob->num_record; ++i) {
629          if (pob->record[i].zone == expand) {
630             cursor = i;
631             break;
632          }
633       }
634       update_cursor = 0;
635    }
636 
637    pob->header[0] = _strdup("zone");
638    if (displayed_quantity == Prof_HIERARCHICAL_TIME) {
639       pob->header[1] = _strdup("hier");
640       pob->header[2] = _strdup("self");
641    } else {
642       pob->header[1] = _strdup("self");
643       pob->header[2] = _strdup("hier");
644    }
645    pob->header[3] = _strdup("count");
646 
647    if (cursor < 0) cursor = 0;
648    if (cursor >= pob->num_record) cursor = pob->num_record-1;
649    pob->hilight = cursor;
650 
651    return pob;
652 }
653 
Prof_free_report(Prof_Report * z)654 void Prof_free_report(Prof_Report *z)
655 {
656    int i;
657    for (i=0; i < NUM_TITLE; ++i)
658       if (z->title[i])
659          free(z->title[i]);
660    for (i=0; i < NUM_HEADER; ++i)
661       if (z->header[i])
662          free(z->header[i]);
663    free(z->record);
664    free(z);
665 }
666 
Prof_move_cursor(int num)667 Prof_extern_C void Prof_move_cursor(int num)
668 {
669    cursor += num;
670 }
671 
Prof_set_cursor(int num)672 Prof_extern_C void Prof_set_cursor(int num)
673 {
674    cursor = num;
675 }
676 
Prof_select(void)677 Prof_extern_C void Prof_select(void)
678 {
679    Prof_Report *b = Prof_create_report();
680    if (b->hilight >= 0) {
681       void *z = b->record[b->hilight].zone;
682       if (z != NULL) {
683          expand = (Prof_Zone *) z;
684          displayed_quantity = Prof_CALL_GRAPH;
685       }
686    }
687    Prof_free_report(b);
688    update_cursor = 1;
689 }
690 
Prof_select_parent(void)691 Prof_extern_C void Prof_select_parent(void)
692 {
693    int i;
694    void *old = (void *) expand;
695    Prof_Report *b = Prof_create_report();
696    for (i=0; i < b->num_record; ++i) {
697       if (b->record[i].indent == 0) break;
698       if (b->record[i].zone == old) continue;
699       expand = (Prof_Zone *) b->record[i].zone;
700    }
701    Prof_free_report(b);
702    update_cursor = 1;
703 }
704 
Prof_set_frame(int num)705 Prof_extern_C void Prof_set_frame(int num)
706 {
707    if (num < 0) num = 0;
708    if (num >= NUM_FRAME_SLOTS) num = NUM_FRAME_SLOTS-1;
709 
710    display_frame = num;
711 }
712 
Prof_move_frame(int delta)713 Prof_extern_C void Prof_move_frame(int delta)
714 {
715    // convert so negative delta = "into the past"
716    Prof_set_frame(display_frame - delta);
717 }
718 
Prof_set_smoothing(int x)719 Prof_extern_C void Prof_set_smoothing(int x)
720 {
721    if (x <= 0) x = 0;
722    if (x >= NUM_PROFILE_TRACKER_HISTORY_SLOTS)
723       x = NUM_PROFILE_TRACKER_HISTORY_SLOTS-1;
724 
725    slot = x;
726 }
727 
728 // currently does nothing
Prof_set_recursion(Prof_Recursion_Mode e)729 Prof_extern_C void Prof_set_recursion(Prof_Recursion_Mode e)
730 {
731    recurse = e;
732 }
733 
id(Prof_Zone * z)734 static int id(Prof_Zone *z)
735 {
736    // hash the string so that the id is consistent from
737    // run to run (rather than using the pointer itself which isn't)
738    // @TODO: only compute this at zone init time?
739 
740    unsigned int h = 0x55555555;
741    char *n = z->name;
742 
743    while (*n)
744       h = (h << 5) + (h >> 27) + *n++;
745 
746    return h;
747 }
748 
Prof_graph(int num_frames,void (* callback)(int id,int x0,int x1,float * values,void * data),void * data)749 void Prof_graph(int num_frames, void (*callback)(int id, int x0, int x1, float *values, void *data), void *data)
750 {
751 #ifdef Prof_ZONE_HISTORY
752    int i,h = history_index;
753    if (num_frames > NUM_FRAME_SLOTS)
754       num_frames = NUM_FRAME_SLOTS;
755 
756    for (i=0; i < Prof_num_zones; ++i) {
757       if (h >= num_frames) {
758          callback(id(Prof_zones[i]), 0, num_frames, &zone_history[i][h-num_frames], data);
759       } else {
760          callback(id(Prof_zones[i]), num_frames - h, num_frames, &zone_history[i][0], data);
761          callback(id(Prof_zones[i]), 0, num_frames-h, &zone_history[i][NUM_FRAME_SLOTS-(num_frames-h)], data);
762       }
763    }
764 
765    // display frame "cursor"
766    if (display_frame != 0) {
767       float value[2] = { 2.0, 0 };
768       callback(0, NUM_FRAME_SLOTS-1-display_frame, NUM_FRAME_SLOTS-1-display_frame, value, data);
769    }
770 #endif
771 }
772 
773 
774 
775