PageRenderTime 23ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/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
  1. /*
  2. * Fenix - Videogame compiler/interpreter
  3. * Current release : FENIX - PROJECT 1.0
  4. * Project documentation : http://fenix.divsite.net
  5. *
  6. * Copyright Š 2008 José Luis Cebrián Pagüe
  7. * Copyright Š 2002 Fenix Team
  8. */
  9. #include <string.h>
  10. #include <stdlib.h>
  11. #include <assert.h>
  12. #include <math.h>
  13. #include <limits.h>
  14. #include <stdio.h>
  15. #include <support/ticks.h>
  16. #include <runtime/runtime.h>
  17. #include <grlib/grlib.h>
  18. typedef struct
  19. {
  20. const char * name;
  21. int valid;
  22. int active;
  23. int called;
  24. Uint64 start;
  25. Uint64 accumulator;
  26. Uint64 children;
  27. int parent;
  28. int parent_count;
  29. }
  30. PROFILE_SAMPLE;
  31. typedef struct
  32. {
  33. const char * name;
  34. int valid;
  35. int calls;
  36. Uint64 total;
  37. Uint64 average;
  38. Uint64 min;
  39. Uint64 max;
  40. Uint64 last;
  41. }
  42. PROFILE_HISTORY;
  43. static PROFILE_SAMPLE * samples = NULL;
  44. static PROFILE_HISTORY * history = NULL;
  45. static Uint64 gprof_frame_start = 0;
  46. static Uint64 gprof_frame_count = 0;
  47. static int gprof_allocated = 0;
  48. static int gprof_sample_count = 0;
  49. static int gprof_history_count = 0;
  50. static int gprof_active = 0;
  51. static int gprof_activate = 1;
  52. /** Internal function used to allocate more profile samples. The number
  53. * of profile entries has no internal limit.
  54. *
  55. * @param count New value for gprof_allocated
  56. **/
  57. static int gprof_allocate (int count)
  58. {
  59. int i;
  60. assert (count > gprof_allocated);
  61. /* Alloc more dynamic memory */
  62. samples = (PROFILE_SAMPLE *) realloc (samples,
  63. count * sizeof(PROFILE_SAMPLE));
  64. history = (PROFILE_HISTORY *) realloc (history,
  65. count * sizeof(PROFILE_HISTORY));
  66. if (samples == NULL || history == NULL)
  67. {
  68. gr_error ("gprof_allocate: sin memoria");
  69. samples = NULL;
  70. history = NULL;
  71. return 0;
  72. }
  73. /* Initialize the new slots */
  74. for (i = gprof_sample_count ; i < gprof_allocated ; i++)
  75. samples[i].valid = 0;
  76. for (i = gprof_history_count ; i < gprof_allocated ; i++)
  77. history[i].valid = 0;
  78. gprof_allocated = count;
  79. return 1;
  80. }
  81. /** Internal function used to sort the list of sample history.
  82. * A sample is drawn first if
  83. * - Both are top-level samples and a < b
  84. * - Both are children of the same parent and a.average < b.average
  85. * - The parent of a should be drawn before b or its parent
  86. *
  87. * @param a Pointer to a sample number
  88. * @param b Pointer to a sample number
  89. * @returns < 1 if sample a should be drawn first
  90. * 0 if it does not matter
  91. * > 1 if sample b should be drawn first
  92. **/
  93. static int gprof_compare (const int * a, const int * b)
  94. {
  95. if (samples[*b].parent == samples[*a].parent)
  96. {
  97. int timediff;
  98. /* Both are top level */
  99. if (samples[*a].parent == -1)
  100. return *a - *b ;
  101. /* Children of the same parent */
  102. timediff = (int)((history[*a].average - history[*b].average) * 100.0);
  103. if (timediff == 0)
  104. return strcmp (samples[*a].name, samples[*b].name);
  105. return -timediff;
  106. }
  107. /* Neither are top-level */
  108. if (samples[*b].parent != -1 && samples[*a].parent != -1)
  109. {
  110. return samples[*a].parent - samples[*b].parent;
  111. }
  112. /* b is top level, a is not */
  113. if (samples[*a].parent != -1)
  114. {
  115. if (*b == samples[*a].parent)
  116. return 1;
  117. return samples[*a].parent - *b;
  118. }
  119. /* a is top level, b is not */
  120. if (samples[*b].parent != -1)
  121. {
  122. if (*a == samples[*b].parent)
  123. return -1;
  124. return *a - samples[*b].parent;
  125. }
  126. assert (!"Not reached");
  127. return 0;
  128. }
  129. /** Internal function used to store a sample into the history
  130. *
  131. * @param i Index of the sample object
  132. * @param total Time elapsed after frame start, in ticks
  133. **/
  134. static void gprof_store (int i, Uint64 total)
  135. {
  136. PROFILE_SAMPLE * sample = &samples[i];
  137. Uint64 value = sample->accumulator;
  138. /* Initialize history */
  139. if (!history[i].valid)
  140. {
  141. history[i].valid = 1;
  142. history[i].name = sample->name;
  143. history[i].total = 0;
  144. }
  145. /* Update history */
  146. if (history[i].total == 0)
  147. {
  148. history[i].total = value;
  149. history[i].max = value;
  150. history[i].min = value;
  151. }
  152. else
  153. {
  154. history[i].total += value;
  155. if (history[i].min > value)
  156. history[i].min = value;
  157. if (history[i].max < value)
  158. history[i].max = value;
  159. }
  160. history[i].calls = sample->called;
  161. history[i].last = value;
  162. history[i].average = history[i].total / gprof_frame_count;
  163. }
  164. /** Initialize the profiler. This should be called at program startup once.
  165. **/
  166. void gprof_init()
  167. {
  168. int i;
  169. samples = (PROFILE_SAMPLE *) malloc(32 * sizeof(PROFILE_SAMPLE));
  170. history = (PROFILE_HISTORY *) malloc(32 * sizeof(PROFILE_HISTORY));
  171. if (samples == NULL || history == NULL)
  172. {
  173. gr_error ("gprof_init: sin memoria");
  174. return;
  175. }
  176. gprof_allocated = 32;
  177. for (i = 0 ; i < gprof_allocated ; i++)
  178. {
  179. samples[i].valid = 0;
  180. history[i].valid = 0;
  181. }
  182. }
  183. /** Starts the timing of a program block
  184. *
  185. * @param name Name of the block (it must the same pointer each time,
  186. * use a constant string or a constant pointer)
  187. **/
  188. void gprof_begin (const char * name)
  189. {
  190. if (name == 0)
  191. return;
  192. static int last_entry = -1;
  193. int i, available = -1;
  194. if (!gprof_active) return;
  195. /* Search for already-existing profile */
  196. for (i = 0 ; i < gprof_sample_count ; i++)
  197. {
  198. if (samples[i].valid)
  199. {
  200. if (samples[i].name == name)
  201. {
  202. if (samples[i].active)
  203. gr_error ("gprof_begin: bloque %s ya activo", name);
  204. samples[i].called++;
  205. samples[i].active++;
  206. samples[i].start = get_ticks();
  207. last_entry = i;
  208. return;
  209. }
  210. }
  211. else if (available == -1)
  212. {
  213. available = i;
  214. }
  215. }
  216. /* No available slot */
  217. if (available == -1)
  218. {
  219. if (gprof_sample_count == gprof_allocated)
  220. {
  221. if (!gprof_allocate (gprof_allocated + 32))
  222. return;
  223. }
  224. available = gprof_sample_count++;
  225. }
  226. samples[available].name = name;
  227. samples[available].valid = 1;
  228. samples[available].parent_count = 0;
  229. samples[available].children = 0;
  230. samples[available].start = get_ticks();
  231. samples[available].active = 1;
  232. samples[available].parent = -1;
  233. samples[available].called = 1;
  234. /* Count parents (any active sample) */
  235. for (i = 0 ; i < gprof_sample_count ; i++)
  236. {
  237. if (samples[i].active && i != available)
  238. {
  239. samples[available].parent_count++;
  240. if (samples[available].parent_count == 1)
  241. samples[available].parent = i;
  242. else if (samples[samples[available].parent].accumulator > samples[i].accumulator)
  243. samples[available].parent = i;
  244. }
  245. }
  246. }
  247. /** End the timing of a program block
  248. *
  249. * @param name Name of the block (same pointer used in gprof_start!)
  250. **/
  251. void gprof_end (const char * name)
  252. {
  253. if (name == 0)
  254. return;
  255. int i;
  256. Uint64 now;
  257. Uint64 elapsed;
  258. if (!gprof_active) return;
  259. /* Search the profile */
  260. for (i = 0 ; i < gprof_sample_count ; i++)
  261. {
  262. if (samples[i].valid && samples[i].name == name)
  263. {
  264. if (samples[i].active < 1)
  265. {
  266. gr_error ("gprof_end: bloque %s inactivo", name);
  267. return;
  268. }
  269. break;
  270. }
  271. }
  272. if (i == gprof_sample_count)
  273. {
  274. gr_error ("gprof_end: no se encontró el bloque %s", name);
  275. return;
  276. }
  277. /* Stop the timing */
  278. now = get_ticks();
  279. elapsed = now - samples[i].start;
  280. /* TODO: Fix this assert */
  281. //assert (samples[i].children <= elapsed);
  282. samples[i].active--;
  283. samples[i].accumulator = elapsed ; /* - samples[i].children? */
  284. /* Search for parents */
  285. if (samples[i].parent != -1)
  286. {
  287. int parent = samples[i].parent;
  288. samples[parent].children += elapsed;
  289. }
  290. }
  291. /** Call this function at frame start time. It will close any opened
  292. * profile samples and update the profiler history.
  293. **/
  294. void gprof_frame()
  295. {
  296. int i;
  297. int count;
  298. int min_parent_count;
  299. Uint64 now;
  300. Uint64 elapsed;
  301. /* Close any open samples, childs first */
  302. do
  303. {
  304. min_parent_count = -1;
  305. count = 0;
  306. /* Count active samples and find those with less parents */
  307. for (i = 0 ; i < gprof_sample_count ; i++)
  308. {
  309. if (samples[i].valid && samples[i].active)
  310. {
  311. if (min_parent_count < samples[i].parent_count)
  312. min_parent_count = samples[i].parent_count;
  313. count++;
  314. }
  315. }
  316. if (min_parent_count == -1)
  317. break;
  318. /* Stop found samples */
  319. for (i = 0 ; i < gprof_sample_count ; i++)
  320. {
  321. if (samples[i].valid && samples[i].active)
  322. {
  323. if (min_parent_count == samples[i].parent_count)
  324. {
  325. gprof_end (samples[i].name);
  326. count--;
  327. }
  328. }
  329. }
  330. }
  331. while (count > 0);
  332. /* First frame? */
  333. if (gprof_frame_count == 0)
  334. {
  335. for (i = 0 ; i < gprof_sample_count ; i++)
  336. {
  337. samples[i].called = 0;
  338. samples[i].accumulator = 0;
  339. samples[i].children = 0;
  340. }
  341. gprof_frame_start = get_ticks();
  342. gprof_frame_count++;
  343. return;
  344. }
  345. if (gprof_activate != gprof_active)
  346. {
  347. if (gprof_activate != 0)
  348. {
  349. gprof_active = 1;
  350. if (gprof_activate < 0)
  351. gprof_activate++;
  352. }
  353. else gprof_active = 0;
  354. }
  355. if (!gprof_active) return;
  356. /* Update frame information */
  357. now = get_ticks();
  358. elapsed = now - gprof_frame_start;
  359. if (elapsed < 1)
  360. return;
  361. gprof_frame_start = now;
  362. gprof_frame_count++;
  363. /* Store sample history */
  364. for (i = 0 ; i < gprof_sample_count ; i++)
  365. {
  366. if (samples[i].called)
  367. {
  368. gprof_store (i, elapsed);
  369. samples[i].called = 0;
  370. samples[i].accumulator = 0;
  371. samples[i].children = 0;
  372. }
  373. }
  374. }
  375. /** Dumps profiler history to a text file (in append mode)
  376. *
  377. * @param filename Name of the file
  378. **/
  379. void gprof_dump (const char * filename)
  380. {
  381. int i = 0;
  382. int n;
  383. int parents_of = -1;
  384. FILE * f = fopen (filename, "a");
  385. if (f == NULL) return;
  386. fputs ("\n", f);
  387. fputs ("-------------------------------------------------------\n", f);
  388. fputs (" Avg : Min : Max : Last : Profile name \n", f);
  389. fputs ("-------------------------------------------------------\n", f);
  390. for (;;)
  391. {
  392. if (i == gprof_sample_count)
  393. {
  394. if (parents_of == -1)
  395. break;
  396. i = parents_of;
  397. parents_of = samples[i].parent;
  398. i++;
  399. continue;
  400. }
  401. if (samples[i].parent != parents_of)
  402. {
  403. i++;
  404. continue;
  405. }
  406. /* Write the history information */
  407. fprintf (f, "%6.02f :%6.02f :%6.02f :%6.02f : ",
  408. fenix->ticks_to_ms(history[i].average),
  409. fenix->ticks_to_ms(history[i].min),
  410. fenix->ticks_to_ms(history[i].max),
  411. fenix->ticks_to_ms(history[i].last));
  412. for (n = 0 ; n < samples[i].parent_count ; n++)
  413. fputs (" ", f);
  414. fputs (samples[i].name, f);
  415. fputs ("\n", f);
  416. /* Now, search children */
  417. parents_of = i;
  418. i = 0;
  419. }
  420. fputs ("-------------------------------------------------------\n", f);
  421. fclose (f);
  422. }
  423. /** Reset the profile history
  424. **/
  425. void gprof_reset ()
  426. {
  427. int i;
  428. for (i = 0 ; i < gprof_sample_count ; i++)
  429. history[i].valid = 0;
  430. gprof_frame_count = 0;
  431. if (!gprof_active)
  432. {
  433. gprof_activate = -2;
  434. }
  435. }
  436. /** Draw the current profile history to a bitmap
  437. *
  438. * @param dest Destination graphic
  439. **/
  440. #define CHARWIDTH 6
  441. #define CHARHEIGHT 8
  442. #define COLUMNS 50
  443. /* This macro is used to find worthless samples. A worthless sample is one
  444. * that could be ignored if there is not enough screen space the full list */
  445. #define ISWORTHLESS(i) (samples[i].parent != -1)
  446. /** Paints a report of the current profile in the destination graph
  447. *
  448. * @param dest Destination graphic (for example, the screen)
  449. * @param area [output] The bounding box of the resulting report
  450. * is stored here before the function returns
  451. **/
  452. void gprof_draw (GRAPH * dest, REGION * area)
  453. {
  454. int x, y;
  455. int i, c;
  456. int n;
  457. char buffer[100];
  458. int count;
  459. int count_worthless = 0;
  460. int show_worthless = 0;
  461. int * list;
  462. assert( area != NULL );
  463. assert( dest != NULL );
  464. /* Count total history lines */
  465. for (i = count = count_worthless = 0 ; i < gprof_sample_count ; i++)
  466. {
  467. if (history[i].valid)
  468. {
  469. count++;
  470. if (ISWORTHLESS(i)) count_worthless++;
  471. }
  472. }
  473. if (count == 0) return;
  474. gprof_begin ("Profiler");
  475. x = (dest->width - COLUMNS*CHARWIDTH)/2 ;
  476. y = 5;
  477. area->x = x;
  478. area->y = y;
  479. area->x2 = x + COLUMNS*CHARWIDTH;
  480. /* Sort the list */
  481. list = (int *)malloc(sizeof(int) * count);
  482. if (list == NULL) return;
  483. for (i = c = 0 ; i < gprof_sample_count ; i++)
  484. {
  485. if (history[i].valid)
  486. list[c++] = i;
  487. }
  488. assert (c == count);
  489. qsort (list, count, sizeof(int), ( int(*)(const void*, const void*) )gprof_compare);
  490. show_worthless = (dest->height/CHARHEIGHT - 7) - (count - count_worthless);
  491. /* Draw the header */
  492. gr_sys_color (0xFFFFFF, 0x404040);
  493. 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);
  494. gr_sys_puts (dest, x, y+=CHARHEIGHT, " Avg Min Max Last # : Profile name ",COLUMNS);
  495. 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);
  496. /* Draw the profiler information box */
  497. for (c = 0 ; c < count ; c++)
  498. {
  499. i = list[c];
  500. if (!history[i].valid) continue;
  501. if (show_worthless > 0 || !ISWORTHLESS(i))
  502. {
  503. if (ISWORTHLESS(i))
  504. show_worthless--;
  505. /* Write the history information */
  506. _snprintf (buffer, 100, "%7.02f%7.02f%7.01f%7.01f%4d : ",
  507. fenix->ticks_to_ms(history[i].average),
  508. fenix->ticks_to_ms(history[i].min),
  509. fenix->ticks_to_ms(history[i].max),
  510. fenix->ticks_to_ms(history[i].last),
  511. history[i].calls);
  512. for (n = 0 ; n < samples[i].parent_count ; n++)
  513. strncat (buffer, " ", 99-strlen(buffer));
  514. strncat (buffer, samples[i].name, 99-strlen(buffer));
  515. gr_sys_puts (dest, x, y+=CHARHEIGHT, buffer, COLUMNS);
  516. }
  517. else if (ISWORTHLESS(i))
  518. {
  519. if (show_worthless == 0)
  520. gr_sys_puts (dest, x, y+=CHARHEIGHT, " - - - - : ....... ",COLUMNS);
  521. show_worthless--;
  522. }
  523. }
  524. 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);
  525. area->y2 = y+CHARHEIGHT;
  526. free (list);
  527. gprof_end ("Profiler");
  528. }
  529. /** Toggles the profiler
  530. *
  531. * @param dest Destination graphic
  532. **/
  533. void gprof_toggle()
  534. {
  535. background_dirty = 1;
  536. gprof_activate = !gprof_activate;
  537. }
  538. int gprof_isactive()
  539. {
  540. return gprof_active;
  541. }