ruby-prof 0.18.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +23 -0
  3. data/LICENSE +2 -2
  4. data/README.rdoc +1 -483
  5. data/Rakefile +3 -6
  6. data/bin/ruby-prof +65 -30
  7. data/ext/ruby_prof/extconf.rb +6 -38
  8. data/ext/ruby_prof/rp_allocation.c +292 -0
  9. data/ext/ruby_prof/rp_allocation.h +31 -0
  10. data/ext/ruby_prof/rp_call_info.c +137 -279
  11. data/ext/ruby_prof/rp_call_info.h +16 -34
  12. data/ext/ruby_prof/rp_measure_allocations.c +25 -49
  13. data/ext/ruby_prof/rp_measure_memory.c +21 -56
  14. data/ext/ruby_prof/rp_measure_process_time.c +28 -36
  15. data/ext/ruby_prof/rp_measure_wall_time.c +36 -19
  16. data/ext/ruby_prof/rp_measurement.c +236 -0
  17. data/ext/ruby_prof/rp_measurement.h +49 -0
  18. data/ext/ruby_prof/rp_method.c +395 -383
  19. data/ext/ruby_prof/rp_method.h +34 -39
  20. data/ext/ruby_prof/rp_profile.c +881 -0
  21. data/ext/ruby_prof/rp_profile.h +36 -0
  22. data/ext/ruby_prof/rp_stack.c +103 -80
  23. data/ext/ruby_prof/rp_stack.h +5 -12
  24. data/ext/ruby_prof/rp_thread.c +149 -88
  25. data/ext/ruby_prof/rp_thread.h +15 -6
  26. data/ext/ruby_prof/ruby_prof.c +11 -757
  27. data/ext/ruby_prof/ruby_prof.h +4 -47
  28. data/ext/ruby_prof/vc/ruby_prof.vcxproj +10 -8
  29. data/lib/ruby-prof.rb +2 -17
  30. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  31. data/lib/ruby-prof/call_info.rb +35 -93
  32. data/lib/ruby-prof/call_info_visitor.rb +19 -21
  33. data/lib/ruby-prof/compatibility.rb +37 -107
  34. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  35. data/lib/ruby-prof/measurement.rb +14 -0
  36. data/lib/ruby-prof/method_info.rb +52 -83
  37. data/lib/ruby-prof/printers/abstract_printer.rb +66 -52
  38. data/lib/ruby-prof/printers/call_info_printer.rb +13 -3
  39. data/lib/ruby-prof/printers/call_stack_printer.rb +32 -28
  40. data/lib/ruby-prof/printers/call_tree_printer.rb +20 -12
  41. data/lib/ruby-prof/printers/dot_printer.rb +5 -5
  42. data/lib/ruby-prof/printers/flat_printer.rb +6 -24
  43. data/lib/ruby-prof/printers/graph_html_printer.rb +7 -192
  44. data/lib/ruby-prof/printers/graph_printer.rb +13 -15
  45. data/lib/ruby-prof/printers/multi_printer.rb +66 -23
  46. data/lib/ruby-prof/profile.rb +10 -3
  47. data/lib/ruby-prof/rack.rb +0 -3
  48. data/lib/ruby-prof/thread.rb +12 -12
  49. data/lib/ruby-prof/version.rb +1 -1
  50. data/ruby-prof.gemspec +2 -2
  51. data/test/abstract_printer_test.rb +0 -27
  52. data/test/alias_test.rb +129 -0
  53. data/test/basic_test.rb +41 -40
  54. data/test/call_info_visitor_test.rb +3 -3
  55. data/test/dynamic_method_test.rb +0 -2
  56. data/test/line_number_test.rb +120 -39
  57. data/test/marshal_test.rb +119 -0
  58. data/test/measure_allocations.rb +30 -0
  59. data/test/measure_allocations_test.rb +371 -12
  60. data/test/measure_allocations_trace_test.rb +385 -0
  61. data/test/measure_memory_trace_test.rb +756 -0
  62. data/test/measure_process_time_test.rb +821 -33
  63. data/test/measure_times.rb +54 -0
  64. data/test/measure_wall_time_test.rb +349 -145
  65. data/test/multi_printer_test.rb +1 -34
  66. data/test/parser_timings.rb +24 -0
  67. data/test/pause_resume_test.rb +5 -5
  68. data/test/prime.rb +2 -0
  69. data/test/printer_call_tree_test.rb +31 -0
  70. data/test/printer_flat_test.rb +68 -0
  71. data/test/printer_graph_html_test.rb +60 -0
  72. data/test/printer_graph_test.rb +41 -0
  73. data/test/printers_test.rb +32 -166
  74. data/test/printing_recursive_graph_test.rb +26 -72
  75. data/test/recursive_test.rb +72 -77
  76. data/test/stack_printer_test.rb +2 -15
  77. data/test/start_stop_test.rb +22 -25
  78. data/test/test_helper.rb +5 -248
  79. data/test/thread_test.rb +11 -54
  80. data/test/unique_call_path_test.rb +16 -28
  81. data/test/yarv_test.rb +1 -0
  82. metadata +24 -34
  83. data/examples/flat.txt +0 -50
  84. data/examples/graph.dot +0 -84
  85. data/examples/graph.html +0 -823
  86. data/examples/graph.txt +0 -139
  87. data/examples/multi.flat.txt +0 -23
  88. data/examples/multi.graph.html +0 -760
  89. data/examples/multi.grind.dat +0 -114
  90. data/examples/multi.stack.html +0 -547
  91. data/examples/stack.html +0 -547
  92. data/ext/ruby_prof/rp_measure.c +0 -40
  93. data/ext/ruby_prof/rp_measure.h +0 -45
  94. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -136
  95. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -73
  96. data/ext/ruby_prof/rp_measure_gc_time.c +0 -60
  97. data/lib/ruby-prof/aggregate_call_info.rb +0 -76
  98. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -83
  99. data/lib/ruby-prof/profile/exclude_common_methods.rb +0 -207
  100. data/lib/ruby-prof/profile/legacy_method_elimination.rb +0 -50
  101. data/test/aggregate_test.rb +0 -136
  102. data/test/block_test.rb +0 -74
  103. data/test/call_info_test.rb +0 -78
  104. data/test/fiber_test.rb +0 -79
  105. data/test/issue137_test.rb +0 -63
  106. data/test/measure_cpu_time_test.rb +0 -212
  107. data/test/measure_gc_runs_test.rb +0 -32
  108. data/test/measure_gc_time_test.rb +0 -36
  109. data/test/measure_memory_test.rb +0 -33
  110. data/test/method_elimination_test.rb +0 -84
  111. data/test/module_test.rb +0 -45
  112. data/test/stack_test.rb +0 -138
@@ -4,50 +4,32 @@
4
4
  #ifndef __RP_CALL_INFO_H__
5
5
  #define __RP_CALL_INFO_H__
6
6
 
7
- #include "rp_measure.h"
7
+ #include "ruby_prof.h"
8
+ #include "rp_measurement.h"
8
9
  #include "rp_method.h"
9
10
 
10
- extern VALUE cCallInfo;
11
-
12
11
  /* Callers and callee information for a method. */
13
12
  typedef struct prof_call_info_t
14
13
  {
15
- prof_method_t *target; /* Use target instead of method to avoid conflict with Ruby method */
16
- struct prof_call_info_t *parent;
17
- st_table *call_infos;
18
-
19
- double total_time;
20
- double self_time;
21
- double wait_time;
22
-
14
+ prof_method_t *method;
15
+ prof_method_t *parent;
16
+ prof_measurement_t *measurement;
23
17
  VALUE object;
24
- VALUE children;
25
18
 
26
- int called;
19
+ int visits; /* Current visits on the stack */
27
20
 
28
- unsigned int recursive : 1;
29
- unsigned int depth : 15;
30
- unsigned int line : 16;
21
+ unsigned int depth;
22
+ unsigned int source_line;
23
+ VALUE source_file;
31
24
  } prof_call_info_t;
32
25
 
33
- /* Array of call_info objects */
34
- typedef struct prof_call_infos_t
35
- {
36
- prof_call_info_t **start;
37
- prof_call_info_t **end;
38
- prof_call_info_t **ptr;
39
- VALUE object;
40
- } prof_call_infos_t;
41
-
42
-
26
+ prof_call_info_t *prof_call_info_create(prof_method_t *method, prof_method_t *parent, VALUE source_file, int source_line);
27
+ void prof_call_info_mark(void *data);
28
+ prof_call_info_t *call_info_table_lookup(st_table* table, st_data_t key);
29
+ size_t call_info_table_insert(st_table *table, st_data_t key, prof_call_info_t *val);
30
+ prof_call_info_t *prof_get_call_info(VALUE self);
31
+ VALUE prof_call_info_wrap(prof_call_info_t* call_info);
32
+ void prof_call_info_free(prof_call_info_t* call_info);
43
33
  void rp_init_call_info(void);
44
- prof_call_infos_t* prof_call_infos_create();
45
- void prof_call_infos_mark(prof_call_infos_t *call_infos);
46
- void prof_call_infos_free(prof_call_infos_t *call_infos);
47
- void prof_add_call_info(prof_call_infos_t *call_infos, prof_call_info_t *call_info);
48
- VALUE prof_call_infos_wrap(prof_call_infos_t *call_infos);
49
- prof_call_info_t * prof_call_info_create(prof_method_t* method, prof_call_info_t* parent);
50
- prof_call_info_t * call_info_table_lookup(st_table *table, const prof_method_key_t *key);
51
- size_t call_info_table_insert(st_table *table, const prof_method_key_t *key, prof_call_info_t *val);
52
34
 
53
35
  #endif //__RP_CALL_INFO_H__
@@ -3,74 +3,50 @@
3
3
 
4
4
  /* :nodoc: */
5
5
 
6
- #include "ruby_prof.h"
6
+ #include "rp_measurement.h"
7
7
 
8
8
  static VALUE cMeasureAllocations;
9
+ VALUE total_allocated_objects_key;
9
10
 
10
- #if defined(HAVE_RB_OS_ALLOCATED_OBJECTS)
11
- unsigned LONG_LONG rb_os_allocated_objects();
12
- #endif
13
-
14
- #if defined(HAVE_RB_GC_STAT)
15
- size_t rb_gc_stat(VALUE key);
16
-
17
- #if RUBY_PROF_RUBY_VERSION >= 20200
18
- #define TOTAL_ALLOCATED_OBJECTS_STRING "total_allocated_objects"
19
- #else
20
- #define TOTAL_ALLOCATED_OBJECTS_STRING "total_allocated_object"
21
- #endif
22
-
23
- #endif
11
+ static double
12
+ measure_allocations_via_gc_stats(rb_trace_arg_t* trace_arg)
13
+ {
14
+ return rb_gc_stat(total_allocated_objects_key);
15
+ }
24
16
 
25
17
  static double
26
- measure_allocations()
18
+ measure_allocations_via_tracing(rb_trace_arg_t* trace_arg)
27
19
  {
28
- #if defined(HAVE_RB_OS_ALLOCATED_OBJECTS)
29
- #define MEASURE_ALLOCATIONS_ENABLED Qtrue
30
- return rb_os_allocated_objects();
20
+ static double result = 0;
31
21
 
32
- #elif defined(HAVE_RB_GC_STAT) && RUBY_PROF_RUBY_VERSION >= 20100
33
- #define MEASURE_ALLOCATIONS_ENABLED Qtrue
34
- static VALUE total_alloc_symbol = 0;
35
- if (!total_alloc_symbol) {
36
- total_alloc_symbol = ID2SYM(rb_intern_const(TOTAL_ALLOCATED_OBJECTS_STRING));
22
+ if (trace_arg)
23
+ {
24
+ rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
25
+ if (event == RUBY_INTERNAL_EVENT_NEWOBJ)
26
+ result++;
37
27
  }
38
- return rb_gc_stat(total_alloc_symbol);
39
-
40
- #else
41
- #define MEASURE_ALLOCATIONS_ENABLED Qfalse
42
- return 0;
43
- #endif
28
+ return result;
44
29
  }
45
30
 
46
-
47
- prof_measurer_t* prof_measurer_allocations()
31
+ prof_measurer_t* prof_measurer_allocations(bool track_allocations)
48
32
  {
49
33
  prof_measurer_t* measure = ALLOC(prof_measurer_t);
50
- measure->measure = measure_allocations;
51
- return measure;
52
- }
34
+ measure->mode = MEASURE_ALLOCATIONS;
35
+ measure->multiplier = 1;
36
+ measure->track_allocations = track_allocations;
53
37
 
54
- /* call-seq:
55
- measure -> int
38
+ if (track_allocations)
39
+ measure->measure = measure_allocations_via_tracing;
40
+ else
41
+ measure->measure = measure_allocations_via_gc_stats;
56
42
 
57
- Returns the number of Ruby object allocations. */
58
-
59
- static VALUE
60
- prof_measure_allocations(VALUE self)
61
- {
62
- #if defined(HAVE_LONG_LONG)
63
- return ULL2NUM(measure_allocations());
64
- #else
65
- return ULONG2NUM(measure_allocations());
66
- #endif
43
+ return measure;
67
44
  }
68
45
 
69
46
  void rp_init_measure_allocations()
70
47
  {
48
+ total_allocated_objects_key = ID2SYM(rb_intern("total_allocated_objects"));
71
49
  rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
72
- rb_define_const(mProf, "ALLOCATIONS_ENABLED", MEASURE_ALLOCATIONS_ENABLED);
73
50
 
74
51
  cMeasureAllocations = rb_define_class_under(mMeasure, "Allocations", rb_cObject);
75
- rb_define_singleton_method(cMeasureAllocations, "measure", prof_measure_allocations, 0);
76
52
  }
@@ -1,77 +1,42 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
1
+ /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
4
  /* :nodoc: */
5
5
 
6
- #include "ruby_prof.h"
6
+ #include "rp_measurement.h"
7
7
 
8
8
  static VALUE cMeasureMemory;
9
9
 
10
-
11
- #if defined(HAVE_RB_GC_ALLOCATED_SIZE)
12
- VALUE rb_gc_allocated_size();
13
- #endif
14
-
15
- #if defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
16
- size_t rb_gc_malloc_allocated_size();
17
- #endif
18
-
19
- #if defined(HAVE_RB_HEAP_TOTAL_MEM)
20
- //FIXME: did not find the patch to check prototype, assuming it to return size_t
21
- size_t rb_heap_total_mem();
22
- #endif
23
-
24
10
  static double
25
- measure_memory()
11
+ measure_memory_via_tracing(rb_trace_arg_t* trace_arg)
26
12
  {
27
- #if defined(HAVE_RB_GC_ALLOCATED_SIZE)
28
- #define MEASURE_MEMORY_ENABLED Qtrue
29
- #if defined(HAVE_LONG_LONG)
30
- return NUM2LL(rb_gc_allocated_size()) / 1024.0;
31
- #else
32
- return NUM2ULONG(rb_gc_allocated_size()) / 1024.0;
33
- #endif
34
-
35
- #elif defined(HAVE_RB_GC_MALLOC_ALLOCATED_SIZE)
36
- #define MEASURE_MEMORY_ENABLED Qtrue
37
- return rb_gc_malloc_allocated_size() / 1024.0;
38
-
39
- #elif defined(HAVE_RB_GC_TOTAL_MALLOCED_BYTES)
40
- #define MEASURE_MEMORY_ENABLED Qtrue
41
- return rb_gc_total_malloced_bytes() / 1024.0;
42
-
43
- #elif defined(HAVE_RB_HEAP_TOTAL_MEM)
44
- #define MEASURE_MEMORY_ENABLED Qtrue
45
- return rb_heap_total_mem() / 1024.0;
46
-
47
- #else
48
- #define MEASURE_MEMORY_ENABLED Qfalse
49
- return 0;
50
- #endif
13
+ static double result = 0;
14
+
15
+ if (trace_arg)
16
+ {
17
+ rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
18
+ if (event == RUBY_INTERNAL_EVENT_NEWOBJ)
19
+ {
20
+ VALUE object = rb_tracearg_object(trace_arg);
21
+ result += rb_obj_memsize_of(object);
22
+ }
23
+ }
24
+ return result;
51
25
  }
52
26
 
53
- prof_measurer_t* prof_measurer_memory()
27
+ prof_measurer_t* prof_measurer_memory(bool track_allocations)
54
28
  {
55
29
  prof_measurer_t* measure = ALLOC(prof_measurer_t);
56
- measure->measure = measure_memory;
30
+ measure->mode = MEASURE_MEMORY;
31
+ measure->measure = measure_memory_via_tracing;
32
+ measure->multiplier = 1;
33
+ measure->track_allocations = true;
57
34
  return measure;
58
35
  }
59
36
 
60
- /* call-seq:
61
- measure_process_time -> float
62
-
63
- Returns the process time.*/
64
- static VALUE
65
- prof_measure_memory(VALUE self)
66
- {
67
- return rb_float_new(measure_memory());
68
- }
69
-
70
37
  void rp_init_measure_memory()
71
38
  {
72
39
  rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
73
- rb_define_const(mProf, "MEMORY_ENABLED", MEASURE_MEMORY_ENABLED);
74
40
 
75
- cMeasureMemory = rb_define_class_under(mMeasure, "Memory", rb_cObject);
76
- rb_define_singleton_method(cMeasureMemory, "measure", prof_measure_memory, 0);
41
+ cMeasureMemory = rb_define_class_under(mMeasure, "Allocations", rb_cObject);
77
42
  }
@@ -1,71 +1,63 @@
1
- /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
- #include "ruby_prof.h"
4
+ #include "rp_measurement.h"
5
5
  #include <time.h>
6
6
 
7
7
  static VALUE cMeasureProcessTime;
8
8
 
9
9
  static double
10
- measure_process_time()
10
+ measure_process_time(rb_trace_arg_t* trace_arg)
11
11
  {
12
- #if defined(__linux__)
13
- struct timespec clock;
14
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
15
- return clock.tv_sec + (clock.tv_nsec/1000000000.0);
16
- #elif defined(_win32)
17
- FILETIME createTime;
18
- FILETIME exitTime;
19
- FILETIME sysTime;
20
- FILETIME cpuTime;
12
+ #if defined(_WIN32)
13
+ FILETIME createTime;
14
+ FILETIME exitTime;
15
+ FILETIME sysTime;
16
+ FILETIME userTime;
21
17
 
22
18
  ULARGE_INTEGER sysTimeInt;
23
- ULARGE_INTEGER cpuTimeInt;
24
- ULONGLONG totalTime;
25
-
26
- GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
19
+ ULARGE_INTEGER userTimeInt;
27
20
 
28
- /* Doing this based on MSFT's recommendation in the FILETIME structure documentation at
29
- http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx*/
21
+ GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &userTime);
30
22
 
31
23
  sysTimeInt.LowPart = sysTime.dwLowDateTime;
32
24
  sysTimeInt.HighPart = sysTime.dwHighDateTime;
33
- cpuTimeInt.LowPart = cpuTime.dwLowDateTime;
34
- cpuTimeInt.HighPart = cpuTime.dwHighDateTime;
25
+ userTimeInt.LowPart = userTime.dwLowDateTime;
26
+ userTimeInt.HighPart = userTime.dwHighDateTime;
35
27
 
36
- totalTime = sysTimeInt.QuadPart + cpuTimeInt.QuadPart;
37
-
38
- // Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
39
- return totalTime / 10000000.0;
28
+ return sysTimeInt.QuadPart + userTimeInt.QuadPart;
40
29
  #else
41
- return ((double)clock()) / CLOCKS_PER_SEC;
30
+ struct timespec clock;
31
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
32
+ return clock.tv_sec + (clock.tv_nsec/1000000000.0);
42
33
  #endif
43
34
  }
44
35
 
45
- /* call-seq:
46
- measure_process_time -> float
47
-
48
- Returns the process time.*/
49
- static VALUE
50
- prof_measure_process_time(VALUE self)
36
+ static double
37
+ multiplier_process_time(void)
51
38
  {
52
- return rb_float_new(measure_process_time());
39
+ #if defined(_WIN32)
40
+ // Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
41
+ return 1.0 / 10000000.0;
42
+ #else
43
+ return 1.0;
44
+ #endif
53
45
  }
54
46
 
55
- prof_measurer_t* prof_measurer_process_time()
47
+ prof_measurer_t* prof_measurer_process_time(bool track_allocations)
56
48
  {
57
49
  prof_measurer_t* measure = ALLOC(prof_measurer_t);
50
+ measure->mode = MEASURE_PROCESS_TIME;
58
51
  measure->measure = measure_process_time;
52
+ measure->multiplier = multiplier_process_time();
53
+ measure->track_allocations = track_allocations;
59
54
  return measure;
60
55
  }
61
56
 
62
-
63
57
  void rp_init_measure_process_time()
64
58
  {
65
59
  rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
66
60
  rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
67
- rb_define_const(mProf, "PROCESS_TIME_ENABLED", Qtrue);
68
61
 
69
62
  cMeasureProcessTime = rb_define_class_under(mMeasure, "ProcessTime", rb_cObject);
70
- rb_define_singleton_method(cMeasureProcessTime, "measure", prof_measure_process_time, 0);
71
63
  }
@@ -2,44 +2,61 @@
2
2
  Please see the LICENSE file for copyright and distribution information */
3
3
 
4
4
  /* :nodoc: */
5
- #include "ruby_prof.h"
6
- #if HAVE_GETTIMEOFDAY && !defined(_WIN32)
7
- #include <sys/time.h>
5
+ #include "rp_measurement.h"
6
+
7
+ #if defined(__APPLE__)
8
+ #include <mach/mach_time.h>
9
+ #elif !defined(_WIN32)
10
+ #include <time.h>
8
11
  #endif
9
12
 
10
13
  static VALUE cMeasureWallTime;
11
14
 
12
15
  static double
13
- measure_wall_time()
16
+ measure_wall_time(rb_trace_arg_t* trace_arg)
14
17
  {
18
+ #if defined(_WIN32)
19
+ return GetTickCount();
20
+ #elif defined(__APPLE__)
21
+ return mach_absolute_time();// * (uint64_t)mach_timebase.numer / (uint64_t)mach_timebase.denom;
22
+ #elif defined(__linux__)
23
+ struct timespec tv;
24
+ clock_gettime(CLOCK_MONOTONIC, &tv);
25
+ return tv.tv_sec + (tv.tv_nsec / 1000000000.0);
26
+ #else
15
27
  struct timeval tv;
16
28
  gettimeofday(&tv, NULL);
17
- return tv.tv_sec + (tv.tv_usec/1000000.0);
29
+ return tv.tv_sec + (tv.tv_usec / 1000000.0);
30
+ #endif
18
31
  }
19
32
 
20
- prof_measurer_t* prof_measurer_wall_time()
33
+ static double
34
+ multiplier_wall_time(void)
21
35
  {
22
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
23
- measure->measure = measure_wall_time;
24
- return measure;
36
+ #if defined(_WIN32)
37
+ return 1.0/1000.0;
38
+ #elif defined(__APPLE__)
39
+ mach_timebase_info_data_t mach_timebase;
40
+ mach_timebase_info (&mach_timebase);
41
+ return (uint64_t)mach_timebase.numer / (uint64_t)mach_timebase.denom / 1000000000.0;
42
+ #else
43
+ return 1.0;
44
+ #endif
25
45
  }
26
46
 
27
- /* Document-method: prof_measure_wall_time
28
- call-seq:
29
- measure_wall_time -> float
30
-
31
- Returns the wall time.*/
32
- static VALUE
33
- prof_measure_wall_time(VALUE self)
47
+ prof_measurer_t* prof_measurer_wall_time(bool track_allocations)
34
48
  {
35
- return rb_float_new(measure_wall_time());
49
+ prof_measurer_t* measure = ALLOC(prof_measurer_t);
50
+ measure->mode = MEASURE_WALL_TIME;
51
+ measure->measure = measure_wall_time;
52
+ measure->multiplier = multiplier_wall_time();
53
+ measure->track_allocations = track_allocations;
54
+ return measure;
36
55
  }
37
56
 
38
57
  void rp_init_measure_wall_time()
39
58
  {
40
59
  rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
41
- rb_define_const(mProf, "WALL_TIME_ENABLED", Qtrue);
42
60
 
43
61
  cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
44
- rb_define_singleton_method(cMeasureWallTime, "measure", prof_measure_wall_time, 0);
45
62
  }
@@ -0,0 +1,236 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "rp_measurement.h"
5
+
6
+ VALUE mMeasure;
7
+ VALUE cRpMeasurement;
8
+
9
+ prof_measurer_t* prof_measurer_allocations(bool track_allocations);
10
+ prof_measurer_t* prof_measurer_memory(bool track_allocations);
11
+ prof_measurer_t* prof_measurer_process_time(bool track_allocations);
12
+ prof_measurer_t* prof_measurer_wall_time(bool track_allocations);
13
+
14
+ void rp_init_measure_allocations(void);
15
+ void rp_init_measure_memory(void);
16
+ void rp_init_measure_process_time(void);
17
+ void rp_init_measure_wall_time(void);
18
+
19
+ prof_measurer_t* prof_get_measurer(prof_measure_mode_t measure, bool track_allocations)
20
+ {
21
+ switch (measure)
22
+ {
23
+ case MEASURE_WALL_TIME:
24
+ return prof_measurer_wall_time(track_allocations);
25
+ case MEASURE_PROCESS_TIME:
26
+ return prof_measurer_process_time(track_allocations);
27
+ case MEASURE_ALLOCATIONS:
28
+ return prof_measurer_allocations(track_allocations);
29
+ case MEASURE_MEMORY:
30
+ return prof_measurer_memory(track_allocations);
31
+ default:
32
+ rb_raise(rb_eArgError, "Unknown measure mode: %d", measure);
33
+ }
34
+ };
35
+
36
+ double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg)
37
+ {
38
+ double measurement = measurer->measure(trace_arg);
39
+ return measurement * measurer->multiplier;
40
+ }
41
+
42
+ /* ======= prof_measurement_t ========*/
43
+ prof_measurement_t *prof_measurement_create(void)
44
+ {
45
+ prof_measurement_t* result = ALLOC(prof_measurement_t);
46
+ result->total_time = 0;
47
+ result->self_time = 0;
48
+ result->wait_time = 0;
49
+ result->called = 0;
50
+ result->object = Qnil;
51
+ return result;
52
+ }
53
+
54
+ static void
55
+ prof_measurement_ruby_gc_free(void *data)
56
+ {
57
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
58
+
59
+ /* Has this thread object been accessed by Ruby? If
60
+ yes clean it up so to avoid a segmentation fault. */
61
+ if (measurement->object != Qnil)
62
+ {
63
+ RDATA(measurement->object)->data = NULL;
64
+ RDATA(measurement->object)->dfree = NULL;
65
+ RDATA(measurement->object)->dmark = NULL;
66
+ }
67
+ measurement->object = Qnil;
68
+ }
69
+
70
+ size_t
71
+ prof_measurement_size(const void *data)
72
+ {
73
+ return sizeof(prof_measurement_t);
74
+ }
75
+
76
+ void
77
+ prof_measurement_mark(void *data)
78
+ {
79
+ prof_measurement_t* measurement = (prof_measurement_t*)data;
80
+ if (measurement->object != Qnil)
81
+ rb_gc_mark(measurement->object);
82
+ }
83
+
84
+ static const rb_data_type_t measurement_type =
85
+ {
86
+ .wrap_struct_name = "Measurement",
87
+ .function =
88
+ {
89
+ .dmark = prof_measurement_mark,
90
+ .dfree = prof_measurement_ruby_gc_free,
91
+ .dsize = prof_measurement_size,
92
+ },
93
+ .data = NULL,
94
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
95
+ };
96
+
97
+ VALUE
98
+ prof_measurement_wrap(prof_measurement_t* measurement)
99
+ {
100
+ if (measurement->object == Qnil)
101
+ {
102
+ measurement->object = TypedData_Wrap_Struct(cRpMeasurement, &measurement_type, measurement);
103
+ }
104
+ return measurement->object;
105
+ }
106
+
107
+ static VALUE
108
+ prof_measurement_allocate(VALUE klass)
109
+ {
110
+ prof_measurement_t *measurement = prof_measurement_create();
111
+ measurement->object = prof_measurement_wrap(measurement);
112
+ return measurement->object;
113
+ }
114
+
115
+ prof_measurement_t*
116
+ prof_get_measurement(VALUE self)
117
+ {
118
+ /* Can't use Data_Get_Struct because that triggers the event hook
119
+ ending up in endless recursion. */
120
+ prof_measurement_t* result = DATA_PTR(self);
121
+
122
+ if (!result)
123
+ rb_raise(rb_eRuntimeError, "This RubyProf::Measurement instance has already been freed, likely because its profile has been freed.");
124
+
125
+ return result;
126
+ }
127
+
128
+ /* call-seq:
129
+ total_time -> float
130
+
131
+ Returns the total amount of time spent in this method and its children. */
132
+ static VALUE
133
+ prof_measurement_total_time(VALUE self)
134
+ {
135
+ prof_measurement_t* result = prof_get_measurement(self);
136
+ return rb_float_new(result->total_time);
137
+ }
138
+
139
+ /* call-seq:
140
+ self_time -> float
141
+
142
+ Returns the total amount of time spent in this method. */
143
+ static VALUE
144
+ prof_measurement_self_time(VALUE self)
145
+ {
146
+ prof_measurement_t* result = prof_get_measurement(self);
147
+
148
+ return rb_float_new(result->self_time);
149
+ }
150
+
151
+ /* call-seq:
152
+ wait_time -> float
153
+
154
+ Returns the total amount of time this method waited for other threads. */
155
+ static VALUE
156
+ prof_measurement_wait_time(VALUE self)
157
+ {
158
+ prof_measurement_t* result = prof_get_measurement(self);
159
+
160
+ return rb_float_new(result->wait_time);
161
+ }
162
+
163
+ /* call-seq:
164
+ called -> int
165
+
166
+ Returns the total amount of times this method was called. */
167
+ static VALUE
168
+ prof_measurement_called(VALUE self)
169
+ {
170
+ prof_measurement_t *result = prof_get_measurement(self);
171
+ return INT2NUM(result->called);
172
+ }
173
+
174
+ /* call-seq:
175
+ called=n -> n
176
+
177
+ Sets the call count to n. */
178
+ static VALUE
179
+ prof_measurement_set_called(VALUE self, VALUE called)
180
+ {
181
+ prof_measurement_t *result = prof_get_measurement(self);
182
+ result->called = NUM2INT(called);
183
+ return called;
184
+ }
185
+
186
+ /* :nodoc: */
187
+ static VALUE
188
+ prof_measurement_dump(VALUE self)
189
+ {
190
+ prof_measurement_t* measurement_data = prof_get_measurement(self);
191
+ VALUE result = rb_hash_new();
192
+
193
+ rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
194
+ rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
195
+ rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
196
+ rb_hash_aset(result, ID2SYM(rb_intern("called")), INT2FIX(measurement_data->called));
197
+
198
+ return result;
199
+ }
200
+
201
+ /* :nodoc: */
202
+ static VALUE
203
+ prof_measurement_load(VALUE self, VALUE data)
204
+ {
205
+ prof_measurement_t* measurement = prof_get_measurement(self);
206
+ measurement->object = self;
207
+
208
+ measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
209
+ measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
210
+ measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
211
+ measurement->called = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("called"))));
212
+
213
+ return data;
214
+ }
215
+
216
+ void rp_init_measure()
217
+ {
218
+ mMeasure = rb_define_module_under(mProf, "Measure");
219
+ rp_init_measure_wall_time();
220
+ rp_init_measure_process_time();
221
+ rp_init_measure_allocations();
222
+ rp_init_measure_memory();
223
+
224
+ cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cData);
225
+ rb_undef_method(CLASS_OF(cRpMeasurement), "new");
226
+ rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);
227
+
228
+ rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
229
+ rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
230
+ rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
231
+ rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
232
+ rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
233
+
234
+ rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
235
+ rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
236
+ }