3 // Copyright (C) 2009-2013 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 #define _GLIBCXX_IMPL_UNORDERED_MAP std::_GLIBCXX_STD_C::unordered_map
39 #include <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_lock
);
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
&);
120 const char* __env_var
;
124 typedef std::_GLIBCXX_STD_C::vector
<__cost_factor
*> __cost_factor_vector
;
126 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_hash_func
*, _S_hash_func
, 0);
127 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_hashtable_size
*, _S_hashtable_size
, 0);
128 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_map2umap
*, _S_map2umap
, 0);
129 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_vector_size
*, _S_vector_size
, 0);
130 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_vector_to_list
*, _S_vector_to_list
, 0);
131 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_list_to_slist
*, _S_list_to_slist
, 0);
132 _GLIBCXX_PROFILE_DEFINE_DATA(__trace_list_to_vector
*, _S_list_to_vector
, 0);
134 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_shift_cost_factor
,
135 {"__vector_shift_cost_factor", 1.0});
136 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_iterate_cost_factor
,
137 {"__vector_iterate_cost_factor", 1.0});
138 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __vector_resize_cost_factor
,
139 {"__vector_resize_cost_factor", 1.0});
140 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_shift_cost_factor
,
141 {"__list_shift_cost_factor", 0.0});
142 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_iterate_cost_factor
,
143 {"__list_iterate_cost_factor", 10.0});
144 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __list_resize_cost_factor
,
145 {"__list_resize_cost_factor", 0.0});
146 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_insert_cost_factor
,
147 {"__map_insert_cost_factor", 1.5});
148 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_erase_cost_factor
,
149 {"__map_erase_cost_factor", 1.5});
150 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_find_cost_factor
,
151 {"__map_find_cost_factor", 1});
152 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __map_iterate_cost_factor
,
153 {"__map_iterate_cost_factor", 2.3});
154 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_insert_cost_factor
,
155 {"__umap_insert_cost_factor", 12.0});
156 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_erase_cost_factor
,
157 {"__umap_erase_cost_factor", 12.0});
158 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_find_cost_factor
,
159 {"__umap_find_cost_factor", 10.0});
160 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor
, __umap_iterate_cost_factor
,
161 {"__umap_iterate_cost_factor", 1.7});
162 _GLIBCXX_PROFILE_DEFINE_DATA(__cost_factor_vector
*, __cost_factors
, 0);
164 _GLIBCXX_PROFILE_DEFINE_DATA(const char*, _S_trace_file_name
,
165 _GLIBCXX_PROFILE_TRACE_PATH_ROOT
);
166 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_warn_count
,
167 _GLIBCXX_PROFILE_MAX_WARN_COUNT
);
168 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_stack_depth
,
169 _GLIBCXX_PROFILE_MAX_STACK_DEPTH
);
170 _GLIBCXX_PROFILE_DEFINE_DATA(std::size_t, _S_max_mem
,
171 _GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC
);
175 { return _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
); }
179 { return _GLIBCXX_PROFILE_DATA(_S_max_mem
); }
181 /** @brief Base class for all trace producers. */
182 template<typename __object_info
, typename __stack_info
>
186 // Do not pick the initial size too large, as we don't know which
187 // diagnostics are more active.
189 : __object_table(10000), __stack_table(10000),
190 __stack_table_byte_size(0), __id(0) { }
192 virtual ~__trace_base() { }
194 void __add_object(__object_t object
, __object_info __info
);
195 __object_info
* __get_object_info(__object_t __object
);
196 void __retire_object(__object_t __object
);
197 void __write(FILE* __f
);
198 void __collect_warnings(__warning_vector_t
& __warnings
);
201 __gnu_cxx::__mutex __object_table_lock
;
202 __gnu_cxx::__mutex __stack_table_lock
;
203 typedef _GLIBCXX_IMPL_UNORDERED_MAP
<__object_t
,
204 __object_info
> __object_table_t
;
205 typedef _GLIBCXX_IMPL_UNORDERED_MAP
<__stack_t
, __stack_info
,
207 __stack_hash
> __stack_table_t
;
208 __object_table_t __object_table
;
209 __stack_table_t __stack_table
;
210 std::size_t __stack_table_byte_size
;
216 template<typename __object_info
, typename __stack_info
>
218 __trace_base
<__object_info
, __stack_info
>::
219 __collect_warnings(__warning_vector_t
& __warnings
)
221 for (typename
__stack_table_t::iterator __it
222 = __stack_table
.begin(); __it
!= __stack_table
.end(); ++__it
)
223 __warnings
.push_back(__warning_data((*__it
).second
.__magnitude(),
225 (*__it
).second
.__advice()));
228 template<typename __object_info
, typename __stack_info
>
230 __trace_base
<__object_info
, __stack_info
>::
231 __add_object(__object_t __object
, __object_info __info
)
234 || __object_table
.size() * sizeof(__object_info
) <= __max_mem())
236 this->__object_table_lock
.lock();
237 __object_table
.insert(typename
__object_table_t::
238 value_type(__object
, __info
));
239 this->__object_table_lock
.unlock();
243 template<typename __object_info
, typename __stack_info
>
245 __trace_base
<__object_info
, __stack_info
>::
246 __get_object_info(__object_t __object
)
248 // XXX: Revisit this to see if we can decrease mutex spans.
249 // Without this mutex, the object table could be rehashed during an
250 // insertion on another thread, which could result in a segfault.
251 this->__object_table_lock
.lock();
252 typename
__object_table_t::iterator __object_it
253 = __object_table
.find(__object
);
255 if (__object_it
== __object_table
.end())
257 this->__object_table_lock
.unlock();
262 this->__object_table_lock
.unlock();
263 return &__object_it
->second
;
267 template<typename __object_info
, typename __stack_info
>
269 __trace_base
<__object_info
, __stack_info
>::
270 __retire_object(__object_t __object
)
272 this->__object_table_lock
.lock();
273 this->__stack_table_lock
.lock();
274 typename
__object_table_t::iterator __object_it
275 = __object_table
.find(__object
);
277 if (__object_it
!= __object_table
.end())
279 const __object_info
& __info
= __object_it
->second
;
280 const __stack_t
& __stack
= __info
.__stack();
281 typename
__stack_table_t::iterator __stack_it
282 = __stack_table
.find(__stack
);
284 if (__stack_it
== __stack_table
.end())
286 // First occurrence of this call context.
287 if (__max_mem() == 0 || __stack_table_byte_size
< __max_mem())
289 __stack_table_byte_size
290 += (sizeof(__instruction_address_t
) * __size(__stack
)
291 + sizeof(__stack
) + sizeof(__stack_info
));
292 __stack_table
.insert(make_pair(__stack
,
293 __stack_info(__info
)));
298 // Merge object info into info summary for this call context.
299 __stack_it
->second
.__merge(__info
);
302 __object_table
.erase(__object
);
305 this->__object_table_lock
.unlock();
306 this->__stack_table_lock
.unlock();
309 template<typename __object_info
, typename __stack_info
>
311 __trace_base
<__object_info
, __stack_info
>::
314 for (typename
__stack_table_t::iterator __it
315 = __stack_table
.begin(); __it
!= __stack_table
.end(); ++__it
)
316 if (__it
->second
.__is_valid())
318 std::fprintf(__f
, __id
);
319 std::fprintf(__f
, "|");
320 __gnu_profile::__write(__f
, __it
->first
);
321 std::fprintf(__f
, "|");
322 __it
->second
.__write(__f
);
327 __env_to_size_t(const char* __env_var
, std::size_t __default_value
)
329 char* __env_value
= std::getenv(__env_var
);
333 long __converted_value
= std::strtol(__env_value
, 0, 10);
334 if (errno
|| __converted_value
< 0)
337 "Bad value for environment variable '%s'.\n",
342 return static_cast<std::size_t>(__converted_value
);
345 return __default_value
;
349 __set_max_stack_trace_depth()
351 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
)
352 = __env_to_size_t(_GLIBCXX_PROFILE_MAX_STACK_DEPTH_ENV_VAR
,
353 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth
));
359 _GLIBCXX_PROFILE_DATA(_S_max_mem
)
360 = __env_to_size_t(_GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC_ENV_VAR
,
361 _GLIBCXX_PROFILE_DATA(_S_max_mem
));
365 __log_magnitude(float __f
)
367 const float __log_base
= 10.0;
377 while (__f
> __log_base
)
382 return __sign
* __result
;
386 __open_output_file(const char* __extension
)
388 // The path is made of _S_trace_file_name + "." + extension.
389 std::size_t __root_len
390 = __builtin_strlen(_GLIBCXX_PROFILE_DATA(_S_trace_file_name
));
391 std::size_t __ext_len
= __builtin_strlen(__extension
);
392 char* __file_name
= new char[__root_len
+ 1 + __ext_len
+ 1];
393 __builtin_memcpy(__file_name
,
394 _GLIBCXX_PROFILE_DATA(_S_trace_file_name
),
396 *(__file_name
+ __root_len
) = '.';
397 __builtin_memcpy(__file_name
+ __root_len
+ 1,
398 __extension
, __ext_len
+ 1);
400 FILE* __out_file
= std::fopen(__file_name
, "w");
403 std::fprintf(stderr
, "Could not open trace file '%s'.\n",
408 delete[] __file_name
;
420 operator()(const __warning_data
& __info
)
422 std::fprintf(__file
, __info
.__warning_id
);
423 std::fprintf(__file
, ": improvement = %d",
424 __log_magnitude(__info
.__magnitude
));
425 std::fprintf(__file
, ": call stack = ");
426 __gnu_profile::__write(__file
, __info
.__context
);
427 std::fprintf(__file
, ": advice = %s\n",
428 __info
.__warning_message
.c_str());
432 /** @brief Final report method, registered with @b atexit.
434 * This can also be called directly by user code, including signal handlers.
435 * It is protected against deadlocks by the reentrance guard in profiler.h.
436 * However, when called from a signal handler that triggers while within
437 * __gnu_profile (under the guarded zone), no output will be produced.
442 _GLIBCXX_PROFILE_DATA(__global_lock
).lock();
444 __warning_vector_t __warnings
, __top_warnings
;
446 FILE* __raw_file
= __open_output_file("raw");
447 __trace_vector_size_report(__raw_file
, __warnings
);
448 __trace_hashtable_size_report(__raw_file
, __warnings
);
449 __trace_hash_func_report(__raw_file
, __warnings
);
450 __trace_vector_to_list_report(__raw_file
, __warnings
);
451 __trace_list_to_slist_report(__raw_file
, __warnings
);
452 __trace_list_to_vector_report(__raw_file
, __warnings
);
453 __trace_map_to_unordered_map_report(__raw_file
, __warnings
);
454 std::fclose(__raw_file
);
456 // Sort data by magnitude, keeping just top N.
457 std::size_t __cutoff
= std::min(_GLIBCXX_PROFILE_DATA(_S_max_warn_count
),
459 __top_n(__warnings
, __top_warnings
, __cutoff
);
461 FILE* __warn_file
= __open_output_file("txt");
462 __for_each(__top_warnings
.begin(), __top_warnings
.end(),
463 __warn(__warn_file
));
464 std::fclose(__warn_file
);
466 _GLIBCXX_PROFILE_DATA(__global_lock
).unlock();
472 char* __env_trace_file_name
= std::getenv(_GLIBCXX_PROFILE_TRACE_ENV_VAR
);
474 if (__env_trace_file_name
)
475 _GLIBCXX_PROFILE_DATA(_S_trace_file_name
) = __env_trace_file_name
;
477 // Make sure early that we can create the trace file.
478 std::fclose(__open_output_file("txt"));
482 __set_max_warn_count()
484 char* __env_max_warn_count_str
485 = std::getenv(_GLIBCXX_PROFILE_MAX_WARN_COUNT_ENV_VAR
);
487 if (__env_max_warn_count_str
)
488 _GLIBCXX_PROFILE_DATA(_S_max_warn_count
)
489 = static_cast<std::size_t>(std::atoi(__env_max_warn_count_str
));
493 __read_cost_factors()
495 std::string
__conf_file_name(_GLIBCXX_PROFILE_DATA(_S_trace_file_name
));
496 __conf_file_name
+= ".conf";
498 std::ifstream
__conf_file(__conf_file_name
.c_str());
500 if (__conf_file
.is_open())
504 while (std::getline(__conf_file
, __line
))
506 std::string::size_type __i
= __line
.find_first_not_of(" \t\n\v");
508 if (__line
.length() <= 0 || __line
[__i
] == '#')
509 // Skip empty lines or comments.
514 __line
.erase(__remove(__line
.begin(), __line
.end(), ' '),
516 std::string::size_type __pos
= __line
.find("=");
517 std::string __factor_name
= __line
.substr(0, __pos
);
518 std::string::size_type __end
= __line
.find_first_of(";\n");
519 std::string __factor_value
= __line
.substr(__pos
+ 1, __end
- __pos
);
521 _GLIBCXX_PROFILE_DATA(__env
)[__factor_name
] = __factor_value
;
525 struct __cost_factor_writer
529 __cost_factor_writer(FILE* __f
)
533 operator() (const __cost_factor
* __factor
)
534 { std::fprintf(__file
, "%s = %f\n", __factor
->__env_var
,
535 __factor
->__value
); }
539 __write_cost_factors()
541 FILE* __file
= __open_output_file("conf.out");
542 __for_each(_GLIBCXX_PROFILE_DATA(__cost_factors
)->begin(),
543 _GLIBCXX_PROFILE_DATA(__cost_factors
)->end(),
544 __cost_factor_writer(__file
));
548 struct __cost_factor_setter
551 operator()(__cost_factor
* __factor
)
553 // Look it up in the process environment first.
554 const char* __env_value
= std::getenv(__factor
->__env_var
);
558 // Look it up in the config file.
559 __env_t::iterator __it
560 = _GLIBCXX_PROFILE_DATA(__env
).find(__factor
->__env_var
);
561 if (__it
!= _GLIBCXX_PROFILE_DATA(__env
).end())
562 __env_value
= (*__it
).second
.c_str();
566 __factor
->__value
= std::atof(__env_value
);
573 _GLIBCXX_PROFILE_DATA(__cost_factors
) = new __cost_factor_vector
;
574 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
575 push_back(&_GLIBCXX_PROFILE_DATA(__vector_shift_cost_factor
));
576 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
577 push_back(&_GLIBCXX_PROFILE_DATA(__vector_iterate_cost_factor
));
578 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
579 push_back(&_GLIBCXX_PROFILE_DATA(__vector_resize_cost_factor
));
580 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
581 push_back(&_GLIBCXX_PROFILE_DATA(__list_shift_cost_factor
));
582 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
583 push_back(&_GLIBCXX_PROFILE_DATA(__list_iterate_cost_factor
));
584 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
585 push_back(&_GLIBCXX_PROFILE_DATA(__list_resize_cost_factor
));
586 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
587 push_back(&_GLIBCXX_PROFILE_DATA(__map_insert_cost_factor
));
588 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
589 push_back(&_GLIBCXX_PROFILE_DATA(__map_erase_cost_factor
));
590 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
591 push_back(&_GLIBCXX_PROFILE_DATA(__map_find_cost_factor
));
592 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
593 push_back(&_GLIBCXX_PROFILE_DATA(__map_iterate_cost_factor
));
594 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
595 push_back(&_GLIBCXX_PROFILE_DATA(__umap_insert_cost_factor
));
596 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
597 push_back(&_GLIBCXX_PROFILE_DATA(__umap_erase_cost_factor
));
598 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
599 push_back(&_GLIBCXX_PROFILE_DATA(__umap_find_cost_factor
));
600 _GLIBCXX_PROFILE_DATA(__cost_factors
)->
601 push_back(&_GLIBCXX_PROFILE_DATA(__umap_iterate_cost_factor
));
602 __for_each(_GLIBCXX_PROFILE_DATA(__cost_factors
)->begin(),
603 _GLIBCXX_PROFILE_DATA(__cost_factors
)->end(),
604 __cost_factor_setter());
608 __profcxx_init_unconditional()
610 _GLIBCXX_PROFILE_DATA(__global_lock
).lock();
614 __set_max_warn_count();
616 if (_GLIBCXX_PROFILE_DATA(_S_max_warn_count
) == 0)
620 __set_max_stack_trace_depth();
623 __read_cost_factors();
624 __set_cost_factors();
625 __write_cost_factors();
627 __trace_vector_size_init();
628 __trace_hashtable_size_init();
629 __trace_hash_func_init();
630 __trace_vector_to_list_init();
631 __trace_list_to_slist_init();
632 __trace_list_to_vector_init();
633 __trace_map_to_unordered_map_init();
635 std::atexit(__report
);
641 _GLIBCXX_PROFILE_DATA(__global_lock
).unlock();
644 /** @brief This function must be called by each instrumentation point.
646 * The common path is inlined fully.
652 __profcxx_init_unconditional();
657 } // namespace __gnu_profile
659 #endif /* _GLIBCXX_PROFILE_PROFILER_TRACE_H */