CACAO
rt-timing.hpp
Go to the documentation of this file.
1 /* src/vm/rt-timing.hpp - POSIX real-time timing utilities
2 
3  Copyright (C) 1996-2013
4  CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5 
6  This file is part of CACAO.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2, or (at
11  your option) any later version.
12 
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  02110-1301, USA.
22 
23 */
24 
25 /**
26  * @file
27  *
28  * This file contains the real-time timing utilities.
29  *
30  * @ingroup rt-timing
31  */
32 
33 /**
34  * @defgroup rt-timing Real-time timing framework
35  * Real-time timing framework.
36  *
37  * For day to day use only the following macros are of importance:
38  * - RT_REGISTER_TIMER(var,name,description)
39  * - RT_REGISTER_GROUP_TIMER(var,name,description,group)
40  * - RT_REGISTER_GROUP(var,name,description)
41  * - RT_REGISTER_SUBGROUP(var,name,description,group)
42  * - RT_TIMER_START(var)
43  * - RT_TIMER_STOP(var)
44  * - RT_TIMER_STOPSTART(var1,var2)
45  * - RT_REGISTER_TIMER_EXTERN(var,name,description)
46  * - RT_REGISTER_GROUP_TIMER_EXTERN(var,name,description,group)
47  * - RT_REGISTER_GROUP_EXTERN(var,name,description)
48  * - RT_REGISTER_SUBGROUP_EXTERN(var,name,description,group)
49  * - RT_DECLARE_TIMER(var)
50  * - RT_DECLARE_GROUP(var)
51  *
52  * A typical usage would look something like this:
53  *
54  * @code
55  * // NOTE: toplevel, outside a function!
56  * RT_REGISTER_GROUP(my_group,"my-group","this is my very own timing group");
57  * RT_REGISTER_GROUP_TIMER(my_timer1,"my-timer1","this is my first timer",my_group);
58  * RT_REGISTER_GROUP_TIMER(my_timer2,"my-timer1","this is my second timer",my_group);
59  *
60  * void do_something() {
61  * RT_TIMER_START(my_timer1);
62  * // do some work
63  * RT_TIMER_STOPSTART(my_timer1,my_timer2);
64  * // do even more work
65  * RT_TIMER_STOP(my_timer2);
66  * }
67  * @endcode
68  *
69  * @note
70  * The implementation of the RT_REGISTER_* macros is a bit tricky. In general
71  * we simply want to create global variables for groups and timers. Unfortunately
72  * RTGroup and RTTimer are no "plain old datatypes" (pod). There is a constructor
73  * which is dependant on other global non-pod variables. The C++ standard does not
74  * guarantee any specific order of construction so it might be the case that
75  * a subgroup is registered before its super group. There are many dirty tricks to
76  * work around this issue. The one we are using does the following. We create a global
77  * variable as well as a function which returns this variable. The C++ standard
78  * guarantees that a global variable is initialized before its first use. Therefore,
79  * if we access the group/timer variables only through these functions it is
80  * guaranteed that everything is initialized in the right order.
81  * Also note that the variable must be global (i.e. they can not be declared as
82  * static function variables) otherwise the objects might be destroyed eagerly.
83  */
84 
85 /**
86  * @addtogroup rt-timing
87  * @{
88  */
89 
90 /**
91  * @def RT_REGISTER_TIMER(var,name,description)
92  * Register a new (toplevel) timer. Create a timer and add it to the toplevel
93  * timing group RTGroup::root().
94  *
95  * @note This macro creates a function RTTimer& var(). Consider using namespaces
96  * to avoid name clashes.
97  */
98 
99 /**
100  * @def RT_REGISTER_GROUP_TIMER(var,name,description,group)
101  * Register a new timer. Create a timer and add it to group specified.
102  *
103  * @note This macro creates a function RTTimer& var(). Consider using namespaces
104  * to avoid name clashes.
105  */
106 
107 /**
108  * @def RT_REGISTER_GROUP(var,name,description)
109  * Register a new (toplevel) group. Create a group and add it to the toplevel
110  * timing group RTGroup::root().
111  *
112  * @note This macro creates a function RTGroup& var(). Consider using namespaces
113  * to avoid name clashes.
114  */
115 
116 /**
117  * @def RT_REGISTER_SUBGROUP(var,name,description,group)
118  * Register a new subgroup. Create a group and add it to group specified.
119  *
120  * @note This macro creates a function RTGroup& var(). Consider using namespaces
121  * to avoid name clashes.
122  */
123 
124 /**
125  * @def RT_REGISTER_TIMER_EXTERN(var,name,description)
126  *
127  * Define external timer.
128  *
129  * @see RT_REGISTER_TIMER
130  * @see RT_DECLARE_TIMER
131  **/
132 
133 /**
134  * @def RT_REGISTER_GROUP_TIMER_EXTERN(var,name,description,group)
135  *
136  * Define external group timer.
137  *
138  * @see RT_REGISTER_GROUP_TIMER
139  * @see RT_DECLARE_TIMER
140  **/
141 
142 /**
143  * @def RT_REGISTER_GROUP_EXTERN(var,name,description)
144  *
145  * Define external group.
146  *
147  * @see RT_REGISTER_GROUP
148  * @see RT_DECLARE_GROUP
149  **/
150 
151 /**
152  * @def RT_REGISTER_SUBGROUP_EXTERN(var,name,description,group)
153  *
154  * Define external subgroup.
155  *
156  * @see RT_REGISTER_SUBGROUP
157  * @see RT_DECLARE_GROUP
158  **/
159 
160 /**
161  * @def RT_DECLARE_TIMER(var)
162  *
163  * Declare an external timer variable.
164  *
165  * @see RT_REGISTER_TIMER_EXTERN
166  * @see RT_REGISTER_GROUP_TIMER_EXTERN
167  **/
168 
169 /**
170  * @def RT_DECLARE_GROUP(var)
171  *
172  * Declare an external timer group.
173  *
174  * @see RT_REGISTER_GROUP_EXTERN
175  * @see RT_REGISTER_SUBGROUP_EXTERN
176  **/
177 
178 /**
179  * @def RT_TIMER_START(var)
180  * Start the timer var.
181  */
182 
183 /**
184  * @def RT_TIMER_STOP(var)
185  * Stop the timer var.
186  */
187 
188 /**
189  * @def RT_TIMER_STOPSTART(var1,var2)
190  * Stop the timer var1 and start the timer var2
191  */
192 
193 /** @} */
194 #ifndef _RT_TIMING_HPP
195 #define _RT_TIMING_HPP
196 
197 #include "config.h"
198 
199 #if defined(ENABLE_RT_TIMING)
200 
201 #include <ctime>
202 #include <cstdlib>
203 #include <cerrno>
204 
205 #include "vm/types.hpp"
206 
207 #include "mm/memory.hpp"
208 
209 #include "vm/global.hpp"
210 
211 #include "toolbox/logging.hpp"
212 
213 // debugging macro
214 // @note: LOG* can not be used because opt_DebugName is not initialized
215 #if 0
216 #define _RT_LOG(expr) do { cacao::dbg() <<expr;} while (0)
217 #else
218 #define _RT_LOG(expr)
219 #endif
220 
221 //namespace {
222 #ifdef __DARWIN__
223 #include <mach/mach.h>
224 #include <mach/mach_time.h>
225 #endif
226 #if 0
227 /**
228  * @note: http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
229  */
230 inline timespec operator-(const timespec &a, timespec b) {
231  timespec result;
232  /* Perform the carry for the later subtraction by updating b. */
233  if (a.tv_nsec < b.tv_nsec) {
234  int xsec = (b.tv_nsec - a.tv_nsec) / 1000000000L + 1;
235  b.tv_nsec -= 1000000000L * xsec;
236  b.tv_sec += xsec;
237  }
238  if (a.tv_nsec - b.tv_nsec > 1000000000L) {
239  int xsec = (a.tv_nsec - b.tv_nsec) / 1000000000L;
240  b.tv_nsec += 1000000000L * xsec;
241  b.tv_sec -= xsec;
242  }
243 
244  /* Compute the time remaining to wait. tv_usec is certainly positive. */
245  result.tv_sec = a.tv_sec - b.tv_sec;
246  result.tv_nsec = a.tv_nsec - b.tv_nsec;
247 
248  return result;
249 }
250 
251 inline timespec operator+(const timespec a, const timespec &b) {
252  timespec result;
253  result.tv_sec = a.tv_sec + b.tv_sec;
254  result.tv_nsec = a.tv_nsec + b.tv_nsec;
255  if (result.tv_nsec > 1000000000L) {
256  result.tv_nsec -= 1000000000L;
257  result.tv_sec ++;
258  }
259 
260  return result;
261 }
262 #endif
263 
264 inline void operator+=(timespec &result, const timespec &b) {
265  result.tv_sec += b.tv_sec;
266  result.tv_nsec += b.tv_nsec;
267  if (result.tv_nsec > 1000000000L) {
268  result.tv_nsec -= 1000000000L;
269  result.tv_sec ++;
270  }
271 }
272 
273 inline timespec operator/(const timespec &a, const int b) {
274  long int r; // remainder
275  timespec result;
276 
277  if (b==0) {
278  result.tv_sec = -1;
279  result.tv_nsec = -1;
280  return result;
281  }
282 
283  r = a.tv_sec % b;
284  result.tv_sec = a.tv_sec / b;
285  result.tv_nsec = r * 1000000000L / b;
286  result.tv_nsec += a.tv_nsec / b;
287 
288  return result;
289 }
290 
291 /**
292  * @note: this is not accurate
293  */
294 inline long int operator/(const timespec &a, const timespec &b) {
295  if (a.tv_sec != 0) {
296  if (b.tv_sec == 0) {
297  return -1;
298  } else {
299  return a.tv_sec / b.tv_sec;
300  }
301  } else {
302  if (b.tv_nsec == 0) {
303  return -1;
304  } else {
305  return a.tv_nsec / b.tv_nsec;
306  }
307  }
308 }
309 
310 inline bool operator==(const timespec &a, const timespec &b) {
311  return (a.tv_sec == b.tv_sec) && (a.tv_nsec == b.tv_nsec);
312 }
313 //} // end anonymous namespace
314 
315 namespace cacao {
316 namespace {
317 inline OStream& operator<<(OStream &ostr, timespec ts) {
318  const char *unit;
319  if (ts.tv_sec >= 10) {
320  // display seconds if at least 10 sec
321  ostr << ts.tv_sec;
322  unit = "sec";
323  } else {
324  ts.tv_nsec += ts.tv_sec * 1000000000L;
325  if (ts.tv_nsec >= 100000000) {
326  // display milliseconds if at least 100ms
327  ostr << ts.tv_nsec/1000000;
328  unit = "msec";
329  } else {
330  // otherwise display microseconds
331  ostr << ts.tv_nsec/1000;
332  unit = "usec";
333  }
334  }
335  ostr << setw(5) << unit;
336  return ostr;
337 }
338 
339 #ifndef __DARWIN__
340 inline void rt_timing_gettime_inline(timespec &ts) {
341  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID,&ts) != 0) {
342  fprintf(stderr,"could not get time by clock_gettime: %s\n",strerror(errno));
343  abort();
344  }
345 }
346 #endif
347 
348 inline long rt_timing_diff_usec_inline(const timespec &a, const timespec &b)
349 {
350  long diff;
351  time_t atime;
352 
353  diff = (b.tv_nsec - a.tv_nsec) / 1000;
354  atime = a.tv_sec;
355  while (atime < b.tv_sec) {
356  atime++;
357  diff += 1000000;
358  }
359  return diff;
360 }
361 
362 } // end anonymous namespace
363 } // end namespace cacao
364 
365 #include <vector>
366 #include <deque>
367 
368 namespace cacao {
369 
370 /**
371  * @addtogroup rt-timing
372  * @{
373  */
374 
375 /**
376  * Superclass of real-time group entries.
377  */
378 class RTEntry {
379 protected:
380  const char* name;
381  const char* description;
382  /// dummy constructor
383  RTEntry() : name(NULL), description(NULL) {}
384 
385  typedef std::deque<const RTEntry*> RtStack;
386 
387  void print_csv_entry(OStream &O,RtStack &s,timespec ts) const {
388  for(RtStack::const_iterator i = s.begin(), e = s.end() ; i != e; ++i) {
389  O << (*i)->name << '.';
390  }
391  O << name << ';' << description << ';';
392  if (ts.tv_sec) {
393  O << ts.tv_sec << cacao::setz(9) << ts.tv_nsec;
394  }
395  else {
396  O << ts.tv_nsec;
397  }
398  O << cacao::nl;
399  }
400 public:
401  static timespec invalid_ts; //< invalid time stamp
402  /**
403  * Constructor.
404  */
405  RTEntry(const char* name, const char* description) : name(name), description(description) {
406  _RT_LOG("RTEntry() name: " << name << nl);
407  }
408  /**
409  * Destructor.
410  */
411  virtual ~RTEntry() {
412  _RT_LOG("~RTEntry() name: " << name << nl);
413  }
414  /**
415  * Print the timing information to an OStream.
416  * @param[in,out] O output stream
417  * @param[in] ref time reference. Used to calculate percentage .
418  * Normally the time of the parent.
419  */
420  virtual void print(OStream &O,timespec ref) const = 0;
421  void print_csv(OStream &O) const {
422  RtStack s;
423  print_csv_intern(O,s);
424  }
425  static void print_csv_header(OStream &O) {
426  O << "name;description;value" << nl;
427  }
428  virtual void print_csv_intern(OStream &O,RtStack &s) const = 0;
429 
430  /**
431  * Get the elapsed time of a RTEntry.
432  */
433  virtual timespec time() const = 0;
434 
435 };
436 
437 /**
438  * Real-time group.
439  * Used to collect a set of RTEntry's
440  */
441 class RTGroup : public RTEntry {
442 private:
443  typedef std::vector<RTEntry*> RTEntryList;
444  /// List of members.
445  RTEntryList members;
446 
447  RTGroup(const char* name, const char* description) : RTEntry(name, description) {
448  _RT_LOG("RTGroup() name: " << name << nl);
449  }
450 public:
451  /**
452  * Get the root group
453  */
454  static RTGroup& root() {
455  static RTGroup rg("vm","vm");
456  return rg;
457  }
458 
459  /**
460  * Create a new real-time group.
461  * @param[in] name name of the group
462  * @param[in] description description of the group
463  * @param[in] group parent group.
464  */
465  RTGroup(const char* name, const char* description, RTGroup &group) : RTEntry(name, description) {
466  group.add(this);
467  }
468 
469  virtual ~RTGroup() {
470  _RT_LOG("~RTGroup() name: " << name << nl);
471  }
472 
473  /**
474  * Add an entry to the group
475  */
476  void add(RTEntry *re) {
477  members.push_back(re);
478  }
479 
480  virtual timespec time() const {
481  timespec time = {0,0};
482  for(RTEntryList::const_iterator i = members.begin(), e = members.end(); i != e; ++i) {
483  RTEntry* re = *i;
484  time += re->time();
485  }
486  return time;
487  }
488 
489  virtual void print(OStream &O,timespec ref = invalid_ts) const {
490  timespec duration = time();
491  if (ref == invalid_ts)
492  ref = duration;
493  // O << setw(10) << left << name << right <<" " << description << nl;
494  //O << indent;
495  for(RTEntryList::const_iterator i = members.begin(), e = members.end(); i != e; ++i) {
496  RTEntry* re = *i;
497  re->print(O,duration);
498  }
499  //O << dedent;
500  int percent = duration / (ref / 100);
501  O << bold
502  << setw(10) << duration << " "
503  << setw(4) << percent << "% "
504  << setw(20) << name << ": total time"
505  << reset_color
506  << nl<< nl;
507  }
508  virtual void print_csv_intern(OStream &O,RtStack &s) const {
509  s.push_back(this);
510  for(RTEntryList::const_iterator i = members.begin(), e = members.end(); i != e; ++i) {
511  RTEntry* re = *i;
512  re->print_csv_intern(O,s);
513  }
514  s.pop_back();
515  print_csv_entry(O,s,time());
516  }
517 };
518 
519 /**
520  * Real-time timer.
521  *
522  * @Cpp11 should be ported to std::chrono if C++11 is available
523  */
524 class RTTimer : public RTEntry {
525 private:
526 #ifdef __DARWIN__
527  uint64_t startstamp;
528 #else
529  timespec startstamp; //< start timestamp
530 #endif
531  long int duration; //< time in usec
532 public:
533  /// dummy constructor
534  RTTimer() : RTEntry() {}
535  /**
536  * Create a new real-time timer.
537  * @param[in] name name of the timer
538  * @param[in] description description of the timer
539  * @param[in] group parent group.
540  */
541  RTTimer(const char* name, const char* description, RTGroup &parent) : RTEntry(name, description), duration(0) {
542  //reset();
543  _RT_LOG("RTTimer() name: " << name << nl);
544  parent.add(this);
545  }
546 
547  /**
548  * Destructor
549  */
550  virtual ~RTTimer() {
551  _RT_LOG("~RTTimer() name: " << name << nl);
552  }
553 
554  /**
555  * Start the timer.
556  * @see stop()
557  */
558  inline void start() {
559 #ifdef __DARWIN__
560  startstamp = mach_absolute_time();
561 #else
562  rt_timing_gettime_inline(startstamp);
563 #endif
564  }
565 
566  /**
567  * Start the timer.
568  * @note Only use after a start() or stopstart()
569  * @see start()
570  */
571  inline void stop() {
572 #ifdef __DARWIN__
573  static mach_timebase_info_data_t sTimebaseInfo;
574  uint64_t elapsed, nano;
575 
576  if ( sTimebaseInfo.denom == 0 ) {
577  (void) mach_timebase_info(&sTimebaseInfo);
578  }
579 
580  elapsed = mach_absolute_time() - startstamp;
581  nano = elapsed * sTimebaseInfo.numer / sTimebaseInfo.denom;
582  duration += (nano / 1000);
583 #else
584  timespec stopstamp;
585  rt_timing_gettime_inline(stopstamp);
586  duration += rt_timing_diff_usec_inline(startstamp,stopstamp);
587 #endif
588  }
589 
590  virtual timespec time() const {
591  timespec ts;
592  int sec = duration/1000000;
593  ts.tv_sec = sec;
594  ts.tv_nsec = (duration - sec * 1000000) * 1000;
595  return ts;
596  }
597 
598  virtual void print(OStream &O,timespec ref = invalid_ts) const {
599  timespec ts = time();
600  int percent;
601 
602  if (ref == invalid_ts) {
603  percent = -1;
604  } else {
605  percent = ts / (ref / 100);
606  }
607 
608  O << setw(10) << ts.tv_nsec/1000 << " usec "
609  << setw(4) << percent << "% "
610  << setw(20) << name << ": " << description << nl;
611  }
612  virtual void print_csv_intern(OStream &O,RtStack &s) const {
613  print_csv_entry(O,s,time());
614  }
615 };
616 
617 /** @} */
618 
619 } // end namespace cacao
620 
621 #undef _RT_LOG
622 
623 #define RT_REGISTER_TIMER(var,name,description) \
624  static inline cacao::RTTimer& var() { \
625  static cacao::RTTimer v(name,description,cacao::RTGroup::root()); \
626  return v; \
627  }
628 
629 #define RT_REGISTER_GROUP_TIMER(var,name,description,group) \
630  static inline cacao::RTTimer& var() { \
631  static cacao::RTTimer v(name, description, group()); \
632  return v; \
633  }
634 
635 #define RT_REGISTER_GROUP(var,name,description) \
636  inline cacao::RTGroup& var() { \
637  static cacao::RTGroup v(name,description,cacao::RTGroup::root()); \
638  return v; \
639  }
640 
641 #define RT_REGISTER_SUBGROUP(var,name,description,group) \
642  inline cacao::RTGroup& var() { \
643  static cacao::RTGroup v(name, description, group()); \
644  return v; \
645  }
646 
647 
648 #define RT_REGISTER_TIMER_EXTERN(var,name,description) \
649  inline cacao::RTTimer& var() { \
650  static cacao::RTTimer v(name,description,cacao::RTGroup::root()); \
651  return v; \
652  }
653 
654 #define RT_REGISTER_GROUP_TIMER_EXTERN(var,name,description,group) \
655  inline cacao::RTTimer& var() { \
656  static cacao::RTTimer v(name, description, group()); \
657  return v; \
658  }
659 
660 #define RT_REGISTER_GROUP_EXTERN(var,name,description) \
661  inline cacao::RTGroup& var() { \
662  static cacao::RTGroup v(name,description,cacao::RTGroup::root()); \
663  return v; \
664  }
665 
666 #define RT_REGISTER_SUBGROUP_EXTERN(var,name,description,group) \
667  inline cacao::RTGroup& var() { \
668  static cacao::RTGroup v(name, description, group()); \
669  return v; \
670  }
671 
672 #define RT_DECLARE_TIMER(var) inline cacao::RTTimer& var();
673 #define RT_DECLARE_GROUP(var) inline cacao::RTGroup& var();
674 
675 #define RT_TIMER_START(var) var().start()
676 #define RT_TIMER_STOP(var) var().stop()
677 #define RT_TIMER_STOPSTART(var1,var2) do { var1().stop();var2().start(); } while(0)
678 
679 #else /* !defined(ENABLE_RT_TIMING) */
680 
681 #define RT_REGISTER_TIMER(var,name,description)
682 #define RT_REGISTER_GROUP_TIMER(var,name,description,group)
683 #define RT_REGISTER_GROUP(var,name,description)
684 #define RT_REGISTER_SUBGROUP(var,name,description,group)
685 
686 #define RT_REGISTER_TIMER_EXTERN(var,name,description)
687 #define RT_REGISTER_GROUP_TIMER_EXTERN(var,name,description,group)
688 #define RT_REGISTER_GROUP_EXTERN(var,name,description)
689 #define RT_REGISTER_SUBGROUP_EXTERN(var,name,description,group)
690 
691 #define RT_DECLARE_TIMER(var)
692 #define RT_DECLARE_GROUP(var)
693 
694 #define RT_TIMER_START(var)
695 #define RT_TIMER_STOP(var)
696 #define RT_TIMER_STOPSTART(var1,var2)
697 
698 #endif /* defined(ENABLE_RT_TIMING) */
699 
700 #endif /* _RT_TIMING_HPP */
701 
702 /*
703  * These are local overrides for various environment variables in Emacs.
704  * Please do not remove this and leave it at the end of the file, where
705  * Emacs will automagically detect them.
706  * ---------------------------------------------------------------------
707  * Local variables:
708  * mode: c++
709  * indent-tabs-mode: t
710  * c-basic-offset: 4
711  * tab-width: 4
712  * End:
713  * vim:noexpandtab:sw=4:ts=4:
714  */
static SetWidth setw(size_t w)
Definition: OStream.hpp:395
typedef void(JNICALL *jvmtiEventSingleStep)(jvmtiEnv *jvmti_env
JNIEnv jclass jobject const char * name
Definition: jvmti.h:312
Bold bold
Definition: OStream.cpp:62
#define abort
Definition: md-asm.hpp:112
OptionPrefix & root()
Definition: Option.cpp:34
MIIterator i
ResetColor reset_color
Definition: OStream.cpp:61
OStream & operator<<(OStream &OS, const std::string &t)
Definition: OStream.hpp:459
MIIterator e
bool operator==(const ordered_list< T, Allocator > &lhs, const ordered_list< T, Allocator > &rhs)
equality
Nl nl
Definition: OStream.cpp:56
static SetZero setz(size_t w)
Definition: OStream.hpp:398
LoopTreeGraph * parent