[gcc/testsuite]
[official-gcc.git] / libstdc++-v3 / include / profile / impl / profiler_trace.h
blob59756fabdadd604860686957e2b4c1f675c43846
1 // -*- C++ -*-
2 //
3 // Copyright (C) 2009-2017 Free Software Foundation, Inc.
4 //
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)
9 // any later version.
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
34 #include <cerrno>
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
40 #else
41 #include <tr1/unordered_map>
42 #define _GLIBCXX_IMPL_UNORDERED_MAP std::tr1::unordered_map
43 #endif
45 #include <ext/concurrence.h>
46 #include <fstream>
47 #include <string>
48 #include <utility>
49 #include <vector>
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. */
73 struct __warning_data
75 float __magnitude;
76 __stack_t __context;
77 const char* __warning_id;
78 std::string __warning_message;
80 __warning_data()
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) { }
88 bool
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();
125 struct __cost_factor
127 const char* __env_var;
128 float __value;
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);
180 inline std::size_t
181 __stack_max_depth()
182 { return _GLIBCXX_PROFILE_DATA(_S_max_stack_depth); }
184 inline std::size_t
185 __max_mem()
186 { return _GLIBCXX_PROFILE_DATA(_S_max_mem); }
188 /** @brief Base class for all trace producers. */
189 template<typename __object_info, typename __stack_info>
190 class __trace_base
192 public:
193 // Do not pick the initial size too large, as we don't know which
194 // diagnostics are more active.
195 __trace_base()
196 : __objects_byte_size(0), __stack_table(10000),
197 __stack_table_byte_size(0), __id(0) { }
199 ~__trace_base()
201 for (typename __stack_table_t::iterator __it
202 = __stack_table.begin(); __it != __stack_table.end(); ++__it)
203 delete __it->first;
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);
210 void __free();
212 private:
213 __gnu_cxx::__mutex __trace_mutex;
214 typedef _GLIBCXX_IMPL_UNORDERED_MAP<__stack_t, __stack_info,
215 __stack_hash,
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;
221 protected:
222 const char* __id;
225 template<typename __object_info, typename __stack_info>
226 __object_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.
231 if (!__stack)
232 return 0;
234 __gnu_cxx::__scoped_lock __lock(this->__trace_mutex);
236 if (__max_mem() != 0 && __objects_byte_size >= __max_mem())
238 delete __stack;
239 return 0;
242 __object_info* __ret = new(std::nothrow) __object_info(__stack);
243 if (!__ret)
245 delete __stack;
246 return 0;
249 __objects_byte_size += sizeof(__object_info);
250 return __ret;
253 template<typename __object_info, typename __stack_info>
254 void
255 __trace_base<__object_info, __stack_info>::
256 __retire_object(__object_info* __obj_info)
258 if (!__obj_info)
259 return;
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)));
279 else
280 delete __stack;
282 else
284 // Merge object info into info summary for this call context.
285 __stack_it->second.__merge(__info);
286 delete __stack;
289 delete __obj_info;
290 __objects_byte_size -= sizeof(__object_info);
293 template<typename __object_info, typename __stack_info>
294 void
295 __trace_base<__object_info, __stack_info>::
296 __write(FILE* __f)
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>
311 void
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(),
318 __it->first, __id,
319 __it->second.__advice()));
322 template<typename __object_info, typename __stack_info>
323 inline void
324 __trace_report(__trace_base<__object_info, __stack_info>* __cont,
325 FILE* __f, __warning_vector_t& __warnings)
327 if (__cont)
329 __cont->__collect_warnings(__warnings);
330 __cont->__write(__f);
334 inline std::size_t
335 __env_to_size_t(const char* __env_var, std::size_t __default_value)
337 char* __env_value = std::getenv(__env_var);
338 if (__env_value)
340 errno = 0;
341 long __converted_value = std::strtol(__env_value, 0, 10);
342 if (errno || __converted_value < 0)
344 std::fprintf(stderr,
345 "Bad value for environment variable '%s'.\n",
346 __env_var);
347 std::abort();
349 else
350 return static_cast<std::size_t>(__converted_value);
352 else
353 return __default_value;
356 inline void
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));
364 inline void
365 __set_max_mem()
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));
372 inline int
373 __log_magnitude(float __f)
375 const float __log_base = 10.0;
376 int __result = 0;
377 int __sign = 1;
379 if (__f < 0)
381 __f = -__f;
382 __sign = -1;
385 while (__f > __log_base)
387 ++__result;
388 __f /= 10.0;
390 return __sign * __result;
393 inline FILE*
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),
403 __root_len);
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");
409 if (!__out_file)
411 std::fprintf(stderr, "Could not open trace file '%s'.\n",
412 __file_name);
413 std::abort();
416 delete[] __file_name;
417 return __out_file;
420 struct __warn
422 FILE* __file;
424 __warn(FILE* __f)
425 { __file = __f; }
427 void
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.
447 inline void
448 __report()
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),
466 __warnings.size());
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);
475 inline void
476 __report_and_free()
478 __report();
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);
490 inline void
491 __set_trace_path()
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"));
502 inline void
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));
513 inline void
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())
523 std::string __line;
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.
531 continue;
534 // Trim.
535 __line.erase(__remove(__line.begin(), __line.end(), ' '),
536 __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
548 FILE* __file;
550 __cost_factor_writer(FILE* __f)
551 : __file(__f) { }
553 void
554 operator() (const __cost_factor* __factor)
555 { std::fprintf(__file, "%s = %f\n", __factor->__env_var,
556 __factor->__value); }
559 inline void
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));
566 std::fclose(__file);
569 struct __cost_factor_setter
571 void
572 operator()(__cost_factor* __factor)
574 // Look it up in the process environment first.
575 const char* __env_value = std::getenv(__factor->__env_var);
577 if (!__env_value)
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();
586 if (__env_value)
587 __factor->__value = std::atof(__env_value);
591 inline void
592 __set_cost_factors()
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());
613 inline void
614 __profcxx_init_unconditional()
616 __gnu_cxx::__scoped_lock __lock(_GLIBCXX_PROFILE_DATA(__global_mutex));
618 if (__is_invalid())
620 __set_max_warn_count();
622 if (_GLIBCXX_PROFILE_DATA(_S_max_warn_count) == 0)
623 __turn_off();
624 else
626 __set_max_stack_trace_depth();
627 __set_max_mem();
628 __set_trace_path();
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);
643 __turn_on();
648 /** @brief This function must be called by each instrumentation point.
650 * The common path is inlined fully.
652 inline bool
653 __profcxx_init()
655 if (__is_invalid())
656 __profcxx_init_unconditional();
658 return __is_on();
661 } // namespace __gnu_profile
663 #endif /* _GLIBCXX_PROFILE_PROFILER_TRACE_H */