3 // Copyright (C) 2009-2016 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License along
21 // with this library; see the file COPYING3. If not see
22 // <http://www.gnu.org/licenses/>.
24 /** @file profile/impl/profiler_trace.h
25 * @brief Data structures to represent profiling traces.
28 // Written by Lixia Liu and Silvius Rus.
30 #ifndef _GLIBCXX_PROFILE_PROFILER_TRACE_H
31 #define _GLIBCXX_PROFILE_PROFILER_TRACE_H 1
33 #include <cstdio> // fopen, fclose, fprintf, FILE
35 #include <cstdlib> // atof, atoi, strtol, getenv, atexit, abort
37 #if __cplusplus >= 201103L
38 #include <unordered_map>
39 #define _GLIBCXX_IMPL_UNORDERED_MAP std::_GLIBCXX_STD_C::unordered_map
41 #include <tr1/unordered_map>
42 #define _GLIBCXX_IMPL_UNORDERED_MAP std::tr1::unordered_map
45 #include <ext/concurrence.h>
51 #include "profile/impl/profiler_algos.h"
52 #include "profile/impl/profiler_state.h"
53 #include "profile/impl/profiler_node.h"
55 namespace __gnu_profile
57 /** @brief Internal environment. Values can be set one of two ways:
58 1. In config file "var = value". The default config file path is
59 libstdcxx-profile.conf.
60 2. By setting process environment variables. For instance, in a Bash
61 shell you can set the unit cost of iterating through a map like this:
62 export __map_iterate_cost_factor=5.0.
63 If a value is set both in the input file and through an environment
64 variable, the environment value takes precedence. */
65 typedef _GLIBCXX_IMPL_UNORDERED_MAP
<std::string
, std::string
> __env_t
;
67 _GLIBCXX_PROFILE_DEFINE_UNINIT_DATA(__env_t
, __env
);
69 /** @brief Master lock. */
70 _GLIBCXX_PROFILE_DEFINE_UNINIT_DATA(__gnu_cxx::__mutex
, __global_mutex
);
72 /** @brief Representation of a warning. */
77 const char* __warning_id
;
78 std::string __warning_message
;
81 : __magnitude(0.0), __context(0), __warning_id(0) { }
83 __warning_data(float __m
, __stack_t __c
, const char* __id
,
84 const std::string
& __msg
)
85 : __magnitude(__m
), __context(__c
), __warning_id(__id
),
86 __warning_message(__msg
) { }
89 operator<(const __warning_data
& __other
) const
90 { return __magnitude
< __other
.__magnitude
; }
93 typedef std::_GLIBCXX_STD_C::vector
<__warning_data
> __warning_vector_t
;
95 // Defined in profiler_<diagnostic name>.h.
96 class __trace_hash_func
;
97 class __trace_hashtable_size
;
98 class __trace_map2umap
;
99 class __trace_vector_size
;
100 class __trace_vector_to_list
;
101 class __trace_list_to_slist
;
102 class __trace_list_to_vector
;
103 void __trace_vector_size_init();
104 void __trace_hashtable_size_init();
105 void __trace_hash_func_init();
106 void __trace_vector_to_list_init();
107 void __trace_list_to_slist_init();
108 void __trace_list_to_vector_init();
109 void __trace_map_to_unordered_map_init();
110 void __trace_vector_size_report(FILE*, __warning_vector_t
&);
111 void __trace_hashtable_size_report(FILE*, __warning_vector_t
&);
112 void __trace_hash_func_report(FILE*, __warning_vector_t
&);
113 void __trace_vector_to_list_report(FILE*, __warning_vector_t
&);
114 void __trace_list_to_slist_report(FILE*, __warning_vector_t
&);
115 void __trace_list_to_vector_report(FILE*, __warning_vector_t
&);
116 void __trace_map_to_unordered_map_report(FILE*, __warning_vector_t
&);
117 void __trace_vector_size_free();
118 void __trace_hashtable_size_free();
119 void __trace_hash_func_free();
120 void __trace_vector_to_list_free();
121 void __trace_list_to_slist_free();
122 void __trace_list_to_vector_free();
123 void __trace_map_to_unordered_map_free();
127 const char* __env_var
;
131 typedef std::_GLIBCXX_STD_C::vector
<__cost_factor
*> __cost_factor_vector
;
133 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_hash_func
*, _S_hash_func
, 0);
134 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_hashtable_size
*, _S_hashtable_size
, 0);
135 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_map2umap
*, _S_map2umap
, 0);
136 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_vector_size
*, _S_vector_size
, 0);
137 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_vector_to_list
*, _S_vector_to_list
, 0);
138 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_list_to_slist
*, _S_list_to_slist
, 0);
139 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_list_to_vector
*, _S_list_to_vector
, 0);
141 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_shift_cost_factor
,
142 {"__vector_shift_cost_factor", 1.0});
143 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_iterate_cost_factor
,
144 {"__vector_iterate_cost_factor", 1.0});
145 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_resize_cost_factor
,
146 {"__vector_resize_cost_factor", 1.0});
147 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_shift_cost_factor
,
148 {"__list_shift_cost_factor", 0.0});
149 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_iterate_cost_factor
,
150 {"__list_iterate_cost_factor", 10.0});
151 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_resize_cost_factor
,
152 {"__list_resize_cost_factor", 0.0});
153 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_insert_cost_factor
,
154 {"__map_insert_cost_factor", 1.5});
155 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_erase_cost_factor
,
156 {"__map_erase_cost_factor", 1.5});
157 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_find_cost_factor
,
158 {"__map_find_cost_factor", 1});
159 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_iterate_cost_factor
,
160 {"__map_iterate_cost_factor", 2.3});
161 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_insert_cost_factor
,
162 {"__umap_insert_cost_factor", 12.0});
163 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_erase_cost_factor
,
164 {"__umap_erase_cost_factor", 12.0});
165 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_find_cost_factor
,
166 {"__umap_find_cost_factor", 10.0});
167 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_iterate_cost_factor
,
168 {"__umap_iterate_cost_factor", 1.7});
169 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor_vector
*, __cost_factors
, 0);
171 _GLIBCXX_PROFILE_DEFINE_DATA(const char*, _S_trace_file_name
,
172 _GLIBCXX_PROFILE_TRACE_PATH_ROOT
);
173 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_warn_count
,
174 _GLIBCXX_PROFILE_MAX_WARN_COUNT
);
175 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_stack_depth
,
176 _GLIBCXX_PROFILE_MAX_STACK_DEPTH
);
177 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_mem
,
178 _GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC
);
182 { return _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
); }
186 { return _GLIBCXX_PROFILE_DATA(_S_max_mem
); }
188 /** @brief Base class for all trace producers. */
189 template<typename __object_info
, typename __stack_info
>
193 // Do not pick the initial size too large, as we don't know which
194 // diagnostics are more active.
196 : __objects_byte_size(0), __stack_table(10000),
197 __stack_table_byte_size(0), __id(0) { }
201 for (typename
__stack_table_t::iterator __it
202 = __stack_table
.begin(); __it
!= __stack_table
.end(); ++__it
)
206 __object_info
* __add_object(__stack_t __stack
);
207 void __retire_object(__object_info
* __info
);
208 void __write(FILE* __f
);
209 void __collect_warnings(__warning_vector_t
& __warnings
);
213 __gnu_cxx::__mutex __trace_mutex
;
214 typedef _GLIBCXX_IMPL_UNORDERED_MAP
<__stack_t
, __stack_info
,
216 __stack_hash
> __stack_table_t
;
217 std::size_t __objects_byte_size
;
218 __stack_table_t __stack_table
;
219 std::size_t __stack_table_byte_size
;
225 template<typename __object_info
, typename __stack_info
>
227 __trace_base
<__object_info
, __stack_info
>::
228 __add_object(__stack_t __stack
)
230 // If we have no backtrace information no need to collect data.
234 __gnu_cxx::__scoped_lock
__lock(this->__trace_mutex
);
236 if (__max_mem() != 0 && __objects_byte_size
>= __max_mem())
242 __object_info
* __ret
= new(std::nothrow
) __object_info(__stack
);
249 __objects_byte_size
+= sizeof(__object_info
);
253 template<typename __object_info
, typename __stack_info
>
255 __trace_base
<__object_info
, __stack_info
>::
256 __retire_object(__object_info
* __obj_info
)
261 __gnu_cxx::__scoped_lock
__lock(this->__trace_mutex
);
263 const __object_info
& __info
= *__obj_info
;
264 __stack_t __stack
= __info
.__stack();
265 typename
__stack_table_t::iterator __stack_it
266 = __stack_table
.find(__stack
);
268 if (__stack_it
== __stack_table
.end())
270 // First occurrence of this call context.
271 if (__max_mem() == 0 || __stack_table_byte_size
< __max_mem())
273 __stack_table_byte_size
274 += (sizeof(__instruction_address_t
) * __size(__stack
)
275 + sizeof(__stack
) + sizeof(__stack_info
));
276 __stack_table
.insert(make_pair(__stack
,
277 __stack_info(__info
)));
284 // Merge object info into info summary for this call context.
285 __stack_it
->second
.__merge(__info
);
290 __objects_byte_size
-= sizeof(__object_info
);
293 template<typename __object_info
, typename __stack_info
>
295 __trace_base
<__object_info
, __stack_info
>::
298 for (typename
__stack_table_t::iterator __it
299 = __stack_table
.begin(); __it
!= __stack_table
.end(); ++__it
)
300 if (__it
->second
.__is_valid())
302 std::fprintf(__f
, __id
);
303 std::fprintf(__f
, "|");
304 __gnu_profile::__write(__f
, __it
->first
);
305 std::fprintf(__f
, "|");
306 __it
->second
.__write(__f
);
310 template<typename __object_info
, typename __stack_info
>
312 __trace_base
<__object_info
, __stack_info
>::
313 __collect_warnings(__warning_vector_t
& __warnings
)
315 for (typename
__stack_table_t::iterator __it
316 = __stack_table
.begin(); __it
!= __stack_table
.end(); ++__it
)
317 __warnings
.push_back(__warning_data(__it
->second
.__magnitude(),
319 __it
->second
.__advice()));
322 template<typename __object_info
, typename __stack_info
>
324 __trace_report(__trace_base
<__object_info
, __stack_info
>* __cont
,
325 FILE* __f
, __warning_vector_t
& __warnings
)
329 __cont
->__collect_warnings(__warnings
);
330 __cont
->__write(__f
);
335 __env_to_size_t(const char* __env_var
, std::size_t __default_value
)
337 char* __env_value
= std::getenv(__env_var
);
341 long __converted_value
= std::strtol(__env_value
, 0, 10);
342 if (errno
|| __converted_value
< 0)
345 "Bad value for environment variable '%s'.\n",
350 return static_cast<std::size_t>(__converted_value
);
353 return __default_value
;
357 __set_max_stack_trace_depth()
359 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
)
360 = __env_to_size_t(_GLIBCXX_PROFILE_MAX_STACK_DEPTH_ENV_VAR
,
361 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
));
367 _GLIBCXX_PROFILE_DATA(_S_max_mem
)
368 = __env_to_size_t(_GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC_ENV_VAR
,
369 _GLIBCXX_PROFILE_DATA(_S_max_mem
));
373 __log_magnitude(float __f
)
375 const float __log_base
= 10.0;
385 while (__f
> __log_base
)
390 return __sign
* __result
;
394 __open_output_file(const char* __extension
)
396 // The path is made of _S_trace_file_name + "." + extension.
397 std::size_t __root_len
398 = __builtin_strlen(_GLIBCXX_PROFILE_DATA(_S_trace_file_name
));
399 std::size_t __ext_len
= __builtin_strlen(__extension
);
400 char* __file_name
= new char[__root_len
+ 1 + __ext_len
+ 1];
401 __builtin_memcpy(__file_name
,
402 _GLIBCXX_PROFILE_DATA(_S_trace_file_name
),
404 *(__file_name
+ __root_len
) = '.';
405 __builtin_memcpy(__file_name
+ __root_len
+ 1,
406 __extension
, __ext_len
+ 1);
408 FILE* __out_file
= std::fopen(__file_name
, "w");
411 std::fprintf(stderr
, "Could not open trace file '%s'.\n",
416 delete[] __file_name
;
428 operator()(const __warning_data
& __info
)
430 std::fprintf(__file
, __info
.__warning_id
);
431 std::fprintf(__file
, ": improvement = %d",
432 __log_magnitude(__info
.__magnitude
));
433 std::fprintf(__file
, ": call stack = ");
434 __gnu_profile::__write(__file
, __info
.__context
);
435 std::fprintf(__file
, ": advice = %s\n",
436 __info
.__warning_message
.c_str());
440 /** @brief Final report method, registered with @b atexit.
442 * This can also be called directly by user code, including signal handlers.
443 * It is protected against deadlocks by the reentrance guard in profiler.h.
444 * However, when called from a signal handler that triggers while within
445 * __gnu_profile (under the guarded zone), no output will be produced.
450 __gnu_cxx::__scoped_lock
__lock(_GLIBCXX_PROFILE_DATA(__global_mutex
));
452 __warning_vector_t __warnings
, __top_warnings
;
454 FILE* __raw_file
= __open_output_file("raw");
455 __trace_vector_size_report(__raw_file
, __warnings
);
456 __trace_hashtable_size_report(__raw_file
, __warnings
);
457 __trace_hash_func_report(__raw_file
, __warnings
);
458 __trace_vector_to_list_report(__raw_file
, __warnings
);
459 __trace_list_to_slist_report(__raw_file
, __warnings
);
460 __trace_list_to_vector_report(__raw_file
, __warnings
);
461 __trace_map_to_unordered_map_report(__raw_file
, __warnings
);
462 std::fclose(__raw_file
);
464 // Sort data by magnitude, keeping just top N.
465 std::size_t __cutoff
= std::min(_GLIBCXX_PROFILE_DATA(_S_max_warn_count
),
467 __top_n(__warnings
, __top_warnings
, __cutoff
);
469 FILE* __warn_file
= __open_output_file("txt");
470 __for_each(__top_warnings
.begin(), __top_warnings
.end(),
471 __warn(__warn_file
));
472 std::fclose(__warn_file
);
480 __trace_map_to_unordered_map_free();
481 __trace_list_to_vector_free();
482 __trace_list_to_slist_free();
483 __trace_vector_to_list_free();
484 __trace_hash_func_free();
485 __trace_hashtable_size_free();
486 __trace_vector_size_free();
487 delete _GLIBCXX_PROFILE_DATA(__cost_factors
);
493 char* __env_trace_file_name
= std::getenv(_GLIBCXX_PROFILE_TRACE_ENV_VAR
);
495 if (__env_trace_file_name
)
496 _GLIBCXX_PROFILE_DATA(_S_trace_file_name
) = __env_trace_file_name
;
498 // Make sure early that we can create the trace file.
499 std::fclose(__open_output_file("txt"));
503 __set_max_warn_count()
505 char* __env_max_warn_count_str
506 = std::getenv(_GLIBCXX_PROFILE_MAX_WARN_COUNT_ENV_VAR
);
508 if (__env_max_warn_count_str
)
509 _GLIBCXX_PROFILE_DATA(_S_max_warn_count
)
510 = static_cast<std::size_t>(std::atoi(__env_max_warn_count_str
));
514 __read_cost_factors()
516 std::string
__conf_file_name(_GLIBCXX_PROFILE_DATA(_S_trace_file_name
));
517 __conf_file_name
+= ".conf";
519 std::ifstream
__conf_file(__conf_file_name
.c_str());
521 if (__conf_file
.is_open())
525 while (std::getline(__conf_file
, __line
))
527 std::string::size_type __i
= __line
.find_first_not_of(" \t\n\v");
529 if (__line
.length() <= 0 || __line
[__i
] == '#')
530 // Skip empty lines or comments.
535 __line
.erase(__remove(__line
.begin(), __line
.end(), ' '),
537 std::string::size_type __pos
= __line
.find("=");
538 std::string __factor_name
= __line
.substr(0, __pos
);
539 std::string::size_type __end
= __line
.find_first_of(";\n");
540 std::string __factor_value
= __line
.substr(__pos
+ 1, __end
- __pos
);
542 _GLIBCXX_PROFILE_DATA(__env
)[__factor_name
] = __factor_value
;
546 struct __cost_factor_writer
550 __cost_factor_writer(FILE* __f
)
554 operator() (const __cost_factor
* __factor
)
555 { std::fprintf(__file
, "%s = %f\n", __factor
->__env_var
,
556 __factor
->__value
); }
560 __write_cost_factors()
562 FILE* __file
= __open_output_file("conf.out");
563 __for_each(_GLIBCXX_PROFILE_DATA(__cost_factors
)->begin(),
564 _GLIBCXX_PROFILE_DATA(__cost_factors
)->end(),
565 __cost_factor_writer(__file
));
569 struct __cost_factor_setter
572 operator()(__cost_factor
* __factor
)
574 // Look it up in the process environment first.
575 const char* __env_value
= std::getenv(__factor
->__env_var
);
579 // Look it up in the config file.
580 __env_t::iterator __it
581 = _GLIBCXX_PROFILE_DATA(__env
).find(__factor
->__env_var
);
582 if (__it
!= _GLIBCXX_PROFILE_DATA(__env
).end())
583 __env_value
= __it
->second
.c_str();
587 __factor
->__value
= std::atof(__env_value
);
594 __cost_factor_vector
* __factors
= new __cost_factor_vector
;
595 _GLIBCXX_PROFILE_DATA(__cost_factors
) = __factors
;
596 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__vector_shift_cost_factor
));
597 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__vector_iterate_cost_factor
));
598 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__vector_resize_cost_factor
));
599 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__list_shift_cost_factor
));
600 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__list_iterate_cost_factor
));
601 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__list_resize_cost_factor
));
602 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__map_insert_cost_factor
));
603 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__map_erase_cost_factor
));
604 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__map_find_cost_factor
));
605 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__map_iterate_cost_factor
));
606 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__umap_insert_cost_factor
));
607 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__umap_erase_cost_factor
));
608 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__umap_find_cost_factor
));
609 __factors
->push_back(&_GLIBCXX_PROFILE_DATA(__umap_iterate_cost_factor
));
610 __for_each(__factors
->begin(), __factors
->end(), __cost_factor_setter());
614 __profcxx_init_unconditional()
616 __gnu_cxx::__scoped_lock
__lock(_GLIBCXX_PROFILE_DATA(__global_mutex
));
620 __set_max_warn_count();
622 if (_GLIBCXX_PROFILE_DATA(_S_max_warn_count
) == 0)
626 __set_max_stack_trace_depth();
629 __read_cost_factors();
630 __set_cost_factors();
631 __write_cost_factors();
633 __trace_vector_size_init();
634 __trace_hashtable_size_init();
635 __trace_hash_func_init();
636 __trace_vector_to_list_init();
637 __trace_list_to_slist_init();
638 __trace_list_to_vector_init();
639 __trace_map_to_unordered_map_init();
641 std::atexit(__report_and_free
);
648 /** @brief This function must be called by each instrumentation point.
650 * The common path is inlined fully.
656 __profcxx_init_unconditional();
661 } // namespace __gnu_profile
663 #endif /* _GLIBCXX_PROFILE_PROFILER_TRACE_H */