Merge pull request #78 from balaskoa/master
[deliverable/titan.core.git] / core / Profiler.cc
CommitLineData
d44e3c4f 1/******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Balasko, Jeno
10 * Baranyi, Botond
11 *
12 ******************************************************************************/
af710487 13
14#include "Profiler.hh"
af710487 15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
3abe9331 18#include <unistd.h>
19#include <pwd.h>
d44e3c4f 20#include <sys/wait.h>
af710487 21#include "memory.h"
22#include "Runtime.hh"
d44e3c4f 23#include "Logger.hh"
a38c6d4c 24
af710487 25////////////////////////////////////
26////////// TTCN3_Profiler //////////
27////////////////////////////////////
28
29TTCN3_Profiler ttcn3_prof;
30
31TTCN3_Profiler::TTCN3_Profiler()
a38c6d4c 32: stopped(FALSE), disable_profiler(FALSE), disable_coverage(FALSE)
3abe9331 33, aggregate_data(FALSE), disable_stats(FALSE), stats_flags(Profiler_Tools::STATS_ALL)
af710487 34{
35 database_filename = mcopystr("profiler.db");
36 stats_filename = mcopystr("profiler.stats");
37 reset();
38}
39
40TTCN3_Profiler::~TTCN3_Profiler()
41{
3abe9331 42 if (!profiler_db.empty() && !TTCN_Runtime::is_undefined() &&
43 (!disable_profiler || !disable_coverage)) {
a38c6d4c 44 if (aggregate_data && (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc())) {
45 // import the data from the previous run
46 import_data();
47 }
48 if (TTCN_Runtime::is_hc()) {
49 // import the data gathered by the other processes (the import function
50 // waits for them to finish exporting)
d44e3c4f 51 for (size_t i = 0; i < pid_list.size(); ++i) {
52 import_data(pid_list[i]);
a38c6d4c 53 }
54 }
af710487 55 export_data();
56 if (!disable_stats && (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc())) {
57 print_stats();
58 }
59 }
60 for (size_t i = 0; i < profiler_db.size(); ++i) {
61 Free(profiler_db[i].filename);
62 for (size_t j = 0; j < profiler_db[i].functions.size(); ++j) {
63 Free(profiler_db[i].functions[j].name);
64 }
65 }
66 Free(database_filename);
67 Free(stats_filename);
68}
69
a38c6d4c 70void TTCN3_Profiler::start()
71{
72 if (stopped) {
73 set_prev(disable_profiler ? -1 : TTCN3_Stack_Depth::depth(), NULL, -1);
74 stopped = FALSE;
75 }
76}
77
78void TTCN3_Profiler::stop()
79{
80 if (!stopped) {
81 if (NULL != prev_file) {
82 // update the previous line's time
3abe9331 83 timeval elapsed = Profiler_Tools::subtract_timeval(get_time(), prev_time);
a38c6d4c 84 add_line_time(elapsed, get_element(prev_file), prev_line);
85 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
86 }
87 stopped = TRUE;
88 }
89}
90
af710487 91void TTCN3_Profiler::set_disable_profiler(boolean p_disable_profiler)
92{
93 disable_profiler = p_disable_profiler;
94}
95
96void TTCN3_Profiler::set_disable_coverage(boolean p_disable_coverage)
97{
98 disable_coverage = p_disable_coverage;
99}
100
3abe9331 101// handles metacharacters in the database or statistics file name
102static char* finalize_filename(const char* p_filename_skeleton)
103{
104 size_t len = strlen(p_filename_skeleton);
105 size_t next_idx = 0;
106 char* ret_val = NULL;
107 for (size_t i = 0; i < len - 1; ++i) {
108 if ('%' == p_filename_skeleton[i]) {
109 ret_val = mputstrn(ret_val, p_filename_skeleton + next_idx, i - next_idx);
110 switch (p_filename_skeleton[i + 1]) {
111 case 'e': // %e -> executable name
112 ret_val = mputstr(ret_val, TTCN_Logger::get_executable_name());
113 break;
114 case 'h': // %h -> host name
115 ret_val = mputstr(ret_val, TTCN_Runtime::get_host_name());
116 break;
117 case 'p': // %p -> process ID
118 ret_val = mputprintf(ret_val, "%ld", (long)getpid());
119 break;
120 case 'l': { // %l -> login name
121 setpwent();
122 struct passwd *p = getpwuid(getuid());
123 if (NULL != p) {
124 ret_val = mputstr(ret_val, p->pw_name);
125 }
126 endpwent();
127 break; }
128 case '%': // %% -> single %
129 ret_val = mputc(ret_val, '%');
130 break;
131 default: // unknown sequence -> leave it as it is
132 ret_val = mputstrn(ret_val, p_filename_skeleton + i, 2);
133 break;
134 }
135 next_idx = i + 2;
136 ++i;
137 }
138 }
139 if (next_idx < len) {
140 ret_val = mputstr(ret_val, p_filename_skeleton + next_idx);
141 }
142 return ret_val;
143}
144
af710487 145void TTCN3_Profiler::set_database_filename(const char* p_database_filename)
146{
147 Free(database_filename);
3abe9331 148 database_filename = finalize_filename(p_database_filename);
af710487 149}
150
151void TTCN3_Profiler::set_aggregate_data(boolean p_aggregate_data)
152{
153 aggregate_data = p_aggregate_data;
154}
155
156void TTCN3_Profiler::set_stats_filename(const char* p_stats_filename)
157{
158 Free(stats_filename);
3abe9331 159 stats_filename = finalize_filename(p_stats_filename);
af710487 160}
161
162void TTCN3_Profiler::set_disable_stats(boolean p_disable_stats)
163{
164 disable_stats = p_disable_stats;
165}
166
a38c6d4c 167void TTCN3_Profiler::reset_stats_flags()
168{
169 stats_flags = 0;
170}
171
172void TTCN3_Profiler::add_stats_flags(unsigned int p_flags)
173{
174 stats_flags |= p_flags;
175}
176
af710487 177boolean TTCN3_Profiler::is_profiler_disabled() const
178{
179 return disable_profiler;
180}
181
a38c6d4c 182boolean TTCN3_Profiler::is_running() const
183{
184 return !stopped;
185}
186
d44e3c4f 187void TTCN3_Profiler::add_child_process(pid_t p_pid)
a38c6d4c 188{
d44e3c4f 189 pid_list.push_back(p_pid);
a38c6d4c 190}
191
d44e3c4f 192void TTCN3_Profiler::import_data(pid_t p_pid /* = 0 */)
af710487 193{
a38c6d4c 194 char* file_name = NULL;
d44e3c4f 195 if (0 == p_pid) {
a38c6d4c 196 // this is the main database file (from the previous run), no suffix needed
197 file_name = database_filename;
af710487 198 }
a38c6d4c 199 else {
d44e3c4f 200 // this is the database for one of the PTCs or the MTC,
201 // suffix the file name with the component's PID
202 file_name = mprintf("%s.%d", database_filename, p_pid);
203
204 // wait for the process to finish
205 int status = 0;
206 waitpid(p_pid, &status, 0);
a38c6d4c 207 }
208
d44e3c4f 209 Profiler_Tools::import_data(profiler_db, file_name, TTCN_warning);
a38c6d4c 210
d44e3c4f 211 if (0 != p_pid) {
a38c6d4c 212 // the process-specific database file is no longer needed
213 remove(file_name);
214 Free(file_name);
215 }
af710487 216}
217
218void TTCN3_Profiler::export_data()
219{
a38c6d4c 220 char* file_name = NULL;
221 if (TTCN_Runtime::is_single() || TTCN_Runtime::is_hc()) {
222 // this is the main database file, no suffix needed
223 file_name = database_filename;
224 }
a38c6d4c 225 else {
d44e3c4f 226 // this is the database for one of the PTCs or the MTC,
227 // suffix the file name with the component's PID
228 file_name = mprintf("%s.%d", database_filename, (int)getpid());
af710487 229 }
230
3abe9331 231 Profiler_Tools::export_data(profiler_db, file_name, disable_profiler,
232 disable_coverage, TTCN_warning);
af710487 233
a38c6d4c 234 if (file_name != database_filename) {
235 Free(file_name);
236 }
a38c6d4c 237}
238
af710487 239void TTCN3_Profiler::print_stats()
240{
241 if (profiler_db.empty()) {
242 return;
243 }
244
3abe9331 245 Profiler_Tools::print_stats(profiler_db, stats_filename, disable_profiler,
246 disable_coverage, stats_flags, TTCN_warning);
af710487 247}
248
249void TTCN3_Profiler::reset()
250{
a38c6d4c 251 prev_time.tv_sec = 0;
252 prev_time.tv_usec = 0;
af710487 253 prev_file = NULL;
254 prev_line = -1;
a38c6d4c 255 prev_stack_len = -1;
af710487 256}
257
a38c6d4c 258timeval TTCN3_Profiler::get_time()
af710487 259{
260 timeval tv;
261 gettimeofday(&tv, NULL);
a38c6d4c 262 return tv;
af710487 263}
264
a38c6d4c 265void TTCN3_Profiler::enter_function(const char* filename, int lineno)
af710487 266{
267 if (disable_profiler && disable_coverage) {
268 return;
269 }
270
271 // Note that the execution time of the last line in a function
272 // is measured by using the stack depth.
273 execute_line(filename, lineno);
274
a38c6d4c 275 if (!stopped) {
276 int element = get_element(filename);
277
278 // store function data
279 if (!disable_coverage) {
280 ++profiler_db[element].functions[get_function(element, lineno)].exec_count;
281 }
af710487 282 }
283}
284
285void TTCN3_Profiler::execute_line(const char* filename, int lineno)
286{
287 if (disable_profiler && disable_coverage) {
288 return;
289 }
290
a38c6d4c 291 if (!disable_profiler && TTCN3_Stack_Depth::depth() > prev_stack_len) {
292 // this line is in a different function than the last one, don't measure anything
293 TTCN3_Stack_Depth::add_stack(prev_stack_len, prev_file, filename, prev_line, lineno);
294 }
295
296 if (!stopped) {
297 if (!disable_profiler && NULL != prev_file) {
298 // this line is in the same function as the previous one, measure the time difference
3abe9331 299 timeval elapsed = Profiler_Tools::subtract_timeval(get_time(), prev_time);
af710487 300
a38c6d4c 301 // add the elapsed time to the total time of the previous line
af710487 302 add_line_time(elapsed, get_element(prev_file), prev_line);
303
304 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
305 }
a38c6d4c 306
307 // functions starting at line 0 are: pre_init_module and post_init_module,
308 // don't include them in the database (as they don't appear in the TTCN-3 code),
309 // but include any actual code lines they may contain
310 // also, several instructions could be in the same line, only count the line once
311 if (0 != lineno && !disable_coverage && (lineno != prev_line || NULL == prev_file ||
312 0 != strcmp(prev_file, filename))) {
313 int element = get_element(filename);
314
315 // increase line execution count
316 ++profiler_db[element].lines[get_line(element, lineno)].exec_count;
317 }
af710487 318 }
319
320 // store the current location as previous for the next call
321 set_prev(disable_profiler ? -1 : TTCN3_Stack_Depth::depth(), filename, lineno);
322}
323
324int TTCN3_Profiler::get_element(const char* filename)
325{
326 for (size_t i = 0; i < profiler_db.size(); ++i) {
327 if (0 == strcmp(profiler_db[i].filename, filename)) {
328 return i;
329 }
330 }
331
3abe9331 332 Profiler_Tools::profiler_db_item_t item;
af710487 333 item.filename = mcopystr(filename);
334 profiler_db.push_back(item);
335 return profiler_db.size() - 1;
336}
337
338int TTCN3_Profiler::get_function(int element, int lineno)
339{
3abe9331 340 return Profiler_Tools::get_function(profiler_db, element, lineno);
af710487 341}
342
343void TTCN3_Profiler::create_function(int element, int lineno, const char* function_name)
344{
3abe9331 345 Profiler_Tools::create_function(profiler_db, element, lineno, function_name);
af710487 346}
347
a38c6d4c 348int TTCN3_Profiler::get_line(int element, int lineno)
af710487 349{
3abe9331 350 return Profiler_Tools::get_line(profiler_db, element, lineno);
a38c6d4c 351}
352
353void TTCN3_Profiler::create_line(int element, int lineno)
354{
3abe9331 355 Profiler_Tools::create_line(profiler_db, element, lineno);
af710487 356}
357
a38c6d4c 358void TTCN3_Profiler::add_line_time(timeval elapsed, int element, int lineno)
af710487 359{
a38c6d4c 360 if (0 == lineno) {
af710487 361 return;
362 }
3abe9331 363 profiler_db[element].lines[get_line(element, lineno)].total_time = Profiler_Tools::add_timeval(
a38c6d4c 364 profiler_db[element].lines[get_line(element, lineno)].total_time, elapsed);
af710487 365}
366
a38c6d4c 367void TTCN3_Profiler::add_function_time(timeval elapsed, int element, int lineno)
af710487 368{
369 int func = get_function(element, lineno);
370 if (-1 == func) {
371 return;
372 }
3abe9331 373 profiler_db[element].functions[func].total_time = Profiler_Tools::add_timeval(
a38c6d4c 374 profiler_db[element].functions[func].total_time, elapsed);
af710487 375}
376
377void TTCN3_Profiler::update_last()
378{
a38c6d4c 379 if (stopped || (0 == prev_time.tv_sec && 0 == prev_time.tv_usec) || NULL == prev_file) {
af710487 380 return;
381 }
a38c6d4c 382
3abe9331 383 timeval elapsed = Profiler_Tools::subtract_timeval(get_time(), prev_time);
af710487 384
385 int element = get_element(prev_file);
386
a38c6d4c 387 // add the elapsed time to the total time of the previous line
af710487 388 add_line_time(elapsed, element, prev_line);
389 TTCN3_Stack_Depth::update_stack_elapsed(elapsed);
390
a38c6d4c 391 // reset measurement
392 prev_time.tv_sec = 0;
393 prev_time.tv_usec = 0;
af710487 394}
395
396void TTCN3_Profiler::set_prev(int stack_len, const char* filename, int lineno)
397{
398 prev_file = filename;
399 prev_line = lineno;
400 if (!disable_profiler) {
401 prev_time = get_time();
402 prev_stack_len = stack_len;
403 }
404}
405
406/////////////////////////////////////
407///////// TTCN3_Stack_Depth /////////
408/////////////////////////////////////
409
410int TTCN3_Stack_Depth::current_depth = -1;
411Vector<TTCN3_Stack_Depth::call_stack_timer_item_t> TTCN3_Stack_Depth::call_stack_timer_db;
a38c6d4c 412boolean TTCN3_Stack_Depth::net_line_times = FALSE;
413boolean TTCN3_Stack_Depth::net_func_times = FALSE;
af710487 414
415TTCN3_Stack_Depth::TTCN3_Stack_Depth()
416{
417 if (ttcn3_prof.is_profiler_disabled()) {
418 return;
419 }
420 ++current_depth;
421}
422
423TTCN3_Stack_Depth::~TTCN3_Stack_Depth()
424{
425 if (ttcn3_prof.is_profiler_disabled()) {
426 return;
427 }
428 ttcn3_prof.update_last();
429 remove_stack();
430 if (0 == current_depth) {
431 ttcn3_prof.reset();
432 }
433 --current_depth;
434}
435
a38c6d4c 436void TTCN3_Stack_Depth::set_net_line_times(boolean p_net_line_times)
437{
438 net_line_times = p_net_line_times;
439}
440
441void TTCN3_Stack_Depth::set_net_func_times(boolean p_net_func_times)
442{
443 net_func_times = p_net_func_times;
444}
445
af710487 446void TTCN3_Stack_Depth::add_stack(int stack_len, const char* caller_file, const char* func_file,
447 int caller_line, int start_line)
448{
449 call_stack_timer_item_t item;
450 item.stack_len = stack_len;
451 item.caller_file = caller_file;
452 item.func_file = func_file;
453 item.caller_line = caller_line;
454 item.start_line = start_line;
a38c6d4c 455 item.elapsed.tv_sec = 0;
456 item.elapsed.tv_usec = 0;
457 item.first_call = true;
458 item.recursive_call = false;
459
460 if (!net_line_times || !net_func_times) {
461 // check if it's a recursive function
462 for (int i = current_depth - 1; i >= 0 ; --i) {
463 if (call_stack_timer_db[i].start_line == start_line &&
464 0 == strcmp(call_stack_timer_db[i].func_file, func_file)) {
465 item.recursive_call = true;
466
467 // check if the caller is new
468 if (call_stack_timer_db[i].caller_line == caller_line &&
469 ((NULL == call_stack_timer_db[i].caller_file && NULL == caller_file) ||
470 (NULL != call_stack_timer_db[i].caller_file && NULL != caller_file &&
471 0 == strcmp(call_stack_timer_db[i].caller_file, caller_file)))) {
472 item.first_call = false;
473 break;
474 }
475 }
476 }
477 }
478
af710487 479 call_stack_timer_db.push_back(item);
480}
481
482void TTCN3_Stack_Depth::remove_stack()
483{
a38c6d4c 484 // add the time gathered for this stack level to the appropriate line and function
485 // except for functions starting at line 0 (pre_init_module and post_init_module)
486 if (0 != call_stack_timer_db[current_depth].start_line) {
487 timeval elapsed = call_stack_timer_db[current_depth].elapsed;
488 if (!net_line_times && NULL != call_stack_timer_db[current_depth].caller_file &&
489 call_stack_timer_db[current_depth].first_call) {
490 // add the elapsed time to the caller line, if it exists
491 // (only add it once for recursive functions, at the first call)
492 ttcn3_prof.add_line_time(elapsed,
493 ttcn3_prof.get_element(call_stack_timer_db[current_depth].caller_file),
494 call_stack_timer_db[current_depth].caller_line);
495 }
496 if (!net_func_times && !call_stack_timer_db[current_depth].recursive_call) {
497 // add the elapsed time to the called function, if it's not recursive
498 // (in case of net function times this has already been done in update_stack_elapsed)
499 ttcn3_prof.add_function_time(elapsed,
500 ttcn3_prof.get_element(call_stack_timer_db[current_depth].func_file),
501 call_stack_timer_db[current_depth].start_line);
502 }
503 }
af710487 504
505 ttcn3_prof.set_prev(call_stack_timer_db[current_depth].stack_len,
506 call_stack_timer_db[current_depth].caller_file,
507 call_stack_timer_db[current_depth].caller_line);
508
509 call_stack_timer_db.erase_at(current_depth);
510}
511
a38c6d4c 512void TTCN3_Stack_Depth::update_stack_elapsed(timeval elapsed)
af710487 513{
a38c6d4c 514 // if function times are net times, only add the elapsed time to the current function
515 if (net_func_times) {
516 ttcn3_prof.add_function_time(elapsed,
517 ttcn3_prof.get_element(call_stack_timer_db[current_depth].func_file),
518 call_stack_timer_db[current_depth].start_line);
af710487 519 }
a38c6d4c 520 if (!net_line_times || !net_func_times) {
521 // cycle through the stack and add the elapsed time to the entries where
522 // the function/caller pair appears for the first time (marked by 'first_call')
523 for(int i = 0; i <= current_depth; ++i) {
524 if (call_stack_timer_db[i].first_call) {
3abe9331 525 call_stack_timer_db[i].elapsed = Profiler_Tools::add_timeval(
526 call_stack_timer_db[i].elapsed, elapsed);
a38c6d4c 527 }
528 }
529 }
530}
This page took 0.06185 seconds and 5 git commands to generate.