Sync with 5.4.0
[deliverable/titan.core.git] / core / ProfilerTools.cc
1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2015 Ericsson Telecom AB
3 // All rights reserved. This program and the accompanying materials
4 // are made available under the terms of the Eclipse Public License v1.0
5 // which accompanies this distribution, and is available at
6 // http://www.eclipse.org/legal/epl-v10.html
7 ///////////////////////////////////////////////////////////////////////////////
8
9 #include "ProfilerTools.hh"
10 #include "JSON_Tokenizer.hh"
11 #include "memory.h"
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 namespace Profiler_Tools {
18
19 ////////////////////////////////////
20 //////// timeval operations ////////
21 ////////////////////////////////////
22
23 timeval string2timeval(const char* str)
24 {
25 // read and store the first part (atoi will read until the decimal dot)
26 long int sec = atoi(str);
27 timeval tv;
28 tv.tv_sec = sec;
29
30 do {
31 // step over each digit
32 sec /= 10;
33 ++str;
34 }
35 while (sec > 9);
36
37 // step over the decimal dot and read the second part of the number
38 tv.tv_usec = atoi(str + 1);
39 return tv;
40 }
41
42 char* timeval2string(timeval tv)
43 {
44 // convert the first part and set the second part to all zeros
45 char* str = mprintf("%ld.000000", tv.tv_sec);
46
47 // go through each digit of the second part and add them to the zeros in the string
48 size_t pos = mstrlen(str) - 1;
49 while (tv.tv_usec > 0) {
50 str[pos] += tv.tv_usec % 10;
51 tv.tv_usec /= 10;
52 --pos;
53 }
54 return str;
55 }
56
57 timeval add_timeval(const timeval operand1, const timeval operand2)
58 {
59 timeval tv;
60 tv.tv_usec = operand1.tv_usec + operand2.tv_usec;
61 tv.tv_sec = operand1.tv_sec + operand2.tv_sec;
62 if (tv.tv_usec >= 1000000) {
63 ++tv.tv_sec;
64 tv.tv_usec -= 1000000;
65 }
66 return tv;
67 }
68
69 timeval subtract_timeval(const timeval operand1, const timeval operand2)
70 {
71 timeval tv;
72 tv.tv_usec = operand1.tv_usec - operand2.tv_usec;
73 tv.tv_sec = operand1.tv_sec - operand2.tv_sec;
74 if (tv.tv_usec < 0) {
75 --tv.tv_sec;
76 tv.tv_usec += 1000000;
77 }
78 return tv;
79 }
80
81 ////////////////////////////////////
82 ///// profiler data operations /////
83 ////////////////////////////////////
84
85 int get_function(const profiler_db_t& p_db, int p_element, int p_lineno)
86 {
87 for (size_t i = 0; i < p_db[p_element].functions.size(); ++i) {
88 if (p_db[p_element].functions[i].lineno == p_lineno) {
89 return i;
90 }
91 }
92 return -1;
93 }
94
95 void create_function(profiler_db_t& p_db, int p_element, int p_lineno,
96 const char* p_function_name)
97 {
98 profiler_db_item_t::profiler_function_data_t func_data;
99 func_data.lineno = p_lineno;
100 func_data.total_time.tv_sec = 0;
101 func_data.total_time.tv_usec = 0;
102 func_data.exec_count = 0;
103 func_data.name = mcopystr(p_function_name);
104 p_db[p_element].functions.push_back(func_data);
105 }
106
107 int get_line(const profiler_db_t& p_db, int p_element, int p_lineno)
108 {
109 for (size_t i = 0; i < p_db[p_element].lines.size(); ++i) {
110 if (p_db[p_element].lines[i].lineno == p_lineno) {
111 return i;
112 }
113 }
114 return -1;
115 }
116
117 void create_line(profiler_db_t& p_db, int p_element, int p_lineno)
118 {
119 profiler_db_item_t::profiler_line_data_t line_data;
120 line_data.lineno = p_lineno;
121 line_data.total_time.tv_sec = 0;
122 line_data.total_time.tv_usec = 0;
123 line_data.exec_count = 0;
124 p_db[p_element].lines.push_back(line_data);
125 }
126
127 #define IMPORT_FORMAT_ERROR(cond) \
128 if (cond) { \
129 p_error_function("Failed to load profiling and/or code coverage database. Invalid format."); \
130 return; \
131 }
132
133 void import_data(profiler_db_t& p_db, const char* p_filename, boolean p_wait,
134 void (*p_error_function)(const char*, ...))
135 {
136 // open the file, if it exists
137 int file_size = 0;
138 FILE* file = fopen(p_filename, "r");
139 if (NULL != file) {
140 // get the file size
141 fseek(file, 0, SEEK_END);
142 file_size = ftell(file);
143 }
144 while (0 == file_size) {
145 if (!p_wait) {
146 return;
147 }
148 // keep reading until the file appears (and is not empty)
149 if (NULL != file) {
150 fclose(file);
151 }
152 usleep(1000);
153 file = fopen(p_filename, "r");
154 if (NULL != file) {
155 // refresh the file size
156 fseek(file, 0, SEEK_END);
157 file_size = ftell(file);
158 }
159 }
160
161 // rewind the file (the file pointer has been moved to the end of the file to
162 // calculate its size)
163 rewind(file);
164
165 // read the entire file into a character buffer
166 char* buffer = (char*)Malloc(file_size);
167 int bytes_read = fread(buffer, 1, file_size, file);
168 fclose(file);
169 if (bytes_read != file_size) {
170 p_error_function("Error reading database file.");
171 return;
172 }
173
174 // initialize a JSON tokenizer with the buffer
175 JSON_Tokenizer json(buffer, file_size);
176 Free(buffer);
177
178 // attempt to read tokens from the buffer
179 // if the format is invalid, abort the importing process
180 json_token_t token = JSON_TOKEN_NONE;
181 char* value = NULL;
182 size_t value_len = 0;
183
184 // start of main array
185 json.get_next_token(&token, NULL, NULL);
186 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
187
188 // read objects (one for each TTCN-3 file), until the main array end mark is reached
189 json.get_next_token(&token, NULL, NULL);
190 while (JSON_TOKEN_OBJECT_START == token) {
191 size_t file_index = 0;
192
193 // file name:
194 json.get_next_token(&token, &value, &value_len);
195 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 4 ||
196 0 != strncmp(value, "file", value_len));
197
198 // read the file name and see if its record already exists
199 json.get_next_token(&token, &value, &value_len);
200 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING != token);
201 for (file_index = 0; file_index < p_db.size(); ++file_index) {
202 if (strlen(p_db[file_index].filename) == value_len - 2 &&
203 0 == strncmp(p_db[file_index].filename, value + 1, value_len - 2)) {
204 break;
205 }
206 }
207
208 // insert a new element if the file was not found
209 if (p_db.size() == file_index) {
210 profiler_db_item_t item;
211 item.filename = mcopystrn(value + 1, value_len - 2);
212 p_db.push_back(item);
213 }
214
215 // functions:
216 json.get_next_token(&token, &value, &value_len);
217 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 9 ||
218 0 != strncmp(value, "functions", value_len));
219
220 // read and store the functions (an array of objects, same as before)
221 json.get_next_token(&token, NULL, NULL);
222 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
223 json.get_next_token(&token, NULL, NULL);
224 while (JSON_TOKEN_OBJECT_START == token) {
225 size_t function_index = 0;
226
227 // function name:
228 json.get_next_token(&token, &value, &value_len);
229 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 4 ||
230 0 != strncmp(value, "name", value_len));
231
232 // read the function name, it will be checked later
233 json.get_next_token(&token, &value, &value_len);
234 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING != token);
235 char* function_name = mcopystrn(value + 1, value_len - 2);
236
237 // function start line:
238 json.get_next_token(&token, &value, &value_len);
239 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
240 0 != strncmp(value, "start line", value_len));
241
242 // read the start line and check if the function already exists
243 json.get_next_token(&token, &value, &value_len);
244 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
245 int start_line = atoi(value);
246 for (function_index = 0; function_index < p_db[file_index].functions.size(); ++function_index) {
247 if (p_db[file_index].functions[function_index].lineno == start_line &&
248 0 == strcmp(p_db[file_index].functions[function_index].name, function_name)) {
249 break;
250 }
251 }
252
253 // insert a new element if the function was not found
254 if (p_db[file_index].functions.size() == function_index) {
255 profiler_db_item_t::profiler_function_data_t func_data;
256 func_data.name = function_name;
257 func_data.lineno = start_line;
258 func_data.exec_count = 0;
259 func_data.total_time.tv_sec = 0;
260 func_data.total_time.tv_usec = 0;
261 p_db[file_index].functions.push_back(func_data);
262 }
263
264 // function execution count:
265 json.get_next_token(&token, &value, &value_len);
266 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 15 ||
267 0 != strncmp(value, "execution count", value_len));
268
269 // read the execution count and add it to the current data
270 json.get_next_token(&token, &value, &value_len);
271 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
272 p_db[file_index].functions[function_index].exec_count += atoi(value);
273
274 // total function execution time:
275 json.get_next_token(&token, &value, &value_len);
276 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
277 0 != strncmp(value, "total time", value_len));
278
279 // read the total time and add it to the current data
280 // note: the database contains a real number, this needs to be split into 2 integers
281 json.get_next_token(&token, &value, &value_len);
282 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
283 p_db[file_index].functions[function_index].total_time = add_timeval(
284 p_db[file_index].functions[function_index].total_time, string2timeval(value));
285
286 // end of the function's object
287 json.get_next_token(&token, NULL, NULL);
288 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
289
290 // read the next token (either the start of another object or the function array end)
291 json.get_next_token(&token, NULL, NULL);
292 }
293
294 // function array end
295 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
296
297 // lines:
298 json.get_next_token(&token, &value, &value_len);
299 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 5 ||
300 0 != strncmp(value, "lines", value_len));
301
302 // read and store the lines (an array of objects, same as before)
303 json.get_next_token(&token, NULL, NULL);
304 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
305 json.get_next_token(&token, NULL, NULL);
306 while (JSON_TOKEN_OBJECT_START == token) {
307 int line_index = 0;
308
309 // line number:
310 json.get_next_token(&token, &value, &value_len);
311 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 6 ||
312 0 != strncmp(value, "number", value_len));
313
314 // read the line number and check if the line already exists
315 json.get_next_token(&token, &value, &value_len);
316 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
317 int lineno = atoi(value);
318 IMPORT_FORMAT_ERROR(lineno < 0);
319 line_index = get_line(p_db, file_index, lineno);
320 if (-1 == line_index) {
321 create_line(p_db, file_index, lineno);
322 line_index = p_db[file_index].lines.size() - 1;
323 }
324
325 // line execution count:
326 json.get_next_token(&token, &value, &value_len);
327 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 15 ||
328 0 != strncmp(value, "execution count", value_len));
329
330 // read the execution count and add it to the current data
331 json.get_next_token(&token, &value, &value_len);
332 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
333 p_db[file_index].lines[line_index].exec_count += atoi(value);
334
335 // total line execution time:
336 json.get_next_token(&token, &value, &value_len);
337 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
338 0 != strncmp(value, "total time", value_len));
339
340 // read the total time and add it to the current data
341 json.get_next_token(&token, &value, &value_len);
342 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
343 p_db[file_index].lines[line_index].total_time = add_timeval(
344 p_db[file_index].lines[line_index].total_time, string2timeval(value));
345
346 // end of the line's object
347 json.get_next_token(&token, NULL, NULL);
348 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
349
350 // read the next token (either the start of another object or the line array end)
351 json.get_next_token(&token, NULL, NULL);
352 }
353
354 // line array end
355 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
356
357 // end of the file's object
358 json.get_next_token(&token, NULL, NULL);
359 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
360
361 // read the next token (either the start of another object or the main array end)
362 json.get_next_token(&token, NULL, NULL);
363 }
364
365 // main array end
366 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
367 }
368
369 void export_data(profiler_db_t& p_db, const char* p_filename,
370 boolean p_disable_profiler, boolean p_disable_coverage,
371 void (*p_error_function)(const char*, ...))
372 {
373 // check whether the file can be opened for writing
374 FILE* file = fopen(p_filename, "w");
375 if (NULL == file) {
376 p_error_function("Could not open file '%s' for writing. Profiling and/or code coverage "
377 "data will not be saved.", p_filename);
378 return;
379 }
380
381 // use the JSON tokenizer to create a JSON document from the database
382 JSON_Tokenizer json(true);
383
384 // main array, contains an element for each file
385 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
386 for (size_t i = 0; i < p_db.size(); ++i) {
387
388 // each file's data is stored in an object
389 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
390
391 // store the file name
392 json.put_next_token(JSON_TOKEN_NAME, "file");
393 char* p_filename_str = mprintf("\"%s\"", p_db[i].filename);
394 json.put_next_token(JSON_TOKEN_STRING, p_filename_str);
395 Free(p_filename_str);
396
397 // store the function data in an array (one element for each function)
398 json.put_next_token(JSON_TOKEN_NAME, "functions");
399 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
400 for (size_t j = 0; j < p_db[i].functions.size(); ++j) {
401
402 // the data is stored in an object for each function
403 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
404
405 // store the function name
406 json.put_next_token(JSON_TOKEN_NAME, "name");
407 char* func_name_str = mprintf("\"%s\"", p_db[i].functions[j].name);
408 json.put_next_token(JSON_TOKEN_STRING, func_name_str);
409 Free(func_name_str);
410
411 // store the function start line
412 json.put_next_token(JSON_TOKEN_NAME, "start line");
413 char* start_line_str = mprintf("%d", p_db[i].functions[j].lineno);
414 json.put_next_token(JSON_TOKEN_NUMBER, start_line_str);
415 Free(start_line_str);
416
417 // store the function execution count
418 json.put_next_token(JSON_TOKEN_NAME, "execution count");
419 char* exec_count_str = mprintf("%d", p_disable_coverage ? 0 :
420 p_db[i].functions[j].exec_count);
421 json.put_next_token(JSON_TOKEN_NUMBER, exec_count_str);
422 Free(exec_count_str);
423
424 // store the function's total execution time
425 json.put_next_token(JSON_TOKEN_NAME, "total time");
426 if (p_disable_profiler) {
427 json.put_next_token(JSON_TOKEN_NUMBER, "0.000000");
428 }
429 else {
430 char* total_time_str = timeval2string(p_db[i].functions[j].total_time);
431 json.put_next_token(JSON_TOKEN_NUMBER, total_time_str);
432 Free(total_time_str);
433 }
434
435 // end of function object
436 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
437 }
438
439 // end of function data array
440 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
441
442 // store the line data in an array (one element for each line with useful data)
443 json.put_next_token(JSON_TOKEN_NAME, "lines");
444 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
445 for (size_t j = 0; j < p_db[i].lines.size(); ++j) {
446
447 // store line data in an object
448 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
449
450 // store the line number
451 json.put_next_token(JSON_TOKEN_NAME, "number");
452 char* line_number_str = mprintf("%d", p_db[i].lines[j].lineno);
453 json.put_next_token(JSON_TOKEN_NUMBER, line_number_str);
454 Free(line_number_str);
455
456 // store the line execution count
457 json.put_next_token(JSON_TOKEN_NAME, "execution count");
458 char* exec_count_str = mprintf("%d", p_disable_coverage ? 0 :
459 p_db[i].lines[j].exec_count);
460 json.put_next_token(JSON_TOKEN_NUMBER, exec_count_str);
461 Free(exec_count_str);
462
463 // store the line's total execution time
464 json.put_next_token(JSON_TOKEN_NAME, "total time");
465 if (p_disable_profiler) {
466 json.put_next_token(JSON_TOKEN_NUMBER, "0.000000");
467 }
468 else {
469 char* total_time_str = timeval2string(p_db[i].lines[j].total_time);
470 json.put_next_token(JSON_TOKEN_NUMBER, total_time_str);
471 Free(total_time_str);
472 }
473
474 // end of this line's object
475 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
476 }
477
478 // end of line data array
479 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
480
481 // end of this file's object
482 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
483 }
484
485 // end of main array
486 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
487
488 // write the JSON document into the file
489 fprintf(file, "%s\n", json.get_buffer());
490 fclose(file);
491 }
492
493 // Structure for one code line or function, used by print_stats for sorting
494 struct stats_data_t {
495 const char* filename; // not owned
496 const char* funcname; // not owned, NULL for code lines that don't start a function
497 int lineno;
498 timeval total_time;
499 int exec_count;
500 };
501
502 // Compare function for sorting stats data based on total execution time (descending)
503 int stats_data_cmp_time(const void* p_left, const void* p_right) {
504 const stats_data_t* p_left_data = (stats_data_t*)p_left;
505 const stats_data_t* p_right_data = (stats_data_t*)p_right;
506 if (p_left_data->total_time.tv_sec > p_right_data->total_time.tv_sec) return -1;
507 if (p_left_data->total_time.tv_sec < p_right_data->total_time.tv_sec) return 1;
508 if (p_left_data->total_time.tv_usec > p_right_data->total_time.tv_usec) return -1;
509 if (p_left_data->total_time.tv_usec < p_right_data->total_time.tv_usec) return 1;
510 return 0;
511 }
512
513 // Compare function for sorting stats data based on execution count (descending)
514 int stats_data_cmp_count(const void* p_left, const void* p_right) {
515 return ((stats_data_t*)p_right)->exec_count - ((stats_data_t*)p_left)->exec_count;
516 }
517
518 // Compare function for sorting stats data based on total time per execution count (descending)
519 int stats_data_cmp_avg(const void* p_left, const void* p_right) {
520 const stats_data_t* p_left_data = (stats_data_t*)p_left;
521 const stats_data_t* p_right_data = (stats_data_t*)p_right;
522 double left_time = p_left_data->total_time.tv_sec + p_left_data->total_time.tv_usec / 1000000.0;
523 double right_time = p_right_data->total_time.tv_sec + p_right_data->total_time.tv_usec / 1000000.0;
524 double diff = (right_time / p_right_data->exec_count) - (left_time / p_left_data->exec_count);
525 if (diff < 0) return -1;
526 if (diff > 0) return 1;
527 return 0;
528 }
529
530 void print_stats(profiler_db_t& p_db, const char* p_filename,
531 boolean p_disable_profiler, boolean p_disable_coverage,
532 unsigned int p_flags, void (*p_error_function)(const char*, ...))
533 {
534 // title
535 char* title_str = mprintf(
536 "##################################################\n"
537 "%s## TTCN-3 %s%s%sstatistics ##%s\n"
538 "##################################################\n\n\n"
539 , p_disable_profiler ? "#######" : (p_disable_coverage ? "#########" : "")
540 , p_disable_profiler ? "" : "profiler "
541 , (p_disable_profiler || p_disable_coverage) ? "" : "and "
542 , p_disable_coverage ? "" : "code coverage "
543 , p_disable_profiler ? "######" : (p_disable_coverage ? "#########" : ""));
544
545 char* line_func_count_str = NULL;
546 if (p_flags & STATS_NUMBER_OF_LINES) {
547 line_func_count_str = mcopystr(
548 "--------------------------------------\n"
549 "- Number of code lines and functions -\n"
550 "--------------------------------------\n");
551 }
552
553 // line data
554 char* line_data_str = NULL;
555 if (p_flags & STATS_LINE_DATA_RAW) {
556 line_data_str = mprintf(
557 "-------------------------------------------------\n"
558 "%s- Code line data (%s%s%s) -%s\n"
559 "-------------------------------------------------\n"
560 , p_disable_profiler ? "-------" : (p_disable_coverage ? "---------" : "")
561 , p_disable_profiler ? "" : "total time"
562 , (p_disable_profiler || p_disable_coverage) ? "" : " / "
563 , p_disable_coverage ? "" : "execution count"
564 , p_disable_profiler ? "------" : (p_disable_coverage ? "---------" : ""));
565 }
566
567 // average time / exec count for lines
568 char* line_avg_str = NULL;
569 if (!p_disable_coverage && !p_disable_profiler && (p_flags & STATS_LINE_AVG_RAW)) {
570 line_avg_str = mcopystr(
571 "-------------------------------------------\n"
572 "- Average time / execution for code lines -\n"
573 "-------------------------------------------\n");
574 }
575
576 // function data
577 char* func_data_str = NULL;
578 if (p_flags & STATS_FUNC_DATA_RAW) {
579 func_data_str = mprintf(
580 "------------------------------------------------\n"
581 "%s- Function data (%s%s%s) -%s\n"
582 "------------------------------------------------\n"
583 , p_disable_profiler ? "-------" : (p_disable_coverage ? "---------" : "")
584 , p_disable_profiler ? "" : "total time"
585 , (p_disable_profiler || p_disable_coverage) ? "" : " / "
586 , p_disable_coverage ? "" : "execution count"
587 , p_disable_profiler ? "------" : (p_disable_coverage ? "---------" : ""));
588 }
589
590 // average time / exec count for functions
591 char* func_avg_str = NULL;
592 if (!p_disable_coverage && !p_disable_profiler && (p_flags & STATS_FUNC_AVG_RAW)) {
593 func_avg_str = mcopystr(
594 "------------------------------------------\n"
595 "- Average time / execution for functions -\n"
596 "------------------------------------------\n");
597 }
598
599 char* line_time_sorted_mod_str = NULL;
600 if (!p_disable_profiler && (p_flags & STATS_LINE_TIMES_SORTED_BY_MOD)) {
601 line_time_sorted_mod_str = mcopystr(
602 "------------------------------------------------\n"
603 "- Total time of code lines, sorted, per module -\n"
604 "------------------------------------------------\n");
605 }
606
607 char* line_count_sorted_mod_str = NULL;
608 if (!p_disable_coverage && (p_flags & STATS_LINE_COUNT_SORTED_BY_MOD)) {
609 line_count_sorted_mod_str = mcopystr(
610 "-----------------------------------------------------\n"
611 "- Execution count of code lines, sorted, per module -\n"
612 "-----------------------------------------------------\n");
613 }
614
615 char* line_avg_sorted_mod_str = NULL;
616 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_LINE_AVG_SORTED_BY_MOD)) {
617 line_avg_sorted_mod_str = mcopystr(
618 "--------------------------------------------------------------\n"
619 "- Average time / execution of code lines, sorted, per module -\n"
620 "--------------------------------------------------------------\n");
621 }
622
623 char* line_time_sorted_tot_str = NULL;
624 if (!p_disable_profiler && (p_flags & STATS_LINE_TIMES_SORTED_TOTAL)) {
625 line_time_sorted_tot_str = mcopystr(
626 "-------------------------------------------\n"
627 "- Total time of code lines, sorted, total -\n"
628 "-------------------------------------------\n");
629 }
630
631 char* line_count_sorted_tot_str = NULL;
632 if (!p_disable_coverage && (p_flags & STATS_LINE_COUNT_SORTED_TOTAL)) {
633 line_count_sorted_tot_str = mcopystr(
634 "------------------------------------------------\n"
635 "- Execution count of code lines, sorted, total -\n"
636 "------------------------------------------------\n");
637 }
638
639 char* line_avg_sorted_tot_str = NULL;
640 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_LINE_AVG_SORTED_TOTAL)) {
641 line_avg_sorted_tot_str = mcopystr(
642 "---------------------------------------------------------\n"
643 "- Average time / execution of code lines, sorted, total -\n"
644 "---------------------------------------------------------\n");
645 }
646
647 char* func_time_sorted_mod_str = NULL;
648 if (!p_disable_profiler && (p_flags & STATS_FUNC_TIMES_SORTED_BY_MOD)) {
649 func_time_sorted_mod_str = mcopystr(
650 "-----------------------------------------------\n"
651 "- Total time of functions, sorted, per module -\n"
652 "-----------------------------------------------\n");
653 }
654
655 char* func_count_sorted_mod_str = NULL;
656 if (!p_disable_coverage && (p_flags & STATS_FUNC_COUNT_SORTED_BY_MOD)) {
657 func_count_sorted_mod_str = mcopystr(
658 "----------------------------------------------------\n"
659 "- Execution count of functions, sorted, per module -\n"
660 "----------------------------------------------------\n");
661 }
662
663 char* func_avg_sorted_mod_str = NULL;
664 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_FUNC_AVG_SORTED_BY_MOD)) {
665 func_avg_sorted_mod_str = mcopystr(
666 "-------------------------------------------------------------\n"
667 "- Average time / execution of functions, sorted, per module -\n"
668 "-------------------------------------------------------------\n");
669 }
670
671 char* func_time_sorted_tot_str = NULL;
672 if (!p_disable_profiler && (p_flags & STATS_FUNC_TIMES_SORTED_TOTAL)) {
673 func_time_sorted_tot_str = mcopystr(
674 "------------------------------------------\n"
675 "- Total time of functions, sorted, total -\n"
676 "------------------------------------------\n");
677 }
678
679 char* func_count_sorted_tot_str = NULL;
680 if (!p_disable_coverage && (p_flags & STATS_FUNC_COUNT_SORTED_TOTAL)) {
681 func_count_sorted_tot_str = mcopystr(
682 "-----------------------------------------------\n"
683 "- Execution count of functions, sorted, total -\n"
684 "-----------------------------------------------\n");
685 }
686
687 char* func_avg_sorted_tot_str = NULL;
688 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_FUNC_AVG_SORTED_TOTAL)) {
689 func_avg_sorted_tot_str = mcopystr(
690 "--------------------------------------------------------\n"
691 "- Average time / execution of functions, sorted, total -\n"
692 "--------------------------------------------------------\n");
693 }
694
695 char* line_time_sorted_top10_str = NULL;
696 if (!p_disable_profiler && (p_flags & STATS_TOP10_LINE_TIMES)) {
697 line_time_sorted_top10_str = mcopystr(
698 "------------------------------------\n"
699 "- Total time of code lines, top 10 -\n"
700 "------------------------------------\n");
701 }
702
703 char* line_count_sorted_top10_str = NULL;
704 if (!p_disable_coverage && (p_flags & STATS_TOP10_LINE_COUNT)) {
705 line_count_sorted_top10_str = mcopystr(
706 "-----------------------------------------\n"
707 "- Execution count of code lines, top 10 -\n"
708 "-----------------------------------------\n");
709 }
710
711 char* line_avg_sorted_top10_str = NULL;
712 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_TOP10_LINE_AVG)) {
713 line_avg_sorted_top10_str = mcopystr(
714 "--------------------------------------------------\n"
715 "- Average time / execution of code lines, top 10 -\n"
716 "--------------------------------------------------\n");
717 }
718
719 char* func_time_sorted_top10_str = NULL;
720 if (!p_disable_profiler && (p_flags & STATS_TOP10_FUNC_TIMES)) {
721 func_time_sorted_top10_str = mcopystr(
722 "-----------------------------------\n"
723 "- Total time of functions, top 10 -\n"
724 "-----------------------------------\n");
725 }
726
727 char* func_count_sorted_top10_str = NULL;
728 if (!p_disable_coverage && (p_flags & STATS_TOP10_FUNC_COUNT)) {
729 func_count_sorted_top10_str = mcopystr(
730 "----------------------------------------\n"
731 "- Execution count of functions, top 10 -\n"
732 "----------------------------------------\n");
733 }
734
735 char* func_avg_sorted_top10_str = NULL;
736 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_TOP10_FUNC_AVG)) {
737 func_avg_sorted_top10_str = mcopystr(
738 "-------------------------------------------------\n"
739 "- Average time / execution of functions, top 10 -\n"
740 "-------------------------------------------------\n");
741 }
742
743 char* unused_lines_str = NULL;
744 char* unused_func_str = NULL;
745 if (!p_disable_coverage && (p_flags & STATS_UNUSED_LINES)) {
746 unused_lines_str = mcopystr(
747 "---------------------\n"
748 "- Unused code lines -\n"
749 "---------------------\n");
750 }
751 if (!p_disable_coverage && (p_flags & STATS_UNUSED_FUNC)) {
752 unused_func_str = mcopystr(
753 "--------------------\n"
754 "- Unused functions -\n"
755 "--------------------\n");
756 }
757
758 // variables for counting totals, and for determining the amount of unused lines/functions
759 size_t total_code_lines = 0;
760 size_t total_functions = 0;
761 size_t used_code_lines = 0;
762 size_t used_functions = 0;
763
764 // cached sizes of statistics data segments, needed to determine whether a separator
765 // is needed or not
766 size_t line_data_str_len = mstrlen(line_data_str);
767 size_t func_data_str_len = mstrlen(func_data_str);
768 size_t unused_lines_str_len = mstrlen(unused_lines_str);
769 size_t unused_func_str_len = mstrlen(unused_func_str);
770 size_t line_avg_str_len = mstrlen(line_avg_str);
771 size_t func_avg_str_len = mstrlen(func_avg_str);
772
773 // cycle through the database and gather the necessary data
774 for (size_t i = 0; i < p_db.size(); ++i) {
775 if (i > 0) {
776 // add separators between files (only add them if the previous file actually added something)
777 if ((p_flags & STATS_LINE_DATA_RAW) && line_data_str_len != mstrlen(line_data_str)) {
778 line_data_str = mputstr(line_data_str, "-------------------------------------------------\n");
779 line_data_str_len = mstrlen(line_data_str);
780 }
781 if ((p_flags & STATS_FUNC_DATA_RAW) && func_data_str_len != mstrlen(func_data_str)) {
782 func_data_str = mputstr(func_data_str, "------------------------------------------------\n");
783 func_data_str_len = mstrlen(func_data_str);
784 }
785 if (!p_disable_coverage) {
786 if ((p_flags & STATS_UNUSED_LINES) && unused_lines_str_len != mstrlen(unused_lines_str)) {
787 unused_lines_str = mputstr(unused_lines_str, "---------------------\n");
788 unused_lines_str_len = mstrlen(unused_lines_str);
789 }
790 if ((p_flags & STATS_UNUSED_FUNC) && unused_func_str_len != mstrlen(unused_func_str)) {
791 unused_func_str = mputstr(unused_func_str, "--------------------\n");
792 unused_func_str_len = mstrlen(unused_func_str);
793 }
794 if (!p_disable_profiler) {
795 if ((p_flags & STATS_LINE_AVG_RAW) && line_avg_str_len != mstrlen(line_avg_str)) {
796 line_avg_str = mputstr(line_avg_str, "-------------------------------------------\n");
797 line_avg_str_len = mstrlen(line_avg_str);
798 }
799 if ((p_flags & STATS_FUNC_AVG_RAW) && func_avg_str_len != mstrlen(func_avg_str)) {
800 func_avg_str = mputstr(func_avg_str, "------------------------------------------\n");
801 func_avg_str_len = mstrlen(func_avg_str);
802 }
803 }
804 }
805 }
806
807 // lines
808 for (size_t j = 0; j < p_db[i].lines.size(); ++j) {
809 // line specification (including function name for the function's start line)
810 char* line_spec_str = mprintf("%s:%d", p_db[i].filename,
811 p_db[i].lines[j].lineno);
812 int func = get_function(p_db, i, p_db[i].lines[j].lineno);
813 if (-1 != func) {
814 line_spec_str = mputprintf(line_spec_str, " [%s]", p_db[i].functions[func].name);
815 }
816 line_spec_str = mputstrn(line_spec_str, "\n", 1);
817
818 if (p_disable_coverage || 0 != p_db[i].lines[j].exec_count) {
819 if (!p_disable_profiler) {
820 if (p_flags & STATS_LINE_DATA_RAW) {
821 char* total_time_str = timeval2string(p_db[i].lines[j].total_time);
822 line_data_str = mputprintf(line_data_str, "%ss", total_time_str);
823 Free(total_time_str);
824 }
825 if (!p_disable_coverage) {
826 if (p_flags & STATS_LINE_DATA_RAW) {
827 line_data_str = mputstrn(line_data_str, "\t/\t", 3);
828 }
829 if (p_flags & STATS_LINE_AVG_RAW) {
830 double avg = (p_db[i].lines[j].total_time.tv_sec +
831 p_db[i].lines[j].total_time.tv_usec / 1000000.0) /
832 p_db[i].lines[j].exec_count;
833 char* total_time_str = timeval2string(p_db[i].lines[j].total_time);
834 line_avg_str = mputprintf(line_avg_str, "%.6lfs\t(%ss / %d)",
835 avg, total_time_str, p_db[i].lines[j].exec_count);
836 Free(total_time_str);
837 }
838 }
839 }
840 if (!p_disable_coverage && (p_flags & STATS_LINE_DATA_RAW)) {
841 line_data_str = mputprintf(line_data_str, "%d", p_db[i].lines[j].exec_count);
842 }
843
844 // add the line spec string to the other strings
845 if (p_flags & STATS_LINE_DATA_RAW) {
846 line_data_str = mputprintf(line_data_str, "\t%s", line_spec_str);
847 }
848 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_LINE_AVG_RAW)) {
849 line_avg_str = mputprintf(line_avg_str, "\t%s", line_spec_str);
850 }
851 ++used_code_lines;
852 }
853 else if (p_flags & STATS_UNUSED_LINES) {
854 // unused line
855 unused_lines_str = mputstr(unused_lines_str, line_spec_str);
856 }
857 Free(line_spec_str);
858 }
859
860 // functions
861 for (size_t j = 0; j < p_db[i].functions.size(); ++j) {
862 // functions specification
863 char* func_spec_str = mprintf("%s:%d [%s]\n", p_db[i].filename,
864 p_db[i].functions[j].lineno, p_db[i].functions[j].name);
865
866 if (p_disable_coverage || 0 != p_db[i].functions[j].exec_count) {
867 if (!p_disable_profiler) {
868 if (p_flags & STATS_FUNC_DATA_RAW) {
869 char* total_time_str = timeval2string(p_db[i].functions[j].total_time);
870 func_data_str = mputprintf(func_data_str, "%ss", total_time_str);
871 Free(total_time_str);
872 }
873 if (!p_disable_coverage) {
874 if (p_flags & STATS_FUNC_DATA_RAW) {
875 func_data_str = mputstrn(func_data_str, "\t/\t", 3);
876 }
877 if (p_flags & STATS_FUNC_AVG_RAW) {
878 double avg = (p_db[i].functions[j].total_time.tv_sec +
879 p_db[i].functions[j].total_time.tv_usec / 1000000.0) /
880 p_db[i].functions[j].exec_count;
881 char* total_time_str = timeval2string(p_db[i].functions[j].total_time);
882 func_avg_str = mputprintf(func_avg_str, "%.6lfs\t(%ss / %d)",
883 avg, total_time_str, p_db[i].functions[j].exec_count);
884 Free(total_time_str);
885 }
886 }
887 }
888 if (!p_disable_coverage && (p_flags & STATS_FUNC_DATA_RAW)) {
889 func_data_str = mputprintf(func_data_str, "%d", p_db[i].functions[j].exec_count);
890 }
891
892 // add the line spec string to the other strings
893 if (p_flags & STATS_FUNC_DATA_RAW) {
894 func_data_str = mputprintf(func_data_str, "\t%s", func_spec_str);
895 }
896 if (!p_disable_profiler && !p_disable_coverage && (p_flags & STATS_FUNC_AVG_RAW)) {
897 func_avg_str = mputprintf(func_avg_str, "\t%s", func_spec_str);
898 }
899
900 ++used_functions;
901 }
902 else if (p_flags & STATS_UNUSED_FUNC) {
903 // unused function
904 unused_func_str = mputstr(unused_func_str, func_spec_str);
905 }
906 Free(func_spec_str);
907 }
908
909 // number of lines and functions
910 if (p_flags & STATS_NUMBER_OF_LINES) {
911 line_func_count_str = mputprintf(line_func_count_str, "%s:\t%lu lines,\t%lu functions\n",
912 p_db[i].filename, p_db[i].lines.size(), p_db[i].functions.size());
913 }
914 total_code_lines += p_db[i].lines.size();
915 total_functions += p_db[i].functions.size();
916 }
917 if (p_flags & STATS_NUMBER_OF_LINES) {
918 line_func_count_str = mputprintf(line_func_count_str,
919 "--------------------------------------\n"
920 "Total:\t%lu lines,\t%lu functions\n", total_code_lines, total_functions);
921 }
922
923 if (p_flags & (STATS_TOP10_ALL_DATA | STATS_ALL_DATA_SORTED)) {
924 // copy code line and function info into stats_data_t containers for sorting
925 stats_data_t* code_line_stats = (stats_data_t*)Malloc(used_code_lines * sizeof(stats_data_t));
926 stats_data_t* function_stats = (stats_data_t*)Malloc(used_functions * sizeof(stats_data_t));
927 int line_index = 0;
928 int func_index = 0;
929
930 for (size_t i = 0; i < p_db.size(); ++i) {
931 for (size_t j = 0; j < p_db[i].lines.size(); ++j) {
932 if (p_disable_coverage || 0 != p_db[i].lines[j].exec_count) {
933 code_line_stats[line_index].filename = p_db[i].filename;
934 code_line_stats[line_index].funcname = NULL;
935 code_line_stats[line_index].lineno = p_db[i].lines[j].lineno;
936 code_line_stats[line_index].total_time = p_db[i].lines[j].total_time;
937 code_line_stats[line_index].exec_count = p_db[i].lines[j].exec_count;
938 int func = get_function(p_db, i, p_db[i].lines[j].lineno);
939 if (-1 != func) {
940 code_line_stats[line_index].funcname = p_db[i].functions[func].name;
941 }
942 ++line_index;
943 }
944 }
945 for (size_t j = 0; j < p_db[i].functions.size(); ++j) {
946 if (p_disable_coverage || 0 != p_db[i].functions[j].exec_count) {
947 function_stats[func_index].filename = p_db[i].filename;
948 function_stats[func_index].funcname = p_db[i].functions[j].name;
949 function_stats[func_index].lineno = p_db[i].functions[j].lineno;
950 function_stats[func_index].total_time = p_db[i].functions[j].total_time;
951 function_stats[func_index].exec_count = p_db[i].functions[j].exec_count;
952 ++func_index;
953 }
954 }
955 }
956
957 if (!p_disable_profiler) {
958 // sort the code lines and functions by total time
959 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_time);
960 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_time);
961
962 if (p_flags & (STATS_LINE_TIMES_SORTED_TOTAL | STATS_TOP10_LINE_TIMES)) {
963 // cycle through the sorted code lines and gather the necessary data
964 for (size_t i = 0; i < used_code_lines; ++i) {
965 char* total_time_str = timeval2string(code_line_stats[i].total_time);
966 char* the_data = mprintf("%ss\t%s:%d", total_time_str,
967 code_line_stats[i].filename, code_line_stats[i].lineno);
968 Free(total_time_str);
969 if (NULL != code_line_stats[i].funcname) {
970 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
971 }
972 the_data = mputstrn(the_data, "\n", 1);
973 if (p_flags & STATS_LINE_TIMES_SORTED_TOTAL) {
974 line_time_sorted_tot_str = mputstr(line_time_sorted_tot_str, the_data);
975 }
976 if (i < 10 && (p_flags & STATS_TOP10_LINE_TIMES)) {
977 line_time_sorted_top10_str = mputprintf(line_time_sorted_top10_str,
978 "%2lu.\t%s", i + 1, the_data);
979 }
980 Free(the_data);
981 }
982 }
983
984 if (p_flags & (STATS_FUNC_TIMES_SORTED_TOTAL | STATS_TOP10_FUNC_TIMES)) {
985 // cycle through the sorted functions and gather the necessary data
986 for (size_t i = 0; i < used_functions; ++i) {
987 char* total_time_str = timeval2string(function_stats[i].total_time);
988 char* the_data = mprintf("%ss\t%s:%d [%s]\n", total_time_str,
989 function_stats[i].filename, function_stats[i].lineno, function_stats[i].funcname);
990 Free(total_time_str);
991 if (p_flags & STATS_FUNC_TIMES_SORTED_TOTAL) {
992 func_time_sorted_tot_str = mputstr(func_time_sorted_tot_str, the_data);
993 }
994 if (i < 10 && (p_flags & STATS_TOP10_FUNC_TIMES)) {
995 func_time_sorted_top10_str = mputprintf(func_time_sorted_top10_str,
996 "%2lu.\t%s", i + 1, the_data);
997 }
998 Free(the_data);
999 }
1000 }
1001
1002 if (p_flags & (STATS_LINE_TIMES_SORTED_BY_MOD | STATS_FUNC_TIMES_SORTED_BY_MOD)) {
1003 // cached string lengths, to avoid multiple separators after each other
1004 size_t line_time_sorted_mod_str_len = mstrlen(line_time_sorted_mod_str);
1005 size_t func_time_sorted_mod_str_len = mstrlen(func_time_sorted_mod_str);
1006
1007 // cycle through the sorted statistics and gather the necessary data per module
1008 for (size_t i = 0; i < p_db.size(); ++i) {
1009 if (i > 0) {
1010 if ((p_flags & STATS_LINE_TIMES_SORTED_BY_MOD) &&
1011 line_time_sorted_mod_str_len != mstrlen(line_time_sorted_mod_str)) {
1012 line_time_sorted_mod_str = mputstr(line_time_sorted_mod_str,
1013 "------------------------------------------------\n");
1014 line_time_sorted_mod_str_len = mstrlen(line_time_sorted_mod_str);
1015 }
1016 if ((p_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) &&
1017 func_time_sorted_mod_str_len != mstrlen(func_time_sorted_mod_str)) {
1018 func_time_sorted_mod_str = mputstr(func_time_sorted_mod_str,
1019 "-----------------------------------------------\n");
1020 func_time_sorted_mod_str_len = mstrlen(func_time_sorted_mod_str);
1021 }
1022 }
1023 if (p_flags & STATS_LINE_TIMES_SORTED_BY_MOD) {
1024 for (size_t j = 0; j < used_code_lines; ++j) {
1025 if (0 == strcmp(code_line_stats[j].filename, p_db[i].filename)) {
1026 char* total_time_str = timeval2string(code_line_stats[j].total_time);
1027 line_time_sorted_mod_str = mputprintf(line_time_sorted_mod_str,
1028 "%ss\t%s:%d", total_time_str, code_line_stats[j].filename,
1029 code_line_stats[j].lineno);
1030 Free(total_time_str);
1031 if (NULL != code_line_stats[j].funcname) {
1032 line_time_sorted_mod_str = mputprintf(line_time_sorted_mod_str,
1033 " [%s]", code_line_stats[j].funcname);
1034 }
1035 line_time_sorted_mod_str = mputstrn(line_time_sorted_mod_str, "\n", 1);
1036 }
1037 }
1038 }
1039 if (p_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) {
1040 for (size_t j = 0; j < used_functions; ++j) {
1041 if (0 == strcmp(function_stats[j].filename, p_db[i].filename)) {
1042 char* total_time_str = timeval2string(function_stats[j].total_time);
1043 func_time_sorted_mod_str = mputprintf(func_time_sorted_mod_str,
1044 "%ss\t%s:%d [%s]\n", total_time_str, function_stats[j].filename,
1045 function_stats[j].lineno, function_stats[j].funcname);
1046 Free(total_time_str);
1047 }
1048 }
1049 }
1050 }
1051 }
1052 }
1053
1054 if (!p_disable_coverage) {
1055 // sort the code lines and functions by execution count
1056 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_count);
1057 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_count);
1058
1059 if (p_flags & (STATS_LINE_COUNT_SORTED_TOTAL | STATS_TOP10_LINE_COUNT)) {
1060 // cycle through the sorted code lines and gather the necessary data
1061 for (size_t i = 0; i < used_code_lines; ++i) {
1062 char* the_data = mprintf("%d\t%s:%d", code_line_stats[i].exec_count,
1063 code_line_stats[i].filename, code_line_stats[i].lineno);
1064 if (NULL != code_line_stats[i].funcname) {
1065 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
1066 }
1067 the_data = mputstrn(the_data, "\n", 1);
1068 if (p_flags & STATS_LINE_COUNT_SORTED_TOTAL) {
1069 line_count_sorted_tot_str = mputstr(line_count_sorted_tot_str, the_data);
1070 }
1071 if (i < 10 && (p_flags & STATS_TOP10_LINE_COUNT)) {
1072 line_count_sorted_top10_str = mputprintf(line_count_sorted_top10_str,
1073 "%2lu.\t%s", i + 1, the_data);
1074 }
1075 Free(the_data);
1076 }
1077 }
1078
1079 if (p_flags & (STATS_FUNC_COUNT_SORTED_TOTAL | STATS_TOP10_FUNC_COUNT)) {
1080 // cycle through the sorted functions and gather the necessary data
1081 for (size_t i = 0; i < used_functions; ++i) {
1082 char* the_data = mprintf("%d\t%s:%d [%s]\n",
1083 function_stats[i].exec_count, function_stats[i].filename,
1084 function_stats[i].lineno, function_stats[i].funcname);
1085 if (p_flags & STATS_FUNC_COUNT_SORTED_TOTAL) {
1086 func_count_sorted_tot_str = mputstr(func_count_sorted_tot_str, the_data);
1087 }
1088 if (i < 10 && (p_flags & STATS_TOP10_FUNC_COUNT)) {
1089 func_count_sorted_top10_str = mputprintf(func_count_sorted_top10_str,
1090 "%2lu.\t%s", i + 1, the_data);
1091 }
1092 Free(the_data);
1093 }
1094 }
1095
1096 if (p_flags & (STATS_LINE_COUNT_SORTED_BY_MOD | STATS_FUNC_COUNT_SORTED_BY_MOD)) {
1097 // cached string lengths, to avoid multiple separators after each other
1098 size_t line_count_sorted_mod_str_len = mstrlen(line_count_sorted_mod_str);
1099 size_t func_count_sorted_mod_str_len = mstrlen(func_count_sorted_mod_str);
1100
1101 // cycle through the sorted statistics and gather the necessary data per module
1102 for (size_t i = 0; i < p_db.size(); ++i) {
1103 if (i > 0) {
1104 if ((p_flags & STATS_LINE_COUNT_SORTED_BY_MOD) &&
1105 line_count_sorted_mod_str_len != mstrlen(line_count_sorted_mod_str)) {
1106 line_count_sorted_mod_str = mputstr(line_count_sorted_mod_str,
1107 "-----------------------------------------------------\n");
1108 line_count_sorted_mod_str_len = mstrlen(line_count_sorted_mod_str);
1109 }
1110 if ((p_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) &&
1111 func_count_sorted_mod_str_len != mstrlen(func_count_sorted_mod_str)) {
1112 func_count_sorted_mod_str = mputstr(func_count_sorted_mod_str,
1113 "----------------------------------------------------\n");
1114 func_count_sorted_mod_str_len = mstrlen(func_count_sorted_mod_str);
1115 }
1116 }
1117 if (p_flags & STATS_LINE_COUNT_SORTED_BY_MOD) {
1118 for (size_t j = 0; j < used_code_lines; ++j) {
1119 if (0 == strcmp(code_line_stats[j].filename, p_db[i].filename)) {
1120 line_count_sorted_mod_str = mputprintf(line_count_sorted_mod_str,
1121 "%d\t%s:%d", code_line_stats[j].exec_count, code_line_stats[j].filename,
1122 code_line_stats[j].lineno);
1123 if (NULL != code_line_stats[j].funcname) {
1124 line_count_sorted_mod_str = mputprintf(line_count_sorted_mod_str,
1125 " [%s]", code_line_stats[j].funcname);
1126 }
1127 line_count_sorted_mod_str = mputstrn(line_count_sorted_mod_str, "\n", 1);
1128 }
1129 }
1130 }
1131 if (p_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) {
1132 for (size_t j = 0; j < used_functions; ++j) {
1133 if (0 == strcmp(function_stats[j].filename, p_db[i].filename)) {
1134 func_count_sorted_mod_str = mputprintf(func_count_sorted_mod_str,
1135 "%d\t%s:%d [%s]\n", function_stats[j].exec_count, function_stats[j].filename,
1136 function_stats[j].lineno, function_stats[j].funcname);
1137 }
1138 }
1139 }
1140 }
1141 }
1142 }
1143
1144 if (!p_disable_profiler && !p_disable_coverage) {
1145 // sort the code lines and functions by average time / execution
1146 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_avg);
1147 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_avg);
1148
1149 if (p_flags & (STATS_LINE_AVG_SORTED_TOTAL | STATS_TOP10_LINE_AVG)) {
1150 // cycle through the sorted code lines and gather the necessary data
1151 for (size_t i = 0; i < used_code_lines; ++i) {
1152 double avg = (code_line_stats[i].total_time.tv_sec +
1153 code_line_stats[i].total_time.tv_usec / 1000000.0) /
1154 code_line_stats[i].exec_count;
1155 char* total_time_str = timeval2string(code_line_stats[i].total_time);
1156 char* the_data = mprintf("%.6lfs\t(%ss / %d)\t%s:%d",
1157 avg, total_time_str, code_line_stats[i].exec_count,
1158 code_line_stats[i].filename, code_line_stats[i].lineno);
1159 Free(total_time_str);
1160 if (NULL != code_line_stats[i].funcname) {
1161 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
1162 }
1163 the_data = mputstrn(the_data, "\n", 1);
1164 if (p_flags & STATS_LINE_AVG_SORTED_TOTAL) {
1165 line_avg_sorted_tot_str = mputstr(line_avg_sorted_tot_str, the_data);
1166 }
1167 if (i < 10 && (p_flags & STATS_TOP10_LINE_AVG)) {
1168 line_avg_sorted_top10_str = mputprintf(line_avg_sorted_top10_str,
1169 "%2lu.\t%s", i + 1, the_data);
1170 }
1171 Free(the_data);
1172 }
1173 }
1174
1175 if (p_flags & (STATS_FUNC_AVG_SORTED_TOTAL | STATS_TOP10_FUNC_AVG)) {
1176 // cycle through the sorted functions and gather the necessary data
1177 for (size_t i = 0; i < used_functions; ++i) {
1178 double avg = (function_stats[i].total_time.tv_sec +
1179 function_stats[i].total_time.tv_usec / 1000000.0) /
1180 function_stats[i].exec_count;
1181 char* total_time_str = timeval2string(function_stats[i].total_time);
1182 char* the_data = mprintf("%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1183 avg, total_time_str, function_stats[i].exec_count,
1184 function_stats[i].filename, function_stats[i].lineno, function_stats[i].funcname);
1185 Free(total_time_str);
1186 if (p_flags & STATS_FUNC_AVG_SORTED_TOTAL) {
1187 func_avg_sorted_tot_str = mputstr(func_avg_sorted_tot_str, the_data);
1188 }
1189 if (i < 10 && (p_flags & STATS_TOP10_FUNC_AVG)) {
1190 func_avg_sorted_top10_str = mputprintf(func_avg_sorted_top10_str,
1191 "%2lu.\t%s", i + 1, the_data);
1192 }
1193 Free(the_data);
1194 }
1195 }
1196
1197 if (p_flags & (STATS_LINE_AVG_SORTED_BY_MOD | STATS_FUNC_AVG_SORTED_BY_MOD)) {
1198 // cached string lengths, to avoid multiple separators after each other
1199 size_t line_avg_sorted_mod_str_len = mstrlen(line_avg_sorted_mod_str);
1200 size_t func_avg_sorted_mod_str_len = mstrlen(func_avg_sorted_mod_str);
1201
1202 // cycle through the sorted statistics and gather the necessary data per module
1203 for (size_t i = 0; i < p_db.size(); ++i) {
1204 if (i > 0) {
1205 if ((p_flags & STATS_LINE_AVG_SORTED_BY_MOD) &&
1206 line_avg_sorted_mod_str_len != mstrlen(line_avg_sorted_mod_str)) {
1207 line_avg_sorted_mod_str = mputstr(line_avg_sorted_mod_str,
1208 "--------------------------------------------------------------\n");
1209 line_avg_sorted_mod_str_len = mstrlen(line_avg_sorted_mod_str);
1210 }
1211 if ((p_flags & STATS_FUNC_AVG_SORTED_BY_MOD) &&
1212 func_avg_sorted_mod_str_len != mstrlen(func_avg_sorted_mod_str)) {
1213 func_avg_sorted_mod_str = mputstr(func_avg_sorted_mod_str,
1214 "-------------------------------------------------------------\n");
1215 func_avg_sorted_mod_str_len = mstrlen(func_avg_sorted_mod_str);
1216 }
1217 }
1218 if (p_flags & STATS_LINE_AVG_SORTED_BY_MOD) {
1219 for (size_t j = 0; j < used_code_lines; ++j) {
1220 if (0 == strcmp(code_line_stats[j].filename, p_db[i].filename)) {
1221 double avg = (code_line_stats[j].total_time.tv_sec +
1222 code_line_stats[j].total_time.tv_usec / 1000000.0) /
1223 code_line_stats[j].exec_count;
1224 char* total_time_str = timeval2string(code_line_stats[j].total_time);
1225 line_avg_sorted_mod_str = mputprintf(line_avg_sorted_mod_str,
1226 "%.6lfs\t(%ss / %d)\t%s:%d",
1227 avg, total_time_str, code_line_stats[j].exec_count,
1228 code_line_stats[j].filename, code_line_stats[j].lineno);
1229 Free(total_time_str);
1230 if (NULL != code_line_stats[j].funcname) {
1231 line_avg_sorted_mod_str = mputprintf(line_avg_sorted_mod_str,
1232 " [%s]", code_line_stats[j].funcname);
1233 }
1234 line_avg_sorted_mod_str = mputstrn(line_avg_sorted_mod_str, "\n", 1);
1235 }
1236 }
1237 }
1238 if (p_flags & STATS_FUNC_AVG_SORTED_BY_MOD) {
1239 for (size_t j = 0; j < used_functions; ++j) {
1240 if (0 == strcmp(function_stats[j].filename, p_db[i].filename)) {
1241 double avg = (function_stats[j].total_time.tv_sec +
1242 function_stats[j].total_time.tv_usec / 1000000.0) /
1243 function_stats[j].exec_count;
1244 char* total_time_str = timeval2string(function_stats[j].total_time);
1245 func_avg_sorted_mod_str = mputprintf(func_avg_sorted_mod_str,
1246 "%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1247 avg, total_time_str, function_stats[j].exec_count,
1248 function_stats[j].filename, function_stats[j].lineno, function_stats[j].funcname);
1249 Free(total_time_str);
1250 }
1251 }
1252 }
1253 }
1254 }
1255 }
1256
1257 // free the stats data
1258 Free(code_line_stats);
1259 Free(function_stats);
1260 }
1261
1262 // add new lines at the end of each segment
1263 if (p_flags & STATS_NUMBER_OF_LINES) {
1264 line_func_count_str = mputstrn(line_func_count_str, "\n", 1);
1265 }
1266 if (p_flags & STATS_LINE_DATA_RAW) {
1267 line_data_str = mputstrn(line_data_str, "\n", 1);
1268 }
1269 if (p_flags & STATS_FUNC_DATA_RAW) {
1270 func_data_str = mputstrn(func_data_str, "\n", 1);
1271 }
1272 if (!p_disable_profiler) {
1273 if (p_flags & STATS_LINE_TIMES_SORTED_BY_MOD) {
1274 line_time_sorted_mod_str = mputstrn(line_time_sorted_mod_str, "\n", 1);
1275 }
1276 if (p_flags & STATS_LINE_TIMES_SORTED_TOTAL) {
1277 line_time_sorted_tot_str = mputstrn(line_time_sorted_tot_str, "\n", 1);
1278 }
1279 if (p_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) {
1280 func_time_sorted_mod_str = mputstrn(func_time_sorted_mod_str, "\n", 1);
1281 }
1282 if (p_flags & STATS_FUNC_TIMES_SORTED_TOTAL) {
1283 func_time_sorted_tot_str = mputstrn(func_time_sorted_tot_str, "\n", 1);
1284 }
1285 if (p_flags & STATS_TOP10_LINE_TIMES) {
1286 line_time_sorted_top10_str = mputstrn(line_time_sorted_top10_str, "\n", 1);
1287 }
1288 if (p_flags & STATS_TOP10_FUNC_TIMES) {
1289 func_time_sorted_top10_str = mputstrn(func_time_sorted_top10_str, "\n", 1);
1290 }
1291 if (!p_disable_coverage) {
1292 if (p_flags & STATS_LINE_AVG_RAW) {
1293 line_avg_str = mputstrn(line_avg_str, "\n", 1);
1294 }
1295 if (p_flags & STATS_LINE_AVG_RAW) {
1296 func_avg_str = mputstrn(func_avg_str, "\n", 1);
1297 }
1298 if (p_flags & STATS_LINE_AVG_SORTED_BY_MOD) {
1299 line_avg_sorted_mod_str = mputstrn(line_avg_sorted_mod_str, "\n", 1);
1300 }
1301 if (p_flags & STATS_LINE_AVG_SORTED_TOTAL) {
1302 line_avg_sorted_tot_str = mputstrn(line_avg_sorted_tot_str, "\n", 1);
1303 }
1304 if (p_flags & STATS_FUNC_AVG_SORTED_BY_MOD) {
1305 func_avg_sorted_mod_str = mputstrn(func_avg_sorted_mod_str, "\n", 1);
1306 }
1307 if (p_flags & STATS_FUNC_AVG_SORTED_TOTAL) {
1308 func_avg_sorted_tot_str = mputstrn(func_avg_sorted_tot_str, "\n", 1);
1309 }
1310 if (p_flags & STATS_TOP10_LINE_AVG) {
1311 line_avg_sorted_top10_str = mputstrn(line_avg_sorted_top10_str, "\n", 1);
1312 }
1313 if (p_flags & STATS_TOP10_FUNC_AVG) {
1314 func_avg_sorted_top10_str = mputstrn(func_avg_sorted_top10_str, "\n", 1);
1315 }
1316 }
1317 }
1318 if (!p_disable_coverage) {
1319 if (p_flags & STATS_LINE_COUNT_SORTED_BY_MOD) {
1320 line_count_sorted_mod_str = mputstrn(line_count_sorted_mod_str, "\n", 1);
1321 }
1322 if (p_flags & STATS_LINE_COUNT_SORTED_TOTAL) {
1323 line_count_sorted_tot_str = mputstrn(line_count_sorted_tot_str, "\n", 1);
1324 }
1325 if (p_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) {
1326 func_count_sorted_mod_str = mputstrn(func_count_sorted_mod_str, "\n", 1);
1327 }
1328 if (p_flags & STATS_FUNC_COUNT_SORTED_TOTAL) {
1329 func_count_sorted_tot_str = mputstrn(func_count_sorted_tot_str, "\n", 1);
1330 }
1331 if (p_flags & STATS_TOP10_LINE_COUNT) {
1332 line_count_sorted_top10_str = mputstrn(line_count_sorted_top10_str, "\n", 1);
1333 }
1334 if (p_flags & STATS_TOP10_FUNC_COUNT) {
1335 func_count_sorted_top10_str = mputstrn(func_count_sorted_top10_str, "\n", 1);
1336 }
1337 if (p_flags & STATS_UNUSED_LINES) {
1338 unused_lines_str = mputstrn(unused_lines_str, "\n", 1);
1339 }
1340 if (p_flags & STATS_UNUSED_FUNC) {
1341 unused_func_str = mputstrn(unused_func_str, "\n", 1);
1342 }
1343 }
1344
1345 // write the statistics to the specified file
1346 FILE* file = fopen(p_filename, "w");
1347 if (NULL == file) {
1348 p_error_function("Could not open file '%s' for writing. Profiling and/or "
1349 "code coverage statistics will not be saved.", p_filename);
1350 return;
1351 }
1352 // by now the strings for all disabled statistics entries should be null
1353 fprintf(file, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
1354 , title_str
1355 , (NULL != line_func_count_str) ? line_func_count_str : ""
1356 , (NULL != line_data_str) ? line_data_str : ""
1357 , (NULL != line_avg_str) ? line_avg_str : ""
1358 , (NULL != func_data_str) ? func_data_str : ""
1359 , (NULL != func_avg_str) ? func_avg_str : ""
1360 , (NULL != line_time_sorted_mod_str) ? line_time_sorted_mod_str : ""
1361 , (NULL != line_time_sorted_tot_str) ? line_time_sorted_tot_str : ""
1362 , (NULL != func_time_sorted_mod_str) ? func_time_sorted_mod_str : ""
1363 , (NULL != func_time_sorted_tot_str) ? func_time_sorted_tot_str : ""
1364 , (NULL != line_count_sorted_mod_str) ? line_count_sorted_mod_str : ""
1365 , (NULL != line_count_sorted_tot_str) ? line_count_sorted_tot_str : ""
1366 , (NULL != func_count_sorted_mod_str) ? func_count_sorted_mod_str : ""
1367 , (NULL != func_count_sorted_tot_str) ? func_count_sorted_tot_str : ""
1368 , (NULL != line_avg_sorted_mod_str) ? line_avg_sorted_mod_str : ""
1369 , (NULL != line_avg_sorted_tot_str) ? line_avg_sorted_tot_str : ""
1370 , (NULL != func_avg_sorted_mod_str) ? func_avg_sorted_mod_str : ""
1371 , (NULL != func_avg_sorted_tot_str) ? func_avg_sorted_tot_str : ""
1372 , (NULL != line_time_sorted_top10_str) ? line_time_sorted_top10_str : ""
1373 , (NULL != func_time_sorted_top10_str) ? func_time_sorted_top10_str : ""
1374 , (NULL != line_count_sorted_top10_str) ? line_count_sorted_top10_str : ""
1375 , (NULL != func_count_sorted_top10_str) ? func_count_sorted_top10_str : ""
1376 , (NULL != line_avg_sorted_top10_str) ? line_avg_sorted_top10_str : ""
1377 , (NULL != func_avg_sorted_top10_str) ? func_avg_sorted_top10_str : ""
1378 , (NULL != unused_lines_str) ? unused_lines_str : ""
1379 , (NULL != unused_func_str) ? unused_func_str : "");
1380
1381 fclose(file);
1382
1383 // free the strings
1384 Free(title_str);
1385 Free(line_func_count_str);
1386 Free(line_data_str);
1387 Free(line_avg_str);
1388 Free(func_data_str);
1389 Free(func_avg_str);
1390 Free(line_time_sorted_mod_str);
1391 Free(line_time_sorted_tot_str);
1392 Free(func_time_sorted_mod_str);
1393 Free(func_time_sorted_tot_str);
1394 Free(line_count_sorted_mod_str);
1395 Free(line_count_sorted_tot_str);
1396 Free(func_count_sorted_mod_str);
1397 Free(func_count_sorted_tot_str);
1398 Free(line_avg_sorted_mod_str);
1399 Free(line_avg_sorted_tot_str);
1400 Free(func_avg_sorted_mod_str);
1401 Free(func_avg_sorted_tot_str);
1402 Free(line_time_sorted_top10_str);
1403 Free(func_time_sorted_top10_str);
1404 Free(line_count_sorted_top10_str);
1405 Free(func_count_sorted_top10_str);
1406 Free(line_avg_sorted_top10_str);
1407 Free(func_avg_sorted_top10_str);
1408 Free(unused_lines_str);
1409 Free(unused_func_str);
1410 }
1411
1412 } // namespace
This page took 0.067362 seconds and 6 git commands to generate.