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 ///////////////////////////////////////////////////////////////////////////////
9 #include "ProfilerTools.hh"
10 #include "JSON_Tokenizer.hh"
17 namespace Profiler_Tools
{
19 ////////////////////////////////////
20 //////// timeval operations ////////
21 ////////////////////////////////////
23 timeval
string2timeval(const char* str
)
25 // read and store the first part (atoi will read until the decimal dot)
26 long int sec
= atoi(str
);
31 // step over each digit
37 // step over the decimal dot and read the second part of the number
38 tv
.tv_usec
= atoi(str
+ 1);
42 char* timeval2string(timeval tv
)
44 // convert the first part and set the second part to all zeros
45 char* str
= mprintf("%ld.000000", tv
.tv_sec
);
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;
57 timeval
add_timeval(const timeval operand1
, const timeval operand2
)
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) {
64 tv
.tv_usec
-= 1000000;
69 timeval
subtract_timeval(const timeval operand1
, const timeval operand2
)
72 tv
.tv_usec
= operand1
.tv_usec
- operand2
.tv_usec
;
73 tv
.tv_sec
= operand1
.tv_sec
- operand2
.tv_sec
;
76 tv
.tv_usec
+= 1000000;
81 ////////////////////////////////////
82 ///// profiler data operations /////
83 ////////////////////////////////////
85 int get_function(const profiler_db_t
& p_db
, int p_element
, int p_lineno
)
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
) {
95 void create_function(profiler_db_t
& p_db
, int p_element
, int p_lineno
,
96 const char* p_function_name
)
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
);
107 int get_line(const profiler_db_t
& p_db
, int p_element
, int p_lineno
)
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
) {
117 void create_line(profiler_db_t
& p_db
, int p_element
, int p_lineno
)
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
);
127 #define IMPORT_FORMAT_ERROR(cond) \
129 p_error_function("Failed to load profiling and/or code coverage database. Invalid format."); \
133 void import_data(profiler_db_t
& p_db
, const char* p_filename
, boolean p_wait
,
134 void (*p_error_function
)(const char*, ...))
136 // open the file, if it exists
138 FILE* file
= fopen(p_filename
, "r");
141 fseek(file
, 0, SEEK_END
);
142 file_size
= ftell(file
);
144 while (0 == file_size
) {
148 // keep reading until the file appears (and is not empty)
153 file
= fopen(p_filename
, "r");
155 // refresh the file size
156 fseek(file
, 0, SEEK_END
);
157 file_size
= ftell(file
);
161 // rewind the file (the file pointer has been moved to the end of the file to
162 // calculate its size)
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
);
169 if (bytes_read
!= file_size
) {
170 p_error_function("Error reading database file.");
174 // initialize a JSON tokenizer with the buffer
175 JSON_Tokenizer
json(buffer
, file_size
);
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
;
182 size_t value_len
= 0;
184 // start of main array
185 json
.get_next_token(&token
, NULL
, NULL
);
186 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
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;
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
));
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)) {
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
);
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
));
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;
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
));
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);
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
));
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
)) {
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
);
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
));
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
);
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
));
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
));
286 // end of the function's object
287 json
.get_next_token(&token
, NULL
, NULL
);
288 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
290 // read the next token (either the start of another object or the function array end)
291 json
.get_next_token(&token
, NULL
, NULL
);
294 // function array end
295 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
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
));
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
) {
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
));
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;
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
));
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
);
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
));
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
));
346 // end of the line's object
347 json
.get_next_token(&token
, NULL
, NULL
);
348 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
350 // read the next token (either the start of another object or the line array end)
351 json
.get_next_token(&token
, NULL
, NULL
);
355 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
357 // end of the file's object
358 json
.get_next_token(&token
, NULL
, NULL
);
359 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
361 // read the next token (either the start of another object or the main array end)
362 json
.get_next_token(&token
, NULL
, NULL
);
366 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
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*, ...))
373 // check whether the file can be opened for writing
374 FILE* file
= fopen(p_filename
, "w");
376 p_error_function("Could not open file '%s' for writing. Profiling and/or code coverage "
377 "data will not be saved.", p_filename
);
381 // use the JSON tokenizer to create a JSON document from the database
382 JSON_Tokenizer
json(true);
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
) {
388 // each file's data is stored in an object
389 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
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
);
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
) {
402 // the data is stored in an object for each function
403 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
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
);
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
);
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
);
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");
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
);
435 // end of function object
436 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
439 // end of function data array
440 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
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
) {
447 // store line data in an object
448 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
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
);
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
);
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");
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
);
474 // end of this line's object
475 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
478 // end of line data array
479 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
481 // end of this file's object
482 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
486 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
488 // write the JSON document into the file
489 fprintf(file
, "%s\n", json
.get_buffer());
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
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;
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
;
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;
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*, ...))
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
? "#########" : ""));
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");
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
? "---------" : ""));
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");
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
? "---------" : ""));
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
751 if (!p_disable_coverage
&& (p_flags
& STATS_UNUSED_FUNC
)) {
752 unused_func_str
= mcopystr(
753 "--------------------\n"
754 "- Unused functions -\n"
755 "--------------------\n");
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;
764 // cached sizes of statistics data segments, needed to determine whether a separator
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
);
773 // cycle through the database and gather the necessary data
774 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
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
);
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
);
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
);
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
);
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
);
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
);
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
);
814 line_spec_str
= mputprintf(line_spec_str
, " [%s]", p_db
[i
].functions
[func
].name
);
816 line_spec_str
= mputstrn(line_spec_str
, "\n", 1);
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
);
825 if (!p_disable_coverage
) {
826 if (p_flags
& STATS_LINE_DATA_RAW
) {
827 line_data_str
= mputstrn(line_data_str
, "\t/\t", 3);
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
);
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
);
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
);
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
);
853 else if (p_flags
& STATS_UNUSED_LINES
) {
855 unused_lines_str
= mputstr(unused_lines_str
, line_spec_str
);
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
);
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
);
873 if (!p_disable_coverage
) {
874 if (p_flags
& STATS_FUNC_DATA_RAW
) {
875 func_data_str
= mputstrn(func_data_str
, "\t/\t", 3);
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
);
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
);
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
);
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
);
902 else if (p_flags
& STATS_UNUSED_FUNC
) {
904 unused_func_str
= mputstr(unused_func_str
, func_spec_str
);
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());
914 total_code_lines
+= p_db
[i
].lines
.size();
915 total_functions
+= p_db
[i
].functions
.size();
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
);
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
));
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
);
940 code_line_stats
[line_index
].funcname
= p_db
[i
].functions
[func
].name
;
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
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1007 // cycle through the sorted statistics and gather the necessary data per module
1008 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
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
);
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
);
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
);
1035 line_time_sorted_mod_str
= mputstrn(line_time_sorted_mod_str
, "\n", 1);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1101 // cycle through the sorted statistics and gather the necessary data per module
1102 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
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
);
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
);
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
);
1127 line_count_sorted_mod_str
= mputstrn(line_count_sorted_mod_str
, "\n", 1);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
1202 // cycle through the sorted statistics and gather the necessary data per module
1203 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
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
);
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
);
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
);
1234 line_avg_sorted_mod_str
= mputstrn(line_avg_sorted_mod_str
, "\n", 1);
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
);
1257 // free the stats data
1258 Free(code_line_stats
);
1259 Free(function_stats
);
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);
1266 if (p_flags
& STATS_LINE_DATA_RAW
) {
1267 line_data_str
= mputstrn(line_data_str
, "\n", 1);
1269 if (p_flags
& STATS_FUNC_DATA_RAW
) {
1270 func_data_str
= mputstrn(func_data_str
, "\n", 1);
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);
1276 if (p_flags
& STATS_LINE_TIMES_SORTED_TOTAL
) {
1277 line_time_sorted_tot_str
= mputstrn(line_time_sorted_tot_str
, "\n", 1);
1279 if (p_flags
& STATS_FUNC_TIMES_SORTED_BY_MOD
) {
1280 func_time_sorted_mod_str
= mputstrn(func_time_sorted_mod_str
, "\n", 1);
1282 if (p_flags
& STATS_FUNC_TIMES_SORTED_TOTAL
) {
1283 func_time_sorted_tot_str
= mputstrn(func_time_sorted_tot_str
, "\n", 1);
1285 if (p_flags
& STATS_TOP10_LINE_TIMES
) {
1286 line_time_sorted_top10_str
= mputstrn(line_time_sorted_top10_str
, "\n", 1);
1288 if (p_flags
& STATS_TOP10_FUNC_TIMES
) {
1289 func_time_sorted_top10_str
= mputstrn(func_time_sorted_top10_str
, "\n", 1);
1291 if (!p_disable_coverage
) {
1292 if (p_flags
& STATS_LINE_AVG_RAW
) {
1293 line_avg_str
= mputstrn(line_avg_str
, "\n", 1);
1295 if (p_flags
& STATS_LINE_AVG_RAW
) {
1296 func_avg_str
= mputstrn(func_avg_str
, "\n", 1);
1298 if (p_flags
& STATS_LINE_AVG_SORTED_BY_MOD
) {
1299 line_avg_sorted_mod_str
= mputstrn(line_avg_sorted_mod_str
, "\n", 1);
1301 if (p_flags
& STATS_LINE_AVG_SORTED_TOTAL
) {
1302 line_avg_sorted_tot_str
= mputstrn(line_avg_sorted_tot_str
, "\n", 1);
1304 if (p_flags
& STATS_FUNC_AVG_SORTED_BY_MOD
) {
1305 func_avg_sorted_mod_str
= mputstrn(func_avg_sorted_mod_str
, "\n", 1);
1307 if (p_flags
& STATS_FUNC_AVG_SORTED_TOTAL
) {
1308 func_avg_sorted_tot_str
= mputstrn(func_avg_sorted_tot_str
, "\n", 1);
1310 if (p_flags
& STATS_TOP10_LINE_AVG
) {
1311 line_avg_sorted_top10_str
= mputstrn(line_avg_sorted_top10_str
, "\n", 1);
1313 if (p_flags
& STATS_TOP10_FUNC_AVG
) {
1314 func_avg_sorted_top10_str
= mputstrn(func_avg_sorted_top10_str
, "\n", 1);
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);
1322 if (p_flags
& STATS_LINE_COUNT_SORTED_TOTAL
) {
1323 line_count_sorted_tot_str
= mputstrn(line_count_sorted_tot_str
, "\n", 1);
1325 if (p_flags
& STATS_FUNC_COUNT_SORTED_BY_MOD
) {
1326 func_count_sorted_mod_str
= mputstrn(func_count_sorted_mod_str
, "\n", 1);
1328 if (p_flags
& STATS_FUNC_COUNT_SORTED_TOTAL
) {
1329 func_count_sorted_tot_str
= mputstrn(func_count_sorted_tot_str
, "\n", 1);
1331 if (p_flags
& STATS_TOP10_LINE_COUNT
) {
1332 line_count_sorted_top10_str
= mputstrn(line_count_sorted_top10_str
, "\n", 1);
1334 if (p_flags
& STATS_TOP10_FUNC_COUNT
) {
1335 func_count_sorted_top10_str
= mputstrn(func_count_sorted_top10_str
, "\n", 1);
1337 if (p_flags
& STATS_UNUSED_LINES
) {
1338 unused_lines_str
= mputstrn(unused_lines_str
, "\n", 1);
1340 if (p_flags
& STATS_UNUSED_FUNC
) {
1341 unused_func_str
= mputstrn(unused_func_str
, "\n", 1);
1345 // write the statistics to the specified file
1346 FILE* file
= fopen(p_filename
, "w");
1348 p_error_function("Could not open file '%s' for writing. Profiling and/or "
1349 "code coverage statistics will not be saved.", p_filename
);
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"
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
: "");
1385 Free(line_func_count_str
);
1386 Free(line_data_str
);
1388 Free(func_data_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
);