OSDN Git Service

37bcb6436e530afd2e0ed2ebdb667a1214d7ccd1
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / include / profile / impl / profiler_trace.h
1 // -*- C++ -*-
2 //
3 // Copyright (C) 2009 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 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
9 // version.
10
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.
15
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.
20
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
29 // Public License.
30
31 /** @file profile/impl/profiler_trace.h
32  *  @brief Data structures to represent profiling traces.
33  */
34
35 // Written by Lixia Liu and Silvius Rus.
36
37 #ifndef PROFCXX_PROFILER_TRACE_H__
38 #define PROFCXX_PROFILER_TRACE_H__ 1
39
40 #ifdef __GXX_EXPERIMENTAL_CXX0X__
41 #include <cerrno>
42 #include <cstdint>
43 #include <cstdio>
44 #include <cstdlib>
45 #define _GLIBCXX_IMPL_UNORDERED_MAP std::_GLIBCXX_STD_PR::unordered_map
46 #include <unordered_map>
47 #else
48 #include <errno.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <tr1/unordered_map>
53 #define _GLIBCXX_IMPL_UNORDERED_MAP std::tr1::unordered_map
54 #endif
55
56 #include <algorithm>
57 #include <utility>
58
59 #if defined _GLIBCXX_PROFILE_THREADS && defined HAVE_TLS
60 #include <pthread.h>
61 #endif
62
63 #include "profile/impl/profiler_state.h"
64 #include "profile/impl/profiler_node.h"
65
66 namespace __cxxprof_impl
67 {
68
69 #if defined _GLIBCXX_PROFILE_THREADS && defined HAVE_TLS
70 #define _GLIBCXX_IMPL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
71 typedef pthread_mutex_t __mutex_t;
72 /** @brief Pthread mutex wrapper.  */
73 template <int _Unused=0>
74 class __mutex {
75  public:
76   static __mutex_t __global_lock;
77   static void __lock(__mutex_t& __m) { pthread_mutex_lock(&__m); }
78   static void __unlock(__mutex_t& __m) { pthread_mutex_unlock(&__m); }
79 };
80 #else
81 #define _GLIBCXX_IMPL_MUTEX_INITIALIZER 0
82 typedef int __mutex_t;
83 /** @brief Mock mutex interface.  */
84 template <int _Unused=0>
85 class __mutex {
86  public:
87   static __mutex_t __global_lock;
88   static void __lock(__mutex_t& __m) {}
89   static void __unlock(__mutex_t& __m) {}
90 };
91 #endif
92
93 template <int _Unused>
94 __mutex_t __mutex<_Unused>::__global_lock = _GLIBCXX_IMPL_MUTEX_INITIALIZER;
95
96 /** @brief Representation of a warning.  */
97 struct __warning_data
98 {
99   float __magnitude;
100   __stack_t __context;
101   const char* __warning_id;
102   const char* __warning_message;
103   __warning_data();
104   __warning_data(float __m, __stack_t __c, const char* __id, 
105                  const char* __msg);
106   bool operator>(const struct __warning_data& other) const;
107 };
108
109 inline __warning_data::__warning_data()
110     : __magnitude(0.0), __context(NULL), __warning_id(NULL),
111       __warning_message(NULL)
112 {
113 }
114
115 inline __warning_data::__warning_data(float __m, __stack_t __c, 
116                                       const char* __id, const char* __msg)
117     : __magnitude(__m), __context(__c), __warning_id(__id),
118       __warning_message(__msg)
119 {
120 }
121
122 inline bool __warning_data::operator>(const struct __warning_data& other) const
123 {
124   return __magnitude > other.__magnitude;
125 }
126
127 typedef std::_GLIBCXX_STD_PR::vector<__warning_data> __warning_vector_t;
128
129 // Defined in profiler_<diagnostic name>.h.
130 class __trace_hash_func;
131 class __trace_hashtable_size;
132 class __trace_map2umap;
133 class __trace_vector_size;
134 class __trace_vector_to_list;
135 void __trace_vector_size_init();
136 void __trace_hashtable_size_init();
137 void __trace_hash_func_init();
138 void __trace_vector_to_list_init();
139 void __trace_map_to_unordered_map_init();
140 void __trace_vector_size_report(FILE*, __warning_vector_t&);
141 void __trace_hashtable_size_report(FILE*, __warning_vector_t&);
142 void __trace_hash_func_report(FILE*, __warning_vector_t&);
143 void __trace_vector_to_list_report(FILE*, __warning_vector_t&);
144 void __trace_map_to_unordered_map_report(FILE*, __warning_vector_t&);
145
146 // Utility functions.
147 inline size_t __max(size_t __a, size_t __b)
148 {
149   return __a >= __b ? __a : __b;
150 }
151
152 inline size_t __min(size_t __a, size_t __b)
153 {
154   return __a <= __b ? __a : __b;
155 }
156
157 /** @brief Storage for diagnostic table entries.  Has only static fields.  */
158 template <int _Unused=0>
159 class __tables
160 {
161  public:
162   static __trace_hash_func* _S_hash_func;
163   static __trace_hashtable_size* _S_hashtable_size;
164   static __trace_map2umap* _S_map2umap;
165   static __trace_vector_size* _S_vector_size;
166   static __trace_vector_to_list* _S_vector_to_list;
167 };
168
169 template <int _Unused>
170 __trace_hash_func* __tables<_Unused>::_S_hash_func = NULL;
171 template <int _Unused>
172 __trace_hashtable_size* __tables<_Unused>::_S_hashtable_size = NULL;
173 template <int _Unused>
174 __trace_map2umap* __tables<_Unused>::_S_map2umap = NULL;
175 template <int _Unused>
176 __trace_vector_size* __tables<_Unused>::_S_vector_size = NULL;
177 template <int _Unused>
178 __trace_vector_to_list* __tables<_Unused>::_S_vector_to_list = NULL;
179
180 /** @brief Storage for user defined parameters.  Has only static fields.  */
181 template <int _Unused=0>
182 class __settings {
183  public:
184   static const char* _S_trace_file_name;
185   static size_t _S_max_warn_count;
186   static size_t _S_max_stack_depth;
187   static size_t _S_max_mem;
188 };
189
190 template <int _Unused>
191 const char* __settings<_Unused>::_S_trace_file_name = 
192     _GLIBCXX_PROFILE_TRACE_PATH_ROOT;
193 template <int _Unused>
194 size_t __settings<_Unused>::_S_max_warn_count =
195     _GLIBCXX_PROFILE_MAX_WARN_COUNT;
196 template <int _Unused>
197 size_t __settings<_Unused>::_S_max_stack_depth =
198     _GLIBCXX_PROFILE_MAX_STACK_DEPTH;
199 template <int _Unused>
200 size_t __settings<_Unused>::_S_max_mem =
201     _GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC;
202
203 inline size_t __stack_max_depth()
204 {
205   return __settings<0>::_S_max_stack_depth;
206 }
207
208 inline size_t __max_mem()
209 {
210   return __settings<0>::_S_max_mem;
211 }
212
213 /** @brief Base class for all trace producers.  */
214 template <typename __object_info, typename __stack_info>
215 class __trace_base
216 {
217  public:
218   __trace_base();
219   virtual ~__trace_base() {}
220
221   void __add_object(__object_t object, __object_info __info);
222   __object_info* __get_object_info(__object_t __object);
223   void __retire_object(__object_t __object);
224   void __write(FILE* f);
225   void __collect_warnings(__warning_vector_t& warnings);
226
227   void __lock_object_table();
228   void __lock_stack_table();
229   void __unlock_object_table();
230   void __unlock_stack_table();
231
232  private:
233   __mutex_t __object_table_lock;
234   __mutex_t __stack_table_lock;
235   typedef _GLIBCXX_IMPL_UNORDERED_MAP<__object_t, 
236                                       __object_info> __object_table_t;
237   typedef _GLIBCXX_IMPL_UNORDERED_MAP<__stack_t, __stack_info, __stack_hash, 
238                                       __stack_hash> __stack_table_t;
239   __object_table_t __object_table;
240   __stack_table_t __stack_table;
241   size_t __stack_table_byte_size;
242
243  protected:
244   const char* __id;
245 };
246
247 template <typename __object_info, typename __stack_info>
248 void __trace_base<__object_info, __stack_info>::__collect_warnings(
249     __warning_vector_t& warnings)
250 {
251   typename __stack_table_t::iterator __i = __stack_table.begin();
252   for ( ; __i != __stack_table.end(); ++__i )
253   {
254     warnings.push_back(__warning_data((*__i).second.__magnitude(), 
255                                       (*__i).first, 
256                                       __id,
257                                       (*__i).second.__advice()));
258   }
259 }
260
261 template <typename __object_info, typename __stack_info>
262 void __trace_base<__object_info, __stack_info>::__lock_object_table()
263 {
264   __mutex<0>::__lock(this->__object_table_lock);
265 }
266
267 template <typename __object_info, typename __stack_info>
268 void __trace_base<__object_info, __stack_info>::__lock_stack_table()
269 {
270   __mutex<0>::__lock(this->__stack_table_lock);
271 }
272
273 template <typename __object_info, typename __stack_info>
274 void __trace_base<__object_info, __stack_info>::__unlock_object_table()
275 {
276   __mutex<0>::__unlock(this->__object_table_lock);
277 }
278
279 template <typename __object_info, typename __stack_info>
280 void __trace_base<__object_info, __stack_info>::__unlock_stack_table()
281 {
282   __mutex<0>::__unlock(this->__stack_table_lock);
283 }
284
285 template <typename __object_info, typename __stack_info>
286 __trace_base<__object_info, __stack_info>::__trace_base()
287 {
288   // Do not pick the initial size too large, as we don't know which diagnostics
289   // are more active.
290   __object_table.rehash(10000);
291   __stack_table.rehash(10000);
292   __stack_table_byte_size = 0;
293   __id = NULL;
294   __object_table_lock = __stack_table_lock = _GLIBCXX_IMPL_MUTEX_INITIALIZER;
295 }
296
297 template <typename __object_info, typename __stack_info>
298 void __trace_base<__object_info, __stack_info>::__add_object(
299     __object_t __object, __object_info __info)
300 {
301   if (__max_mem() == 0 
302       || __object_table.size() * sizeof(__object_info) <= __max_mem()) {
303     __lock_object_table();
304     __object_table.insert(
305         typename __object_table_t::value_type(__object, __info));
306     __unlock_object_table();
307   }
308 }
309
310 template <typename __object_info, typename __stack_info>
311 __object_info* __trace_base<__object_info, __stack_info>::__get_object_info(
312     __object_t __object)
313 {
314   // XXX: Revisit this to see if we can decrease mutex spans.
315   // Without this mutex, the object table could be rehashed during an
316   // insertion on another thread, which could result in a segfault.
317   __lock_object_table();
318   typename __object_table_t::iterator __object_it = 
319       __object_table.find(__object);
320   if (__object_it == __object_table.end()){
321     __unlock_object_table();
322     return NULL;
323   } else {
324     __unlock_object_table();
325     return &__object_it->second;
326   }
327 }
328
329 template <typename __object_info, typename __stack_info>
330 void __trace_base<__object_info, __stack_info>::__retire_object(
331     __object_t __object)
332 {
333   __lock_object_table();
334   __lock_stack_table();
335   typename __object_table_t::iterator __object_it =
336       __object_table.find(__object);
337   if (__object_it != __object_table.end()){
338     const __object_info& __info = __object_it->second;
339     const __stack_t& __stack = __info.__stack();
340     typename __stack_table_t::iterator __stack_it = 
341         __stack_table.find(__stack);
342     if (__stack_it == __stack_table.end()) {
343       // First occurence of this call context.
344       if (__max_mem() == 0 || __stack_table_byte_size < __max_mem()) {
345         __stack_table_byte_size += 
346             (sizeof(__instruction_address_t) * __size(__stack)
347              + sizeof(__stack) + sizeof(__stack_info));
348         __stack_table.insert(make_pair(__stack, __stack_info(__info)));
349       }
350     } else {
351       // Merge object info into info summary for this call context.
352       __stack_it->second.__merge(__info);
353       delete __stack;
354     }
355     __object_table.erase(__object);
356   }
357   __unlock_stack_table();
358   __unlock_object_table();
359 }
360
361 template <typename __object_info, typename __stack_info>
362 void __trace_base<__object_info, __stack_info>::__write(FILE* __f)
363 {
364   typename __stack_table_t::iterator __it;
365
366   for (__it = __stack_table.begin(); __it != __stack_table.end(); __it++) {
367     if (__it->second.__is_valid()) {
368       fprintf(__f, __id);
369       fprintf(__f, "|");
370       __cxxprof_impl::__write(__f, __it->first);
371       fprintf(__f, "|");
372       __it->second.__write(__f);
373     }
374   }
375 }
376
377 inline size_t __env_to_size_t(const char* __env_var, size_t __default_value)
378 {
379   char* __env_value = getenv(__env_var);
380   if (__env_value) {
381     long int __converted_value = strtol(__env_value, NULL, 10);
382     if (errno || __converted_value < 0) {
383       fprintf(stderr, "Bad value for environment variable '%s'.", __env_var);
384       abort();
385     } else {
386       return static_cast<size_t>(__converted_value);
387     }
388   } else {
389     return __default_value;
390   }
391 }
392
393 inline void __set_max_stack_trace_depth()
394 {
395   __settings<0>::_S_max_stack_depth = __env_to_size_t(
396       _GLIBCXX_PROFILE_MAX_STACK_DEPTH_ENV_VAR,
397       __settings<0>::_S_max_stack_depth);
398 }
399
400 inline void __set_max_mem()
401 {
402   __settings<0>::_S_max_mem = __env_to_size_t(
403       _GLIBCXX_PROFILE_MEM_PER_DIAGNOSTIC_ENV_VAR, __settings<0>::_S_max_mem);
404 }
405
406 inline int __log_magnitude(float f)
407 {
408   const float log_base = 10.0;
409   int result = 0;
410   int sign = 1;
411   if (f < 0) {
412     f = -f;
413     sign = -1;
414   }
415   while (f > log_base) {
416     ++result;
417     f /= 10.0;
418   }
419   return sign * result;
420 }
421
422 struct __warn
423 {
424   FILE* __file;
425   __warn(FILE* __f) { __file = __f; }
426   void operator() (const __warning_data& __info)
427   {
428     fprintf(__file,  __info.__warning_id);
429     fprintf(__file, ": improvement = %d", __log_magnitude(__info.__magnitude));
430     fprintf(__file, ": call stack = ");
431     __cxxprof_impl::__write(__file, __info.__context);
432     fprintf(__file, ": advice = %s\n", __info.__warning_message);
433   }
434 };
435
436 inline FILE* __open_output_file(const char* extension)
437 {
438   // The path is made of _S_trace_file_name + "." + extension.
439   size_t root_len = strlen(__settings<0>::_S_trace_file_name);
440   size_t ext_len = strlen(extension);
441   char* file_name = new char[root_len + 1 + ext_len + 1];
442   char* p = file_name;
443   memcpy(file_name, __settings<0>::_S_trace_file_name, root_len);
444   *(file_name + root_len) = '.';
445   memcpy(file_name + root_len + 1, extension, ext_len + 1);
446   FILE* out_file = fopen(file_name, "w");
447   if (out_file) {
448     return out_file;
449   } else {
450     fprintf(stderr, "Could not open trace file '%s'.", file_name);
451     abort();
452   }
453 }
454
455 /** @brief Final report method, registered with "atexit".
456  *
457  * This can also be called directly by user code, including signal handlers.
458  * It is protected against deadlocks by the reentrance guard in profiler.h.
459  * However, when called from a signal handler that triggers while within
460  * __cxxprof_impl (under the guarded zone), no output will be produced.
461  */
462 inline void __report(void)
463 {
464   __mutex<0>::__lock(__mutex<0>::__global_lock);
465
466   __warning_vector_t __warnings;
467
468   FILE* __raw_file = __open_output_file("raw");
469   __trace_vector_size_report(__raw_file, __warnings);
470   __trace_hashtable_size_report(__raw_file, __warnings);
471   __trace_hash_func_report(__raw_file, __warnings);
472   __trace_vector_to_list_report(__raw_file, __warnings);
473   __trace_map_to_unordered_map_report(__raw_file, __warnings);
474   fclose(__raw_file);
475
476   // Sort data by magnitude.
477   // XXX: instead of sorting, should collect only top N for better performance.
478   size_t __cutoff = __min(__settings<0>::_S_max_warn_count, 
479                           __warnings.size());
480
481   std::sort(__warnings.begin(), __warnings.end(),
482             std::greater<__warning_vector_t::value_type>());
483   __warnings.resize(__cutoff);
484
485   FILE* __warn_file = __open_output_file("txt");
486   std::for_each(__warnings.begin(), __warnings.end(), __warn(__warn_file));
487   fclose(__warn_file);
488
489   __mutex<0>::__unlock(__mutex<0>::__global_lock);
490 }
491
492 inline void __set_trace_path()
493 {
494   char* __env_trace_file_name = getenv(_GLIBCXX_PROFILE_TRACE_ENV_VAR);
495
496   if (__env_trace_file_name) { 
497     __settings<0>::_S_trace_file_name = __env_trace_file_name; 
498   }
499
500   // Make sure early that we can create the trace file.
501   fclose(__open_output_file("txt"));
502 }
503
504 inline void __set_max_warn_count()
505 {
506   char* __env_max_warn_count_str = getenv(
507       _GLIBCXX_PROFILE_MAX_WARN_COUNT_ENV_VAR);
508
509   if (__env_max_warn_count_str) {
510     __settings<0>::_S_max_warn_count = static_cast<size_t>(
511         atoi(__env_max_warn_count_str));
512   }
513 }
514
515 inline void __profcxx_init_unconditional()
516 {
517   __mutex<0>::__lock(__mutex<0>::__global_lock);
518
519   __set_max_warn_count();
520
521   if (__is_invalid()) {
522
523     if (__settings<0>::_S_max_warn_count == 0) {
524
525       __turn_off();
526
527     } else {
528
529       __set_max_stack_trace_depth();
530       __set_max_mem();
531       __set_trace_path();
532
533       __trace_vector_size_init();
534       __trace_hashtable_size_init();
535       __trace_hash_func_init();
536       __trace_vector_to_list_init();
537       __trace_map_to_unordered_map_init();
538
539       atexit(__report);
540
541       __turn_on();
542
543     }
544   }
545
546   __mutex<0>::__unlock(__mutex<0>::__global_lock);
547 }
548
549 /** @brief This function must be called by each instrumentation point.
550  *
551  * The common path is inlined fully.
552  */
553 inline bool __profcxx_init(void)
554 {
555   if (__is_invalid()) {
556     __profcxx_init_unconditional();
557   }
558
559   return __is_on();
560 }
561
562 } // namespace __cxxprof_impl
563
564 #endif /* PROFCXX_PROFILER_TRACE_H__ */