3 // Copyright (C) 2009, 2010 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 terms
7 // of the GNU General Public License as published by the Free Software
8 // Foundation; either version 2, or (at your option) any later
11 // This library is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this library; see the file COPYING. If not, write to
18 // the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
19 // MA 02111-1307, USA.
21 // As a special exception, you may use this file as part of a free
22 // software library without restriction. Specifically, if other files
23 // instantiate templates or use macros or inline functions from this
24 // file, or you compile this file and link it with other files to
25 // produce an executable, this file does not by itself cause the
26 // resulting executable to be covered by the GNU General Public
27 // License. This exception does not however invalidate any other
28 // reasons why the executable file might be covered by the GNU General
31 /** @file profile/impl/profiler_trace.h
32 * @brief Data structures to represent profiling traces.
35 // Written by Lixia Liu and Silvius Rus.
37 #ifndef _GLIBCXX_PROFILE_PROFILER_TRACE_H
38 #define _GLIBCXX_PROFILE_PROFILER_TRACE_H 1
40 #include <cstdio> // fopen, fclose, fprintf, FILE
42 #include <cstdlib> // atof, atoi, strtol, getenv, atexit, abort
44 #ifdef __GXX_EXPERIMENTAL_CXX0X__
45 #define _GLIBCXX_IMPL_UNORDERED_MAP std::_GLIBCXX_STD_C::unordered_map
46 #include <unordered_map>
48 #include <tr1/unordered_map>
49 #define _GLIBCXX_IMPL_UNORDERED_MAP std::tr1::unordered_map
52 #include <ext/concurrence.h>
58 #include "profile/impl/profiler_algos.h"
59 #include "profile/impl/profiler_state.h"
60 #include "profile/impl/profiler_node.h"
62 namespace __gnu_profile
64 /** @brief Internal environment. Values can be set one of two ways:
65 1. In config file "var = value". The default config file path is
66 libstdcxx-profile.conf.
67 2. By setting process environment variables. For instance, in a Bash
68 shell you can set the unit cost of iterating through a map like this:
69 export __map_iterate_cost_factor=5.0.
70 If a value is set both in the input file and through an environment
71 variable, the environment value takes precedence. */
72 typedef _GLIBCXX_IMPL_UNORDERED_MAP<std::string, std::string> __env_t;
74 _GLIBCXX_PROFILE_DEFINE_UNINIT_DATA(__env_t, __env);
76 /** @brief Master lock. */
77 _GLIBCXX_PROFILE_DEFINE_UNINIT_DATA(__gnu_cxx::__mutex, __global_lock);
79 /** @brief Representation of a warning. */
84 const char* __warning_id;
85 std::string __warning_message;
88 : __magnitude(0.0), __context(0), __warning_id(0) { }
90 __warning_data(float __m, __stack_t __c, const char* __id,
91 const std::string& __msg)
92 : __magnitude(__m), __context(__c), __warning_id(__id),
93 __warning_message(__msg) { }
96 operator<(const __warning_data& __other) const
97 { return __magnitude < __other.__magnitude; }
100 typedef std::_GLIBCXX_STD_C::vector<__warning_data> __warning_vector_t;
102 // Defined in profiler_<diagnostic name>.h.
103 class __trace_hash_func;
104 class __trace_hashtable_size;
105 class __trace_map2umap;
106 class __trace_vector_size;
107 class __trace_vector_to_list;
108 class __trace_list_to_slist;
109 class __trace_list_to_vector;
110 void __trace_vector_size_init();
111 void __trace_hashtable_size_init();
112 void __trace_hash_func_init();
113 void __trace_vector_to_list_init();
114 void __trace_list_to_slist_init();
115 void __trace_list_to_vector_init();
116 void __trace_map_to_unordered_map_init();
117 void __trace_vector_size_report(FILE*, __warning_vector_t&);
118 void __trace_hashtable_size_report(FILE*, __warning_vector_t&);
119 void __trace_hash_func_report(FILE*, __warning_vector_t&);
120 void __trace_vector_to_list_report(FILE*, __warning_vector_t&);
121 void __trace_list_to_slist_report(FILE*, __warning_vector_t&);
122 void __trace_list_to_vector_report(FILE*, __warning_vector_t&);
123 void __trace_map_to_unordered_map_report(FILE*, __warning_vector_t&);
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 : __object_table(10000), __stack_table(10000),
197 __stack_table_byte_size(0), __id(0) { }
199 virtual ~__trace_base() { }
201 void __add_object(__object_t object, __object_info __info);
202 __object_info* __get_object_info(__object_t __object);
203 void __retire_object(__object_t __object);
204 void __write(FILE* __f);
205 void __collect_warnings(__warning_vector_t& __warnings);
208 __gnu_cxx::__mutex __object_table_lock;
209 __gnu_cxx::__mutex __stack_table_lock;
210 typedef _GLIBCXX_IMPL_UNORDERED_MAP<__object_t,
211 __object_info> __object_table_t;
212 typedef _GLIBCXX_IMPL_UNORDERED_MAP<__stack_t, __stack_info,
214 __stack_hash> __stack_table_t;
215 __object_table_t __object_table;
216 __stack_table_t __stack_table;
217 std::size_t __stack_table_byte_size;
223 template<typename __object_info, typename __stack_info>
225 __trace_base<__object_info, __stack_info>::
226 __collect_warnings(__warning_vector_t& __warnings)
228 for (typename __stack_table_t::iterator __it
229 = __stack_table.begin(); __it != __stack_table.end(); ++__it)
230 __warnings.push_back(__warning_data((*__it).second.__magnitude(),
232 (*__it).second.__advice()));
235 template<typename __object_info, typename __stack_info>
237 __trace_base<__object_info, __stack_info>::
238 __add_object(__object_t __object, __object_info __info)
241 || __object_table.size() * sizeof(__object_info) <= __max_mem())
243 this->__object_table_lock.lock();
244 __object_table.insert(typename __object_table_t::
245 value_type(__object, __info));
246 this->__object_table_lock.unlock();
250 template<typename __object_info, typename __stack_info>
252 __trace_base<__object_info, __stack_info>::
253 __get_object_info(__object_t __object)
255 // XXX: Revisit this to see if we can decrease mutex spans.
256 // Without this mutex, the object table could be rehashed during an
257 // insertion on another thread, which could result in a segfault.
258 this->__object_table_lock.lock();
259 typename __object_table_t::iterator __object_it
260 = __object_table.find(__object);
262 if (__object_it == __object_table.end())
264 this->__object_table_lock.unlock();
269 this->__object_table_lock.unlock();
270 return &__object_it->second;
274 template<typename __object_info, typename __stack_info>
276 __trace_base<__object_info, __stack_info>::
277 __retire_object(__object_t __object)
279 this->__object_table_lock.lock();
280 this->__stack_table_lock.lock();
281 typename __object_table_t::iterator __object_it
282 = __object_table.find(__object);
284 if (__object_it != __object_table.end())
286 const __object_info& __info = __object_it->second;
287 const __stack_t& __stack = __info.__stack();
288 typename __stack_table_t::iterator __stack_it
289 = __stack_table.find(__stack);
291 if (__stack_it == __stack_table.end())
293 // First occurence of this call context.
294 if (__max_mem() == 0 || __stack_table_byte_size < __max_mem())
296 __stack_table_byte_size
297 += (sizeof(__instruction_address_t) * __size(__stack)
298 + sizeof(__stack) + sizeof(__stack_info));
299 __stack_table.insert(make_pair(__stack,
300 __stack_info(__info)));
305 // Merge object info into info summary for this call context.
306 __stack_it->second.__merge(__info);
309 __object_table.erase(__object);
312 this->__object_table_lock.unlock();
313 this->__stack_table_lock.unlock();
316 template<typename __object_info, typename __stack_info>
318 __trace_base<__object_info, __stack_info>::
321 for (typename __stack_table_t::iterator __it
322 = __stack_table.begin(); __it != __stack_table.end(); ++__it)
323 if (__it->second.__is_valid())
325 std::fprintf(__f, __id);
326 std::fprintf(__f, "|");
327 __gnu_profile::__write(__f, __it->first);
328 std::fprintf(__f, "|");
329 __it->second.__write(__f);
334 __env_to_size_t(const char* __env_var, std::size_t __default_value)
336 char* __env_value = std::getenv(__env_var);
340 long __converted_value = std::strtol(__env_value, 0, 10);
341 if (errno || __converted_value < 0)
344 "Bad value for environment variable '%s'.\n",
349 return static_cast<std::size_t>(__converted_value);
352 return __default_value;
356 __set_max_stack_trace_depth()
358 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth)
359 = __env_to_size_t(_GLIBCXX_PROFILE_MAX_STACK_DEPTH_ENV_VAR,
360 _GLIBCXX_PROFILE_DATA(_S_max_stack_depth));
366 _GLIBCXX_PROFILE_DATA(_S_max_mem)
367 = __env_to_size_t(_GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC_ENV_VAR,
368 _GLIBCXX_PROFILE_DATA(_S_max_mem));
372 __log_magnitude(float __f)
374 const float __log_base = 10.0;
384 while (__f > __log_base)
389 return __sign * __result;
393 __open_output_file(const char* __extension)
395 // The path is made of _S_trace_file_name + "." + extension.
396 std::size_t __root_len
397 = __builtin_strlen(_GLIBCXX_PROFILE_DATA(_S_trace_file_name));
398 std::size_t __ext_len = __builtin_strlen(__extension);
399 char* __file_name = new char[__root_len + 1 + __ext_len + 1];
400 __builtin_memcpy(__file_name,
401 _GLIBCXX_PROFILE_DATA(_S_trace_file_name),
403 *(__file_name + __root_len) = '.';
404 __builtin_memcpy(__file_name + __root_len + 1,
405 __extension, __ext_len + 1);
407 FILE* __out_file = std::fopen(__file_name, "w");
410 std::fprintf(stderr, "Could not open trace file '%s'.\n",
415 delete[] __file_name;
427 operator()(const __warning_data& __info)
429 std::fprintf(__file, __info.__warning_id);
430 std::fprintf(__file, ": improvement = %d",
431 __log_magnitude(__info.__magnitude));
432 std::fprintf(__file, ": call stack = ");
433 __gnu_profile::__write(__file, __info.__context);
434 std::fprintf(__file, ": advice = %s\n",
435 __info.__warning_message.c_str());
439 /** @brief Final report method, registered with @b atexit.
441 * This can also be called directly by user code, including signal handlers.
442 * It is protected against deadlocks by the reentrance guard in profiler.h.
443 * However, when called from a signal handler that triggers while within
444 * __gnu_profile (under the guarded zone), no output will be produced.
449 _GLIBCXX_PROFILE_DATA(__global_lock).lock();
451 __warning_vector_t __warnings, __top_warnings;
453 FILE* __raw_file = __open_output_file("raw");
454 __trace_vector_size_report(__raw_file, __warnings);
455 __trace_hashtable_size_report(__raw_file, __warnings);
456 __trace_hash_func_report(__raw_file, __warnings);
457 __trace_vector_to_list_report(__raw_file, __warnings);
458 __trace_list_to_slist_report(__raw_file, __warnings);
459 __trace_list_to_vector_report(__raw_file, __warnings);
460 __trace_map_to_unordered_map_report(__raw_file, __warnings);
461 std::fclose(__raw_file);
463 // Sort data by magnitude, keeping just top N.
464 std::size_t __cutoff = std::min(_GLIBCXX_PROFILE_DATA(_S_max_warn_count),
466 __top_n(__warnings, __top_warnings, __cutoff);
468 FILE* __warn_file = __open_output_file("txt");
469 __for_each(__top_warnings.begin(), __top_warnings.end(),
470 __warn(__warn_file));
471 std::fclose(__warn_file);
473 _GLIBCXX_PROFILE_DATA(__global_lock).unlock();
479 char* __env_trace_file_name = std::getenv(_GLIBCXX_PROFILE_TRACE_ENV_VAR);
481 if (__env_trace_file_name)
482 _GLIBCXX_PROFILE_DATA(_S_trace_file_name) = __env_trace_file_name;
484 // Make sure early that we can create the trace file.
485 std::fclose(__open_output_file("txt"));
489 __set_max_warn_count()
491 char* __env_max_warn_count_str
492 = std::getenv(_GLIBCXX_PROFILE_MAX_WARN_COUNT_ENV_VAR);
494 if (__env_max_warn_count_str)
495 _GLIBCXX_PROFILE_DATA(_S_max_warn_count)
496 = static_cast<std::size_t>(std::atoi(__env_max_warn_count_str));
500 __read_cost_factors()
502 std::string __conf_file_name(_GLIBCXX_PROFILE_DATA(_S_trace_file_name));
503 __conf_file_name += ".conf";
505 std::ifstream __conf_file(__conf_file_name.c_str());
507 if (__conf_file.is_open())
511 while (std::getline(__conf_file, __line))
513 std::string::size_type __i = __line.find_first_not_of(" \t\n\v");
515 if (__line.length() <= 0 || __line[__i] == '#')
516 // Skip empty lines or comments.
521 __line.erase(__remove(__line.begin(), __line.end(), ' '),
523 std::string::size_type __pos = __line.find("=");
524 std::string __factor_name = __line.substr(0, __pos);
525 std::string::size_type __end = __line.find_first_of(";\n");
526 std::string __factor_value = __line.substr(__pos + 1, __end - __pos);
528 _GLIBCXX_PROFILE_DATA(__env)[__factor_name] = __factor_value;
532 struct __cost_factor_writer
536 __cost_factor_writer(FILE* __f)
540 operator() (const __cost_factor* __factor)
541 { std::fprintf(__file, "%s = %f\n", __factor->__env_var,
542 __factor->__value); }
546 __write_cost_factors()
548 FILE* __file = __open_output_file("conf.out");
549 __for_each(_GLIBCXX_PROFILE_DATA(__cost_factors)->begin(),
550 _GLIBCXX_PROFILE_DATA(__cost_factors)->end(),
551 __cost_factor_writer(__file));
555 struct __cost_factor_setter
558 operator()(__cost_factor* __factor)
560 // Look it up in the process environment first.
561 const char* __env_value = std::getenv(__factor->__env_var);
565 // Look it up in the config file.
566 __env_t::iterator __it
567 = _GLIBCXX_PROFILE_DATA(__env).find(__factor->__env_var);
568 if (__it != _GLIBCXX_PROFILE_DATA(__env).end())
569 __env_value = (*__it).second.c_str();
573 __factor->__value = std::atof(__env_value);
580 _GLIBCXX_PROFILE_DATA(__cost_factors) = new __cost_factor_vector;
581 _GLIBCXX_PROFILE_DATA(__cost_factors)->
582 push_back(&_GLIBCXX_PROFILE_DATA(__vector_shift_cost_factor));
583 _GLIBCXX_PROFILE_DATA(__cost_factors)->
584 push_back(&_GLIBCXX_PROFILE_DATA(__vector_iterate_cost_factor));
585 _GLIBCXX_PROFILE_DATA(__cost_factors)->
586 push_back(&_GLIBCXX_PROFILE_DATA(__vector_resize_cost_factor));
587 _GLIBCXX_PROFILE_DATA(__cost_factors)->
588 push_back(&_GLIBCXX_PROFILE_DATA(__list_shift_cost_factor));
589 _GLIBCXX_PROFILE_DATA(__cost_factors)->
590 push_back(&_GLIBCXX_PROFILE_DATA(__list_iterate_cost_factor));
591 _GLIBCXX_PROFILE_DATA(__cost_factors)->
592 push_back(&_GLIBCXX_PROFILE_DATA(__list_resize_cost_factor));
593 _GLIBCXX_PROFILE_DATA(__cost_factors)->
594 push_back(&_GLIBCXX_PROFILE_DATA(__map_insert_cost_factor));
595 _GLIBCXX_PROFILE_DATA(__cost_factors)->
596 push_back(&_GLIBCXX_PROFILE_DATA(__map_erase_cost_factor));
597 _GLIBCXX_PROFILE_DATA(__cost_factors)->
598 push_back(&_GLIBCXX_PROFILE_DATA(__map_find_cost_factor));
599 _GLIBCXX_PROFILE_DATA(__cost_factors)->
600 push_back(&_GLIBCXX_PROFILE_DATA(__map_iterate_cost_factor));
601 _GLIBCXX_PROFILE_DATA(__cost_factors)->
602 push_back(&_GLIBCXX_PROFILE_DATA(__umap_insert_cost_factor));
603 _GLIBCXX_PROFILE_DATA(__cost_factors)->
604 push_back(&_GLIBCXX_PROFILE_DATA(__umap_erase_cost_factor));
605 _GLIBCXX_PROFILE_DATA(__cost_factors)->
606 push_back(&_GLIBCXX_PROFILE_DATA(__umap_find_cost_factor));
607 _GLIBCXX_PROFILE_DATA(__cost_factors)->
608 push_back(&_GLIBCXX_PROFILE_DATA(__umap_iterate_cost_factor));
609 __for_each(_GLIBCXX_PROFILE_DATA(__cost_factors)->begin(),
610 _GLIBCXX_PROFILE_DATA(__cost_factors)->end(),
611 __cost_factor_setter());
615 __profcxx_init_unconditional()
617 _GLIBCXX_PROFILE_DATA(__global_lock).lock();
621 __set_max_warn_count();
623 if (_GLIBCXX_PROFILE_DATA(_S_max_warn_count) == 0)
627 __set_max_stack_trace_depth();
630 __read_cost_factors();
631 __set_cost_factors();
632 __write_cost_factors();
634 __trace_vector_size_init();
635 __trace_hashtable_size_init();
636 __trace_hash_func_init();
637 __trace_vector_to_list_init();
638 __trace_list_to_slist_init();
639 __trace_list_to_vector_init();
640 __trace_map_to_unordered_map_init();
642 std::atexit(__report);
648 _GLIBCXX_PROFILE_DATA(__global_lock).unlock();
651 /** @brief This function must be called by each instrumentation point.
653 * The common path is inlined fully.
659 __profcxx_init_unconditional();
664 } // namespace __gnu_profile
666 #endif /* _GLIBCXX_PROFILE_PROFILER_TRACE_H */