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 ///////////////////////////////////////////////////////////////////////////////
14 #include "JSON_Tokenizer.hh"
18 ////////////////////////////////////
19 ////////// TTCN3_Profiler //////////
20 ////////////////////////////////////
22 TTCN3_Profiler ttcn3_prof
;
24 TTCN3_Profiler::TTCN3_Profiler()
25 : disable_profiler(FALSE
), disable_coverage(FALSE
), aggregate_data(FALSE
)
26 , disable_stats(FALSE
)
28 database_filename
= mcopystr("profiler.db");
29 stats_filename
= mcopystr("profiler.stats");
33 TTCN3_Profiler::~TTCN3_Profiler()
35 if (!disable_profiler
|| !disable_coverage
) {
38 if (!disable_stats
&& (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc())) {
42 for (size_t i
= 0; i
< profiler_db
.size(); ++i
) {
43 Free(profiler_db
[i
].filename
);
44 for (size_t j
= 0; j
< profiler_db
[i
].functions
.size(); ++j
) {
45 Free(profiler_db
[i
].functions
[j
].name
);
48 Free(database_filename
);
52 void TTCN3_Profiler::set_disable_profiler(boolean p_disable_profiler
)
54 disable_profiler
= p_disable_profiler
;
57 void TTCN3_Profiler::set_disable_coverage(boolean p_disable_coverage
)
59 disable_coverage
= p_disable_coverage
;
62 void TTCN3_Profiler::set_database_filename(const char* p_database_filename
)
64 Free(database_filename
);
65 database_filename
= mcopystr(p_database_filename
);
68 void TTCN3_Profiler::set_aggregate_data(boolean p_aggregate_data
)
70 aggregate_data
= p_aggregate_data
;
73 void TTCN3_Profiler::set_stats_filename(const char* p_stats_filename
)
76 stats_filename
= mcopystr(p_stats_filename
);
79 void TTCN3_Profiler::set_disable_stats(boolean p_disable_stats
)
81 disable_stats
= p_disable_stats
;
84 boolean
TTCN3_Profiler::is_profiler_disabled() const
86 return disable_profiler
;
89 #define IMPORT_FORMAT_ERROR(cond) \
91 TTCN_warning("Database format is invalid. Profiler and/or code coverage data will not be loaded."); \
95 void TTCN3_Profiler::init_data_file()
97 // delete the database file (from the previous run) if data aggregation is not set
98 if (!aggregate_data
&& (!disable_profiler
|| !disable_coverage
)) {
99 remove(database_filename
);
103 void TTCN3_Profiler::import_data()
105 // open the file, if it exists
106 FILE* file
= fopen(database_filename
, "r");
112 fseek(file
, 0, SEEK_END
);
113 int file_size
= ftell(file
);
116 // read the entire file into a character buffer
117 char* buffer
= (char*)Malloc(file_size
);
118 fread(buffer
, 1, file_size
, file
);
120 // initialize a JSON tokenizer with the buffer
121 JSON_Tokenizer
json(buffer
, file_size
);
124 // attempt to read tokens from the buffer
125 // if the format is invalid, abort the importing process
126 json_token_t token
= JSON_TOKEN_NONE
;
128 size_t value_len
= 0;
130 // start of main array
131 json
.get_next_token(&token
, NULL
, NULL
);
132 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
134 // read objects (one for each TTCN-3 file), until the main array end mark is reached
135 json
.get_next_token(&token
, NULL
, NULL
);
136 while (JSON_TOKEN_OBJECT_START
== token
) {
137 size_t file_index
= 0;
140 json
.get_next_token(&token
, &value
, &value_len
);
141 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 4 ||
142 0 != strncmp(value
, "file", value_len
));
144 // read the file name and see if its record already exists
145 json
.get_next_token(&token
, &value
, &value_len
);
146 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
);
147 for (file_index
= 0; file_index
< profiler_db
.size(); ++file_index
) {
148 if (strlen(profiler_db
[file_index
].filename
) == value_len
- 2 &&
149 0 == strncmp(profiler_db
[file_index
].filename
, value
+ 1, value_len
- 2)) {
154 // insert a new element if the file was not found
155 if (profiler_db
.size() == file_index
) {
156 profiler_db_item_t item
;
157 item
.filename
= mcopystrn(value
+ 1, value_len
- 2);
158 profiler_db
.push_back(item
);
162 json
.get_next_token(&token
, &value
, &value_len
);
163 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 9 ||
164 0 != strncmp(value
, "functions", value_len
));
166 // read and store the functions (an array of objects, same as before)
167 json
.get_next_token(&token
, NULL
, NULL
);
168 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
169 json
.get_next_token(&token
, NULL
, NULL
);
170 while (JSON_TOKEN_OBJECT_START
== token
) {
171 size_t function_index
= 0;
174 json
.get_next_token(&token
, &value
, &value_len
);
175 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 4 ||
176 0 != strncmp(value
, "name", value_len
));
178 // read the function name, it will be checked later
179 json
.get_next_token(&token
, &value
, &value_len
);
180 IMPORT_FORMAT_ERROR(JSON_TOKEN_STRING
!= token
);
181 char* function_name
= mcopystrn(value
+ 1, value_len
- 2);
183 // function start line:
184 json
.get_next_token(&token
, &value
, &value_len
);
185 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
186 0 != strncmp(value
, "start line", value_len
));
188 // read the start line and check if the function already exists
189 json
.get_next_token(&token
, &value
, &value_len
);
190 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
191 int start_line
= atoi(value
);
192 for (function_index
= 0; function_index
< profiler_db
[file_index
].functions
.size(); ++function_index
) {
193 if (profiler_db
[file_index
].functions
[function_index
].lineno
== start_line
&&
194 0 == strcmp(profiler_db
[file_index
].functions
[function_index
].name
, function_name
)) {
199 // insert a new element if the function was not found
200 if (profiler_db
[file_index
].functions
.size() == function_index
) {
201 profiler_db_item_t::profiler_function_data_t func_data
;
202 func_data
.name
= function_name
;
203 func_data
.lineno
= start_line
;
204 func_data
.exec_count
= 0;
205 func_data
.total_time
= 0.0;
206 profiler_db
[file_index
].functions
.push_back(func_data
);
209 if (!disable_coverage
) {
210 // function execution count:
211 json
.get_next_token(&token
, &value
, &value_len
);
212 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 15 ||
213 0 != strncmp(value
, "execution count", value_len
));
215 // read the execution count and add it to the current data
216 json
.get_next_token(&token
, &value
, &value_len
);
217 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
218 profiler_db
[file_index
].functions
[function_index
].exec_count
+= atoi(value
);
221 if (!disable_profiler
) {
222 // total function execution time:
223 json
.get_next_token(&token
, &value
, &value_len
);
224 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
225 0 != strncmp(value
, "total time", value_len
));
227 // read the total time and add it to the current data
228 json
.get_next_token(&token
, &value
, &value_len
);
229 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
230 profiler_db
[file_index
].functions
[function_index
].total_time
+= atof(value
);
233 // end of the function's object
234 json
.get_next_token(&token
, NULL
, NULL
);
235 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
237 // read the next token (either the start of another object or the function array end)
238 json
.get_next_token(&token
, NULL
, NULL
);
241 // function array end
242 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
245 json
.get_next_token(&token
, &value
, &value_len
);
246 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 5 ||
247 0 != strncmp(value
, "lines", value_len
));
249 // read and store the lines (an array of objects, same as before)
250 json
.get_next_token(&token
, NULL
, NULL
);
251 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_START
!= token
);
252 json
.get_next_token(&token
, NULL
, NULL
);
253 while (JSON_TOKEN_OBJECT_START
== token
) {
256 json
.get_next_token(&token
, &value
, &value_len
);
257 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 6 ||
258 0 != strncmp(value
, "number", value_len
));
260 // read the line number and check if the line already exists
261 json
.get_next_token(&token
, &value
, &value_len
);
262 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
263 int lineno
= atoi(value
);
264 IMPORT_FORMAT_ERROR(lineno
< 0);
265 size_t line_no
= lineno
;
266 if (line_no
>= profiler_db
[file_index
].lines
.size()) {
267 for (size_t i
= profiler_db
[file_index
].lines
.size(); i
<= line_no
; ++i
) {
268 profiler_db_item_t::profiler_line_data_t line_data
;
269 line_data
.total_time
= 0.0;
270 line_data
.exec_count
= 0;
271 profiler_db
[file_index
].lines
.push_back(line_data
);
275 if (!disable_coverage
) {
276 // line execution count:
277 json
.get_next_token(&token
, &value
, &value_len
);
278 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 15 ||
279 0 != strncmp(value
, "execution count", value_len
));
281 // read the execution count and add it to the current data
282 json
.get_next_token(&token
, &value
, &value_len
);
283 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
284 profiler_db
[file_index
].lines
[line_no
].exec_count
+= atoi(value
);
287 if (!disable_profiler
) {
288 // total line execution time:
289 json
.get_next_token(&token
, &value
, &value_len
);
290 IMPORT_FORMAT_ERROR(JSON_TOKEN_NAME
!= token
|| value_len
!= 10 ||
291 0 != strncmp(value
, "total time", value_len
));
293 // read the total time and add it to the current data
294 json
.get_next_token(&token
, &value
, &value_len
);
295 IMPORT_FORMAT_ERROR(JSON_TOKEN_NUMBER
!= token
);
296 profiler_db
[file_index
].lines
[line_no
].total_time
+= atof(value
);
299 // end of the line's object
300 json
.get_next_token(&token
, NULL
, NULL
);
301 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
303 // read the next token (either the start of another object or the line array end)
304 json
.get_next_token(&token
, NULL
, NULL
);
308 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
310 // end of the file's object
311 json
.get_next_token(&token
, NULL
, NULL
);
312 IMPORT_FORMAT_ERROR(JSON_TOKEN_OBJECT_END
!= token
);
314 // read the next token (either the start of another object or the main array end)
315 json
.get_next_token(&token
, NULL
, NULL
);
319 IMPORT_FORMAT_ERROR(JSON_TOKEN_ARRAY_END
!= token
);
322 void TTCN3_Profiler::export_data()
324 // nothing to export if the database is empty
325 if (profiler_db
.empty()) {
329 // check whether the file can be opened for writing
330 FILE* file
= fopen(database_filename
, "w");
332 TTCN_warning("Could not open file '%s' for writing. Profiler and/or code coverage "
333 "data will not be saved.", database_filename
);
337 // use the JSON tokenizer to create a JSON document from the database
338 JSON_Tokenizer
json(true);
340 // main array, contains an element for each file
341 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
342 for (size_t i
= 0; i
< profiler_db
.size(); ++i
) {
344 // each file's data is stored in an object
345 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
347 // store the file name
348 json
.put_next_token(JSON_TOKEN_NAME
, "file");
349 char* file_name_str
= mprintf("\"%s\"", profiler_db
[i
].filename
);
350 json
.put_next_token(JSON_TOKEN_STRING
, file_name_str
);
353 // store the function data in an array (one element for each function)
354 json
.put_next_token(JSON_TOKEN_NAME
, "functions");
355 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
356 for (size_t j
= 0; j
< profiler_db
[i
].functions
.size(); ++j
) {
358 // the data is stored in an object for each function
359 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
361 // store the function name
362 json
.put_next_token(JSON_TOKEN_NAME
, "name");
363 char* func_name_str
= mprintf("\"%s\"", profiler_db
[i
].functions
[j
].name
);
364 json
.put_next_token(JSON_TOKEN_STRING
, func_name_str
);
367 // store the function start line
368 json
.put_next_token(JSON_TOKEN_NAME
, "start line");
369 char* start_line_str
= mprintf("%d", profiler_db
[i
].functions
[j
].lineno
);
370 json
.put_next_token(JSON_TOKEN_NUMBER
, start_line_str
);
371 Free(start_line_str
);
373 if (!disable_coverage
) {
374 // store the function execution count
375 json
.put_next_token(JSON_TOKEN_NAME
, "execution count");
376 char* exec_count_str
= mprintf("%d", profiler_db
[i
].functions
[j
].exec_count
);
377 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
378 Free(exec_count_str
);
381 if (!disable_profiler
) {
382 // store the function's total execution time
383 json
.put_next_token(JSON_TOKEN_NAME
, "total time");
384 char* exec_count_str
= mprintf("%.6lf", profiler_db
[i
].functions
[j
].total_time
);
385 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
386 Free(exec_count_str
);
389 // end of function object
390 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
393 // end of function data array
394 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
396 // store the line data in an array (one element for each line with useful data)
397 json
.put_next_token(JSON_TOKEN_NAME
, "lines");
398 json
.put_next_token(JSON_TOKEN_ARRAY_START
, NULL
);
399 for (size_t j
= 0; j
< profiler_db
[i
].lines
.size(); ++j
) {
400 if (0.0 != profiler_db
[i
].lines
[j
].total_time
||
401 0 != profiler_db
[i
].lines
[j
].exec_count
) {
403 // store line data in an object
404 json
.put_next_token(JSON_TOKEN_OBJECT_START
, NULL
);
406 // store the line number
407 json
.put_next_token(JSON_TOKEN_NAME
, "number");
408 char* line_number_str
= mprintf("%lu", j
);
409 json
.put_next_token(JSON_TOKEN_NUMBER
, line_number_str
);
410 Free(line_number_str
);
412 if (!disable_coverage
) {
413 // store the line execution count
414 json
.put_next_token(JSON_TOKEN_NAME
, "execution count");
415 char* exec_count_str
= mprintf("%d", profiler_db
[i
].lines
[j
].exec_count
);
416 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
417 Free(exec_count_str
);
420 if (!disable_profiler
) {
421 // store the line's total execution time
422 json
.put_next_token(JSON_TOKEN_NAME
, "total time");
423 char* exec_count_str
= mprintf("%.6lf", profiler_db
[i
].lines
[j
].total_time
);
424 json
.put_next_token(JSON_TOKEN_NUMBER
, exec_count_str
);
425 Free(exec_count_str
);
428 // end of this line's object
429 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
433 // end of line data array
434 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
436 // end of this file's object
437 json
.put_next_token(JSON_TOKEN_OBJECT_END
, NULL
);
441 json
.put_next_token(JSON_TOKEN_ARRAY_END
, NULL
);
443 // write the JSON document into the file
444 fprintf(file
, "%s\n", json
.get_buffer());
448 void TTCN3_Profiler::print_stats()
450 if (profiler_db
.empty()) {
455 char* title_str
= mprintf(
456 "##################################################\n"
457 "%s## TTCN-3 %s%s%sstatistics ##%s\n"
458 "##################################################\n\n\n"
459 , disable_profiler
? "#######" : (disable_coverage
? "#########" : "")
460 , disable_profiler
? "" : "profiler "
461 , (disable_profiler
|| disable_coverage
) ? "" : "and "
462 , disable_coverage
? "" : "code coverage "
463 , disable_profiler
? "######" : (disable_coverage
? "#########" : ""));
466 char* line_data_str
= mprintf(
467 "-------------------------------------------------\n"
468 "%s- Code line data (%s%s%s) -%s\n"
469 "-------------------------------------------------\n"
470 , disable_profiler
? "-------" : (disable_coverage
? "---------" : "")
471 , disable_profiler
? "" : "total time"
472 , (disable_profiler
|| disable_coverage
) ? "" : " / "
473 , disable_coverage
? "" : "execution count"
474 , disable_profiler
? "------" : (disable_coverage
? "---------" : ""));
476 // average time / exec count for lines
477 char* line_avg_str
= NULL
;
478 if (!disable_coverage
&& !disable_profiler
) {
479 line_avg_str
= mcopystr(
480 "-------------------------------------------------\n"
481 "- Average time / execution count for code lines -\n"
482 "-------------------------------------------------\n");
486 char* func_data_str
= mprintf(
487 "------------------------------------------------\n"
488 "%s- Function data (%s%s%s) -%s\n"
489 "------------------------------------------------\n"
490 , disable_profiler
? "-------" : (disable_coverage
? "---------" : "")
491 , disable_profiler
? "" : "total time"
492 , (disable_profiler
|| disable_coverage
) ? "" : " / "
493 , disable_coverage
? "" : "execution count"
494 , disable_profiler
? "------" : (disable_coverage
? "---------" : ""));
496 // average time / exec count for functions
497 char* func_avg_str
= NULL
;
498 if (!disable_coverage
&& !disable_profiler
) {
499 func_avg_str
= mcopystr(
500 "------------------------------------------------\n"
501 "- Average time / execution count for functions -\n"
502 "------------------------------------------------\n");
505 // cycle through the database and gather the necessary data
506 for (size_t i
= 0; i
< profiler_db
.size(); ++i
) {
508 // add separators between files
509 line_data_str
= mputstr(line_data_str
, "-------------------------------------------------\n");
510 func_data_str
= mputstr(func_data_str
, "------------------------------------------------\n");
511 if (!disable_profiler
&& !disable_coverage
) {
512 line_avg_str
= mputstr(line_avg_str
, "-------------------------------------------------\n");
513 func_avg_str
= mputstr(func_avg_str
, "------------------------------------------------\n");
518 for (size_t j
= 0; j
< profiler_db
[i
].lines
.size(); ++j
) {
519 if (0.0 != profiler_db
[i
].lines
[j
].total_time
||
520 0 != profiler_db
[i
].lines
[j
].exec_count
) {
521 if (!disable_profiler
) {
522 line_data_str
= mputprintf(line_data_str
, "%.6lfs", profiler_db
[i
].lines
[j
].total_time
);
523 if (!disable_coverage
) {
524 line_data_str
= mputstrn(line_data_str
, "\t/\t", 3);
525 line_avg_str
= mputprintf(line_avg_str
, "%.6lfs",
526 profiler_db
[i
].lines
[j
].total_time
/ profiler_db
[i
].lines
[j
].exec_count
);
529 if (!disable_coverage
) {
530 line_data_str
= mputprintf(line_data_str
, "%d", profiler_db
[i
].lines
[j
].exec_count
);
533 // line specification (including function name for the function's start line)
534 char* line_spec_str
= mprintf("\t%s:%lu", profiler_db
[i
].filename
, j
);
535 int func
= get_function(i
, j
);
537 line_spec_str
= mputprintf(line_spec_str
, " [%s]", profiler_db
[i
].functions
[func
].name
);
539 line_spec_str
= mputstrn(line_spec_str
, "\n", 1);
541 // add the line spec string to the other strings
542 line_data_str
= mputstr(line_data_str
, line_spec_str
);
543 if (!disable_profiler
&& !disable_coverage
) {
544 line_avg_str
= mputstr(line_avg_str
, line_spec_str
);
550 for (size_t j
= 0; j
< profiler_db
[i
].functions
.size(); ++j
) {
551 if (!disable_profiler
) {
552 func_data_str
= mputprintf(func_data_str
, "%.6lfs", profiler_db
[i
].functions
[j
].total_time
);
553 if (!disable_coverage
) {
554 func_data_str
= mputstrn(func_data_str
, "\t/\t", 3);
555 func_avg_str
= mputprintf(func_avg_str
, "%.6lfs",
556 profiler_db
[i
].functions
[j
].total_time
/ profiler_db
[i
].functions
[j
].exec_count
);
559 if (!disable_coverage
) {
560 func_data_str
= mputprintf(func_data_str
, "%d", profiler_db
[i
].functions
[j
].exec_count
);
563 // functions specification
564 char* func_spec_str
= mprintf("\t%s:%d [%s]\n", profiler_db
[i
].filename
,
565 profiler_db
[i
].functions
[j
].lineno
, profiler_db
[i
].functions
[j
].name
);
567 // add the line spec string to the other strings
568 func_data_str
= mputstr(func_data_str
, func_spec_str
);
569 if (!disable_profiler
&& !disable_coverage
) {
570 func_avg_str
= mputstr(func_avg_str
, func_spec_str
);
575 // add new lines at the end of each segment
576 line_data_str
= mputstrn(line_data_str
, "\n", 1);
577 func_data_str
= mputstrn(func_data_str
, "\n", 1);
578 if (!disable_profiler
&& !disable_coverage
) {
579 line_avg_str
= mputstrn(line_avg_str
, "\n", 1);
580 func_avg_str
= mputstrn(func_avg_str
, "\n", 1);
583 // write the statistics to the specified file
584 FILE* file
= fopen(stats_filename
, "w");
586 TTCN_warning("Could not open file '%s' for writing. Profiler and/or code coverage "
587 "statistics will not be saved.", stats_filename
);
590 fprintf(file
, "%s%s%s%s%s"
591 , title_str
, line_data_str
592 , (disable_profiler
|| disable_coverage
) ? "" : line_avg_str
593 , func_data_str
, (disable_profiler
|| disable_coverage
) ? "" : func_avg_str
);
597 void TTCN3_Profiler::reset()
605 double TTCN3_Profiler::get_time()
608 gettimeofday(&tv
, NULL
);
609 return tv
.tv_sec
+ tv
.tv_usec
/ 1000000.0;
612 void TTCN3_Profiler::enter_function(const char* filename
, int lineno
, const char* function_name
)
614 if (disable_profiler
&& disable_coverage
) {
618 // Note that the execution time of the last line in a function
619 // is measured by using the stack depth.
620 execute_line(filename
, lineno
);
622 int element
= get_element(filename
);
624 // store function data
625 int func
= get_function(element
, lineno
);
627 create_function(element
, lineno
, function_name
);
628 func
= profiler_db
[element
].functions
.size() - 1;
631 if (!disable_coverage
) {
632 ++profiler_db
[element
].functions
[func
].exec_count
;
636 void TTCN3_Profiler::execute_line(const char* filename
, int lineno
)
638 if (disable_profiler
&& disable_coverage
) {
642 if (!disable_profiler
) {
643 double currentTime
= get_time();
645 // prev line should not be measured, because it is still running: we are in longer stack level
646 if (0.0 == prev_time
|| TTCN3_Stack_Depth::depth() > prev_stack_len
) {
647 // add prev timer to call stack:
648 TTCN3_Stack_Depth::add_stack(prev_stack_len
, prev_file
, filename
, prev_line
, lineno
);
651 // if stack level is the same or higher: current line should be measured:
652 double elapsed
= currentTime
- prev_time
;
654 // add the elapsed time to the time of the previous line:
655 add_line_time(elapsed
, get_element(prev_file
), prev_line
);
657 TTCN3_Stack_Depth::update_stack_elapsed(elapsed
);
661 // several instructions could be in the same line, only count the line once
662 if (!disable_coverage
&& (lineno
!= prev_line
|| NULL
== prev_file
||
663 0 != strcmp(prev_file
, filename
))) {
664 int element
= get_element(filename
);
666 // make sure the line exists in the database
667 create_lines(element
, lineno
);
669 // increase line execution count
670 ++profiler_db
[element
].lines
[lineno
].exec_count
;
673 // store the current location as previous for the next call
674 set_prev(disable_profiler
? -1 : TTCN3_Stack_Depth::depth(), filename
, lineno
);
677 int TTCN3_Profiler::get_element(const char* filename
)
679 for (size_t i
= 0; i
< profiler_db
.size(); ++i
) {
680 if (0 == strcmp(profiler_db
[i
].filename
, filename
)) {
685 profiler_db_item_t item
;
686 item
.filename
= mcopystr(filename
);
687 profiler_db
.push_back(item
);
688 return profiler_db
.size() - 1;
691 int TTCN3_Profiler::get_function(int element
, int lineno
)
693 for (size_t i
= 0; i
< profiler_db
[element
].functions
.size(); ++i
) {
694 if (profiler_db
[element
].functions
[i
].lineno
== lineno
) {
701 void TTCN3_Profiler::create_function(int element
, int lineno
, const char* function_name
)
703 profiler_db_item_t::profiler_function_data_t func_data
;
704 func_data
.lineno
= lineno
;
705 func_data
.total_time
= 0.0;
706 func_data
.exec_count
= 0;
707 func_data
.name
= mcopystr(function_name
);
708 profiler_db
[element
].functions
.push_back(func_data
);
711 void TTCN3_Profiler::create_lines(int element
, int lineno
)
713 // set 0 for the unknown lines
714 size_t line_no
= lineno
;
715 if (profiler_db
[element
].lines
.size() <= line_no
) {
716 for (size_t i
= profiler_db
[element
].lines
.size(); i
<= line_no
; ++i
) {
717 profiler_db_item_t::profiler_line_data_t line_data
;
718 line_data
.total_time
= 0.0;
719 line_data
.exec_count
= 0;
720 profiler_db
[element
].lines
.push_back(line_data
);
725 void TTCN3_Profiler::add_line_time(double elapsed
, int element
, int lineno
)
731 // ensure the line data exists
732 create_lines(element
, lineno
);
734 // increase the time of the line in the current file:
735 profiler_db
[element
].lines
[lineno
].total_time
+= elapsed
;
738 void TTCN3_Profiler::add_function_time(double elapsed
, int element
, int lineno
)
740 int func
= get_function(element
, lineno
);
744 profiler_db
[element
].functions
[func
].total_time
+= elapsed
;
747 void TTCN3_Profiler::update_last()
749 if (0.0 == prev_time
) {
753 double currentTime
= get_time();
754 double elapsed
= currentTime
- prev_time
;
756 int element
= get_element(prev_file
);
758 // add the elapsed time to the time of the previous line:
759 add_line_time(elapsed
, element
, prev_line
);
760 TTCN3_Stack_Depth::update_stack_elapsed(elapsed
);
762 // reset measurement:
766 void TTCN3_Profiler::set_prev(int stack_len
, const char* filename
, int lineno
)
768 prev_file
= filename
;
770 if (!disable_profiler
) {
771 prev_time
= get_time();
772 prev_stack_len
= stack_len
;
776 /////////////////////////////////////
777 ///////// TTCN3_Stack_Depth /////////
778 /////////////////////////////////////
780 int TTCN3_Stack_Depth::current_depth
= -1;
781 Vector
<TTCN3_Stack_Depth::call_stack_timer_item_t
> TTCN3_Stack_Depth::call_stack_timer_db
;
783 TTCN3_Stack_Depth::TTCN3_Stack_Depth()
785 if (ttcn3_prof
.is_profiler_disabled()) {
791 TTCN3_Stack_Depth::~TTCN3_Stack_Depth()
793 if (ttcn3_prof
.is_profiler_disabled()) {
796 ttcn3_prof
.update_last();
798 if (0 == current_depth
) {
804 void TTCN3_Stack_Depth::add_stack(int stack_len
, const char* caller_file
, const char* func_file
,
805 int caller_line
, int start_line
)
807 call_stack_timer_item_t item
;
808 item
.stack_len
= stack_len
;
809 item
.caller_file
= caller_file
;
810 item
.func_file
= func_file
;
811 item
.caller_line
= caller_line
;
812 item
.start_line
= start_line
;
814 call_stack_timer_db
.push_back(item
);
817 void TTCN3_Stack_Depth::remove_stack()
819 // if stack level is the same or higher: measure the time:
820 double elapsed
= call_stack_timer_db
[current_depth
].elapsed
;
822 int element
= ttcn3_prof
.get_element(call_stack_timer_db
[current_depth
].func_file
);
824 // add elapsed time to the total execution time of the previous line:
825 ttcn3_prof
.add_function_time(elapsed
, element
, call_stack_timer_db
[current_depth
].start_line
);
827 ttcn3_prof
.set_prev(call_stack_timer_db
[current_depth
].stack_len
,
828 call_stack_timer_db
[current_depth
].caller_file
,
829 call_stack_timer_db
[current_depth
].caller_line
);
831 call_stack_timer_db
.erase_at(current_depth
);
834 void TTCN3_Stack_Depth::update_stack_elapsed(double elapsed
)
836 for(int i
= 0; i
<= current_depth
; i
++) {
837 call_stack_timer_db
[i
].elapsed
+= elapsed
;