Update README.linux
[deliverable/titan.core.git] / core / Profiler.cc
CommitLineData
af710487 1///////////////////////////////////////////////////////////////////////////////
2// Copyright (c) 2000-2015 Ericsson Telecom AB
3// All rights reserved. This program and the accompanying materials
4// are made available under the terms of the Eclipse Public License v1.0
5// which accompanies this distribution, and is available at
6// http://www.eclipse.org/legal/epl-v10.html
7///////////////////////////////////////////////////////////////////////////////
8
9#include "Profiler.hh"
af710487 10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include "JSON_Tokenizer.hh"
14#include "memory.h"
15#include "Runtime.hh"
a38c6d4c 16#include <unistd.h>
17#include "Component.hh"
18
19////////////////////////////////////
20//////// timeval operations ////////
21////////////////////////////////////
22
23/** Reads a timeval value from the given string. The parameter must contain the
24 * string representation of a real number with 6 digits after the decimal dot. */
25static timeval string2timeval(const char* str)
26{
27 // read and store the first part (atoi will read until the decimal dot)
28 long int sec = atoi(str);
29 timeval tv;
30 tv.tv_sec = sec;
31
32 do {
33 // step over each digit
34 sec /= 10;
35 ++str;
36 }
37 while (sec > 9);
38
39 // step over the decimal dot and read the second part of the number
40 tv.tv_usec = atoi(str + 1);
41 return tv;
42}
43
44/** Returns the string representation of a real number (with 6 digits after the
45 * decimal dot) equivalent to the timeval parameter.
46 * The returned character pointer needs to be freed. */
47static char* timeval2string(timeval tv)
48{
49 // convert the first part and set the second part to all zeros
50 char* str = mprintf("%ld.000000", tv.tv_sec);
51
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;
56 tv.tv_usec /= 10;
57 --pos;
58 }
59 return str;
60}
61
62/** Adds the two timeval parameters together and returns the result. */
63static timeval add_timeval(const timeval operand1, const timeval operand2)
64{
65 timeval tv;
66 tv.tv_usec = operand1.tv_usec + operand2.tv_usec;
67 tv.tv_sec = operand1.tv_sec + operand2.tv_sec;
68 if (tv.tv_usec >= 1000000) {
69 ++tv.tv_sec;
70 tv.tv_usec -= 1000000;
71 }
72 return tv;
73}
74
75/** Subtracts the second timeval parameter from the first one and returns the result. */
76static timeval subtract_timeval(const timeval operand1, const timeval operand2)
77{
78 timeval tv;
79 tv.tv_usec = operand1.tv_usec - operand2.tv_usec;
80 tv.tv_sec = operand1.tv_sec - operand2.tv_sec;
81 if (tv.tv_usec < 0) {
82 --tv.tv_sec;
83 tv.tv_usec += 1000000;
84 }
85 return tv;
86}
af710487 87
88////////////////////////////////////
89////////// TTCN3_Profiler //////////
90////////////////////////////////////
91
92TTCN3_Profiler ttcn3_prof;
93
94TTCN3_Profiler::TTCN3_Profiler()
a38c6d4c 95: stopped(FALSE), disable_profiler(FALSE), disable_coverage(FALSE)
96, aggregate_data(FALSE), disable_stats(FALSE), stats_flags(STATS_ALL)
af710487 97{
98 database_filename = mcopystr("profiler.db");
99 stats_filename = mcopystr("profiler.stats");
100 reset();
101}
102
103TTCN3_Profiler::~TTCN3_Profiler()
104{
a38c6d4c 105 if (!profiler_db.empty() && (!disable_profiler || !disable_coverage)) {
106 if (aggregate_data && (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc())) {
107 // import the data from the previous run
108 import_data();
109 }
110 if (TTCN_Runtime::is_hc()) {
111 // import the data gathered by the other processes (the import function
112 // waits for them to finish exporting)
113 import_data(MTC_COMPREF);
114 for (size_t i = 0; i < ptc_list.size(); ++i) {
115 import_data(ptc_list[i]);
116 }
117 }
af710487 118 export_data();
119 if (!disable_stats && (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc())) {
120 print_stats();
121 }
122 }
123 for (size_t i = 0; i < profiler_db.size(); ++i) {
124 Free(profiler_db[i].filename);
125 for (size_t j = 0; j < profiler_db[i].functions.size(); ++j) {
126 Free(profiler_db[i].functions[j].name);
127 }
128 }
129 Free(database_filename);
130 Free(stats_filename);
131}
132
a38c6d4c 133void TTCN3_Profiler::start()
134{
135 if (stopped) {
136 set_prev(disable_profiler ? -1 : TTCN3_Stack_Depth::depth(), NULL, -1);
137 stopped = FALSE;
138 }
139}
140
141void TTCN3_Profiler::stop()
142{
143 if (!stopped) {
144 if (NULL != prev_file) {
145 // update the previous line's time
146 timeval elapsed = subtract_timeval(get_time(), prev_time);
147 add_line_time(elapsed, get_element(prev_file), prev_line);
148 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
149 }
150 stopped = TRUE;
151 }
152}
153
af710487 154void TTCN3_Profiler::set_disable_profiler(boolean p_disable_profiler)
155{
156 disable_profiler = p_disable_profiler;
157}
158
159void TTCN3_Profiler::set_disable_coverage(boolean p_disable_coverage)
160{
161 disable_coverage = p_disable_coverage;
162}
163
164void TTCN3_Profiler::set_database_filename(const char* p_database_filename)
165{
166 Free(database_filename);
167 database_filename = mcopystr(p_database_filename);
168}
169
170void TTCN3_Profiler::set_aggregate_data(boolean p_aggregate_data)
171{
172 aggregate_data = p_aggregate_data;
173}
174
175void TTCN3_Profiler::set_stats_filename(const char* p_stats_filename)
176{
177 Free(stats_filename);
178 stats_filename = mcopystr(p_stats_filename);
179}
180
181void TTCN3_Profiler::set_disable_stats(boolean p_disable_stats)
182{
183 disable_stats = p_disable_stats;
184}
185
a38c6d4c 186void TTCN3_Profiler::reset_stats_flags()
187{
188 stats_flags = 0;
189}
190
191void TTCN3_Profiler::add_stats_flags(unsigned int p_flags)
192{
193 stats_flags |= p_flags;
194}
195
af710487 196boolean TTCN3_Profiler::is_profiler_disabled() const
197{
198 return disable_profiler;
199}
200
a38c6d4c 201boolean TTCN3_Profiler::is_running() const
202{
203 return !stopped;
204}
205
206void TTCN3_Profiler::add_ptc(component p_comp_ref)
207{
208 ptc_list.push_back(p_comp_ref);
209}
210
af710487 211#define IMPORT_FORMAT_ERROR(cond) \
212 if (cond) { \
213 TTCN_warning("Database format is invalid. Profiler and/or code coverage data will not be loaded."); \
214 return; \
215 }
216
a38c6d4c 217void TTCN3_Profiler::import_data(component p_comp_ref /* = NULL_COMPREF */)
af710487 218{
a38c6d4c 219 char* file_name = NULL;
220 if (NULL_COMPREF == p_comp_ref) {
221 // this is the main database file (from the previous run), no suffix needed
222 file_name = database_filename;
af710487 223 }
a38c6d4c 224 else if (MTC_COMPREF == p_comp_ref) {
225 // this is the database for the MTC, suffix the file name with "mtc"
226 file_name = mprintf("%s.mtc", database_filename);
227 }
228 else {
229 // this is the database for one of the PTCs, suffix the file name with the
230 // component reference
231 file_name = mprintf("%s.%d", database_filename, p_comp_ref);
232 }
233
af710487 234 // open the file, if it exists
a38c6d4c 235 int file_size = 0;
236 FILE* file = fopen(file_name, "r");
237 if (NULL != file) {
238 // get the file size
239 fseek(file, 0, SEEK_END);
240 file_size = ftell(file);
241 }
242 while (0 == file_size) {
243 if (NULL_COMPREF == p_comp_ref) {
244 // no data from the previous run
245 return;
246 }
247 // as for the process-specific database files: keep reading until it appears
248 if (NULL != file) {
249 fclose(file);
250 }
251 usleep(1000);
252 file = fopen(file_name, "r");
253 if (NULL != file) {
254 // refresh the file size
255 fseek(file, 0, SEEK_END);
256 file_size = ftell(file);
257 }
af710487 258 }
259
a38c6d4c 260 // rewind the file (the file pointer has been moved to the end of the file to
261 // calculate its size)
af710487 262 rewind(file);
263
264 // read the entire file into a character buffer
265 char* buffer = (char*)Malloc(file_size);
266 fread(buffer, 1, file_size, file);
a38c6d4c 267 fclose(file);
268
269 if (NULL_COMPREF != p_comp_ref) {
270 // the process-specific database file is no longer needed
271 remove(file_name);
272 Free(file_name);
273 }
af710487 274
275 // initialize a JSON tokenizer with the buffer
276 JSON_Tokenizer json(buffer, file_size);
277 Free(buffer);
278
279 // attempt to read tokens from the buffer
280 // if the format is invalid, abort the importing process
281 json_token_t token = JSON_TOKEN_NONE;
282 char* value = NULL;
283 size_t value_len = 0;
284
285 // start of main array
286 json.get_next_token(&token, NULL, NULL);
287 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
288
289 // read objects (one for each TTCN-3 file), until the main array end mark is reached
290 json.get_next_token(&token, NULL, NULL);
291 while (JSON_TOKEN_OBJECT_START == token) {
292 size_t file_index = 0;
293
294 // file name:
295 json.get_next_token(&token, &value, &value_len);
296 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 4 ||
297 0 != strncmp(value, "file", value_len));
298
299 // read the file name and see if its record already exists
300 json.get_next_token(&token, &value, &value_len);
301 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING != token);
302 for (file_index = 0; file_index < profiler_db.size(); ++file_index) {
303 if (strlen(profiler_db[file_index].filename) == value_len - 2 &&
304 0 == strncmp(profiler_db[file_index].filename, value + 1, value_len - 2)) {
305 break;
306 }
307 }
308
309 // insert a new element if the file was not found
310 if (profiler_db.size() == file_index) {
311 profiler_db_item_t item;
312 item.filename = mcopystrn(value + 1, value_len - 2);
313 profiler_db.push_back(item);
314 }
315
316 // functions:
317 json.get_next_token(&token, &value, &value_len);
318 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 9 ||
319 0 != strncmp(value, "functions", value_len));
320
321 // read and store the functions (an array of objects, same as before)
322 json.get_next_token(&token, NULL, NULL);
323 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
324 json.get_next_token(&token, NULL, NULL);
325 while (JSON_TOKEN_OBJECT_START == token) {
326 size_t function_index = 0;
327
328 // function name:
329 json.get_next_token(&token, &value, &value_len);
330 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 4 ||
331 0 != strncmp(value, "name", value_len));
332
333 // read the function name, it will be checked later
334 json.get_next_token(&token, &value, &value_len);
335 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING != token);
336 char* function_name = mcopystrn(value + 1, value_len - 2);
337
338 // function start line:
339 json.get_next_token(&token, &value, &value_len);
340 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
341 0 != strncmp(value, "start line", value_len));
342
343 // read the start line and check if the function already exists
344 json.get_next_token(&token, &value, &value_len);
345 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
346 int start_line = atoi(value);
347 for (function_index = 0; function_index < profiler_db[file_index].functions.size(); ++function_index) {
348 if (profiler_db[file_index].functions[function_index].lineno == start_line &&
349 0 == strcmp(profiler_db[file_index].functions[function_index].name, function_name)) {
350 break;
351 }
352 }
353
354 // insert a new element if the function was not found
355 if (profiler_db[file_index].functions.size() == function_index) {
356 profiler_db_item_t::profiler_function_data_t func_data;
357 func_data.name = function_name;
358 func_data.lineno = start_line;
359 func_data.exec_count = 0;
a38c6d4c 360 func_data.total_time.tv_sec = 0;
361 func_data.total_time.tv_usec = 0;
af710487 362 profiler_db[file_index].functions.push_back(func_data);
363 }
364
a38c6d4c 365 // function execution count:
366 json.get_next_token(&token, &value, &value_len);
367 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 15 ||
368 0 != strncmp(value, "execution count", value_len));
af710487 369
a38c6d4c 370 // read the execution count and add it to the current data
371 json.get_next_token(&token, &value, &value_len);
372 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
373 profiler_db[file_index].functions[function_index].exec_count += atoi(value);
af710487 374
a38c6d4c 375 // total function execution time:
376 json.get_next_token(&token, &value, &value_len);
377 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
378 0 != strncmp(value, "total time", value_len));
379
380 // read the total time and add it to the current data
381 // note: the database contains a real number, this needs to be split into 2 integers
382 json.get_next_token(&token, &value, &value_len);
383 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
384 profiler_db[file_index].functions[function_index].total_time = add_timeval(
385 profiler_db[file_index].functions[function_index].total_time, string2timeval(value));
af710487 386
387 // end of the function's object
388 json.get_next_token(&token, NULL, NULL);
389 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
390
391 // read the next token (either the start of another object or the function array end)
392 json.get_next_token(&token, NULL, NULL);
393 }
394
395 // function array end
396 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
397
398 // lines:
399 json.get_next_token(&token, &value, &value_len);
400 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 5 ||
401 0 != strncmp(value, "lines", value_len));
402
403 // read and store the lines (an array of objects, same as before)
404 json.get_next_token(&token, NULL, NULL);
405 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START != token);
406 json.get_next_token(&token, NULL, NULL);
407 while (JSON_TOKEN_OBJECT_START == token) {
a38c6d4c 408 int line_index = 0;
af710487 409
410 // line number:
411 json.get_next_token(&token, &value, &value_len);
412 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 6 ||
413 0 != strncmp(value, "number", value_len));
414
415 // read the line number and check if the line already exists
416 json.get_next_token(&token, &value, &value_len);
417 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
418 int lineno = atoi(value);
419 IMPORT_FORMAT_ERROR(lineno < 0);
a38c6d4c 420 line_index = get_line(file_index, lineno);
421 if (-1 == line_index) {
422 create_line(file_index, lineno);
423 line_index = profiler_db[file_index].lines.size() - 1;
af710487 424 }
425
a38c6d4c 426 // line execution count:
427 json.get_next_token(&token, &value, &value_len);
428 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 15 ||
429 0 != strncmp(value, "execution count", value_len));
af710487 430
a38c6d4c 431 // read the execution count and add it to the current data
432 json.get_next_token(&token, &value, &value_len);
433 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
434 profiler_db[file_index].lines[line_index].exec_count += atoi(value);
af710487 435
a38c6d4c 436 // total line execution time:
437 json.get_next_token(&token, &value, &value_len);
438 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME != token || value_len != 10 ||
439 0 != strncmp(value, "total time", value_len));
af710487 440
a38c6d4c 441 // read the total time and add it to the current data
442 json.get_next_token(&token, &value, &value_len);
443 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER != token);
444 profiler_db[file_index].lines[line_index].total_time = add_timeval(
445 profiler_db[file_index].lines[line_index].total_time, string2timeval(value));
af710487 446
447 // end of the line's object
448 json.get_next_token(&token, NULL, NULL);
449 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
450
451 // read the next token (either the start of another object or the line array end)
452 json.get_next_token(&token, NULL, NULL);
453 }
454
455 // line array end
456 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
457
458 // end of the file's object
459 json.get_next_token(&token, NULL, NULL);
460 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END != token);
461
462 // read the next token (either the start of another object or the main array end)
463 json.get_next_token(&token, NULL, NULL);
464 }
465
466 // main array end
467 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END != token);
468}
469
470void TTCN3_Profiler::export_data()
471{
a38c6d4c 472 char* file_name = NULL;
473 if (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc()) {
474 // this is the main database file, no suffix needed
475 file_name = database_filename;
476 }
477 else if (TTCN_Runtime::is_mtc()) {
478 // this is the database for the MTC, suffix the file name with "mtc"
479 file_name = mprintf("%s.mtc", database_filename);
480 }
481 else {
482 // this is the database for one of the PTCs, suffix the file name with the
483 // component reference
484 file_name = mprintf("%s.%d", database_filename, (component)self);
af710487 485 }
486
487 // check whether the file can be opened for writing
a38c6d4c 488 FILE* file = fopen(file_name, "w");
af710487 489 if (NULL == file) {
490 TTCN_warning("Could not open file '%s' for writing. Profiler and/or code coverage "
a38c6d4c 491 "data will not be saved.", file_name);
492 if (file_name != database_filename) {
493 Free(file_name);
494 }
af710487 495 return;
496 }
497
a38c6d4c 498 if (file_name != database_filename) {
499 Free(file_name);
500 }
501
af710487 502 // use the JSON tokenizer to create a JSON document from the database
503 JSON_Tokenizer json(true);
504
505 // main array, contains an element for each file
506 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
507 for (size_t i = 0; i < profiler_db.size(); ++i) {
508
509 // each file's data is stored in an object
510 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
511
512 // store the file name
513 json.put_next_token(JSON_TOKEN_NAME, "file");
514 char* file_name_str = mprintf("\"%s\"", profiler_db[i].filename);
515 json.put_next_token(JSON_TOKEN_STRING, file_name_str);
516 Free(file_name_str);
517
518 // store the function data in an array (one element for each function)
519 json.put_next_token(JSON_TOKEN_NAME, "functions");
520 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
521 for (size_t j = 0; j < profiler_db[i].functions.size(); ++j) {
a38c6d4c 522 // only store functions with actual data
523 if ((0 != profiler_db[i].functions[j].total_time.tv_sec &&
524 0 != profiler_db[i].functions[j].total_time.tv_usec) ||
525 0 != profiler_db[i].functions[j].exec_count) {
af710487 526
a38c6d4c 527 // the data is stored in an object for each function
528 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
529
530 // store the function name
531 json.put_next_token(JSON_TOKEN_NAME, "name");
532 char* func_name_str = mprintf("\"%s\"", profiler_db[i].functions[j].name);
533 json.put_next_token(JSON_TOKEN_STRING, func_name_str);
534 Free(func_name_str);
535
536 // store the function start line
537 json.put_next_token(JSON_TOKEN_NAME, "start line");
538 char* start_line_str = mprintf("%d", profiler_db[i].functions[j].lineno);
539 json.put_next_token(JSON_TOKEN_NUMBER, start_line_str);
540 Free(start_line_str);
541
af710487 542 // store the function execution count
543 json.put_next_token(JSON_TOKEN_NAME, "execution count");
a38c6d4c 544 char* exec_count_str = mprintf("%d", disable_coverage ? 0 :
545 profiler_db[i].functions[j].exec_count);
af710487 546 json.put_next_token(JSON_TOKEN_NUMBER, exec_count_str);
547 Free(exec_count_str);
a38c6d4c 548
af710487 549 // store the function's total execution time
550 json.put_next_token(JSON_TOKEN_NAME, "total time");
a38c6d4c 551 if (disable_profiler) {
552 json.put_next_token(JSON_TOKEN_NUMBER, "0.000000");
553 }
554 else {
555 char* total_time_str = timeval2string(profiler_db[i].functions[j].total_time);
556 json.put_next_token(JSON_TOKEN_NUMBER, total_time_str);
557 Free(total_time_str);
558 }
559
560 // end of function object
561 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
af710487 562 }
af710487 563 }
564
565 // end of function data array
566 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
567
568 // store the line data in an array (one element for each line with useful data)
569 json.put_next_token(JSON_TOKEN_NAME, "lines");
570 json.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
571 for (size_t j = 0; j < profiler_db[i].lines.size(); ++j) {
a38c6d4c 572 // only store lines with actual data
573 if ((0 != profiler_db[i].lines[j].total_time.tv_sec &&
574 0 != profiler_db[i].lines[j].total_time.tv_usec) ||
af710487 575 0 != profiler_db[i].lines[j].exec_count) {
a38c6d4c 576
af710487 577 // store line data in an object
578 json.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
a38c6d4c 579
af710487 580 // store the line number
581 json.put_next_token(JSON_TOKEN_NAME, "number");
a38c6d4c 582 char* line_number_str = mprintf("%d", profiler_db[i].lines[j].lineno);
af710487 583 json.put_next_token(JSON_TOKEN_NUMBER, line_number_str);
584 Free(line_number_str);
af710487 585
a38c6d4c 586 // store the line execution count
587 json.put_next_token(JSON_TOKEN_NAME, "execution count");
588 char* exec_count_str = mprintf("%d", disable_coverage ? 0 :
589 profiler_db[i].lines[j].exec_count);
590 json.put_next_token(JSON_TOKEN_NUMBER, exec_count_str);
591 Free(exec_count_str);
592
593 // store the line's total execution time
594 json.put_next_token(JSON_TOKEN_NAME, "total time");
595 if (disable_profiler) {
596 json.put_next_token(JSON_TOKEN_NUMBER, "0.000000");
af710487 597 }
a38c6d4c 598 else {
599 char* total_time_str = timeval2string(profiler_db[i].lines[j].total_time);
600 json.put_next_token(JSON_TOKEN_NUMBER, total_time_str);
601 Free(total_time_str);
602 }
603
af710487 604 // end of this line's object
605 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
606 }
607 }
608
609 // end of line data array
610 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
611
612 // end of this file's object
613 json.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
614 }
615
616 // end of main array
617 json.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
618
619 // write the JSON document into the file
620 fprintf(file, "%s\n", json.get_buffer());
621 fclose(file);
622}
623
a38c6d4c 624// Structure for one code line or function, used by print_stats for sorting
625struct stats_data_t {
626 const char* filename; // not owned
627 const char* funcname; // not owned, NULL for code lines that don't start a function
628 int lineno;
629 timeval total_time;
630 int exec_count;
631};
632
633// Compare function for sorting stats data based on total execution time (descending)
634int stats_data_cmp_time(const void* p_left, const void* p_right) {
635 const stats_data_t* p_left_data = (stats_data_t*)p_left;
636 const stats_data_t* p_right_data = (stats_data_t*)p_right;
637 if (p_left_data->total_time.tv_sec > p_right_data->total_time.tv_sec) return -1;
638 if (p_left_data->total_time.tv_sec < p_right_data->total_time.tv_sec) return 1;
639 if (p_left_data->total_time.tv_usec > p_right_data->total_time.tv_usec) return -1;
640 if (p_left_data->total_time.tv_usec < p_right_data->total_time.tv_usec) return 1;
641 return 0;
642}
643
644// Compare function for sorting stats data based on execution count (descending)
645int stats_data_cmp_count(const void* p_left, const void* p_right) {
646 return ((stats_data_t*)p_right)->exec_count - ((stats_data_t*)p_left)->exec_count;
647}
648
649// Compare function for sorting stats data based on total time per execution count (descending)
650int stats_data_cmp_avg(const void* p_left, const void* p_right) {
651 const stats_data_t* p_left_data = (stats_data_t*)p_left;
652 const stats_data_t* p_right_data = (stats_data_t*)p_right;
653 double left_time = p_left_data->total_time.tv_sec + p_left_data->total_time.tv_usec / 1000000.0;
654 double right_time = p_right_data->total_time.tv_sec + p_right_data->total_time.tv_usec / 1000000.0;
655 double diff = (right_time / p_right_data->exec_count) - (left_time / p_left_data->exec_count);
656 if (diff < 0) return -1;
657 if (diff > 0) return 1;
658 return 0;
659}
660
af710487 661void TTCN3_Profiler::print_stats()
662{
663 if (profiler_db.empty()) {
664 return;
665 }
666
667 // title
668 char* title_str = mprintf(
669 "##################################################\n"
670 "%s## TTCN-3 %s%s%sstatistics ##%s\n"
671 "##################################################\n\n\n"
672 , disable_profiler ? "#######" : (disable_coverage ? "#########" : "")
673 , disable_profiler ? "" : "profiler "
674 , (disable_profiler || disable_coverage) ? "" : "and "
675 , disable_coverage ? "" : "code coverage "
676 , disable_profiler ? "######" : (disable_coverage ? "#########" : ""));
677
a38c6d4c 678 char* line_func_count_str = NULL;
679 if (stats_flags & STATS_NUMBER_OF_LINES) {
680 line_func_count_str = mcopystr(
681 "--------------------------------------\n"
682 "- Number of code lines and functions -\n"
683 "--------------------------------------\n");
684 }
685
af710487 686 // line data
a38c6d4c 687 char* line_data_str = NULL;
688 if (stats_flags & STATS_LINE_DATA_RAW) {
689 line_data_str = mprintf(
690 "-------------------------------------------------\n"
691 "%s- Code line data (%s%s%s) -%s\n"
692 "-------------------------------------------------\n"
693 , disable_profiler ? "-------" : (disable_coverage ? "---------" : "")
694 , disable_profiler ? "" : "total time"
695 , (disable_profiler || disable_coverage) ? "" : " / "
696 , disable_coverage ? "" : "execution count"
697 , disable_profiler ? "------" : (disable_coverage ? "---------" : ""));
698 }
af710487 699
700 // average time / exec count for lines
701 char* line_avg_str = NULL;
a38c6d4c 702 if (!disable_coverage && !disable_profiler && (stats_flags & STATS_LINE_AVG_RAW)) {
af710487 703 line_avg_str = mcopystr(
a38c6d4c 704 "-------------------------------------------\n"
705 "- Average time / execution for code lines -\n"
706 "-------------------------------------------\n");
af710487 707 }
708
709 // function data
a38c6d4c 710 char* func_data_str = NULL;
711 if (stats_flags & STATS_FUNC_DATA_RAW) {
712 func_data_str = mprintf(
713 "------------------------------------------------\n"
714 "%s- Function data (%s%s%s) -%s\n"
715 "------------------------------------------------\n"
716 , disable_profiler ? "-------" : (disable_coverage ? "---------" : "")
717 , disable_profiler ? "" : "total time"
718 , (disable_profiler || disable_coverage) ? "" : " / "
719 , disable_coverage ? "" : "execution count"
720 , disable_profiler ? "------" : (disable_coverage ? "---------" : ""));
721 }
af710487 722
723 // average time / exec count for functions
724 char* func_avg_str = NULL;
a38c6d4c 725 if (!disable_coverage && !disable_profiler && (stats_flags & STATS_FUNC_AVG_RAW)) {
af710487 726 func_avg_str = mcopystr(
a38c6d4c 727 "------------------------------------------\n"
728 "- Average time / execution for functions -\n"
729 "------------------------------------------\n");
730 }
731
732 char* line_time_sorted_mod_str = NULL;
733 if (!disable_profiler && (stats_flags & STATS_LINE_TIMES_SORTED_BY_MOD)) {
734 line_time_sorted_mod_str = mcopystr(
735 "------------------------------------------------\n"
736 "- Total time of code lines, sorted, per module -\n"
737 "------------------------------------------------\n");
738 }
739
740 char* line_count_sorted_mod_str = NULL;
741 if (!disable_coverage && (stats_flags & STATS_LINE_COUNT_SORTED_BY_MOD)) {
742 line_count_sorted_mod_str = mcopystr(
743 "-----------------------------------------------------\n"
744 "- Execution count of code lines, sorted, per module -\n"
745 "-----------------------------------------------------\n");
746 }
747
748 char* line_avg_sorted_mod_str = NULL;
749 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_LINE_AVG_SORTED_BY_MOD)) {
750 line_avg_sorted_mod_str = mcopystr(
751 "--------------------------------------------------------------\n"
752 "- Average time / execution of code lines, sorted, per module -\n"
753 "--------------------------------------------------------------\n");
754 }
755
756 char* line_time_sorted_tot_str = NULL;
757 if (!disable_profiler && (stats_flags & STATS_LINE_TIMES_SORTED_TOTAL)) {
758 line_time_sorted_tot_str = mcopystr(
759 "-------------------------------------------\n"
760 "- Total time of code lines, sorted, total -\n"
761 "-------------------------------------------\n");
762 }
763
764 char* line_count_sorted_tot_str = NULL;
765 if (!disable_coverage && (stats_flags & STATS_LINE_COUNT_SORTED_TOTAL)) {
766 line_count_sorted_tot_str = mcopystr(
af710487 767 "------------------------------------------------\n"
a38c6d4c 768 "- Execution count of code lines, sorted, total -\n"
af710487 769 "------------------------------------------------\n");
770 }
771
a38c6d4c 772 char* line_avg_sorted_tot_str = NULL;
773 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_LINE_AVG_SORTED_TOTAL)) {
774 line_avg_sorted_tot_str = mcopystr(
775 "---------------------------------------------------------\n"
776 "- Average time / execution of code lines, sorted, total -\n"
777 "---------------------------------------------------------\n");
778 }
779
780 char* func_time_sorted_mod_str = NULL;
781 if (!disable_profiler && (stats_flags & STATS_FUNC_TIMES_SORTED_BY_MOD)) {
782 func_time_sorted_mod_str = mcopystr(
783 "-----------------------------------------------\n"
784 "- Total time of functions, sorted, per module -\n"
785 "-----------------------------------------------\n");
786 }
787
788 char* func_count_sorted_mod_str = NULL;
789 if (!disable_coverage && (stats_flags & STATS_FUNC_COUNT_SORTED_BY_MOD)) {
790 func_count_sorted_mod_str = mcopystr(
791 "----------------------------------------------------\n"
792 "- Execution count of functions, sorted, per module -\n"
793 "----------------------------------------------------\n");
794 }
795
796 char* func_avg_sorted_mod_str = NULL;
797 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_FUNC_AVG_SORTED_BY_MOD)) {
798 func_avg_sorted_mod_str = mcopystr(
799 "-------------------------------------------------------------\n"
800 "- Average time / execution of functions, sorted, per module -\n"
801 "-------------------------------------------------------------\n");
802 }
803
804 char* func_time_sorted_tot_str = NULL;
805 if (!disable_profiler && (stats_flags & STATS_FUNC_TIMES_SORTED_TOTAL)) {
806 func_time_sorted_tot_str = mcopystr(
807 "------------------------------------------\n"
808 "- Total time of functions, sorted, total -\n"
809 "------------------------------------------\n");
810 }
811
812 char* func_count_sorted_tot_str = NULL;
813 if (!disable_coverage && (stats_flags & STATS_FUNC_COUNT_SORTED_TOTAL)) {
814 func_count_sorted_tot_str = mcopystr(
815 "-----------------------------------------------\n"
816 "- Execution count of functions, sorted, total -\n"
817 "-----------------------------------------------\n");
818 }
819
820 char* func_avg_sorted_tot_str = NULL;
821 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_FUNC_AVG_SORTED_TOTAL)) {
822 func_avg_sorted_tot_str = mcopystr(
823 "--------------------------------------------------------\n"
824 "- Average time / execution of functions, sorted, total -\n"
825 "--------------------------------------------------------\n");
826 }
827
828 char* line_time_sorted_top10_str = NULL;
829 if (!disable_profiler && (stats_flags & STATS_TOP10_LINE_TIMES)) {
830 line_time_sorted_top10_str = mcopystr(
831 "------------------------------------\n"
832 "- Total time of code lines, top 10 -\n"
833 "------------------------------------\n");
834 }
835
836 char* line_count_sorted_top10_str = NULL;
837 if (!disable_coverage && (stats_flags & STATS_TOP10_LINE_COUNT)) {
838 line_count_sorted_top10_str = mcopystr(
839 "-----------------------------------------\n"
840 "- Execution count of code lines, top 10 -\n"
841 "-----------------------------------------\n");
842 }
843
844 char* line_avg_sorted_top10_str = NULL;
845 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_TOP10_LINE_AVG)) {
846 line_avg_sorted_top10_str = mcopystr(
847 "--------------------------------------------------\n"
848 "- Average time / execution of code lines, top 10 -\n"
849 "--------------------------------------------------\n");
850 }
851
852 char* func_time_sorted_top10_str = NULL;
853 if (!disable_profiler && (stats_flags & STATS_TOP10_FUNC_TIMES)) {
854 func_time_sorted_top10_str = mcopystr(
855 "-----------------------------------\n"
856 "- Total time of functions, top 10 -\n"
857 "-----------------------------------\n");
858 }
859
860 char* func_count_sorted_top10_str = NULL;
861 if (!disable_coverage && (stats_flags & STATS_TOP10_FUNC_COUNT)) {
862 func_count_sorted_top10_str = mcopystr(
863 "----------------------------------------\n"
864 "- Execution count of functions, top 10 -\n"
865 "----------------------------------------\n");
866 }
867
868 char* func_avg_sorted_top10_str = NULL;
869 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_TOP10_FUNC_AVG)) {
870 func_avg_sorted_top10_str = mcopystr(
871 "-------------------------------------------------\n"
872 "- Average time / execution of functions, top 10 -\n"
873 "-------------------------------------------------\n");
874 }
875
876 char* unused_lines_str = NULL;
877 char* unused_func_str = NULL;
878 if (!disable_coverage && (stats_flags & STATS_UNUSED_LINES)) {
879 unused_lines_str = mcopystr(
880 "---------------------\n"
881 "- Unused code lines -\n"
882 "---------------------\n");
883 }
884 if (!disable_coverage && (stats_flags & STATS_UNUSED_FUNC)) {
885 unused_func_str = mcopystr(
886 "--------------------\n"
887 "- Unused functions -\n"
888 "--------------------\n");
889 }
890
891 // variables for counting totals, and for determining the amount of unused lines/functions
892 size_t total_code_lines = 0;
893 size_t total_functions = 0;
894 size_t used_code_lines = 0;
895 size_t used_functions = 0;
896
897 // cached sizes of statistics data segments, needed to determine whether a separator
898 // is needed or not
899 size_t line_data_str_len = mstrlen(line_data_str);
900 size_t func_data_str_len = mstrlen(func_data_str);
901 size_t unused_lines_str_len = mstrlen(unused_lines_str);
902 size_t unused_func_str_len = mstrlen(unused_func_str);
903 size_t line_avg_str_len = mstrlen(line_avg_str);
904 size_t func_avg_str_len = mstrlen(func_avg_str);
905
af710487 906 // cycle through the database and gather the necessary data
907 for (size_t i = 0; i < profiler_db.size(); ++i) {
908 if (i > 0) {
a38c6d4c 909 // add separators between files (only add them if the previous file actually added something)
910 if ((stats_flags & STATS_LINE_DATA_RAW) && line_data_str_len != mstrlen(line_data_str)) {
911 line_data_str = mputstr(line_data_str, "-------------------------------------------------\n");
912 line_data_str_len = mstrlen(line_data_str);
913 }
914 if ((stats_flags & STATS_FUNC_DATA_RAW) && func_data_str_len != mstrlen(func_data_str)) {
915 func_data_str = mputstr(func_data_str, "------------------------------------------------\n");
916 func_data_str_len = mstrlen(func_data_str);
917 }
918 if (!disable_coverage) {
919 if ((stats_flags & STATS_UNUSED_LINES) && unused_lines_str_len != mstrlen(unused_lines_str)) {
920 unused_lines_str = mputstr(unused_lines_str, "---------------------\n");
921 unused_lines_str_len = mstrlen(unused_lines_str);
922 }
923 if ((stats_flags & STATS_UNUSED_FUNC) && unused_func_str_len != mstrlen(unused_func_str)) {
924 unused_func_str = mputstr(unused_func_str, "--------------------\n");
925 unused_func_str_len = mstrlen(unused_func_str);
926 }
927 if (!disable_profiler) {
928 if ((stats_flags & STATS_LINE_AVG_RAW) && line_avg_str_len != mstrlen(line_avg_str)) {
929 line_avg_str = mputstr(line_avg_str, "-------------------------------------------\n");
930 line_avg_str_len = mstrlen(line_avg_str);
931 }
932 if ((stats_flags & STATS_FUNC_AVG_RAW) && func_avg_str_len != mstrlen(func_avg_str)) {
933 func_avg_str = mputstr(func_avg_str, "------------------------------------------\n");
934 func_avg_str_len = mstrlen(func_avg_str);
935 }
936 }
af710487 937 }
938 }
939
940 // lines
941 for (size_t j = 0; j < profiler_db[i].lines.size(); ++j) {
a38c6d4c 942 // line specification (including function name for the function's start line)
943 char* line_spec_str = mprintf("%s:%d", profiler_db[i].filename,
944 profiler_db[i].lines[j].lineno);
945 int func = get_function(i, profiler_db[i].lines[j].lineno);
946 if (-1 != func) {
947 line_spec_str = mputprintf(line_spec_str, " [%s]", profiler_db[i].functions[func].name);
948 }
949 line_spec_str = mputstrn(line_spec_str, "\n", 1);
950
951 if (disable_coverage || 0 != profiler_db[i].lines[j].exec_count) {
af710487 952 if (!disable_profiler) {
a38c6d4c 953 if (stats_flags & STATS_LINE_DATA_RAW) {
954 char* total_time_str = timeval2string(profiler_db[i].lines[j].total_time);
955 line_data_str = mputprintf(line_data_str, "%ss", total_time_str);
956 Free(total_time_str);
957 }
af710487 958 if (!disable_coverage) {
a38c6d4c 959 if (stats_flags & STATS_LINE_DATA_RAW) {
960 line_data_str = mputstrn(line_data_str, "\t/\t", 3);
961 }
962 if (stats_flags & STATS_LINE_AVG_RAW) {
963 double avg = (profiler_db[i].lines[j].total_time.tv_sec +
964 profiler_db[i].lines[j].total_time.tv_usec / 1000000.0) /
965 profiler_db[i].lines[j].exec_count;
966 char* total_time_str = timeval2string(profiler_db[i].lines[j].total_time);
967 line_avg_str = mputprintf(line_avg_str, "%.6lfs\t(%ss / %d)",
968 avg, total_time_str, profiler_db[i].lines[j].exec_count);
969 Free(total_time_str);
970 }
af710487 971 }
972 }
a38c6d4c 973 if (!disable_coverage && (stats_flags & STATS_LINE_DATA_RAW)) {
af710487 974 line_data_str = mputprintf(line_data_str, "%d", profiler_db[i].lines[j].exec_count);
975 }
a38c6d4c 976
af710487 977 // add the line spec string to the other strings
a38c6d4c 978 if (stats_flags & STATS_LINE_DATA_RAW) {
979 line_data_str = mputprintf(line_data_str, "\t%s", line_spec_str);
980 }
981 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_LINE_AVG_RAW)) {
982 line_avg_str = mputprintf(line_avg_str, "\t%s", line_spec_str);
af710487 983 }
a38c6d4c 984 ++used_code_lines;
af710487 985 }
a38c6d4c 986 else if (stats_flags & STATS_UNUSED_LINES) {
987 // unused line
988 unused_lines_str = mputstr(unused_lines_str, line_spec_str);
989 }
990 Free(line_spec_str);
af710487 991 }
992
993 // functions
994 for (size_t j = 0; j < profiler_db[i].functions.size(); ++j) {
a38c6d4c 995 // functions specification
996 char* func_spec_str = mprintf("%s:%d [%s]\n", profiler_db[i].filename,
997 profiler_db[i].functions[j].lineno, profiler_db[i].functions[j].name);
998
999 if (disable_coverage || 0 != profiler_db[i].functions[j].exec_count) {
1000 if (!disable_profiler) {
1001 if (stats_flags & STATS_FUNC_DATA_RAW) {
1002 char* total_time_str = timeval2string(profiler_db[i].functions[j].total_time);
1003 func_data_str = mputprintf(func_data_str, "%ss", total_time_str);
1004 Free(total_time_str);
1005 }
1006 if (!disable_coverage) {
1007 if (stats_flags & STATS_FUNC_DATA_RAW) {
1008 func_data_str = mputstrn(func_data_str, "\t/\t", 3);
1009 }
1010 if (stats_flags & STATS_FUNC_AVG_RAW) {
1011 double avg = (profiler_db[i].functions[j].total_time.tv_sec +
1012 profiler_db[i].functions[j].total_time.tv_usec / 1000000.0) /
1013 profiler_db[i].functions[j].exec_count;
1014 char* total_time_str = timeval2string(profiler_db[i].functions[j].total_time);
1015 func_avg_str = mputprintf(func_avg_str, "%.6lfs\t(%ss / %d)",
1016 avg, total_time_str, profiler_db[i].functions[j].exec_count);
1017 Free(total_time_str);
1018 }
1019 }
1020 }
1021 if (!disable_coverage && (stats_flags & STATS_FUNC_DATA_RAW)) {
1022 func_data_str = mputprintf(func_data_str, "%d", profiler_db[i].functions[j].exec_count);
1023 }
1024
1025 // add the line spec string to the other strings
1026 if (stats_flags & STATS_FUNC_DATA_RAW) {
1027 func_data_str = mputprintf(func_data_str, "\t%s", func_spec_str);
af710487 1028 }
a38c6d4c 1029 if (!disable_profiler && !disable_coverage && (stats_flags & STATS_FUNC_AVG_RAW)) {
1030 func_avg_str = mputprintf(func_avg_str, "\t%s", func_spec_str);
1031 }
1032
1033 ++used_functions;
af710487 1034 }
a38c6d4c 1035 else if (stats_flags & STATS_UNUSED_FUNC) {
1036 // unused function
1037 unused_func_str = mputprintf(unused_func_str, func_spec_str);
1038 }
1039 Free(func_spec_str);
1040 }
1041
1042 // number of lines and functions
1043 if (stats_flags & STATS_NUMBER_OF_LINES) {
1044 line_func_count_str = mputprintf(line_func_count_str, "%s:\t%lu lines,\t%lu functions\n",
1045 profiler_db[i].filename, profiler_db[i].lines.size(), profiler_db[i].functions.size());
1046 }
1047 total_code_lines += profiler_db[i].lines.size();
1048 total_functions += profiler_db[i].functions.size();
1049 }
1050 if (stats_flags & STATS_NUMBER_OF_LINES) {
1051 line_func_count_str = mputprintf(line_func_count_str,
1052 "--------------------------------------\n"
1053 "Total:\t%lu lines,\t%lu functions\n", total_code_lines, total_functions);
1054 }
1055
1056 if (stats_flags & (STATS_TOP10_ALL_DATA | STATS_ALL_DATA_SORTED)) {
1057 // copy code line and function info into stats_data_t containers for sorting
1058 stats_data_t* code_line_stats = (stats_data_t*)Malloc(used_code_lines * sizeof(stats_data_t));
1059 stats_data_t* function_stats = (stats_data_t*)Malloc(used_functions * sizeof(stats_data_t));
1060 int line_index = 0;
1061 int func_index = 0;
1062
1063 for (size_t i = 0; i < profiler_db.size(); ++i) {
1064 for (size_t j = 0; j < profiler_db[i].lines.size(); ++j) {
1065 if (disable_coverage || 0 != profiler_db[i].lines[j].exec_count) {
1066 code_line_stats[line_index].filename = profiler_db[i].filename;
1067 code_line_stats[line_index].funcname = NULL;
1068 code_line_stats[line_index].lineno = profiler_db[i].lines[j].lineno;
1069 code_line_stats[line_index].total_time = profiler_db[i].lines[j].total_time;
1070 code_line_stats[line_index].exec_count = profiler_db[i].lines[j].exec_count;
1071 int func = get_function(i, profiler_db[i].lines[j].lineno);
1072 if (-1 != func) {
1073 code_line_stats[line_index].funcname = profiler_db[i].functions[func].name;
1074 }
1075 ++line_index;
1076 }
1077 }
1078 for (size_t j = 0; j < profiler_db[i].functions.size(); ++j) {
1079 if (disable_coverage || 0 != profiler_db[i].functions[j].exec_count) {
1080 function_stats[func_index].filename = profiler_db[i].filename;
1081 function_stats[func_index].funcname = profiler_db[i].functions[j].name;
1082 function_stats[func_index].lineno = profiler_db[i].functions[j].lineno;
1083 function_stats[func_index].total_time = profiler_db[i].functions[j].total_time;
1084 function_stats[func_index].exec_count = profiler_db[i].functions[j].exec_count;
1085 ++func_index;
1086 }
af710487 1087 }
a38c6d4c 1088 }
af710487 1089
a38c6d4c 1090 if (!disable_profiler) {
1091 // sort the code lines and functions by total time
1092 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_time);
1093 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_time);
1094
1095 if (stats_flags & (STATS_LINE_TIMES_SORTED_TOTAL | STATS_TOP10_LINE_TIMES)) {
1096 // cycle through the sorted code lines and gather the necessary data
1097 for (size_t i = 0; i < used_code_lines; ++i) {
1098 char* total_time_str = timeval2string(code_line_stats[i].total_time);
1099 char* the_data = mprintf("%ss\t%s:%d", total_time_str,
1100 code_line_stats[i].filename, code_line_stats[i].lineno);
1101 Free(total_time_str);
1102 if (NULL != code_line_stats[i].funcname) {
1103 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
1104 }
1105 the_data = mputstrn(the_data, "\n", 1);
1106 if (stats_flags & STATS_LINE_TIMES_SORTED_TOTAL) {
1107 line_time_sorted_tot_str = mputstr(line_time_sorted_tot_str, the_data);
1108 }
1109 if (i < 10 && (stats_flags & STATS_TOP10_LINE_TIMES)) {
1110 line_time_sorted_top10_str = mputprintf(line_time_sorted_top10_str,
1111 "%2lu.\t%s", i + 1, the_data);
1112 }
1113 Free(the_data);
1114 }
1115 }
1116
1117 if (stats_flags & (STATS_FUNC_TIMES_SORTED_TOTAL | STATS_TOP10_FUNC_TIMES)) {
1118 // cycle through the sorted functions and gather the necessary data
1119 for (size_t i = 0; i < used_functions; ++i) {
1120 char* total_time_str = timeval2string(function_stats[i].total_time);
1121 char* the_data = mprintf("%ss\t%s:%d [%s]\n", total_time_str,
1122 function_stats[i].filename, function_stats[i].lineno, function_stats[i].funcname);
1123 Free(total_time_str);
1124 if (stats_flags & STATS_FUNC_TIMES_SORTED_TOTAL) {
1125 func_time_sorted_tot_str = mputstr(func_time_sorted_tot_str, the_data);
1126 }
1127 if (i < 10 && (stats_flags & STATS_TOP10_FUNC_TIMES)) {
1128 func_time_sorted_top10_str = mputprintf(func_time_sorted_top10_str,
1129 "%2lu.\t%s", i + 1, the_data);
1130 }
1131 Free(the_data);
1132 }
1133 }
1134
1135 if (stats_flags & (STATS_LINE_TIMES_SORTED_BY_MOD | STATS_FUNC_TIMES_SORTED_BY_MOD)) {
1136 // cached string lengths, to avoid multiple separators after each other
1137 size_t line_time_sorted_mod_str_len = mstrlen(line_time_sorted_mod_str);
1138 size_t func_time_sorted_mod_str_len = mstrlen(func_time_sorted_mod_str);
1139
1140 // cycle through the sorted statistics and gather the necessary data per module
1141 for (size_t i = 0; i < profiler_db.size(); ++i) {
1142 if (i > 0) {
1143 if ((stats_flags & STATS_LINE_TIMES_SORTED_BY_MOD) &&
1144 line_time_sorted_mod_str_len != mstrlen(line_time_sorted_mod_str)) {
1145 line_time_sorted_mod_str = mputstr(line_time_sorted_mod_str,
1146 "------------------------------------------------\n");
1147 line_time_sorted_mod_str_len = mstrlen(line_time_sorted_mod_str);
1148 }
1149 if ((stats_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) &&
1150 func_time_sorted_mod_str_len != mstrlen(func_time_sorted_mod_str)) {
1151 func_time_sorted_mod_str = mputstr(func_time_sorted_mod_str,
1152 "-----------------------------------------------\n");
1153 func_time_sorted_mod_str_len = mstrlen(func_time_sorted_mod_str);
1154 }
1155 }
1156 if (stats_flags & STATS_LINE_TIMES_SORTED_BY_MOD) {
1157 for (size_t j = 0; j < used_code_lines; ++j) {
1158 if (0 == strcmp(code_line_stats[j].filename, profiler_db[i].filename)) {
1159 char* total_time_str = timeval2string(code_line_stats[j].total_time);
1160 line_time_sorted_mod_str = mputprintf(line_time_sorted_mod_str,
1161 "%ss\t%s:%d", total_time_str, code_line_stats[j].filename,
1162 code_line_stats[j].lineno);
1163 Free(total_time_str);
1164 if (NULL != code_line_stats[j].funcname) {
1165 line_time_sorted_mod_str = mputprintf(line_time_sorted_mod_str,
1166 " [%s]", code_line_stats[j].funcname);
1167 }
1168 line_time_sorted_mod_str = mputstrn(line_time_sorted_mod_str, "\n", 1);
1169 }
1170 }
1171 }
1172 if (stats_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) {
1173 for (size_t j = 0; j < used_functions; ++j) {
1174 if (0 == strcmp(function_stats[j].filename, profiler_db[i].filename)) {
1175 char* total_time_str = timeval2string(function_stats[j].total_time);
1176 func_time_sorted_mod_str = mputprintf(func_time_sorted_mod_str,
1177 "%ss\t%s:%d [%s]\n", total_time_str, function_stats[j].filename,
1178 function_stats[j].lineno, function_stats[j].funcname);
1179 Free(total_time_str);
1180 }
1181 }
1182 }
1183 }
1184 }
1185 }
1186
1187 if (!disable_coverage) {
1188 // sort the code lines and functions by execution count
1189 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_count);
1190 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_count);
1191
1192 if (stats_flags & (STATS_LINE_COUNT_SORTED_TOTAL | STATS_TOP10_LINE_COUNT)) {
1193 // cycle through the sorted code lines and gather the necessary data
1194 for (size_t i = 0; i < used_code_lines; ++i) {
1195 char* the_data = mprintf("%d\t%s:%d", code_line_stats[i].exec_count,
1196 code_line_stats[i].filename, code_line_stats[i].lineno);
1197 if (NULL != code_line_stats[i].funcname) {
1198 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
1199 }
1200 the_data = mputstrn(the_data, "\n", 1);
1201 if (stats_flags & STATS_LINE_COUNT_SORTED_TOTAL) {
1202 line_count_sorted_tot_str = mputstr(line_count_sorted_tot_str, the_data);
1203 }
1204 if (i < 10 && (stats_flags & STATS_TOP10_LINE_COUNT)) {
1205 line_count_sorted_top10_str = mputprintf(line_count_sorted_top10_str,
1206 "%2lu.\t%s", i + 1, the_data);
1207 }
1208 Free(the_data);
1209 }
1210 }
1211
1212 if (stats_flags & (STATS_FUNC_COUNT_SORTED_TOTAL | STATS_TOP10_FUNC_COUNT)) {
1213 // cycle through the sorted functions and gather the necessary data
1214 for (size_t i = 0; i < used_functions; ++i) {
1215 char* the_data = mprintf("%d\t%s:%d [%s]\n",
1216 function_stats[i].exec_count, function_stats[i].filename,
1217 function_stats[i].lineno, function_stats[i].funcname);
1218 if (stats_flags & STATS_FUNC_COUNT_SORTED_TOTAL) {
1219 func_count_sorted_tot_str = mputstr(func_count_sorted_tot_str, the_data);
1220 }
1221 if (i < 10 && (stats_flags & STATS_TOP10_FUNC_COUNT)) {
1222 func_count_sorted_top10_str = mputprintf(func_count_sorted_top10_str,
1223 "%2lu.\t%s", i + 1, the_data);
1224 }
1225 Free(the_data);
1226 }
1227 }
1228
1229 if (stats_flags & (STATS_LINE_COUNT_SORTED_BY_MOD | STATS_FUNC_COUNT_SORTED_BY_MOD)) {
1230 // cached string lengths, to avoid multiple separators after each other
1231 size_t line_count_sorted_mod_str_len = mstrlen(line_count_sorted_mod_str);
1232 size_t func_count_sorted_mod_str_len = mstrlen(func_count_sorted_mod_str);
1233
1234 // cycle through the sorted statistics and gather the necessary data per module
1235 for (size_t i = 0; i < profiler_db.size(); ++i) {
1236 if (i > 0) {
1237 if ((stats_flags & STATS_LINE_COUNT_SORTED_BY_MOD) &&
1238 line_count_sorted_mod_str_len != mstrlen(line_count_sorted_mod_str)) {
1239 line_count_sorted_mod_str = mputstr(line_count_sorted_mod_str,
1240 "-----------------------------------------------------\n");
1241 line_count_sorted_mod_str_len = mstrlen(line_count_sorted_mod_str);
1242 }
1243 if ((stats_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) &&
1244 func_count_sorted_mod_str_len != mstrlen(func_count_sorted_mod_str)) {
1245 func_count_sorted_mod_str = mputstr(func_count_sorted_mod_str,
1246 "----------------------------------------------------\n");
1247 func_count_sorted_mod_str_len = mstrlen(func_count_sorted_mod_str);
1248 }
1249 }
1250 if (stats_flags & STATS_LINE_COUNT_SORTED_BY_MOD) {
1251 for (size_t j = 0; j < used_code_lines; ++j) {
1252 if (0 == strcmp(code_line_stats[j].filename, profiler_db[i].filename)) {
1253 line_count_sorted_mod_str = mputprintf(line_count_sorted_mod_str,
1254 "%d\t%s:%d", code_line_stats[j].exec_count, code_line_stats[j].filename,
1255 code_line_stats[j].lineno);
1256 if (NULL != code_line_stats[j].funcname) {
1257 line_count_sorted_mod_str = mputprintf(line_count_sorted_mod_str,
1258 " [%s]", code_line_stats[j].funcname);
1259 }
1260 line_count_sorted_mod_str = mputstrn(line_count_sorted_mod_str, "\n", 1);
1261 }
1262 }
1263 }
1264 if (stats_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) {
1265 for (size_t j = 0; j < used_functions; ++j) {
1266 if (0 == strcmp(function_stats[j].filename, profiler_db[i].filename)) {
1267 func_count_sorted_mod_str = mputprintf(func_count_sorted_mod_str,
1268 "%d\t%s:%d [%s]\n", function_stats[j].exec_count, function_stats[j].filename,
1269 function_stats[j].lineno, function_stats[j].funcname);
1270 }
1271 }
1272 }
1273 }
1274 }
1275 }
1276
1277 if (!disable_profiler && !disable_coverage) {
1278 // sort the code lines and functions by average time / execution
1279 qsort(code_line_stats, used_code_lines, sizeof(stats_data_t), &stats_data_cmp_avg);
1280 qsort(function_stats, used_functions, sizeof(stats_data_t), &stats_data_cmp_avg);
1281
1282 if (stats_flags & (STATS_LINE_AVG_SORTED_TOTAL | STATS_TOP10_LINE_AVG)) {
1283 // cycle through the sorted code lines and gather the necessary data
1284 for (size_t i = 0; i < used_code_lines; ++i) {
1285 double avg = (code_line_stats[i].total_time.tv_sec +
1286 code_line_stats[i].total_time.tv_usec / 1000000.0) /
1287 code_line_stats[i].exec_count;
1288 char* total_time_str = timeval2string(code_line_stats[i].total_time);
1289 char* the_data = mprintf("%.6lfs\t(%ss / %d)\t%s:%d",
1290 avg, total_time_str, code_line_stats[i].exec_count,
1291 code_line_stats[i].filename, code_line_stats[i].lineno);
1292 Free(total_time_str);
1293 if (NULL != code_line_stats[i].funcname) {
1294 the_data = mputprintf(the_data, " [%s]", code_line_stats[i].funcname);
1295 }
1296 the_data = mputstrn(the_data, "\n", 1);
1297 if (stats_flags & STATS_LINE_AVG_SORTED_TOTAL) {
1298 line_avg_sorted_tot_str = mputstr(line_avg_sorted_tot_str, the_data);
1299 }
1300 if (i < 10 && (stats_flags & STATS_TOP10_LINE_AVG)) {
1301 line_avg_sorted_top10_str = mputprintf(line_avg_sorted_top10_str,
1302 "%2lu.\t%s", i + 1, the_data);
1303 }
1304 Free(the_data);
1305 }
1306 }
1307
1308 if (stats_flags & (STATS_FUNC_AVG_SORTED_TOTAL | STATS_TOP10_FUNC_AVG)) {
1309 // cycle through the sorted functions and gather the necessary data
1310 for (size_t i = 0; i < used_functions; ++i) {
1311 double avg = (function_stats[i].total_time.tv_sec +
1312 function_stats[i].total_time.tv_usec / 1000000.0) /
1313 function_stats[i].exec_count;
1314 char* total_time_str = timeval2string(function_stats[i].total_time);
1315 char* the_data = mprintf("%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1316 avg, total_time_str, function_stats[i].exec_count,
1317 function_stats[i].filename, function_stats[i].lineno, function_stats[i].funcname);
1318 Free(total_time_str);
1319 if (stats_flags & STATS_FUNC_AVG_SORTED_TOTAL) {
1320 func_avg_sorted_tot_str = mputstr(func_avg_sorted_tot_str, the_data);
1321 }
1322 if (i < 10 && (stats_flags & STATS_TOP10_FUNC_AVG)) {
1323 func_avg_sorted_top10_str = mputprintf(func_avg_sorted_top10_str,
1324 "%2lu.\t%s", i + 1, the_data);
1325 }
1326 Free(the_data);
1327 }
1328 }
1329
1330 if (stats_flags & (STATS_LINE_AVG_SORTED_BY_MOD | STATS_FUNC_AVG_SORTED_BY_MOD)) {
1331 // cached string lengths, to avoid multiple separators after each other
1332 size_t line_avg_sorted_mod_str_len = mstrlen(line_avg_sorted_mod_str);
1333 size_t func_avg_sorted_mod_str_len = mstrlen(func_avg_sorted_mod_str);
af710487 1334
a38c6d4c 1335 // cycle through the sorted statistics and gather the necessary data per module
1336 for (size_t i = 0; i < profiler_db.size(); ++i) {
1337 if (i > 0) {
1338 if ((stats_flags & STATS_LINE_AVG_SORTED_BY_MOD) &&
1339 line_avg_sorted_mod_str_len != mstrlen(line_avg_sorted_mod_str)) {
1340 line_avg_sorted_mod_str = mputstr(line_avg_sorted_mod_str,
1341 "--------------------------------------------------------------\n");
1342 line_avg_sorted_mod_str_len = mstrlen(line_avg_sorted_mod_str);
1343 }
1344 if ((stats_flags & STATS_FUNC_AVG_SORTED_BY_MOD) &&
1345 func_avg_sorted_mod_str_len != mstrlen(func_avg_sorted_mod_str)) {
1346 func_avg_sorted_mod_str = mputstr(func_avg_sorted_mod_str,
1347 "-------------------------------------------------------------\n");
1348 func_avg_sorted_mod_str_len = mstrlen(func_avg_sorted_mod_str);
1349 }
1350 }
1351 if (stats_flags & STATS_LINE_AVG_SORTED_BY_MOD) {
1352 for (size_t j = 0; j < used_code_lines; ++j) {
1353 if (0 == strcmp(code_line_stats[j].filename, profiler_db[i].filename)) {
1354 double avg = (code_line_stats[j].total_time.tv_sec +
1355 code_line_stats[j].total_time.tv_usec / 1000000.0) /
1356 code_line_stats[j].exec_count;
1357 char* total_time_str = timeval2string(code_line_stats[j].total_time);
1358 line_avg_sorted_mod_str = mputprintf(line_avg_sorted_mod_str,
1359 "%.6lfs\t(%ss / %d)\t%s:%d",
1360 avg, total_time_str, code_line_stats[j].exec_count,
1361 code_line_stats[j].filename, code_line_stats[j].lineno);
1362 Free(total_time_str);
1363 if (NULL != code_line_stats[j].funcname) {
1364 line_avg_sorted_mod_str = mputprintf(line_avg_sorted_mod_str,
1365 " [%s]", code_line_stats[j].funcname);
1366 }
1367 line_avg_sorted_mod_str = mputstrn(line_avg_sorted_mod_str, "\n", 1);
1368 }
1369 }
1370 }
1371 if (stats_flags & STATS_FUNC_AVG_SORTED_BY_MOD) {
1372 for (size_t j = 0; j < used_functions; ++j) {
1373 if (0 == strcmp(function_stats[j].filename, profiler_db[i].filename)) {
1374 double avg = (function_stats[j].total_time.tv_sec +
1375 function_stats[j].total_time.tv_usec / 1000000.0) /
1376 function_stats[j].exec_count;
1377 char* total_time_str = timeval2string(function_stats[j].total_time);
1378 func_avg_sorted_mod_str = mputprintf(func_avg_sorted_mod_str,
1379 "%.6lfs\t(%ss / %d)\t%s:%d [%s]\n",
1380 avg, total_time_str, function_stats[j].exec_count,
1381 function_stats[j].filename, function_stats[j].lineno, function_stats[j].funcname);
1382 Free(total_time_str);
1383 }
1384 }
1385 }
1386 }
af710487 1387 }
1388 }
a38c6d4c 1389
1390 // free the stats data
1391 Free(code_line_stats);
1392 Free(function_stats);
af710487 1393 }
1394
1395 // add new lines at the end of each segment
a38c6d4c 1396 if (stats_flags & STATS_NUMBER_OF_LINES) {
1397 line_func_count_str = mputstrn(line_func_count_str, "\n", 1);
1398 }
1399 if (stats_flags & STATS_LINE_DATA_RAW) {
1400 line_data_str = mputstrn(line_data_str, "\n", 1);
1401 }
1402 if (stats_flags & STATS_FUNC_DATA_RAW) {
1403 func_data_str = mputstrn(func_data_str, "\n", 1);
1404 }
1405 if (!disable_profiler) {
1406 if (stats_flags & STATS_LINE_TIMES_SORTED_BY_MOD) {
1407 line_time_sorted_mod_str = mputstrn(line_time_sorted_mod_str, "\n", 1);
1408 }
1409 if (stats_flags & STATS_LINE_TIMES_SORTED_TOTAL) {
1410 line_time_sorted_tot_str = mputstrn(line_time_sorted_tot_str, "\n", 1);
1411 }
1412 if (stats_flags & STATS_FUNC_TIMES_SORTED_BY_MOD) {
1413 func_time_sorted_mod_str = mputstrn(func_time_sorted_mod_str, "\n", 1);
1414 }
1415 if (stats_flags & STATS_FUNC_TIMES_SORTED_TOTAL) {
1416 func_time_sorted_tot_str = mputstrn(func_time_sorted_tot_str, "\n", 1);
1417 }
1418 if (stats_flags & STATS_TOP10_LINE_TIMES) {
1419 line_time_sorted_top10_str = mputstrn(line_time_sorted_top10_str, "\n", 1);
1420 }
1421 if (stats_flags & STATS_TOP10_FUNC_TIMES) {
1422 func_time_sorted_top10_str = mputstrn(func_time_sorted_top10_str, "\n", 1);
1423 }
1424 if (!disable_coverage) {
1425 if (stats_flags & STATS_LINE_AVG_RAW) {
1426 line_avg_str = mputstrn(line_avg_str, "\n", 1);
1427 }
1428 if (stats_flags & STATS_LINE_AVG_RAW) {
1429 func_avg_str = mputstrn(func_avg_str, "\n", 1);
1430 }
1431 if (stats_flags & STATS_LINE_AVG_SORTED_BY_MOD) {
1432 line_avg_sorted_mod_str = mputstrn(line_avg_sorted_mod_str, "\n", 1);
1433 }
1434 if (stats_flags & STATS_LINE_AVG_SORTED_TOTAL) {
1435 line_avg_sorted_tot_str = mputstrn(line_avg_sorted_tot_str, "\n", 1);
1436 }
1437 if (stats_flags & STATS_FUNC_AVG_SORTED_BY_MOD) {
1438 func_avg_sorted_mod_str = mputstrn(func_avg_sorted_mod_str, "\n", 1);
1439 }
1440 if (stats_flags & STATS_FUNC_AVG_SORTED_TOTAL) {
1441 func_avg_sorted_tot_str = mputstrn(func_avg_sorted_tot_str, "\n", 1);
1442 }
1443 if (stats_flags & STATS_TOP10_LINE_AVG) {
1444 line_avg_sorted_top10_str = mputstrn(line_avg_sorted_top10_str, "\n", 1);
1445 }
1446 if (stats_flags & STATS_TOP10_FUNC_AVG) {
1447 func_avg_sorted_top10_str = mputstrn(func_avg_sorted_top10_str, "\n", 1);
1448 }
1449 }
1450 }
1451 if (!disable_coverage) {
1452 if (stats_flags & STATS_LINE_COUNT_SORTED_BY_MOD) {
1453 line_count_sorted_mod_str = mputstrn(line_count_sorted_mod_str, "\n", 1);
1454 }
1455 if (stats_flags & STATS_LINE_COUNT_SORTED_TOTAL) {
1456 line_count_sorted_tot_str = mputstrn(line_count_sorted_tot_str, "\n", 1);
1457 }
1458 if (stats_flags & STATS_FUNC_COUNT_SORTED_BY_MOD) {
1459 func_count_sorted_mod_str = mputstrn(func_count_sorted_mod_str, "\n", 1);
1460 }
1461 if (stats_flags & STATS_FUNC_COUNT_SORTED_TOTAL) {
1462 func_count_sorted_tot_str = mputstrn(func_count_sorted_tot_str, "\n", 1);
1463 }
1464 if (stats_flags & STATS_TOP10_LINE_COUNT) {
1465 line_count_sorted_top10_str = mputstrn(line_count_sorted_top10_str, "\n", 1);
1466 }
1467 if (stats_flags & STATS_TOP10_FUNC_COUNT) {
1468 func_count_sorted_top10_str = mputstrn(func_count_sorted_top10_str, "\n", 1);
1469 }
1470 if (stats_flags & STATS_UNUSED_LINES) {
1471 unused_lines_str = mputstrn(unused_lines_str, "\n", 1);
1472 }
1473 if (stats_flags & STATS_UNUSED_FUNC) {
1474 unused_func_str = mputstrn(unused_func_str, "\n", 1);
1475 }
af710487 1476 }
1477
1478 // write the statistics to the specified file
1479 FILE* file = fopen(stats_filename, "w");
1480 if (NULL == file) {
1481 TTCN_warning("Could not open file '%s' for writing. Profiler and/or code coverage "
1482 "statistics will not be saved.", stats_filename);
1483 return;
1484 }
a38c6d4c 1485 // by now the strings for all disabled statistics entries should be null
1486 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"
1487 , title_str
1488 , (NULL != line_func_count_str) ? line_func_count_str : ""
1489 , (NULL != line_data_str) ? line_data_str : ""
1490 , (NULL != line_avg_str) ? line_avg_str : ""
1491 , (NULL != func_data_str) ? func_data_str : ""
1492 , (NULL != func_avg_str) ? func_avg_str : ""
1493 , (NULL != line_time_sorted_mod_str) ? line_time_sorted_mod_str : ""
1494 , (NULL != line_time_sorted_tot_str) ? line_time_sorted_tot_str : ""
1495 , (NULL != func_time_sorted_mod_str) ? func_time_sorted_mod_str : ""
1496 , (NULL != func_time_sorted_tot_str) ? func_time_sorted_tot_str : ""
1497 , (NULL != line_count_sorted_mod_str) ? line_count_sorted_mod_str : ""
1498 , (NULL != line_count_sorted_tot_str) ? line_count_sorted_tot_str : ""
1499 , (NULL != func_count_sorted_mod_str) ? func_count_sorted_mod_str : ""
1500 , (NULL != func_count_sorted_tot_str) ? func_count_sorted_tot_str : ""
1501 , (NULL != line_avg_sorted_mod_str) ? line_avg_sorted_mod_str : ""
1502 , (NULL != line_avg_sorted_tot_str) ? line_avg_sorted_tot_str : ""
1503 , (NULL != func_avg_sorted_mod_str) ? func_avg_sorted_mod_str : ""
1504 , (NULL != func_avg_sorted_tot_str) ? func_avg_sorted_tot_str : ""
1505 , (NULL != line_time_sorted_top10_str) ? line_time_sorted_top10_str : ""
1506 , (NULL != func_time_sorted_top10_str) ? func_time_sorted_top10_str : ""
1507 , (NULL != line_count_sorted_top10_str) ? line_count_sorted_top10_str : ""
1508 , (NULL != func_count_sorted_top10_str) ? func_count_sorted_top10_str : ""
1509 , (NULL != line_avg_sorted_top10_str) ? line_avg_sorted_top10_str : ""
1510 , (NULL != func_avg_sorted_top10_str) ? func_avg_sorted_top10_str : ""
1511 , (NULL != unused_lines_str) ? unused_lines_str : ""
1512 , (NULL != unused_func_str) ? unused_func_str : "");
1513
af710487 1514 fclose(file);
a38c6d4c 1515
1516 // free the strings
1517 Free(title_str);
1518 Free(line_func_count_str);
1519 Free(line_data_str);
1520 Free(line_avg_str);
1521 Free(func_data_str);
1522 Free(func_avg_str);
1523 Free(line_time_sorted_mod_str);
1524 Free(line_time_sorted_tot_str);
1525 Free(func_time_sorted_mod_str);
1526 Free(func_time_sorted_tot_str);
1527 Free(line_count_sorted_mod_str);
1528 Free(line_count_sorted_tot_str);
1529 Free(func_count_sorted_mod_str);
1530 Free(func_count_sorted_tot_str);
1531 Free(line_avg_sorted_mod_str);
1532 Free(line_avg_sorted_tot_str);
1533 Free(func_avg_sorted_mod_str);
1534 Free(func_avg_sorted_tot_str);
1535 Free(line_time_sorted_top10_str);
1536 Free(func_time_sorted_top10_str);
1537 Free(line_count_sorted_top10_str);
1538 Free(func_count_sorted_top10_str);
1539 Free(line_avg_sorted_top10_str);
1540 Free(func_avg_sorted_top10_str);
1541 Free(unused_lines_str);
1542 Free(unused_func_str);
af710487 1543}
1544
1545void TTCN3_Profiler::reset()
1546{
a38c6d4c 1547 prev_time.tv_sec = 0;
1548 prev_time.tv_usec = 0;
af710487 1549 prev_file = NULL;
1550 prev_line = -1;
a38c6d4c 1551 prev_stack_len = -1;
af710487 1552}
1553
a38c6d4c 1554timeval TTCN3_Profiler::get_time()
af710487 1555{
1556 timeval tv;
1557 gettimeofday(&tv, NULL);
a38c6d4c 1558 return tv;
af710487 1559}
1560
a38c6d4c 1561void TTCN3_Profiler::enter_function(const char* filename, int lineno)
af710487 1562{
1563 if (disable_profiler && disable_coverage) {
1564 return;
1565 }
1566
1567 // Note that the execution time of the last line in a function
1568 // is measured by using the stack depth.
1569 execute_line(filename, lineno);
1570
a38c6d4c 1571 if (!stopped) {
1572 int element = get_element(filename);
1573
1574 // store function data
1575 if (!disable_coverage) {
1576 ++profiler_db[element].functions[get_function(element, lineno)].exec_count;
1577 }
af710487 1578 }
1579}
1580
1581void TTCN3_Profiler::execute_line(const char* filename, int lineno)
1582{
1583 if (disable_profiler && disable_coverage) {
1584 return;
1585 }
1586
a38c6d4c 1587 if (!disable_profiler && TTCN3_Stack_Depth::depth() > prev_stack_len) {
1588 // this line is in a different function than the last one, don't measure anything
1589 TTCN3_Stack_Depth::add_stack(prev_stack_len, prev_file, filename, prev_line, lineno);
1590 }
1591
1592 if (!stopped) {
1593 if (!disable_profiler && NULL != prev_file) {
1594 // this line is in the same function as the previous one, measure the time difference
1595 timeval elapsed = subtract_timeval(get_time(), prev_time);
af710487 1596
a38c6d4c 1597 // add the elapsed time to the total time of the previous line
af710487 1598 add_line_time(elapsed, get_element(prev_file), prev_line);
1599
1600 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
1601 }
a38c6d4c 1602
1603 // functions starting at line 0 are: pre_init_module and post_init_module,
1604 // don't include them in the database (as they don't appear in the TTCN-3 code),
1605 // but include any actual code lines they may contain
1606 // also, several instructions could be in the same line, only count the line once
1607 if (0 != lineno && !disable_coverage && (lineno != prev_line || NULL == prev_file ||
1608 0 != strcmp(prev_file, filename))) {
1609 int element = get_element(filename);
1610
1611 // increase line execution count
1612 ++profiler_db[element].lines[get_line(element, lineno)].exec_count;
1613 }
af710487 1614 }
1615
1616 // store the current location as previous for the next call
1617 set_prev(disable_profiler ? -1 : TTCN3_Stack_Depth::depth(), filename, lineno);
1618}
1619
1620int TTCN3_Profiler::get_element(const char* filename)
1621{
1622 for (size_t i = 0; i < profiler_db.size(); ++i) {
1623 if (0 == strcmp(profiler_db[i].filename, filename)) {
1624 return i;
1625 }
1626 }
1627
1628 profiler_db_item_t item;
1629 item.filename = mcopystr(filename);
1630 profiler_db.push_back(item);
1631 return profiler_db.size() - 1;
1632}
1633
1634int TTCN3_Profiler::get_function(int element, int lineno)
1635{
1636 for (size_t i = 0; i < profiler_db[element].functions.size(); ++i) {
1637 if (profiler_db[element].functions[i].lineno == lineno) {
1638 return i;
1639 }
1640 }
1641 return -1;
1642}
1643
1644void TTCN3_Profiler::create_function(int element, int lineno, const char* function_name)
1645{
1646 profiler_db_item_t::profiler_function_data_t func_data;
1647 func_data.lineno = lineno;
a38c6d4c 1648 func_data.total_time.tv_sec = 0;
1649 func_data.total_time.tv_usec = 0;
af710487 1650 func_data.exec_count = 0;
1651 func_data.name = mcopystr(function_name);
1652 profiler_db[element].functions.push_back(func_data);
1653}
1654
a38c6d4c 1655int TTCN3_Profiler::get_line(int element, int lineno)
af710487 1656{
a38c6d4c 1657 for (size_t i = 0; i < profiler_db[element].lines.size(); ++i) {
1658 if (profiler_db[element].lines[i].lineno == lineno) {
1659 return i;
af710487 1660 }
1661 }
a38c6d4c 1662 return -1;
1663}
1664
1665void TTCN3_Profiler::create_line(int element, int lineno)
1666{
1667 profiler_db_item_t::profiler_line_data_t line_data;
1668 line_data.lineno = lineno;
1669 line_data.total_time.tv_sec = 0;
1670 line_data.total_time.tv_usec = 0;
1671 line_data.exec_count = 0;
1672 profiler_db[element].lines.push_back(line_data);
af710487 1673}
1674
a38c6d4c 1675void TTCN3_Profiler::add_line_time(timeval elapsed, int element, int lineno)
af710487 1676{
a38c6d4c 1677 if (0 == lineno) {
af710487 1678 return;
1679 }
a38c6d4c 1680 profiler_db[element].lines[get_line(element, lineno)].total_time = add_timeval(
1681 profiler_db[element].lines[get_line(element, lineno)].total_time, elapsed);
af710487 1682}
1683
a38c6d4c 1684void TTCN3_Profiler::add_function_time(timeval elapsed, int element, int lineno)
af710487 1685{
1686 int func = get_function(element, lineno);
1687 if (-1 == func) {
1688 return;
1689 }
a38c6d4c 1690 profiler_db[element].functions[func].total_time = add_timeval(
1691 profiler_db[element].functions[func].total_time, elapsed);
af710487 1692}
1693
1694void TTCN3_Profiler::update_last()
1695{
a38c6d4c 1696 if (stopped || (0 == prev_time.tv_sec && 0 == prev_time.tv_usec) || NULL == prev_file) {
af710487 1697 return;
1698 }
a38c6d4c 1699
1700 timeval elapsed = subtract_timeval(get_time(), prev_time);
af710487 1701
1702 int element = get_element(prev_file);
1703
a38c6d4c 1704 // add the elapsed time to the total time of the previous line
af710487 1705 add_line_time(elapsed, element, prev_line);
1706 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
1707
a38c6d4c 1708 // reset measurement
1709 prev_time.tv_sec = 0;
1710 prev_time.tv_usec = 0;
af710487 1711}
1712
1713void TTCN3_Profiler::set_prev(int stack_len, const char* filename, int lineno)
1714{
1715 prev_file = filename;
1716 prev_line = lineno;
1717 if (!disable_profiler) {
1718 prev_time = get_time();
1719 prev_stack_len = stack_len;
1720 }
1721}
1722
1723/////////////////////////////////////
1724///////// TTCN3_Stack_Depth /////////
1725/////////////////////////////////////
1726
1727int TTCN3_Stack_Depth::current_depth = -1;
1728Vector<TTCN3_Stack_Depth::call_stack_timer_item_t> TTCN3_Stack_Depth::call_stack_timer_db;
a38c6d4c 1729boolean TTCN3_Stack_Depth::net_line_times = FALSE;
1730boolean TTCN3_Stack_Depth::net_func_times = FALSE;
af710487 1731
1732TTCN3_Stack_Depth::TTCN3_Stack_Depth()
1733{
1734 if (ttcn3_prof.is_profiler_disabled()) {
1735 return;
1736 }
1737 ++current_depth;
1738}
1739
1740TTCN3_Stack_Depth::~TTCN3_Stack_Depth()
1741{
1742 if (ttcn3_prof.is_profiler_disabled()) {
1743 return;
1744 }
1745 ttcn3_prof.update_last();
1746 remove_stack();
1747 if (0 == current_depth) {
1748 ttcn3_prof.reset();
1749 }
1750 --current_depth;
1751}
1752
a38c6d4c 1753void TTCN3_Stack_Depth::set_net_line_times(boolean p_net_line_times)
1754{
1755 net_line_times = p_net_line_times;
1756}
1757
1758void TTCN3_Stack_Depth::set_net_func_times(boolean p_net_func_times)
1759{
1760 net_func_times = p_net_func_times;
1761}
1762
af710487 1763void TTCN3_Stack_Depth::add_stack(int stack_len, const char* caller_file, const char* func_file,
1764 int caller_line, int start_line)
1765{
1766 call_stack_timer_item_t item;
1767 item.stack_len = stack_len;
1768 item.caller_file = caller_file;
1769 item.func_file = func_file;
1770 item.caller_line = caller_line;
1771 item.start_line = start_line;
a38c6d4c 1772 item.elapsed.tv_sec = 0;
1773 item.elapsed.tv_usec = 0;
1774 item.first_call = true;
1775 item.recursive_call = false;
1776
1777 if (!net_line_times || !net_func_times) {
1778 // check if it's a recursive function
1779 for (int i = current_depth - 1; i >= 0 ; --i) {
1780 if (call_stack_timer_db[i].start_line == start_line &&
1781 0 == strcmp(call_stack_timer_db[i].func_file, func_file)) {
1782 item.recursive_call = true;
1783
1784 // check if the caller is new
1785 if (call_stack_timer_db[i].caller_line == caller_line &&
1786 ((NULL == call_stack_timer_db[i].caller_file && NULL == caller_file) ||
1787 (NULL != call_stack_timer_db[i].caller_file && NULL != caller_file &&
1788 0 == strcmp(call_stack_timer_db[i].caller_file, caller_file)))) {
1789 item.first_call = false;
1790 break;
1791 }
1792 }
1793 }
1794 }
1795
af710487 1796 call_stack_timer_db.push_back(item);
1797}
1798
1799void TTCN3_Stack_Depth::remove_stack()
1800{
a38c6d4c 1801 // add the time gathered for this stack level to the appropriate line and function
1802 // except for functions starting at line 0 (pre_init_module and post_init_module)
1803 if (0 != call_stack_timer_db[current_depth].start_line) {
1804 timeval elapsed = call_stack_timer_db[current_depth].elapsed;
1805 if (!net_line_times && NULL != call_stack_timer_db[current_depth].caller_file &&
1806 call_stack_timer_db[current_depth].first_call) {
1807 // add the elapsed time to the caller line, if it exists
1808 // (only add it once for recursive functions, at the first call)
1809 ttcn3_prof.add_line_time(elapsed,
1810 ttcn3_prof.get_element(call_stack_timer_db[current_depth].caller_file),
1811 call_stack_timer_db[current_depth].caller_line);
1812 }
1813 if (!net_func_times && !call_stack_timer_db[current_depth].recursive_call) {
1814 // add the elapsed time to the called function, if it's not recursive
1815 // (in case of net function times this has already been done in update_stack_elapsed)
1816 ttcn3_prof.add_function_time(elapsed,
1817 ttcn3_prof.get_element(call_stack_timer_db[current_depth].func_file),
1818 call_stack_timer_db[current_depth].start_line);
1819 }
1820 }
af710487 1821
1822 ttcn3_prof.set_prev(call_stack_timer_db[current_depth].stack_len,
1823 call_stack_timer_db[current_depth].caller_file,
1824 call_stack_timer_db[current_depth].caller_line);
1825
1826 call_stack_timer_db.erase_at(current_depth);
1827}
1828
a38c6d4c 1829void TTCN3_Stack_Depth::update_stack_elapsed(timeval elapsed)
af710487 1830{
a38c6d4c 1831 // if function times are net times, only add the elapsed time to the current function
1832 if (net_func_times) {
1833 ttcn3_prof.add_function_time(elapsed,
1834 ttcn3_prof.get_element(call_stack_timer_db[current_depth].func_file),
1835 call_stack_timer_db[current_depth].start_line);
af710487 1836 }
a38c6d4c 1837 if (!net_line_times || !net_func_times) {
1838 // cycle through the stack and add the elapsed time to the entries where
1839 // the function/caller pair appears for the first time (marked by 'first_call')
1840 for(int i = 0; i <= current_depth; ++i) {
1841 if (call_stack_timer_db[i].first_call) {
1842 call_stack_timer_db[i].elapsed = add_timeval(call_stack_timer_db[i].elapsed, elapsed);
1843 }
1844 }
1845 }
1846}
This page took 0.103305 seconds and 5 git commands to generate.