1 /******************************************************************************
2 * Copyright (c) 2000-2016 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
12 ******************************************************************************/
14 #include "ProfilerTools.hh"
15 #include "JSON_Tokenizer.hh"
22 namespace Profiler_Tools
{
24 ////////////////////////////////////
25 //////// timeval operations ////////
26 ////////////////////////////////////
28 timeval
string2timeval(const char* str
)
30 // read and store the first part (atoi will read until the decimal dot)
31 long int sec
= atoi(str
);
36 // step over each digit
42 // step over the decimal dot and read the second part of the number
43 tv
.tv_usec
= atoi(str
+ 1);
47 char* timeval2string(timeval tv
)
49 // convert the first part and set the second part to all zeros
50 char* str
= mprintf("%ld.000000", tv
.tv_sec
);
52 // go through each digit of the second part and add them to the zeros in the string
53 size_t pos
= mstrlen(str
) - 1;
54 while (tv
.tv_usec
> 0) {
55 str
[pos
] += tv
.tv_usec
% 10;
62 timeval
add_timeval(const timeval operand1
, const timeval operand2
)
65 tv
.tv_usec
= operand1
.tv_usec
+ operand2
.tv_usec
;
66 tv
.tv_sec
= operand1
.tv_sec
+ operand2
.tv_sec
;
67 if (tv
.tv_usec
>= 1000000) {
69 tv
.tv_usec
-= 1000000;
74 timeval
subtract_timeval(const timeval operand1
, const timeval operand2
)
77 tv
.tv_usec
= operand1
.tv_usec
- operand2
.tv_usec
;
78 tv
.tv_sec
= operand1
.tv_sec
- operand2
.tv_sec
;
81 tv
.tv_usec
+= 1000000;
86 ////////////////////////////////////
87 ///// profiler data operations /////
88 ////////////////////////////////////
90 int get_function(const profiler_db_t
& p_db
, int p_element
, int p_lineno
)
92 for (size_t i
= 0; i
< p_db
[p_element
].functions
.size(); ++i
) {
93 if (p_db
[p_element
].functions
[i
].lineno
== p_lineno
) {
100 void create_function(profiler_db_t
& p_db
, int p_element
, int p_lineno
,
101 const char* p_function_name
)
103 profiler_db_item_t::profiler_function_data_t func_data
;
104 func_data
.lineno
= p_lineno
;
105 func_data
.total_time
.tv_sec
= 0;
106 func_data
.total_time
.tv_usec
= 0;
107 func_data
.exec_count
= 0;
108 func_data
.name
= mcopystr(p_function_name
);
109 p_db
[p_element
].functions
.push_back(func_data
);
112 int get_line(const profiler_db_t
& p_db
, int p_element
, int p_lineno
)
114 for (size_t i
= 0; i
< p_db
[p_element
].lines
.size(); ++i
) {
115 if (p_db
[p_element
].lines
[i
].lineno
== p_lineno
) {
122 void create_line(profiler_db_t
& p_db
, int p_element
, int p_lineno
)
124 profiler_db_item_t::profiler_line_data_t line_data
;
125 line_data
.lineno
= p_lineno
;
126 line_data
.total_time
.tv_sec
= 0;
127 line_data
.total_time
.tv_usec
= 0;
128 line_data
.exec_count
= 0;
129 p_db
[p_element
].lines
.push_back(line_data
);
132 #define IMPORT_FORMAT_ERROR(cond) \
134 p_error_function("Failed to load profiling and/or code coverage database. Invalid format."); \
138 void import_data(profiler_db_t
& p_db
, const char* p_filename
,
139 void (*p_error_function
)(const char*, ...))
141 // open the file, if it exists
142 FILE* file
= fopen(p_filename
, "r");
144 p_error_function("Profiler database file '%s' does not exist.", p_filename
);
149 fseek(file
, 0, SEEK_END
);
150 int file_size
= ftell(file
);
153 // read the entire file into a character buffer
154 char* buffer
= (char*)Malloc(file_size
);
155 int bytes_read
= fread(buffer
, 1, file_size
, file
);
157 if (bytes_read
!= file_size
) {
158 p_error_function("Error reading database file.");
162 // initialize a JSON tokenizer with the buffer
163 JSON_Tokenizer
json(buffer
, file_size
);
166 // attempt to read tokens from the buffer
167 // if the format is invalid, abort the importing process
168 json_token_t token
= JSON_TOKEN_NONE
;
170 size_t value_len
= 0;
172 // start of main array
173 json
.get_next_token(&token
, NULL
, NULL
);
174 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
176 // read objects (one for each TTCN-3 file), until the main array end mark is reached
177 json
.get_next_token(&token
, NULL
, NULL
);
178 while (JSON_TOKEN_OBJECT_START
== token
) {
179 size_t file_index
= 0;
182 json
.get_next_token(&token
, &value
, &value_len
);
183 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 4 ||
184 0 != strncmp(value
, "file", value_len
));
186 // read the file name and see if its record already exists
187 json
.get_next_token(&token
, &value
, &value_len
);
188 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
);
189 for (file_index
= 0; file_index
< p_db
.size(); ++file_index
) {
190 if (strlen(p_db
[file_index
].filename
) == value_len
- 2 &&
191 0 == strncmp(p_db
[file_index
].filename
, value
+ 1, value_len
- 2)) {
196 // insert a new element if the file was not found
197 if (p_db
.size() == file_index
) {
198 profiler_db_item_t item
;
199 item
.filename
= mcopystrn(value
+ 1, value_len
- 2);
200 p_db
.push_back(item
);
204 json
.get_next_token(&token
, &value
, &value_len
);
205 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 9 ||
206 0 != strncmp(value
, "functions", value_len
));
208 // read and store the functions (an array of objects, same as before)
209 json
.get_next_token(&token
, NULL
, NULL
);
210 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
211 json
.get_next_token(&token
, NULL
, NULL
);
212 while (JSON_TOKEN_OBJECT_START
== token
) {
213 size_t function_index
= 0;
216 json
.get_next_token(&token
, &value
, &value_len
);
217 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 4 ||
218 0 != strncmp(value
, "name", value_len
));
220 // read the function name, it will be checked later
221 json
.get_next_token(&token
, &value
, &value_len
);
222 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
);
223 char* function_name
= mcopystrn(value
+ 1, value_len
- 2);
225 // function start line:
226 json
.get_next_token(&token
, &value
, &value_len
);
227 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
228 0 != strncmp(value
, "start line", value_len
));
230 // read the start line and check if the function already exists
231 json
.get_next_token(&token
, &value
, &value_len
);
232 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
233 int start_line
= atoi(value
);
234 for (function_index
= 0; function_index
< p_db
[file_index
].functions
.size(); ++function_index
) {
235 if (p_db
[file_index
].functions
[function_index
].lineno
== start_line
&&
236 0 == strcmp(p_db
[file_index
].functions
[function_index
].name
, function_name
)) {
241 // insert a new element if the function was not found
242 if (p_db
[file_index
].functions
.size() == function_index
) {
243 profiler_db_item_t::profiler_function_data_t func_data
;
244 func_data
.name
= function_name
;
245 func_data
.lineno
= start_line
;
246 func_data
.exec_count
= 0;
247 func_data
.total_time
.tv_sec
= 0;
248 func_data
.total_time
.tv_usec
= 0;
249 p_db
[file_index
].functions
.push_back(func_data
);
252 // function execution count:
253 json
.get_next_token(&token
, &value
, &value_len
);
254 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 15 ||
255 0 != strncmp(value
, "execution count", value_len
));
257 // read the execution count and add it to the current data
258 json
.get_next_token(&token
, &value
, &value_len
);
259 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
260 p_db
[file_index
].functions
[function_index
].exec_count
+= atoi(value
);
262 // total function execution time:
263 json
.get_next_token(&token
, &value
, &value_len
);
264 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
265 0 != strncmp(value
, "total time", value_len
));
267 // read the total time and add it to the current data
268 // note: the database contains a real number, this needs to be split into 2 integers
269 json
.get_next_token(&token
, &value
, &value_len
);
270 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
271 p_db
[file_index
].functions
[function_index
].total_time
= add_timeval(
272 p_db
[file_index
].functions
[function_index
].total_time
, string2timeval(value
));
274 // end of the function's object
275 json
.get_next_token(&token
, NULL
, NULL
);
276 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
278 // read the next token (either the start of another object or the function array end)
279 json
.get_next_token(&token
, NULL
, NULL
);
282 // function array end
283 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
286 json
.get_next_token(&token
, &value
, &value_len
);
287 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 5 ||
288 0 != strncmp(value
, "lines", value_len
));
290 // read and store the lines (an array of objects, same as before)
291 json
.get_next_token(&token
, NULL
, NULL
);
292 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
293 json
.get_next_token(&token
, NULL
, NULL
);
294 while (JSON_TOKEN_OBJECT_START
== token
) {
298 json
.get_next_token(&token
, &value
, &value_len
);
299 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 6 ||
300 0 != strncmp(value
, "number", value_len
));
302 // read the line number and check if the line already exists
303 json
.get_next_token(&token
, &value
, &value_len
);
304 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
305 int lineno
= atoi(value
);
306 IMPORT_FORMAT_ERROR(lineno
< 0);
307 line_index
= get_line(p_db
, file_index
, lineno
);
308 if (-1 == line_index
) {
309 create_line(p_db
, file_index
, lineno
);
310 line_index
= p_db
[file_index
].lines
.size() - 1;
313 // line execution count:
314 json
.get_next_token(&token
, &value
, &value_len
);
315 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 15 ||
316 0 != strncmp(value
, "execution count", value_len
));
318 // read the execution count and add it to the current data
319 json
.get_next_token(&token
, &value
, &value_len
);
320 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
321 p_db
[file_index
].lines
[line_index
].exec_count
+= atoi(value
);
323 // total line execution time:
324 json
.get_next_token(&token
, &value
, &value_len
);
325 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
326 0 != strncmp(value
, "total time", value_len
));
328 // read the total time and add it to the current data
329 json
.get_next_token(&token
, &value
, &value_len
);
330 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
331 p_db
[file_index
].lines
[line_index
].total_time
= add_timeval(
332 p_db
[file_index
].lines
[line_index
].total_time
, string2timeval(value
));
334 // end of the line's object
335 json
.get_next_token(&token
, NULL
, NULL
);
336 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
338 // read the next token (either the start of another object or the line array end)
339 json
.get_next_token(&token
, NULL
, NULL
);
343 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
345 // end of the file's object
346 json
.get_next_token(&token
, NULL
, NULL
);
347 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
349 // read the next token (either the start of another object or the main array end)
350 json
.get_next_token(&token
, NULL
, NULL
);
354 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
357 void export_data(profiler_db_t
& p_db
, const char* p_filename
,
358 boolean p_disable_profiler
, boolean p_disable_coverage
,
359 void (*p_error_function
)(const char*, ...))
361 // check whether the file can be opened for writing
362 FILE* file
= fopen(p_filename
, "w");
364 p_error_function("Could not open file '%s' for writing. Profiling and/or code coverage "
365 "data will not be saved.", p_filename
);
369 // use the JSON tokenizer to create a JSON document from the database
370 JSON_Tokenizer
json(true);
372 // main array, contains an element for each file
373 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
374 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
376 // each file's data is stored in an object
377 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
379 // store the file name
380 json
.put_next_token(JSON_TOKEN_NAME
, "file");
381 char* p_filename_str
= mprintf("\"%s\"", p_db
[i
].filename
);
382 json
.put_next_token(JSON_TOKEN_STRING
, p_filename_str
);
383 Free(p_filename_str
);
385 // store the function data in an array (one element for each function)
386 json
.put_next_token(JSON_TOKEN_NAME
, "functions");
387 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
388 for (size_t j
= 0; j
< p_db
[i
].functions
.size(); ++j
) {
390 // the data is stored in an object for each function
391 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
393 // store the function name
394 json
.put_next_token(JSON_TOKEN_NAME
, "name");
395 char* func_name_str
= mprintf("\"%s\"", p_db
[i
].functions
[j
].name
);
396 json
.put_next_token(JSON_TOKEN_STRING
, func_name_str
);
399 // store the function start line
400 json
.put_next_token(JSON_TOKEN_NAME
, "start line");
401 char* start_line_str
= mprintf("%d", p_db
[i
].functions
[j
].lineno
);
402 json
.put_next_token(JSON_TOKEN_NUMBER
, start_line_str
);
403 Free(start_line_str
);
405 // store the function execution count
406 json
.put_next_token(JSON_TOKEN_NAME
, "execution count");
407 char* exec_count_str
= mprintf("%d", p_disable_coverage
? 0 :
408 p_db
[i
].functions
[j
].exec_count
);
409 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
410 Free(exec_count_str
);
412 // store the function's total execution time
413 json
.put_next_token(JSON_TOKEN_NAME
, "total time");
414 if (p_disable_profiler
) {
415 json
.put_next_token(JSON_TOKEN_NUMBER
, "0.000000");
418 char* total_time_str
= timeval2string(p_db
[i
].functions
[j
].total_time
);
419 json
.put_next_token(JSON_TOKEN_NUMBER
, total_time_str
);
420 Free(total_time_str
);
423 // end of function object
424 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
427 // end of function data array
428 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
430 // store the line data in an array (one element for each line with useful data)
431 json
.put_next_token(JSON_TOKEN_NAME
, "lines");
432 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
433 for (size_t j
= 0; j
< p_db
[i
].lines
.size(); ++j
) {
435 // store line data in an object
436 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
438 // store the line number
439 json
.put_next_token(JSON_TOKEN_NAME
, "number");
440 char* line_number_str
= mprintf("%d", p_db
[i
].lines
[j
].lineno
);
441 json
.put_next_token(JSON_TOKEN_NUMBER
, line_number_str
);
442 Free(line_number_str
);
444 // store the line execution count
445 json
.put_next_token(JSON_TOKEN_NAME
, "execution count");
446 char* exec_count_str
= mprintf("%d", p_disable_coverage
? 0 :
447 p_db
[i
].lines
[j
].exec_count
);
448 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
449 Free(exec_count_str
);
451 // store the line's total execution time
452 json
.put_next_token(JSON_TOKEN_NAME
, "total time");
453 if (p_disable_profiler
) {
454 json
.put_next_token(JSON_TOKEN_NUMBER
, "0.000000");
457 char* total_time_str
= timeval2string(p_db
[i
].lines
[j
].total_time
);
458 json
.put_next_token(JSON_TOKEN_NUMBER
, total_time_str
);
459 Free(total_time_str
);
462 // end of this line's object
463 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
466 // end of line data array
467 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
469 // end of this file's object
470 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
474 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
476 // write the JSON document into the file
477 fprintf(file
, "%s\n", json
.get_buffer());
481 // Structure for one code line or function, used by print_stats for sorting
482 struct stats_data_t
{
483 const char* filename
; // not owned
484 const char* funcname
; // not owned, NULL for code lines that don't start a function
490 // Compare function for sorting stats data based on total execution time (descending)
491 int stats_data_cmp_time(const void* p_left
, const void* p_right
) {
492 const stats_data_t
* p_left_data
= (stats_data_t
*)p_left
;
493 const stats_data_t
* p_right_data
= (stats_data_t
*)p_right
;
494 if (p_left_data
->total_time
.tv_sec
> p_right_data
->total_time
.tv_sec
) return -1;
495 if (p_left_data
->total_time
.tv_sec
< p_right_data
->total_time
.tv_sec
) return 1;
496 if (p_left_data
->total_time
.tv_usec
> p_right_data
->total_time
.tv_usec
) return -1;
497 if (p_left_data
->total_time
.tv_usec
< p_right_data
->total_time
.tv_usec
) return 1;
501 // Compare function for sorting stats data based on execution count (descending)
502 int stats_data_cmp_count(const void* p_left
, const void* p_right
) {
503 return ((stats_data_t
*)p_right
)->exec_count
- ((stats_data_t
*)p_left
)->exec_count
;
506 // Compare function for sorting stats data based on total time per execution count (descending)
507 int stats_data_cmp_avg(const void* p_left
, const void* p_right
) {
508 const stats_data_t
* p_left_data
= (stats_data_t
*)p_left
;
509 const stats_data_t
* p_right_data
= (stats_data_t
*)p_right
;
510 double left_time
= p_left_data
->total_time
.tv_sec
+ p_left_data
->total_time
.tv_usec
/ 1000000.0;
511 double right_time
= p_right_data
->total_time
.tv_sec
+ p_right_data
->total_time
.tv_usec
/ 1000000.0;
512 double diff
= (right_time
/ p_right_data
->exec_count
) - (left_time
/ p_left_data
->exec_count
);
513 if (diff
< 0) return -1;
514 if (diff
> 0) return 1;
518 void print_stats(profiler_db_t
& p_db
, const char* p_filename
,
519 boolean p_disable_profiler
, boolean p_disable_coverage
,
520 unsigned int p_flags
, void (*p_error_function
)(const char*, ...))
523 char* title_str
= mprintf(
524 "##################################################\n"
525 "%s## TTCN-3 %s%s%sstatistics ##%s\n"
526 "##################################################\n\n\n"
527 , p_disable_profiler
? "#######" : (p_disable_coverage
? "#########" : "")
528 , p_disable_profiler
? "" : "profiler "
529 , (p_disable_profiler
|| p_disable_coverage
) ? "" : "and "
530 , p_disable_coverage
? "" : "code coverage "
531 , p_disable_profiler
? "######" : (p_disable_coverage
? "#########" : ""));
533 char* line_func_count_str
= NULL
;
534 if (p_flags
& STATS_NUMBER_OF_LINES
) {
535 line_func_count_str
= mcopystr(
536 "--------------------------------------\n"
537 "- Number of code lines and functions -\n"
538 "--------------------------------------\n");
542 char* line_data_str
= NULL
;
543 if (p_flags
& STATS_LINE_DATA_RAW
) {
544 line_data_str
= mprintf(
545 "-------------------------------------------------\n"
546 "%s- Code line data (%s%s%s) -%s\n"
547 "-------------------------------------------------\n"
548 , p_disable_profiler
? "-------" : (p_disable_coverage
? "---------" : "")
549 , p_disable_profiler
? "" : "total time"
550 , (p_disable_profiler
|| p_disable_coverage
) ? "" : " / "
551 , p_disable_coverage
? "" : "execution count"
552 , p_disable_profiler
? "------" : (p_disable_coverage
? "---------" : ""));
555 // average time / exec count for lines
556 char* line_avg_str
= NULL
;
557 if (!p_disable_coverage
&& !p_disable_profiler
&& (p_flags
& STATS_LINE_AVG_RAW
)) {
558 line_avg_str
= mcopystr(
559 "-------------------------------------------\n"
560 "- Average time / execution for code lines -\n"
561 "-------------------------------------------\n");
565 char* func_data_str
= NULL
;
566 if (p_flags
& STATS_FUNC_DATA_RAW
) {
567 func_data_str
= mprintf(
568 "------------------------------------------------\n"
569 "%s- Function data (%s%s%s) -%s\n"
570 "------------------------------------------------\n"
571 , p_disable_profiler
? "-------" : (p_disable_coverage
? "---------" : "")
572 , p_disable_profiler
? "" : "total time"
573 , (p_disable_profiler
|| p_disable_coverage
) ? "" : " / "
574 , p_disable_coverage
? "" : "execution count"
575 , p_disable_profiler
? "------" : (p_disable_coverage
? "---------" : ""));
578 // average time / exec count for functions
579 char* func_avg_str
= NULL
;
580 if (!p_disable_coverage
&& !p_disable_profiler
&& (p_flags
& STATS_FUNC_AVG_RAW
)) {
581 func_avg_str
= mcopystr(
582 "------------------------------------------\n"
583 "- Average time / execution for functions -\n"
584 "------------------------------------------\n");
587 char* line_time_sorted_mod_str
= NULL
;
588 if (!p_disable_profiler
&& (p_flags
& STATS_LINE_TIMES_SORTED_BY_MOD
)) {
589 line_time_sorted_mod_str
= mcopystr(
590 "------------------------------------------------\n"
591 "- Total time of code lines, sorted, per module -\n"
592 "------------------------------------------------\n");
595 char* line_count_sorted_mod_str
= NULL
;
596 if (!p_disable_coverage
&& (p_flags
& STATS_LINE_COUNT_SORTED_BY_MOD
)) {
597 line_count_sorted_mod_str
= mcopystr(
598 "-----------------------------------------------------\n"
599 "- Execution count of code lines, sorted, per module -\n"
600 "-----------------------------------------------------\n");
603 char* line_avg_sorted_mod_str
= NULL
;
604 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_LINE_AVG_SORTED_BY_MOD
)) {
605 line_avg_sorted_mod_str
= mcopystr(
606 "--------------------------------------------------------------\n"
607 "- Average time / execution of code lines, sorted, per module -\n"
608 "--------------------------------------------------------------\n");
611 char* line_time_sorted_tot_str
= NULL
;
612 if (!p_disable_profiler
&& (p_flags
& STATS_LINE_TIMES_SORTED_TOTAL
)) {
613 line_time_sorted_tot_str
= mcopystr(
614 "-------------------------------------------\n"
615 "- Total time of code lines, sorted, total -\n"
616 "-------------------------------------------\n");
619 char* line_count_sorted_tot_str
= NULL
;
620 if (!p_disable_coverage
&& (p_flags
& STATS_LINE_COUNT_SORTED_TOTAL
)) {
621 line_count_sorted_tot_str
= mcopystr(
622 "------------------------------------------------\n"
623 "- Execution count of code lines, sorted, total -\n"
624 "------------------------------------------------\n");
627 char* line_avg_sorted_tot_str
= NULL
;
628 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_LINE_AVG_SORTED_TOTAL
)) {
629 line_avg_sorted_tot_str
= mcopystr(
630 "---------------------------------------------------------\n"
631 "- Average time / execution of code lines, sorted, total -\n"
632 "---------------------------------------------------------\n");
635 char* func_time_sorted_mod_str
= NULL
;
636 if (!p_disable_profiler
&& (p_flags
& STATS_FUNC_TIMES_SORTED_BY_MOD
)) {
637 func_time_sorted_mod_str
= mcopystr(
638 "-----------------------------------------------\n"
639 "- Total time of functions, sorted, per module -\n"
640 "-----------------------------------------------\n");
643 char* func_count_sorted_mod_str
= NULL
;
644 if (!p_disable_coverage
&& (p_flags
& STATS_FUNC_COUNT_SORTED_BY_MOD
)) {
645 func_count_sorted_mod_str
= mcopystr(
646 "----------------------------------------------------\n"
647 "- Execution count of functions, sorted, per module -\n"
648 "----------------------------------------------------\n");
651 char* func_avg_sorted_mod_str
= NULL
;
652 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_FUNC_AVG_SORTED_BY_MOD
)) {
653 func_avg_sorted_mod_str
= mcopystr(
654 "-------------------------------------------------------------\n"
655 "- Average time / execution of functions, sorted, per module -\n"
656 "-------------------------------------------------------------\n");
659 char* func_time_sorted_tot_str
= NULL
;
660 if (!p_disable_profiler
&& (p_flags
& STATS_FUNC_TIMES_SORTED_TOTAL
)) {
661 func_time_sorted_tot_str
= mcopystr(
662 "------------------------------------------\n"
663 "- Total time of functions, sorted, total -\n"
664 "------------------------------------------\n");
667 char* func_count_sorted_tot_str
= NULL
;
668 if (!p_disable_coverage
&& (p_flags
& STATS_FUNC_COUNT_SORTED_TOTAL
)) {
669 func_count_sorted_tot_str
= mcopystr(
670 "-----------------------------------------------\n"
671 "- Execution count of functions, sorted, total -\n"
672 "-----------------------------------------------\n");
675 char* func_avg_sorted_tot_str
= NULL
;
676 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_FUNC_AVG_SORTED_TOTAL
)) {
677 func_avg_sorted_tot_str
= mcopystr(
678 "--------------------------------------------------------\n"
679 "- Average time / execution of functions, sorted, total -\n"
680 "--------------------------------------------------------\n");
683 char* line_time_sorted_top10_str
= NULL
;
684 if (!p_disable_profiler
&& (p_flags
& STATS_TOP10_LINE_TIMES
)) {
685 line_time_sorted_top10_str
= mcopystr(
686 "------------------------------------\n"
687 "- Total time of code lines, top 10 -\n"
688 "------------------------------------\n");
691 char* line_count_sorted_top10_str
= NULL
;
692 if (!p_disable_coverage
&& (p_flags
& STATS_TOP10_LINE_COUNT
)) {
693 line_count_sorted_top10_str
= mcopystr(
694 "-----------------------------------------\n"
695 "- Execution count of code lines, top 10 -\n"
696 "-----------------------------------------\n");
699 char* line_avg_sorted_top10_str
= NULL
;
700 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_TOP10_LINE_AVG
)) {
701 line_avg_sorted_top10_str
= mcopystr(
702 "--------------------------------------------------\n"
703 "- Average time / execution of code lines, top 10 -\n"
704 "--------------------------------------------------\n");
707 char* func_time_sorted_top10_str
= NULL
;
708 if (!p_disable_profiler
&& (p_flags
& STATS_TOP10_FUNC_TIMES
)) {
709 func_time_sorted_top10_str
= mcopystr(
710 "-----------------------------------\n"
711 "- Total time of functions, top 10 -\n"
712 "-----------------------------------\n");
715 char* func_count_sorted_top10_str
= NULL
;
716 if (!p_disable_coverage
&& (p_flags
& STATS_TOP10_FUNC_COUNT
)) {
717 func_count_sorted_top10_str
= mcopystr(
718 "----------------------------------------\n"
719 "- Execution count of functions, top 10 -\n"
720 "----------------------------------------\n");
723 char* func_avg_sorted_top10_str
= NULL
;
724 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_TOP10_FUNC_AVG
)) {
725 func_avg_sorted_top10_str
= mcopystr(
726 "-------------------------------------------------\n"
727 "- Average time / execution of functions, top 10 -\n"
728 "-------------------------------------------------\n");
731 char* unused_lines_str
= NULL
;
732 char* unused_func_str
= NULL
;
733 if (!p_disable_coverage
&& (p_flags
& STATS_UNUSED_LINES
)) {
734 unused_lines_str
= mcopystr(
735 "---------------------\n"
736 "- Unused code lines -\n"
737 "---------------------\n");
739 if (!p_disable_coverage
&& (p_flags
& STATS_UNUSED_FUNC
)) {
740 unused_func_str
= mcopystr(
741 "--------------------\n"
742 "- Unused functions -\n"
743 "--------------------\n");
746 // variables for counting totals, and for determining the amount of unused lines/functions
747 size_t total_code_lines
= 0;
748 size_t total_functions
= 0;
749 size_t used_code_lines
= 0;
750 size_t used_functions
= 0;
752 // cached sizes of statistics data segments, needed to determine whether a separator
754 size_t line_data_str_len
= mstrlen(line_data_str
);
755 size_t func_data_str_len
= mstrlen(func_data_str
);
756 size_t unused_lines_str_len
= mstrlen(unused_lines_str
);
757 size_t unused_func_str_len
= mstrlen(unused_func_str
);
758 size_t line_avg_str_len
= mstrlen(line_avg_str
);
759 size_t func_avg_str_len
= mstrlen(func_avg_str
);
761 // cycle through the database and gather the necessary data
762 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
764 // add separators between files (only add them if the previous file actually added something)
765 if ((p_flags
& STATS_LINE_DATA_RAW
) && line_data_str_len
!= mstrlen(line_data_str
)) {
766 line_data_str
= mputstr(line_data_str
, "-------------------------------------------------\n");
767 line_data_str_len
= mstrlen(line_data_str
);
769 if ((p_flags
& STATS_FUNC_DATA_RAW
) && func_data_str_len
!= mstrlen(func_data_str
)) {
770 func_data_str
= mputstr(func_data_str
, "------------------------------------------------\n");
771 func_data_str_len
= mstrlen(func_data_str
);
773 if (!p_disable_coverage
) {
774 if ((p_flags
& STATS_UNUSED_LINES
) && unused_lines_str_len
!= mstrlen(unused_lines_str
)) {
775 unused_lines_str
= mputstr(unused_lines_str
, "---------------------\n");
776 unused_lines_str_len
= mstrlen(unused_lines_str
);
778 if ((p_flags
& STATS_UNUSED_FUNC
) && unused_func_str_len
!= mstrlen(unused_func_str
)) {
779 unused_func_str
= mputstr(unused_func_str
, "--------------------\n");
780 unused_func_str_len
= mstrlen(unused_func_str
);
782 if (!p_disable_profiler
) {
783 if ((p_flags
& STATS_LINE_AVG_RAW
) && line_avg_str_len
!= mstrlen(line_avg_str
)) {
784 line_avg_str
= mputstr(line_avg_str
, "-------------------------------------------\n");
785 line_avg_str_len
= mstrlen(line_avg_str
);
787 if ((p_flags
& STATS_FUNC_AVG_RAW
) && func_avg_str_len
!= mstrlen(func_avg_str
)) {
788 func_avg_str
= mputstr(func_avg_str
, "------------------------------------------\n");
789 func_avg_str_len
= mstrlen(func_avg_str
);
796 for (size_t j
= 0; j
< p_db
[i
].lines
.size(); ++j
) {
797 // line specification (including function name for the function's start line)
798 char* line_spec_str
= mprintf("%s:%d", p_db
[i
].filename
,
799 p_db
[i
].lines
[j
].lineno
);
800 int func
= get_function(p_db
, i
, p_db
[i
].lines
[j
].lineno
);
802 line_spec_str
= mputprintf(line_spec_str
, " [%s]", p_db
[i
].functions
[func
].name
);
804 line_spec_str
= mputstrn(line_spec_str
, "\n", 1);
806 if (p_disable_coverage
|| 0 != p_db
[i
].lines
[j
].exec_count
) {
807 if (!p_disable_profiler
) {
808 if (p_flags
& STATS_LINE_DATA_RAW
) {
809 char* total_time_str
= timeval2string(p_db
[i
].lines
[j
].total_time
);
810 line_data_str
= mputprintf(line_data_str
, "%ss", total_time_str
);
811 Free(total_time_str
);
813 if (!p_disable_coverage
) {
814 if (p_flags
& STATS_LINE_DATA_RAW
) {
815 line_data_str
= mputstrn(line_data_str
, "\t/\t", 3);
817 if (p_flags
& STATS_LINE_AVG_RAW
) {
818 double avg
= (p_db
[i
].lines
[j
].total_time
.tv_sec
+
819 p_db
[i
].lines
[j
].total_time
.tv_usec
/ 1000000.0) /
820 p_db
[i
].lines
[j
].exec_count
;
821 char* total_time_str
= timeval2string(p_db
[i
].lines
[j
].total_time
);
822 line_avg_str
= mputprintf(line_avg_str
, "%.6lfs\t(%ss / %d)",
823 avg
, total_time_str
, p_db
[i
].lines
[j
].exec_count
);
824 Free(total_time_str
);
828 if (!p_disable_coverage
&& (p_flags
& STATS_LINE_DATA_RAW
)) {
829 line_data_str
= mputprintf(line_data_str
, "%d", p_db
[i
].lines
[j
].exec_count
);
832 // add the line spec string to the other strings
833 if (p_flags
& STATS_LINE_DATA_RAW
) {
834 line_data_str
= mputprintf(line_data_str
, "\t%s", line_spec_str
);
836 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_LINE_AVG_RAW
)) {
837 line_avg_str
= mputprintf(line_avg_str
, "\t%s", line_spec_str
);
841 else if (p_flags
& STATS_UNUSED_LINES
) {
843 unused_lines_str
= mputstr(unused_lines_str
, line_spec_str
);
849 for (size_t j
= 0; j
< p_db
[i
].functions
.size(); ++j
) {
850 // functions specification
851 char* func_spec_str
= mprintf("%s:%d [%s]\n", p_db
[i
].filename
,
852 p_db
[i
].functions
[j
].lineno
, p_db
[i
].functions
[j
].name
);
854 if (p_disable_coverage
|| 0 != p_db
[i
].functions
[j
].exec_count
) {
855 if (!p_disable_profiler
) {
856 if (p_flags
& STATS_FUNC_DATA_RAW
) {
857 char* total_time_str
= timeval2string(p_db
[i
].functions
[j
].total_time
);
858 func_data_str
= mputprintf(func_data_str
, "%ss", total_time_str
);
859 Free(total_time_str
);
861 if (!p_disable_coverage
) {
862 if (p_flags
& STATS_FUNC_DATA_RAW
) {
863 func_data_str
= mputstrn(func_data_str
, "\t/\t", 3);
865 if (p_flags
& STATS_FUNC_AVG_RAW
) {
866 double avg
= (p_db
[i
].functions
[j
].total_time
.tv_sec
+
867 p_db
[i
].functions
[j
].total_time
.tv_usec
/ 1000000.0) /
868 p_db
[i
].functions
[j
].exec_count
;
869 char* total_time_str
= timeval2string(p_db
[i
].functions
[j
].total_time
);
870 func_avg_str
= mputprintf(func_avg_str
, "%.6lfs\t(%ss / %d)",
871 avg
, total_time_str
, p_db
[i
].functions
[j
].exec_count
);
872 Free(total_time_str
);
876 if (!p_disable_coverage
&& (p_flags
& STATS_FUNC_DATA_RAW
)) {
877 func_data_str
= mputprintf(func_data_str
, "%d", p_db
[i
].functions
[j
].exec_count
);
880 // add the line spec string to the other strings
881 if (p_flags
& STATS_FUNC_DATA_RAW
) {
882 func_data_str
= mputprintf(func_data_str
, "\t%s", func_spec_str
);
884 if (!p_disable_profiler
&& !p_disable_coverage
&& (p_flags
& STATS_FUNC_AVG_RAW
)) {
885 func_avg_str
= mputprintf(func_avg_str
, "\t%s", func_spec_str
);
890 else if (p_flags
& STATS_UNUSED_FUNC
) {
892 unused_func_str
= mputstr(unused_func_str
, func_spec_str
);
897 // number of lines and functions
898 if (p_flags
& STATS_NUMBER_OF_LINES
) {
899 line_func_count_str
= mputprintf(line_func_count_str
, "%s:\t%lu lines,\t%lu functions\n",
900 p_db
[i
].filename
, (unsigned long)(p_db
[i
].lines
.size()), (unsigned long)(p_db
[i
].functions
.size()));
902 total_code_lines
+= p_db
[i
].lines
.size();
903 total_functions
+= p_db
[i
].functions
.size();
905 if (p_flags
& STATS_NUMBER_OF_LINES
) {
906 line_func_count_str
= mputprintf(line_func_count_str
,
907 "--------------------------------------\n"
908 "Total:\t%lu lines,\t%lu functions\n", (unsigned long)total_code_lines
, (unsigned long)total_functions
);
911 if (p_flags
& (STATS_TOP10_ALL_DATA
| STATS_ALL_DATA_SORTED
)) {
912 // copy code line and function info into stats_data_t containers for sorting
913 stats_data_t
* code_line_stats
= (stats_data_t
*)Malloc(used_code_lines
* sizeof(stats_data_t
));
914 stats_data_t
* function_stats
= (stats_data_t
*)Malloc(used_functions
* sizeof(stats_data_t
));
918 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
919 for (size_t j
= 0; j
< p_db
[i
].lines
.size(); ++j
) {
920 if (p_disable_coverage
|| 0 != p_db
[i
].lines
[j
].exec_count
) {
921 code_line_stats
[line_index
].filename
= p_db
[i
].filename
;
922 code_line_stats
[line_index
].funcname
= NULL
;
923 code_line_stats
[line_index
].lineno
= p_db
[i
].lines
[j
].lineno
;
924 code_line_stats
[line_index
].total_time
= p_db
[i
].lines
[j
].total_time
;
925 code_line_stats
[line_index
].exec_count
= p_db
[i
].lines
[j
].exec_count
;
926 int func
= get_function(p_db
, i
, p_db
[i
].lines
[j
].lineno
);
928 code_line_stats
[line_index
].funcname
= p_db
[i
].functions
[func
].name
;
933 for (size_t j
= 0; j
< p_db
[i
].functions
.size(); ++j
) {
934 if (p_disable_coverage
|| 0 != p_db
[i
].functions
[j
].exec_count
) {
935 function_stats
[func_index
].filename
= p_db
[i
].filename
;
936 function_stats
[func_index
].funcname
= p_db
[i
].functions
[j
].name
;
937 function_stats
[func_index
].lineno
= p_db
[i
].functions
[j
].lineno
;
938 function_stats
[func_index
].total_time
= p_db
[i
].functions
[j
].total_time
;
939 function_stats
[func_index
].exec_count
= p_db
[i
].functions
[j
].exec_count
;
945 if (!p_disable_profiler
) {
946 // sort the code lines and functions by total time
947 qsort(code_line_stats
, used_code_lines
, sizeof(stats_data_t
), &stats_data_cmp_time
);
948 qsort(function_stats
, used_functions
, sizeof(stats_data_t
), &stats_data_cmp_time
);
950 if (p_flags
& (STATS_LINE_TIMES_SORTED_TOTAL
| STATS_TOP10_LINE_TIMES
)) {
951 // cycle through the sorted code lines and gather the necessary data
952 for (size_t i
= 0; i
< used_code_lines
; ++i
) {
953 char* total_time_str
= timeval2string(code_line_stats
[i
].total_time
);
954 char* the_data
= mprintf("%ss\t%s:%d", total_time_str
,
955 code_line_stats
[i
].filename
, code_line_stats
[i
].lineno
);
956 Free(total_time_str
);
957 if (NULL
!= code_line_stats
[i
].funcname
) {
958 the_data
= mputprintf(the_data
, " [%s]", code_line_stats
[i
].funcname
);
960 the_data
= mputstrn(the_data
, "\n", 1);
961 if (p_flags
& STATS_LINE_TIMES_SORTED_TOTAL
) {
962 line_time_sorted_tot_str
= mputstr(line_time_sorted_tot_str
, the_data
);
964 if (i
< 10 && (p_flags
& STATS_TOP10_LINE_TIMES
)) {
965 line_time_sorted_top10_str
= mputprintf(line_time_sorted_top10_str
,
966 "%2lu.\t%s", i
+ 1, the_data
);
972 if (p_flags
& (STATS_FUNC_TIMES_SORTED_TOTAL
| STATS_TOP10_FUNC_TIMES
)) {
973 // cycle through the sorted functions and gather the necessary data
974 for (size_t i
= 0; i
< used_functions
; ++i
) {
975 char* total_time_str
= timeval2string(function_stats
[i
].total_time
);
976 char* the_data
= mprintf("%ss\t%s:%d [%s]\n", total_time_str
,
977 function_stats
[i
].filename
, function_stats
[i
].lineno
, function_stats
[i
].funcname
);
978 Free(total_time_str
);
979 if (p_flags
& STATS_FUNC_TIMES_SORTED_TOTAL
) {
980 func_time_sorted_tot_str
= mputstr(func_time_sorted_tot_str
, the_data
);
982 if (i
< 10 && (p_flags
& STATS_TOP10_FUNC_TIMES
)) {
983 func_time_sorted_top10_str
= mputprintf(func_time_sorted_top10_str
,
984 "%2lu.\t%s", i
+ 1, the_data
);
990 if (p_flags
& (STATS_LINE_TIMES_SORTED_BY_MOD
| STATS_FUNC_TIMES_SORTED_BY_MOD
)) {
991 // cached string lengths, to avoid multiple separators after each other
992 size_t line_time_sorted_mod_str_len
= mstrlen(line_time_sorted_mod_str
);
993 size_t func_time_sorted_mod_str_len
= mstrlen(func_time_sorted_mod_str
);
995 // cycle through the sorted statistics and gather the necessary data per module
996 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
998 if ((p_flags
& STATS_LINE_TIMES_SORTED_BY_MOD
) &&
999 line_time_sorted_mod_str_len
!= mstrlen(line_time_sorted_mod_str
)) {
1000 line_time_sorted_mod_str
= mputstr(line_time_sorted_mod_str
,
1001 "------------------------------------------------\n");
1002 line_time_sorted_mod_str_len
= mstrlen(line_time_sorted_mod_str
);
1004 if ((p_flags
& STATS_FUNC_TIMES_SORTED_BY_MOD
) &&
1005 func_time_sorted_mod_str_len
!= mstrlen(func_time_sorted_mod_str
)) {
1006 func_time_sorted_mod_str
= mputstr(func_time_sorted_mod_str
,
1007 "-----------------------------------------------\n");
1008 func_time_sorted_mod_str_len
= mstrlen(func_time_sorted_mod_str
);
1011 if (p_flags
& STATS_LINE_TIMES_SORTED_BY_MOD
) {
1012 for (size_t j
= 0; j
< used_code_lines
; ++j
) {
1013 if (0 == strcmp(code_line_stats
[j
].filename
, p_db
[i
].filename
)) {
1014 char* total_time_str
= timeval2string(code_line_stats
[j
].total_time
);
1015 line_time_sorted_mod_str
= mputprintf(line_time_sorted_mod_str
,
1016 "%ss\t%s:%d", total_time_str
, code_line_stats
[j
].filename
,
1017 code_line_stats
[j
].lineno
);
1018 Free(total_time_str
);
1019 if (NULL
!= code_line_stats
[j
].funcname
) {
1020 line_time_sorted_mod_str
= mputprintf(line_time_sorted_mod_str
,
1021 " [%s]", code_line_stats
[j
].funcname
);
1023 line_time_sorted_mod_str
= mputstrn(line_time_sorted_mod_str
, "\n", 1);
1027 if (p_flags
& STATS_FUNC_TIMES_SORTED_BY_MOD
) {
1028 for (size_t j
= 0; j
< used_functions
; ++j
) {
1029 if (0 == strcmp(function_stats
[j
].filename
, p_db
[i
].filename
)) {
1030 char* total_time_str
= timeval2string(function_stats
[j
].total_time
);
1031 func_time_sorted_mod_str
= mputprintf(func_time_sorted_mod_str
,
1032 "%ss\t%s:%d [%s]\n", total_time_str
, function_stats
[j
].filename
,
1033 function_stats
[j
].lineno
, function_stats
[j
].funcname
);
1034 Free(total_time_str
);
1042 if (!p_disable_coverage
) {
1043 // sort the code lines and functions by execution count
1044 qsort(code_line_stats
, used_code_lines
, sizeof(stats_data_t
), &stats_data_cmp_count
);
1045 qsort(function_stats
, used_functions
, sizeof(stats_data_t
), &stats_data_cmp_count
);
1047 if (p_flags
& (STATS_LINE_COUNT_SORTED_TOTAL
| STATS_TOP10_LINE_COUNT
)) {
1048 // cycle through the sorted code lines and gather the necessary data
1049 for (size_t i
= 0; i
< used_code_lines
; ++i
) {
1050 char* the_data
= mprintf("%d\t%s:%d", code_line_stats
[i
].exec_count
,
1051 code_line_stats
[i
].filename
, code_line_stats
[i
].lineno
);
1052 if (NULL
!= code_line_stats
[i
].funcname
) {
1053 the_data
= mputprintf(the_data
, " [%s]", code_line_stats
[i
].funcname
);
1055 the_data
= mputstrn(the_data
, "\n", 1);
1056 if (p_flags
& STATS_LINE_COUNT_SORTED_TOTAL
) {
1057 line_count_sorted_tot_str
= mputstr(line_count_sorted_tot_str
, the_data
);
1059 if (i
< 10 && (p_flags
& STATS_TOP10_LINE_COUNT
)) {
1060 line_count_sorted_top10_str
= mputprintf(line_count_sorted_top10_str
,
1061 "%2lu.\t%s", i
+ 1, the_data
);
1067 if (p_flags
& (STATS_FUNC_COUNT_SORTED_TOTAL
| STATS_TOP10_FUNC_COUNT
)) {
1068 // cycle through the sorted functions and gather the necessary data
1069 for (size_t i
= 0; i
< used_functions
; ++i
) {
1070 char* the_data
= mprintf("%d\t%s:%d [%s]\n",
1071 function_stats
[i
].exec_count
, function_stats
[i
].filename
,
1072 function_stats
[i
].lineno
, function_stats
[i
].funcname
);
1073 if (p_flags
& STATS_FUNC_COUNT_SORTED_TOTAL
) {
1074 func_count_sorted_tot_str
= mputstr(func_count_sorted_tot_str
, the_data
);
1076 if (i
< 10 && (p_flags
& STATS_TOP10_FUNC_COUNT
)) {
1077 func_count_sorted_top10_str
= mputprintf(func_count_sorted_top10_str
,
1078 "%2lu.\t%s", i
+ 1, the_data
);
1084 if (p_flags
& (STATS_LINE_COUNT_SORTED_BY_MOD
| STATS_FUNC_COUNT_SORTED_BY_MOD
)) {
1085 // cached string lengths, to avoid multiple separators after each other
1086 size_t line_count_sorted_mod_str_len
= mstrlen(line_count_sorted_mod_str
);
1087 size_t func_count_sorted_mod_str_len
= mstrlen(func_count_sorted_mod_str
);
1089 // cycle through the sorted statistics and gather the necessary data per module
1090 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
1092 if ((p_flags
& STATS_LINE_COUNT_SORTED_BY_MOD
) &&
1093 line_count_sorted_mod_str_len
!= mstrlen(line_count_sorted_mod_str
)) {
1094 line_count_sorted_mod_str
= mputstr(line_count_sorted_mod_str
,
1095 "-----------------------------------------------------\n");
1096 line_count_sorted_mod_str_len
= mstrlen(line_count_sorted_mod_str
);
1098 if ((p_flags
& STATS_FUNC_COUNT_SORTED_BY_MOD
) &&
1099 func_count_sorted_mod_str_len
!= mstrlen(func_count_sorted_mod_str
)) {
1100 func_count_sorted_mod_str
= mputstr(func_count_sorted_mod_str
,
1101 "----------------------------------------------------\n");
1102 func_count_sorted_mod_str_len
= mstrlen(func_count_sorted_mod_str
);
1105 if (p_flags
& STATS_LINE_COUNT_SORTED_BY_MOD
) {
1106 for (size_t j
= 0; j
< used_code_lines
; ++j
) {
1107 if (0 == strcmp(code_line_stats
[j
].filename
, p_db
[i
].filename
)) {
1108 line_count_sorted_mod_str
= mputprintf(line_count_sorted_mod_str
,
1109 "%d\t%s:%d", code_line_stats
[j
].exec_count
, code_line_stats
[j
].filename
,
1110 code_line_stats
[j
].lineno
);
1111 if (NULL
!= code_line_stats
[j
].funcname
) {
1112 line_count_sorted_mod_str
= mputprintf(line_count_sorted_mod_str
,
1113 " [%s]", code_line_stats
[j
].funcname
);
1115 line_count_sorted_mod_str
= mputstrn(line_count_sorted_mod_str
, "\n", 1);
1119 if (p_flags
& STATS_FUNC_COUNT_SORTED_BY_MOD
) {
1120 for (size_t j
= 0; j
< used_functions
; ++j
) {
1121 if (0 == strcmp(function_stats
[j
].filename
, p_db
[i
].filename
)) {
1122 func_count_sorted_mod_str
= mputprintf(func_count_sorted_mod_str
,
1123 "%d\t%s:%d [%s]\n", function_stats
[j
].exec_count
, function_stats
[j
].filename
,
1124 function_stats
[j
].lineno
, function_stats
[j
].funcname
);
1132 if (!p_disable_profiler
&& !p_disable_coverage
) {
1133 // sort the code lines and functions by average time / execution
1134 qsort(code_line_stats
, used_code_lines
, sizeof(stats_data_t
), &stats_data_cmp_avg
);
1135 qsort(function_stats
, used_functions
, sizeof(stats_data_t
), &stats_data_cmp_avg
);
1137 if (p_flags
& (STATS_LINE_AVG_SORTED_TOTAL
| STATS_TOP10_LINE_AVG
)) {
1138 // cycle through the sorted code lines and gather the necessary data
1139 for (size_t i
= 0; i
< used_code_lines
; ++i
) {
1140 double avg
= (code_line_stats
[i
].total_time
.tv_sec
+
1141 code_line_stats
[i
].total_time
.tv_usec
/ 1000000.0) /
1142 code_line_stats
[i
].exec_count
;
1143 char* total_time_str
= timeval2string(code_line_stats
[i
].total_time
);
1144 char* the_data
= mprintf("%.6lfs\t(%ss / %d)\t%s:%d",
1145 avg
, total_time_str
, code_line_stats
[i
].exec_count
,
1146 code_line_stats
[i
].filename
, code_line_stats
[i
].lineno
);
1147 Free(total_time_str
);
1148 if (NULL
!= code_line_stats
[i
].funcname
) {
1149 the_data
= mputprintf(the_data
, " [%s]", code_line_stats
[i
].funcname
);
1151 the_data
= mputstrn(the_data
, "\n", 1);
1152 if (p_flags
& STATS_LINE_AVG_SORTED_TOTAL
) {
1153 line_avg_sorted_tot_str
= mputstr(line_avg_sorted_tot_str
, the_data
);
1155 if (i
< 10 && (p_flags
& STATS_TOP10_LINE_AVG
)) {
1156 line_avg_sorted_top10_str
= mputprintf(line_avg_sorted_top10_str
,
1157 "%2lu.\t%s", i
+ 1, the_data
);
1163 if (p_flags
& (STATS_FUNC_AVG_SORTED_TOTAL
| STATS_TOP10_FUNC_AVG
)) {
1164 // cycle through the sorted functions and gather the necessary data
1165 for (size_t i
= 0; i
< used_functions
; ++i
) {
1166 double avg
= (function_stats
[i
].total_time
.tv_sec
+
1167 function_stats
[i
].total_time
.tv_usec
/ 1000000.0) /
1168 function_stats
[i
].exec_count
;
1169 char* total_time_str
= timeval2string(function_stats
[i
].total_time
);
1170 char* the_data
= mprintf("%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1171 avg
, total_time_str
, function_stats
[i
].exec_count
,
1172 function_stats
[i
].filename
, function_stats
[i
].lineno
, function_stats
[i
].funcname
);
1173 Free(total_time_str
);
1174 if (p_flags
& STATS_FUNC_AVG_SORTED_TOTAL
) {
1175 func_avg_sorted_tot_str
= mputstr(func_avg_sorted_tot_str
, the_data
);
1177 if (i
< 10 && (p_flags
& STATS_TOP10_FUNC_AVG
)) {
1178 func_avg_sorted_top10_str
= mputprintf(func_avg_sorted_top10_str
,
1179 "%2lu.\t%s", i
+ 1, the_data
);
1185 if (p_flags
& (STATS_LINE_AVG_SORTED_BY_MOD
| STATS_FUNC_AVG_SORTED_BY_MOD
)) {
1186 // cached string lengths, to avoid multiple separators after each other
1187 size_t line_avg_sorted_mod_str_len
= mstrlen(line_avg_sorted_mod_str
);
1188 size_t func_avg_sorted_mod_str_len
= mstrlen(func_avg_sorted_mod_str
);
1190 // cycle through the sorted statistics and gather the necessary data per module
1191 for (size_t i
= 0; i
< p_db
.size(); ++i
) {
1193 if ((p_flags
& STATS_LINE_AVG_SORTED_BY_MOD
) &&
1194 line_avg_sorted_mod_str_len
!= mstrlen(line_avg_sorted_mod_str
)) {
1195 line_avg_sorted_mod_str
= mputstr(line_avg_sorted_mod_str
,
1196 "--------------------------------------------------------------\n");
1197 line_avg_sorted_mod_str_len
= mstrlen(line_avg_sorted_mod_str
);
1199 if ((p_flags
& STATS_FUNC_AVG_SORTED_BY_MOD
) &&
1200 func_avg_sorted_mod_str_len
!= mstrlen(func_avg_sorted_mod_str
)) {
1201 func_avg_sorted_mod_str
= mputstr(func_avg_sorted_mod_str
,
1202 "-------------------------------------------------------------\n");
1203 func_avg_sorted_mod_str_len
= mstrlen(func_avg_sorted_mod_str
);
1206 if (p_flags
& STATS_LINE_AVG_SORTED_BY_MOD
) {
1207 for (size_t j
= 0; j
< used_code_lines
; ++j
) {
1208 if (0 == strcmp(code_line_stats
[j
].filename
, p_db
[i
].filename
)) {
1209 double avg
= (code_line_stats
[j
].total_time
.tv_sec
+
1210 code_line_stats
[j
].total_time
.tv_usec
/ 1000000.0) /
1211 code_line_stats
[j
].exec_count
;
1212 char* total_time_str
= timeval2string(code_line_stats
[j
].total_time
);
1213 line_avg_sorted_mod_str
= mputprintf(line_avg_sorted_mod_str
,
1214 "%.6lfs\t(%ss / %d)\t%s:%d",
1215 avg
, total_time_str
, code_line_stats
[j
].exec_count
,
1216 code_line_stats
[j
].filename
, code_line_stats
[j
].lineno
);
1217 Free(total_time_str
);
1218 if (NULL
!= code_line_stats
[j
].funcname
) {
1219 line_avg_sorted_mod_str
= mputprintf(line_avg_sorted_mod_str
,
1220 " [%s]", code_line_stats
[j
].funcname
);
1222 line_avg_sorted_mod_str
= mputstrn(line_avg_sorted_mod_str
, "\n", 1);
1226 if (p_flags
& STATS_FUNC_AVG_SORTED_BY_MOD
) {
1227 for (size_t j
= 0; j
< used_functions
; ++j
) {
1228 if (0 == strcmp(function_stats
[j
].filename
, p_db
[i
].filename
)) {
1229 double avg
= (function_stats
[j
].total_time
.tv_sec
+
1230 function_stats
[j
].total_time
.tv_usec
/ 1000000.0) /
1231 function_stats
[j
].exec_count
;
1232 char* total_time_str
= timeval2string(function_stats
[j
].total_time
);
1233 func_avg_sorted_mod_str
= mputprintf(func_avg_sorted_mod_str
,
1234 "%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1235 avg
, total_time_str
, function_stats
[j
].exec_count
,
1236 function_stats
[j
].filename
, function_stats
[j
].lineno
, function_stats
[j
].funcname
);
1237 Free(total_time_str
);
1245 // free the stats data
1246 Free(code_line_stats
);
1247 Free(function_stats
);
1250 // add new lines at the end of each segment
1251 if (p_flags
& STATS_NUMBER_OF_LINES
) {
1252 line_func_count_str
= mputstrn(line_func_count_str
, "\n", 1);
1254 if (p_flags
& STATS_LINE_DATA_RAW
) {
1255 line_data_str
= mputstrn(line_data_str
, "\n", 1);
1257 if (p_flags
& STATS_FUNC_DATA_RAW
) {
1258 func_data_str
= mputstrn(func_data_str
, "\n", 1);
1260 if (!p_disable_profiler
) {
1261 if (p_flags
& STATS_LINE_TIMES_SORTED_BY_MOD
) {
1262 line_time_sorted_mod_str
= mputstrn(line_time_sorted_mod_str
, "\n", 1);
1264 if (p_flags
& STATS_LINE_TIMES_SORTED_TOTAL
) {
1265 line_time_sorted_tot_str
= mputstrn(line_time_sorted_tot_str
, "\n", 1);
1267 if (p_flags
& STATS_FUNC_TIMES_SORTED_BY_MOD
) {
1268 func_time_sorted_mod_str
= mputstrn(func_time_sorted_mod_str
, "\n", 1);
1270 if (p_flags
& STATS_FUNC_TIMES_SORTED_TOTAL
) {
1271 func_time_sorted_tot_str
= mputstrn(func_time_sorted_tot_str
, "\n", 1);
1273 if (p_flags
& STATS_TOP10_LINE_TIMES
) {
1274 line_time_sorted_top10_str
= mputstrn(line_time_sorted_top10_str
, "\n", 1);
1276 if (p_flags
& STATS_TOP10_FUNC_TIMES
) {
1277 func_time_sorted_top10_str
= mputstrn(func_time_sorted_top10_str
, "\n", 1);
1279 if (!p_disable_coverage
) {
1280 if (p_flags
& STATS_LINE_AVG_RAW
) {
1281 line_avg_str
= mputstrn(line_avg_str
, "\n", 1);
1283 if (p_flags
& STATS_LINE_AVG_RAW
) {
1284 func_avg_str
= mputstrn(func_avg_str
, "\n", 1);
1286 if (p_flags
& STATS_LINE_AVG_SORTED_BY_MOD
) {
1287 line_avg_sorted_mod_str
= mputstrn(line_avg_sorted_mod_str
, "\n", 1);
1289 if (p_flags
& STATS_LINE_AVG_SORTED_TOTAL
) {
1290 line_avg_sorted_tot_str
= mputstrn(line_avg_sorted_tot_str
, "\n", 1);
1292 if (p_flags
& STATS_FUNC_AVG_SORTED_BY_MOD
) {
1293 func_avg_sorted_mod_str
= mputstrn(func_avg_sorted_mod_str
, "\n", 1);
1295 if (p_flags
& STATS_FUNC_AVG_SORTED_TOTAL
) {
1296 func_avg_sorted_tot_str
= mputstrn(func_avg_sorted_tot_str
, "\n", 1);
1298 if (p_flags
& STATS_TOP10_LINE_AVG
) {
1299 line_avg_sorted_top10_str
= mputstrn(line_avg_sorted_top10_str
, "\n", 1);
1301 if (p_flags
& STATS_TOP10_FUNC_AVG
) {
1302 func_avg_sorted_top10_str
= mputstrn(func_avg_sorted_top10_str
, "\n", 1);
1306 if (!p_disable_coverage
) {
1307 if (p_flags
& STATS_LINE_COUNT_SORTED_BY_MOD
) {
1308 line_count_sorted_mod_str
= mputstrn(line_count_sorted_mod_str
, "\n", 1);
1310 if (p_flags
& STATS_LINE_COUNT_SORTED_TOTAL
) {
1311 line_count_sorted_tot_str
= mputstrn(line_count_sorted_tot_str
, "\n", 1);
1313 if (p_flags
& STATS_FUNC_COUNT_SORTED_BY_MOD
) {
1314 func_count_sorted_mod_str
= mputstrn(func_count_sorted_mod_str
, "\n", 1);
1316 if (p_flags
& STATS_FUNC_COUNT_SORTED_TOTAL
) {
1317 func_count_sorted_tot_str
= mputstrn(func_count_sorted_tot_str
, "\n", 1);
1319 if (p_flags
& STATS_TOP10_LINE_COUNT
) {
1320 line_count_sorted_top10_str
= mputstrn(line_count_sorted_top10_str
, "\n", 1);
1322 if (p_flags
& STATS_TOP10_FUNC_COUNT
) {
1323 func_count_sorted_top10_str
= mputstrn(func_count_sorted_top10_str
, "\n", 1);
1325 if (p_flags
& STATS_UNUSED_LINES
) {
1326 unused_lines_str
= mputstrn(unused_lines_str
, "\n", 1);
1328 if (p_flags
& STATS_UNUSED_FUNC
) {
1329 unused_func_str
= mputstrn(unused_func_str
, "\n", 1);
1333 // write the statistics to the specified file
1334 FILE* file
= fopen(p_filename
, "w");
1336 p_error_function("Could not open file '%s' for writing. Profiling and/or "
1337 "code coverage statistics will not be saved.", p_filename
);
1340 // by now the strings for all disabled statistics entries should be null
1341 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"
1343 , (NULL
!= line_func_count_str
) ? line_func_count_str
: ""
1344 , (NULL
!= line_data_str
) ? line_data_str
: ""
1345 , (NULL
!= line_avg_str
) ? line_avg_str
: ""
1346 , (NULL
!= func_data_str
) ? func_data_str
: ""
1347 , (NULL
!= func_avg_str
) ? func_avg_str
: ""
1348 , (NULL
!= line_time_sorted_mod_str
) ? line_time_sorted_mod_str
: ""
1349 , (NULL
!= line_time_sorted_tot_str
) ? line_time_sorted_tot_str
: ""
1350 , (NULL
!= func_time_sorted_mod_str
) ? func_time_sorted_mod_str
: ""
1351 , (NULL
!= func_time_sorted_tot_str
) ? func_time_sorted_tot_str
: ""
1352 , (NULL
!= line_count_sorted_mod_str
) ? line_count_sorted_mod_str
: ""
1353 , (NULL
!= line_count_sorted_tot_str
) ? line_count_sorted_tot_str
: ""
1354 , (NULL
!= func_count_sorted_mod_str
) ? func_count_sorted_mod_str
: ""
1355 , (NULL
!= func_count_sorted_tot_str
) ? func_count_sorted_tot_str
: ""
1356 , (NULL
!= line_avg_sorted_mod_str
) ? line_avg_sorted_mod_str
: ""
1357 , (NULL
!= line_avg_sorted_tot_str
) ? line_avg_sorted_tot_str
: ""
1358 , (NULL
!= func_avg_sorted_mod_str
) ? func_avg_sorted_mod_str
: ""
1359 , (NULL
!= func_avg_sorted_tot_str
) ? func_avg_sorted_tot_str
: ""
1360 , (NULL
!= line_time_sorted_top10_str
) ? line_time_sorted_top10_str
: ""
1361 , (NULL
!= func_time_sorted_top10_str
) ? func_time_sorted_top10_str
: ""
1362 , (NULL
!= line_count_sorted_top10_str
) ? line_count_sorted_top10_str
: ""
1363 , (NULL
!= func_count_sorted_top10_str
) ? func_count_sorted_top10_str
: ""
1364 , (NULL
!= line_avg_sorted_top10_str
) ? line_avg_sorted_top10_str
: ""
1365 , (NULL
!= func_avg_sorted_top10_str
) ? func_avg_sorted_top10_str
: ""
1366 , (NULL
!= unused_lines_str
) ? unused_lines_str
: ""
1367 , (NULL
!= unused_func_str
) ? unused_func_str
: "");
1373 Free(line_func_count_str
);
1374 Free(line_data_str
);
1376 Free(func_data_str
);
1378 Free(line_time_sorted_mod_str
);
1379 Free(line_time_sorted_tot_str
);
1380 Free(func_time_sorted_mod_str
);
1381 Free(func_time_sorted_tot_str
);
1382 Free(line_count_sorted_mod_str
);
1383 Free(line_count_sorted_tot_str
);
1384 Free(func_count_sorted_mod_str
);
1385 Free(func_count_sorted_tot_str
);
1386 Free(line_avg_sorted_mod_str
);
1387 Free(line_avg_sorted_tot_str
);
1388 Free(func_avg_sorted_mod_str
);
1389 Free(func_avg_sorted_tot_str
);
1390 Free(line_time_sorted_top10_str
);
1391 Free(func_time_sorted_top10_str
);
1392 Free(line_count_sorted_top10_str
);
1393 Free(func_count_sorted_top10_str
);
1394 Free(line_avg_sorted_top10_str
);
1395 Free(func_avg_sorted_top10_str
);
1396 Free(unused_lines_str
);
1397 Free(unused_func_str
);