ruby-prof 0.13.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +579 -371
  3. data/LICENSE +24 -23
  4. data/README.rdoc +5 -433
  5. data/Rakefile +98 -110
  6. data/bin/ruby-prof +328 -329
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +16 -59
  9. data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
  10. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  11. data/ext/ruby_prof/rp_allocation.c +287 -0
  12. data/ext/ruby_prof/rp_allocation.h +31 -0
  13. data/ext/ruby_prof/rp_call_tree.c +369 -0
  14. data/ext/ruby_prof/rp_call_tree.h +43 -0
  15. data/ext/ruby_prof/rp_call_trees.c +288 -0
  16. data/ext/ruby_prof/rp_call_trees.h +28 -0
  17. data/ext/ruby_prof/rp_measure_allocations.c +50 -65
  18. data/ext/ruby_prof/rp_measure_memory.c +42 -73
  19. data/ext/ruby_prof/rp_measure_process_time.c +65 -71
  20. data/ext/ruby_prof/rp_measure_wall_time.c +64 -42
  21. data/ext/ruby_prof/rp_measurement.c +237 -0
  22. data/ext/ruby_prof/rp_measurement.h +50 -0
  23. data/ext/ruby_prof/rp_method.c +491 -420
  24. data/ext/ruby_prof/rp_method.h +62 -57
  25. data/ext/ruby_prof/rp_profile.c +908 -0
  26. data/ext/ruby_prof/rp_profile.h +35 -0
  27. data/ext/ruby_prof/rp_stack.c +212 -128
  28. data/ext/ruby_prof/rp_stack.h +53 -51
  29. data/ext/ruby_prof/rp_thread.c +362 -268
  30. data/ext/ruby_prof/rp_thread.h +39 -27
  31. data/ext/ruby_prof/ruby_prof.c +52 -695
  32. data/ext/ruby_prof/ruby_prof.h +26 -55
  33. data/ext/ruby_prof/vc/ruby_prof.sln +28 -21
  34. data/ext/ruby_prof/vc/{ruby_prof_20.vcxproj → ruby_prof.vcxproj} +56 -8
  35. data/lib/ruby-prof.rb +52 -67
  36. data/lib/ruby-prof/assets/call_stack_printer.html.erb +710 -0
  37. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  38. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  39. data/lib/ruby-prof/call_tree.rb +57 -0
  40. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  41. data/lib/ruby-prof/compatibility.rb +99 -169
  42. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  43. data/lib/ruby-prof/measurement.rb +17 -0
  44. data/lib/ruby-prof/method_info.rb +78 -131
  45. data/lib/ruby-prof/printers/abstract_printer.rb +137 -85
  46. data/lib/ruby-prof/printers/call_info_printer.rb +53 -41
  47. data/lib/ruby-prof/printers/call_stack_printer.rb +180 -773
  48. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -92
  49. data/lib/ruby-prof/printers/dot_printer.rb +132 -132
  50. data/lib/ruby-prof/printers/flat_printer.rb +53 -69
  51. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -255
  52. data/lib/ruby-prof/printers/graph_printer.rb +113 -116
  53. data/lib/ruby-prof/printers/multi_printer.rb +127 -56
  54. data/lib/ruby-prof/profile.rb +37 -77
  55. data/lib/ruby-prof/rack.rb +62 -15
  56. data/lib/ruby-prof/task.rb +147 -147
  57. data/lib/ruby-prof/thread.rb +10 -12
  58. data/lib/ruby-prof/version.rb +3 -0
  59. data/lib/unprof.rb +10 -10
  60. data/ruby-prof.gemspec +65 -61
  61. data/test/abstract_printer_test.rb +26 -0
  62. data/test/alias_test.rb +126 -0
  63. data/test/basic_test.rb +43 -128
  64. data/test/call_tree_visitor_test.rb +32 -0
  65. data/test/call_trees_test.rb +66 -0
  66. data/test/duplicate_names_test.rb +32 -32
  67. data/test/dynamic_method_test.rb +53 -74
  68. data/test/enumerable_test.rb +21 -16
  69. data/test/exceptions_test.rb +24 -16
  70. data/test/exclude_methods_test.rb +151 -0
  71. data/test/exclude_threads_test.rb +53 -54
  72. data/test/fiber_test.rb +129 -65
  73. data/test/gc_test.rb +90 -0
  74. data/test/inverse_call_tree_test.rb +175 -0
  75. data/test/line_number_test.rb +158 -71
  76. data/test/marshal_test.rb +113 -0
  77. data/test/measure_allocations.rb +30 -0
  78. data/test/measure_allocations_test.rb +375 -25
  79. data/test/measure_allocations_trace_test.rb +375 -0
  80. data/test/measure_memory_trace_test.rb +1101 -0
  81. data/test/measure_process_time_test.rb +785 -62
  82. data/test/measure_times.rb +56 -0
  83. data/test/measure_wall_time_test.rb +434 -254
  84. data/test/multi_printer_test.rb +71 -82
  85. data/test/no_method_class_test.rb +15 -15
  86. data/test/pause_resume_test.rb +175 -166
  87. data/test/prime.rb +54 -54
  88. data/test/prime_script.rb +6 -0
  89. data/test/printer_call_stack_test.rb +27 -0
  90. data/test/printer_call_tree_test.rb +30 -0
  91. data/test/printer_flat_test.rb +99 -0
  92. data/test/printer_graph_html_test.rb +59 -0
  93. data/test/printer_graph_test.rb +40 -0
  94. data/test/printers_test.rb +141 -257
  95. data/test/printing_recursive_graph_test.rb +81 -0
  96. data/test/profile_test.rb +16 -0
  97. data/test/rack_test.rb +93 -0
  98. data/test/recursive_test.rb +206 -215
  99. data/test/singleton_test.rb +38 -38
  100. data/test/stack_printer_test.rb +64 -78
  101. data/test/start_stop_test.rb +109 -112
  102. data/test/test_helper.rb +13 -115
  103. data/test/thread_test.rb +144 -178
  104. data/test/unique_call_path_test.rb +120 -224
  105. data/test/yarv_test.rb +56 -0
  106. metadata +77 -133
  107. data/doc/LICENSE.html +0 -155
  108. data/doc/README_rdoc.html +0 -648
  109. data/doc/Rack.html +0 -167
  110. data/doc/Rack/RubyProf.html +0 -319
  111. data/doc/RubyProf.html +0 -1000
  112. data/doc/RubyProf/AbstractPrinter.html +0 -580
  113. data/doc/RubyProf/AggregateCallInfo.html +0 -570
  114. data/doc/RubyProf/CallInfo.html +0 -512
  115. data/doc/RubyProf/CallInfoPrinter.html +0 -190
  116. data/doc/RubyProf/CallInfoVisitor.html +0 -332
  117. data/doc/RubyProf/CallStackPrinter.html +0 -1600
  118. data/doc/RubyProf/CallTreePrinter.html +0 -413
  119. data/doc/RubyProf/Cmd.html +0 -669
  120. data/doc/RubyProf/DotPrinter.html +0 -312
  121. data/doc/RubyProf/FlatPrinter.html +0 -229
  122. data/doc/RubyProf/FlatPrinterWithLineNumbers.html +0 -267
  123. data/doc/RubyProf/GraphHtmlPrinter.html +0 -630
  124. data/doc/RubyProf/GraphPrinter.html +0 -209
  125. data/doc/RubyProf/MethodInfo.html +0 -713
  126. data/doc/RubyProf/MultiPrinter.html +0 -407
  127. data/doc/RubyProf/Profile.html +0 -821
  128. data/doc/RubyProf/ProfileTask.html +0 -532
  129. data/doc/RubyProf/Test.html +0 -578
  130. data/doc/RubyProf/Thread.html +0 -262
  131. data/doc/created.rid +0 -32
  132. data/doc/examples/flat_txt.html +0 -191
  133. data/doc/examples/graph_txt.html +0 -305
  134. data/doc/images/add.png +0 -0
  135. data/doc/images/brick.png +0 -0
  136. data/doc/images/brick_link.png +0 -0
  137. data/doc/images/bug.png +0 -0
  138. data/doc/images/bullet_black.png +0 -0
  139. data/doc/images/bullet_toggle_minus.png +0 -0
  140. data/doc/images/bullet_toggle_plus.png +0 -0
  141. data/doc/images/date.png +0 -0
  142. data/doc/images/delete.png +0 -0
  143. data/doc/images/find.png +0 -0
  144. data/doc/images/loadingAnimation.gif +0 -0
  145. data/doc/images/macFFBgHack.png +0 -0
  146. data/doc/images/package.png +0 -0
  147. data/doc/images/page_green.png +0 -0
  148. data/doc/images/page_white_text.png +0 -0
  149. data/doc/images/page_white_width.png +0 -0
  150. data/doc/images/plugin.png +0 -0
  151. data/doc/images/ruby.png +0 -0
  152. data/doc/images/tag_blue.png +0 -0
  153. data/doc/images/tag_green.png +0 -0
  154. data/doc/images/transparent.png +0 -0
  155. data/doc/images/wrench.png +0 -0
  156. data/doc/images/wrench_orange.png +0 -0
  157. data/doc/images/zoom.png +0 -0
  158. data/doc/index.html +0 -647
  159. data/doc/js/darkfish.js +0 -155
  160. data/doc/js/jquery.js +0 -18
  161. data/doc/js/navigation.js +0 -142
  162. data/doc/js/search.js +0 -94
  163. data/doc/js/search_index.js +0 -1
  164. data/doc/js/searcher.js +0 -228
  165. data/doc/rdoc.css +0 -543
  166. data/doc/table_of_contents.html +0 -462
  167. data/examples/empty.png +0 -0
  168. data/examples/flat.txt +0 -55
  169. data/examples/graph.dot +0 -106
  170. data/examples/graph.html +0 -823
  171. data/examples/graph.png +0 -0
  172. data/examples/graph.txt +0 -170
  173. data/examples/minus.png +0 -0
  174. data/examples/multi.flat.txt +0 -23
  175. data/examples/multi.graph.html +0 -906
  176. data/examples/multi.grind.dat +0 -194
  177. data/examples/multi.stack.html +0 -573
  178. data/examples/plus.png +0 -0
  179. data/examples/stack.html +0 -573
  180. data/ext/ruby_prof/rp_call_info.c +0 -407
  181. data/ext/ruby_prof/rp_call_info.h +0 -48
  182. data/ext/ruby_prof/rp_measure.c +0 -48
  183. data/ext/ruby_prof/rp_measure.h +0 -45
  184. data/ext/ruby_prof/rp_measure_cpu_time.c +0 -112
  185. data/ext/ruby_prof/rp_measure_gc_runs.c +0 -65
  186. data/ext/ruby_prof/rp_measure_gc_time.c +0 -57
  187. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +0 -108
  188. data/ext/ruby_prof/vc/ruby_prof_19.vcxproj +0 -110
  189. data/ext/ruby_prof/version.h +0 -7
  190. data/lib/ruby-prof/aggregate_call_info.rb +0 -72
  191. data/lib/ruby-prof/call_info.rb +0 -89
  192. data/lib/ruby-prof/call_info_visitor.rb +0 -44
  193. data/lib/ruby-prof/images/empty.png +0 -0
  194. data/lib/ruby-prof/images/minus.png +0 -0
  195. data/lib/ruby-prof/images/plus.png +0 -0
  196. data/lib/ruby-prof/printers/flat_printer_with_line_numbers.rb +0 -57
  197. data/lib/ruby-prof/test.rb +0 -150
  198. data/test/aggregate_test.rb +0 -136
  199. data/test/call_info_test.rb +0 -78
  200. data/test/call_info_visitor_test.rb +0 -31
  201. data/test/exec_test.rb +0 -14
  202. data/test/measure_cpu_time_test.rb +0 -220
  203. data/test/measure_gc_runs_test.rb +0 -32
  204. data/test/measure_gc_time_test.rb +0 -36
  205. data/test/measure_memory_test.rb +0 -31
  206. data/test/method_elimination_test.rb +0 -84
  207. data/test/module_test.rb +0 -45
  208. data/test/stack_test.rb +0 -138
  209. data/test/test_suite.rb +0 -37
@@ -1,57 +1,62 @@
1
- /* Copyright (C) 2005-2013 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
- #ifndef __RP_METHOD_INFO__
5
- #define __RP_METHOD_INFO__
6
-
7
- #include <ruby.h>
8
-
9
- #ifndef RUBY_VM
10
- #include <st.h>
11
- typedef st_data_t st_index_t;
12
- #endif
13
-
14
- extern VALUE cMethodInfo;
15
-
16
- /* A key used to identify each method */
17
- typedef struct
18
- {
19
- VALUE klass; /* The method's class. */
20
- ID mid; /* The method id. */
21
- st_index_t key; /* Cache calculated key */
22
- } prof_method_key_t;
23
-
24
-
25
- /* Forward declaration, see rp_call_info.h */
26
- struct prof_call_infos_t;
27
-
28
- /* Profiling information for each method. */
29
- typedef struct
30
- {
31
- prof_method_key_t *key; /* Method key */
32
- const char *source_file; /* The method's source file */
33
- int line; /* The method's line number. */
34
- struct prof_call_infos_t *call_infos; /* Call info objects for this method */
35
- VALUE object; /* Cached ruby object */
36
- } prof_method_t;
37
-
38
- void rp_init_method_info(void);
39
-
40
- void method_key(prof_method_key_t* key, VALUE klass, ID mid);
41
-
42
- st_table * method_table_create();
43
- prof_method_t * method_table_lookup(st_table *table, const prof_method_key_t* key);
44
- size_t method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val);
45
- void method_table_free(st_table *table);
46
-
47
- prof_method_t* prof_method_create(VALUE klass, ID mid, const char* source_file, int line);
48
- VALUE prof_method_wrap(prof_method_t *result);
49
- void prof_method_mark(prof_method_t *method);
50
-
51
- /* Setup infrastructure to use method keys as hash comparisons */
52
- int method_table_cmp(prof_method_key_t *key1, prof_method_key_t *key2);
53
- st_index_t method_table_hash(prof_method_key_t *key);
54
-
55
- extern struct st_hash_type type_method_hash;
56
-
57
- #endif
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
+ #ifndef __RP_METHOD_INFO__
5
+ #define __RP_METHOD_INFO__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_measurement.h"
9
+
10
+ extern VALUE cRpMethodInfo;
11
+
12
+ // Source relation bit offsets.
13
+ enum {
14
+ kModuleIncludee = 0x1, // Included in module
15
+ kClassSingleton = 0x2, // Singleton of a class
16
+ kModuleSingleton = 0x4, // Singleton of a module
17
+ kObjectSingleton = 0x8, // Singleton of an object
18
+ kOtherSingleton = 0x10 // Singleton of unkown object
19
+ };
20
+
21
+ // Profiling information for each method.
22
+ // Excluded methods have no call_trees, source_klass, or source_file.
23
+ typedef struct prof_method_t
24
+ {
25
+ VALUE profile; // Profile this method is associated with - needed for mark phase
26
+ struct prof_call_trees_t* call_trees; // Call infos that call this method
27
+ st_table* allocations_table; // Tracks object allocations
28
+
29
+ st_data_t key; // Table key
30
+ unsigned int klass_flags; // Information about the type of class
31
+ VALUE klass; // Resolved klass
32
+ VALUE klass_name; // Resolved klass name for this method
33
+ VALUE method_name; // Resolved method name for this method
34
+
35
+ VALUE object; // Cached ruby object
36
+
37
+ bool recursive;
38
+ int visits; // Current visits on the stack
39
+ VALUE source_file; // Source file
40
+ int source_line; // Line number
41
+
42
+ prof_measurement_t* measurement; // Stores measurement data for this method
43
+ } prof_method_t;
44
+
45
+ void rp_init_method_info(void);
46
+
47
+ st_data_t method_key(VALUE klass, VALUE msym);
48
+
49
+ st_table* method_table_create(void);
50
+ prof_method_t* method_table_lookup(st_table* table, st_data_t key);
51
+ size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val);
52
+ void method_table_free(st_table* table);
53
+ prof_method_t* prof_method_create(VALUE profile, VALUE klass, VALUE msym, VALUE source_file, int source_line);
54
+ prof_method_t* prof_get_method(VALUE self);
55
+
56
+ VALUE prof_method_wrap(prof_method_t* result);
57
+ void prof_method_mark(void* data);
58
+
59
+ VALUE resolve_klass(VALUE klass, unsigned int* klass_flags);
60
+ VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags);
61
+
62
+ #endif //__RP_METHOD_INFO__
@@ -0,0 +1,908 @@
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
+ /* Document-class: RubyProf::Profile
5
+
6
+ The Profile class represents a single profiling run and provides the main API for using ruby-prof.
7
+ After creating a Profile instance, start profiling code by calling the Profile#start method. To finish profiling,
8
+ call Profile#stop. Once profiling is completed, the Profile instance contains the results.
9
+
10
+ profile = RubyProf::Profile.new
11
+ profile.start
12
+ ...
13
+ result = profile.stop
14
+
15
+ Alternatively, you can use the block syntax:
16
+
17
+ profile = RubyProf::Profile.profile do
18
+ ...
19
+ end
20
+ */
21
+
22
+ #include <assert.h>
23
+
24
+ #include "rp_allocation.h"
25
+ #include "rp_call_trees.h"
26
+ #include "rp_call_tree.h"
27
+ #include "rp_profile.h"
28
+ #include "rp_method.h"
29
+
30
+ VALUE cProfile;
31
+
32
+ /* support tracing ruby events from ruby-prof. useful for getting at
33
+ what actually happens inside the ruby interpreter (and ruby-prof).
34
+ set environment variable RUBY_PROF_TRACE to filename you want to
35
+ find the trace in.
36
+ */
37
+ FILE* trace_file = NULL;
38
+
39
+ static const char* get_event_name(rb_event_flag_t event)
40
+ {
41
+ switch (event) {
42
+ case RUBY_EVENT_LINE:
43
+ return "line";
44
+ case RUBY_EVENT_CLASS:
45
+ return "class";
46
+ case RUBY_EVENT_END:
47
+ return "end";
48
+ case RUBY_EVENT_CALL:
49
+ return "call";
50
+ case RUBY_EVENT_RETURN:
51
+ return "return";
52
+ case RUBY_EVENT_B_CALL:
53
+ return "b-call";
54
+ case RUBY_EVENT_B_RETURN:
55
+ return "b-return";
56
+ case RUBY_EVENT_C_CALL:
57
+ return "c-call";
58
+ case RUBY_EVENT_C_RETURN:
59
+ return "c-return";
60
+ case RUBY_EVENT_THREAD_BEGIN:
61
+ return "thread-begin";
62
+ case RUBY_EVENT_THREAD_END:
63
+ return "thread-end";
64
+ case RUBY_EVENT_FIBER_SWITCH:
65
+ return "fiber-switch";
66
+ case RUBY_EVENT_RAISE:
67
+ return "raise";
68
+ case RUBY_INTERNAL_EVENT_NEWOBJ:
69
+ return "newobj";
70
+ default:
71
+ return "unknown";
72
+ }
73
+ }
74
+
75
+ thread_data_t* check_fiber(prof_profile_t* profile, double measurement)
76
+ {
77
+ thread_data_t* result = NULL;
78
+
79
+ // Get the current fiber
80
+ VALUE fiber = rb_fiber_current();
81
+
82
+ /* We need to switch the profiling context if we either had none before,
83
+ we don't merge fibers and the fiber ids differ, or the thread ids differ. */
84
+ if (profile->last_thread_data->fiber != fiber)
85
+ {
86
+ result = threads_table_lookup(profile, fiber);
87
+ if (!result)
88
+ {
89
+ result = threads_table_insert(profile, fiber);
90
+ }
91
+ switch_thread(profile, result, measurement);
92
+ }
93
+ else
94
+ {
95
+ result = profile->last_thread_data;
96
+ }
97
+ return result;
98
+ }
99
+
100
+ static int excludes_method(st_data_t key, prof_profile_t* profile)
101
+ {
102
+ return (profile->exclude_methods_tbl &&
103
+ method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
104
+ }
105
+
106
+ static prof_method_t* create_method(VALUE profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
107
+ {
108
+ prof_method_t* result = prof_method_create(profile, klass, msym, source_file, source_line);
109
+
110
+ prof_profile_t* profile_t = prof_get_profile(profile);
111
+ method_table_insert(profile_t->last_thread_data->method_table, result->key, result);
112
+
113
+ return result;
114
+ }
115
+
116
+ static prof_method_t* check_parent_method(VALUE profile, thread_data_t* thread_data)
117
+ {
118
+ VALUE msym = ID2SYM(rb_intern("_inserted_parent_"));
119
+ st_data_t key = method_key(cProfile, msym);
120
+
121
+ prof_method_t* result = method_table_lookup(thread_data->method_table, key);
122
+
123
+ if (!result)
124
+ {
125
+ result = create_method(profile, key, cProfile, msym, Qnil, 0);
126
+ }
127
+
128
+ return result;
129
+ }
130
+
131
+ prof_method_t* check_method(VALUE profile, rb_trace_arg_t* trace_arg, rb_event_flag_t event, thread_data_t* thread_data)
132
+ {
133
+ VALUE klass = rb_tracearg_defined_class(trace_arg);
134
+
135
+ /* Special case - skip any methods from the mProf
136
+ module or cProfile class since they clutter
137
+ the results but aren't important to them results. */
138
+ if (klass == cProfile)
139
+ return NULL;
140
+
141
+ #ifdef HAVE_RB_TRACEARG_CALLEE_ID
142
+ VALUE msym = rb_tracearg_callee_id(trace_arg);
143
+ #else
144
+ VALUE msym = rb_tracearg_method_id(trace_arg);
145
+ #endif
146
+
147
+ st_data_t key = method_key(klass, msym);
148
+
149
+ prof_profile_t* profile_t = prof_get_profile(profile);
150
+ if (excludes_method(key, profile_t))
151
+ return NULL;
152
+
153
+ prof_method_t* result = method_table_lookup(thread_data->method_table, key);
154
+
155
+ if (!result)
156
+ {
157
+ VALUE source_file = (event != RUBY_EVENT_C_CALL ? rb_tracearg_path(trace_arg) : Qnil);
158
+ int source_line = (event != RUBY_EVENT_C_CALL ? FIX2INT(rb_tracearg_lineno(trace_arg)) : 0);
159
+ result = create_method(profile, key, klass, msym, source_file, source_line);
160
+ }
161
+
162
+ return result;
163
+ }
164
+
165
+ /* =========== Profiling ================= */
166
+ static void prof_trace(prof_profile_t* profile, rb_trace_arg_t* trace_arg, double measurement)
167
+ {
168
+ static VALUE last_fiber = Qnil;
169
+ VALUE fiber = rb_fiber_current();
170
+
171
+ rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
172
+ const char* event_name = get_event_name(event);
173
+
174
+ VALUE source_file = rb_tracearg_path(trace_arg);
175
+ int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
176
+
177
+ #ifdef HAVE_RB_TRACEARG_CALLEE_ID
178
+ VALUE msym = rb_tracearg_callee_id(trace_arg);
179
+ #else
180
+ VALUE msym = rb_tracearg_method_id(trace_arg);
181
+ #endif
182
+
183
+ unsigned int klass_flags;
184
+ VALUE klass = rb_tracearg_defined_class(trace_arg);
185
+ VALUE resolved_klass = resolve_klass(klass, &klass_flags);
186
+ const char* class_name = "";
187
+
188
+ if (resolved_klass != Qnil)
189
+ class_name = rb_class2name(resolved_klass);
190
+
191
+ if (last_fiber != fiber)
192
+ {
193
+ fprintf(trace_file, "\n");
194
+ }
195
+
196
+ const char* method_name_char = (msym != Qnil ? rb_id2name(SYM2ID(msym)) : "");
197
+ const char* source_file_char = (source_file != Qnil ? StringValuePtr(source_file) : "");
198
+
199
+ fprintf(trace_file, "%2lu:%2f %-8s %s#%s %s:%2d\n",
200
+ FIX2ULONG(fiber), (double)measurement,
201
+ event_name, class_name, method_name_char, source_file_char, source_line);
202
+ fflush(trace_file);
203
+ last_fiber = fiber;
204
+ }
205
+
206
+ static void prof_event_hook(VALUE trace_point, void* data)
207
+ {
208
+ VALUE profile = (VALUE)data;
209
+ prof_profile_t* profile_t = prof_get_profile(profile);
210
+
211
+ rb_trace_arg_t* trace_arg = rb_tracearg_from_tracepoint(trace_point);
212
+ double measurement = prof_measure(profile_t->measurer, trace_arg);
213
+ rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
214
+ VALUE self = rb_tracearg_self(trace_arg);
215
+
216
+ if (trace_file != NULL)
217
+ {
218
+ prof_trace(profile_t, trace_arg, measurement);
219
+ }
220
+
221
+ /* Special case - skip any methods from the mProf
222
+ module since they clutter the results but aren't important to them results. */
223
+ if (self == mProf)
224
+ return;
225
+
226
+ thread_data_t* thread_data = check_fiber(profile_t, measurement);
227
+
228
+ if (!thread_data->trace)
229
+ return;
230
+
231
+ switch (event)
232
+ {
233
+ case RUBY_EVENT_LINE:
234
+ {
235
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
236
+
237
+ if (!frame)
238
+ {
239
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
240
+
241
+ if (!method)
242
+ break;
243
+
244
+ prof_call_tree_t* call_tree = prof_call_tree_create(method, NULL, method->source_file, method->source_line);
245
+ prof_add_call_tree(method->call_trees, call_tree);
246
+
247
+ if (thread_data->call_tree)
248
+ {
249
+ prof_call_tree_add_parent(thread_data->call_tree, call_tree);
250
+ frame = prof_frame_unshift(thread_data->stack, call_tree, thread_data->call_tree, measurement);
251
+ }
252
+ else
253
+ {
254
+ frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
255
+ }
256
+
257
+ thread_data->call_tree = call_tree;
258
+ }
259
+
260
+ frame->source_file = rb_tracearg_path(trace_arg);
261
+ frame->source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
262
+
263
+ break;
264
+ }
265
+ case RUBY_EVENT_CALL:
266
+ case RUBY_EVENT_C_CALL:
267
+ {
268
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
269
+
270
+ if (!method)
271
+ break;
272
+
273
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
274
+ prof_call_tree_t* parent_call_tree = NULL;
275
+ prof_call_tree_t* call_tree = NULL;
276
+
277
+ // Frame can be NULL if we are switching from one fiber to another (see FiberTest#fiber_test)
278
+ if (frame)
279
+ {
280
+ parent_call_tree = frame->call_tree;
281
+ call_tree = call_tree_table_lookup(parent_call_tree->children, method->key);
282
+ }
283
+ else if (!frame && thread_data->call_tree)
284
+ {
285
+ // There is no current parent - likely we have returned out of the highest level method we have profiled so far.
286
+ // This can happen with enumerators (see fiber_test.rb). So create a new dummy parent.
287
+ prof_method_t* parent_method = check_parent_method(profile, thread_data);
288
+ parent_call_tree = prof_call_tree_create(parent_method, NULL, Qnil, 0);
289
+ prof_add_call_tree(parent_method->call_trees, parent_call_tree);
290
+ prof_call_tree_add_parent(thread_data->call_tree, parent_call_tree);
291
+ frame = prof_frame_unshift(thread_data->stack, parent_call_tree, thread_data->call_tree, measurement);
292
+ thread_data->call_tree = parent_call_tree;
293
+ }
294
+
295
+ if (!call_tree)
296
+ {
297
+ // This call info does not yet exist. So create it and add it to previous CallTree's children and the current method.
298
+ call_tree = prof_call_tree_create(method, parent_call_tree, frame ? frame->source_file : Qnil, frame? frame->source_line : 0);
299
+ prof_add_call_tree(method->call_trees, call_tree);
300
+ if (parent_call_tree)
301
+ prof_call_tree_add_child(parent_call_tree, call_tree);
302
+ }
303
+
304
+ if (!thread_data->call_tree)
305
+ thread_data->call_tree = call_tree;
306
+
307
+ // Push a new frame onto the stack for a new c-call or ruby call (into a method)
308
+ prof_frame_t* next_frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile_t->paused));
309
+ next_frame->source_file = method->source_file;
310
+ next_frame->source_line = method->source_line;
311
+ break;
312
+ }
313
+ case RUBY_EVENT_RETURN:
314
+ case RUBY_EVENT_C_RETURN:
315
+ {
316
+ // We need to check for excluded methods so that we don't pop them off the stack
317
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
318
+
319
+ if (!method)
320
+ break;
321
+
322
+ prof_frame_pop(thread_data->stack, measurement);
323
+ break;
324
+ }
325
+ case RUBY_INTERNAL_EVENT_NEWOBJ:
326
+ {
327
+ /* We want to assign the allocations lexically, not the execution context (otherwise all allocations will
328
+ show up under Class#new */
329
+ int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
330
+ VALUE source_file = rb_tracearg_path(trace_arg);
331
+
332
+ prof_method_t* method = prof_find_method(thread_data->stack, source_file, source_line);
333
+ if (method)
334
+ prof_allocate_increment(method, trace_arg);
335
+
336
+ break;
337
+ }
338
+ }
339
+ }
340
+
341
+ void prof_install_hook(VALUE self)
342
+ {
343
+ prof_profile_t* profile = prof_get_profile(self);
344
+
345
+ VALUE event_tracepoint = rb_tracepoint_new(Qnil,
346
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
347
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
348
+ RUBY_EVENT_LINE,
349
+ prof_event_hook, (void*)self);
350
+ rb_ary_push(profile->tracepoints, event_tracepoint);
351
+
352
+ if (profile->measurer->track_allocations)
353
+ {
354
+ VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook, (void*)self);
355
+ rb_ary_push(profile->tracepoints, allocation_tracepoint);
356
+ }
357
+
358
+ for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
359
+ {
360
+ rb_tracepoint_enable(rb_ary_entry(profile->tracepoints, i));
361
+ }
362
+ }
363
+
364
+ void prof_remove_hook(VALUE self)
365
+ {
366
+ prof_profile_t* profile = prof_get_profile(self);
367
+
368
+ for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
369
+ {
370
+ rb_tracepoint_disable(rb_ary_entry(profile->tracepoints, i));
371
+ }
372
+ rb_ary_clear(profile->tracepoints);
373
+ }
374
+
375
+ prof_profile_t* prof_get_profile(VALUE self)
376
+ {
377
+ /* Can't use Data_Get_Struct because that triggers the event hook
378
+ ending up in endless recursion. */
379
+ return RTYPEDDATA_DATA(self);
380
+ }
381
+
382
+ static int collect_threads(st_data_t key, st_data_t value, st_data_t result)
383
+ {
384
+ thread_data_t* thread_data = (thread_data_t*)value;
385
+ if (thread_data->trace)
386
+ {
387
+ VALUE threads_array = (VALUE)result;
388
+ rb_ary_push(threads_array, prof_thread_wrap(thread_data));
389
+ }
390
+ return ST_CONTINUE;
391
+ }
392
+
393
+ /* ======== Profile Class ====== */
394
+ static int mark_threads(st_data_t key, st_data_t value, st_data_t result)
395
+ {
396
+ thread_data_t* thread = (thread_data_t*)value;
397
+ prof_thread_mark(thread);
398
+ return ST_CONTINUE;
399
+ }
400
+
401
+ static int prof_profile_mark_methods(st_data_t key, st_data_t value, st_data_t result)
402
+ {
403
+ prof_method_t* method = (prof_method_t*)value;
404
+ prof_method_mark(method);
405
+ return ST_CONTINUE;
406
+ }
407
+
408
+ static void prof_profile_mark(void* data)
409
+ {
410
+ prof_profile_t* profile = (prof_profile_t*)data;
411
+ rb_gc_mark(profile->tracepoints);
412
+ rb_gc_mark(profile->running);
413
+ rb_gc_mark(profile->paused);
414
+
415
+ // If GC stress is true (useful for debugging), when threads_table_create is called in the
416
+ // allocate method Ruby will immediately call this mark method. Thus the threads_tbl will be NULL.
417
+ if (profile->threads_tbl)
418
+ rb_st_foreach(profile->threads_tbl, mark_threads, 0);
419
+
420
+ if (profile->exclude_methods_tbl)
421
+ rb_st_foreach(profile->exclude_methods_tbl, prof_profile_mark_methods, 0);
422
+ }
423
+
424
+ /* Freeing the profile creates a cascade of freeing. It frees its threads table, which frees
425
+ each thread and its associated call treee and methods. */
426
+ static void prof_profile_ruby_gc_free(void* data)
427
+ {
428
+ prof_profile_t* profile = (prof_profile_t*)data;
429
+ profile->last_thread_data = NULL;
430
+
431
+ threads_table_free(profile->threads_tbl);
432
+ profile->threads_tbl = NULL;
433
+
434
+ if (profile->exclude_threads_tbl)
435
+ {
436
+ rb_st_free_table(profile->exclude_threads_tbl);
437
+ profile->exclude_threads_tbl = NULL;
438
+ }
439
+
440
+ if (profile->include_threads_tbl)
441
+ {
442
+ rb_st_free_table(profile->include_threads_tbl);
443
+ profile->include_threads_tbl = NULL;
444
+ }
445
+
446
+ /* This table owns the excluded sentinels for now. */
447
+ method_table_free(profile->exclude_methods_tbl);
448
+ profile->exclude_methods_tbl = NULL;
449
+
450
+ xfree(profile->measurer);
451
+ profile->measurer = NULL;
452
+
453
+ xfree(profile);
454
+ }
455
+
456
+ size_t prof_profile_size(const void* data)
457
+ {
458
+ return sizeof(prof_profile_t);
459
+ }
460
+
461
+ static const rb_data_type_t profile_type =
462
+ {
463
+ .wrap_struct_name = "Profile",
464
+ .function =
465
+ {
466
+ .dmark = prof_profile_mark,
467
+ .dfree = prof_profile_ruby_gc_free,
468
+ .dsize = prof_profile_size,
469
+ },
470
+ .data = NULL,
471
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
472
+ };
473
+
474
+ static VALUE prof_allocate(VALUE klass)
475
+ {
476
+ VALUE result;
477
+ prof_profile_t* profile;
478
+ result = TypedData_Make_Struct(klass, prof_profile_t, &profile_type, profile);
479
+ profile->threads_tbl = threads_table_create();
480
+ profile->exclude_threads_tbl = NULL;
481
+ profile->include_threads_tbl = NULL;
482
+ profile->running = Qfalse;
483
+ profile->allow_exceptions = false;
484
+ profile->exclude_methods_tbl = method_table_create();
485
+ profile->running = Qfalse;
486
+ profile->tracepoints = rb_ary_new();
487
+ return result;
488
+ }
489
+
490
+ static void prof_exclude_common_methods(VALUE profile)
491
+ {
492
+ rb_funcall(profile, rb_intern("exclude_common_methods!"), 0);
493
+ }
494
+
495
+ static int pop_frames(VALUE key, st_data_t value, st_data_t data)
496
+ {
497
+ thread_data_t* thread_data = (thread_data_t*)value;
498
+ prof_profile_t* profile = (prof_profile_t*)data;
499
+ double measurement = prof_measure(profile->measurer, NULL);
500
+
501
+ if (profile->last_thread_data->fiber != thread_data->fiber)
502
+ switch_thread(profile, thread_data, measurement);
503
+
504
+ while (prof_frame_pop(thread_data->stack, measurement));
505
+
506
+ return ST_CONTINUE;
507
+ }
508
+
509
+ static void
510
+ prof_stop_threads(prof_profile_t* profile)
511
+ {
512
+ rb_st_foreach(profile->threads_tbl, pop_frames, (st_data_t)profile);
513
+ }
514
+
515
+ /* call-seq:
516
+ new()
517
+ new(options)
518
+
519
+ Returns a new profiler. Possible options for the options hash are:
520
+
521
+ measure_mode: Measure mode. Specifies the profile measure mode.
522
+ If not specified, defaults to RubyProf::WALL_TIME.
523
+ allow_exceptions: Whether to raise exceptions encountered during profiling,
524
+ or to suppress all exceptions during profiling
525
+ track_allocations: Whether to track object allocations while profiling. True or false.
526
+ exclude_common: Exclude common methods from the profile. True or false.
527
+ exclude_threads: Threads to exclude from the profiling results.
528
+ include_threads: Focus profiling on only the given threads. This will ignore
529
+ all other threads. */
530
+ static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
531
+ {
532
+ prof_profile_t* profile = prof_get_profile(self);
533
+ VALUE mode_or_options;
534
+ VALUE mode = Qnil;
535
+ VALUE exclude_threads = Qnil;
536
+ VALUE include_threads = Qnil;
537
+ VALUE exclude_common = Qnil;
538
+ VALUE allow_exceptions = Qfalse;
539
+ VALUE track_allocations = Qfalse;
540
+
541
+ int i;
542
+
543
+ switch (rb_scan_args(argc, argv, "02", &mode_or_options, &exclude_threads))
544
+ {
545
+ case 0:
546
+ break;
547
+ case 1:
548
+ if (FIXNUM_P(mode_or_options))
549
+ {
550
+ mode = mode_or_options;
551
+ }
552
+ else
553
+ {
554
+ Check_Type(mode_or_options, T_HASH);
555
+ mode = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("measure_mode")));
556
+ track_allocations = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("track_allocations")));
557
+ allow_exceptions = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("allow_exceptions")));
558
+ exclude_common = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_common")));
559
+ exclude_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_threads")));
560
+ include_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("include_threads")));
561
+ }
562
+ break;
563
+ case 2:
564
+ Check_Type(exclude_threads, T_ARRAY);
565
+ break;
566
+ }
567
+
568
+ if (mode == Qnil)
569
+ {
570
+ mode = INT2NUM(MEASURE_WALL_TIME);
571
+ }
572
+ else
573
+ {
574
+ Check_Type(mode, T_FIXNUM);
575
+ }
576
+ profile->measurer = prof_get_measurer(NUM2INT(mode), track_allocations == Qtrue);
577
+ profile->allow_exceptions = (allow_exceptions == Qtrue);
578
+
579
+ if (exclude_threads != Qnil)
580
+ {
581
+ Check_Type(exclude_threads, T_ARRAY);
582
+ assert(profile->exclude_threads_tbl == NULL);
583
+ profile->exclude_threads_tbl = threads_table_create();
584
+ for (i = 0; i < RARRAY_LEN(exclude_threads); i++)
585
+ {
586
+ VALUE thread = rb_ary_entry(exclude_threads, i);
587
+ rb_st_insert(profile->exclude_threads_tbl, thread, Qtrue);
588
+ }
589
+ }
590
+
591
+ if (include_threads != Qnil)
592
+ {
593
+ Check_Type(include_threads, T_ARRAY);
594
+ assert(profile->include_threads_tbl == NULL);
595
+ profile->include_threads_tbl = threads_table_create();
596
+ for (i = 0; i < RARRAY_LEN(include_threads); i++)
597
+ {
598
+ VALUE thread = rb_ary_entry(include_threads, i);
599
+ rb_st_insert(profile->include_threads_tbl, thread, Qtrue);
600
+ }
601
+ }
602
+
603
+ if (RTEST(exclude_common))
604
+ {
605
+ prof_exclude_common_methods(self);
606
+ }
607
+
608
+ return self;
609
+ }
610
+
611
+ /* call-seq:
612
+ paused? -> boolean
613
+
614
+ Returns whether a profile is currently paused.*/
615
+ static VALUE prof_paused(VALUE self)
616
+ {
617
+ prof_profile_t* profile = prof_get_profile(self);
618
+ return profile->paused;
619
+ }
620
+
621
+ /* call-seq:
622
+ running? -> boolean
623
+
624
+ Returns whether a profile is currently running.*/
625
+ static VALUE
626
+ prof_running(VALUE self)
627
+ {
628
+ prof_profile_t* profile = prof_get_profile(self);
629
+ return profile->running;
630
+ }
631
+
632
+ /* call-seq:
633
+ mode -> measure_mode
634
+
635
+ Returns the measure mode used in this profile.*/
636
+ static VALUE prof_profile_measure_mode(VALUE self)
637
+ {
638
+ prof_profile_t* profile = prof_get_profile(self);
639
+ return INT2NUM(profile->measurer->mode);
640
+ }
641
+
642
+ /* call-seq:
643
+ track_allocations -> boolean
644
+
645
+ Returns if object allocations were tracked in this profile.*/
646
+ static VALUE prof_profile_track_allocations(VALUE self)
647
+ {
648
+ prof_profile_t* profile = prof_get_profile(self);
649
+ return profile->measurer->track_allocations ? Qtrue : Qfalse;
650
+ }
651
+
652
+ /* call-seq:
653
+ start -> self
654
+
655
+ Starts recording profile data.*/
656
+ static VALUE prof_start(VALUE self)
657
+ {
658
+ char* trace_file_name;
659
+
660
+ prof_profile_t* profile = prof_get_profile(self);
661
+
662
+ if (profile->running == Qtrue)
663
+ {
664
+ rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
665
+ }
666
+
667
+ profile->running = Qtrue;
668
+ profile->paused = Qfalse;
669
+ profile->last_thread_data = threads_table_insert(profile, rb_fiber_current());
670
+
671
+ /* open trace file if environment wants it */
672
+ trace_file_name = getenv("RUBY_PROF_TRACE");
673
+
674
+ if (trace_file_name != NULL)
675
+ {
676
+ if (strcmp(trace_file_name, "stdout") == 0)
677
+ {
678
+ trace_file = stdout;
679
+ }
680
+ else if (strcmp(trace_file_name, "stderr") == 0)
681
+ {
682
+ trace_file = stderr;
683
+ }
684
+ else
685
+ {
686
+ trace_file = fopen(trace_file_name, "w");
687
+ }
688
+ }
689
+
690
+ prof_install_hook(self);
691
+ return self;
692
+ }
693
+
694
+ /* call-seq:
695
+ pause -> self
696
+
697
+ Pauses collecting profile data. */
698
+ static VALUE prof_pause(VALUE self)
699
+ {
700
+ prof_profile_t* profile = prof_get_profile(self);
701
+ if (profile->running == Qfalse)
702
+ {
703
+ rb_raise(rb_eRuntimeError, "RubyProf is not running.");
704
+ }
705
+
706
+ if (profile->paused == Qfalse)
707
+ {
708
+ profile->paused = Qtrue;
709
+ profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
710
+ rb_st_foreach(profile->threads_tbl, pause_thread, (st_data_t)profile);
711
+ }
712
+
713
+ return self;
714
+ }
715
+
716
+ /* call-seq:
717
+ resume -> self
718
+ resume(&block) -> self
719
+
720
+ Resumes recording profile data.*/
721
+ static VALUE prof_resume(VALUE self)
722
+ {
723
+ prof_profile_t* profile = prof_get_profile(self);
724
+ if (profile->running == Qfalse)
725
+ {
726
+ rb_raise(rb_eRuntimeError, "RubyProf is not running.");
727
+ }
728
+
729
+ if (profile->paused == Qtrue)
730
+ {
731
+ profile->paused = Qfalse;
732
+ profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
733
+ rb_st_foreach(profile->threads_tbl, unpause_thread, (st_data_t)profile);
734
+ }
735
+
736
+ return rb_block_given_p() ? rb_ensure(rb_yield, self, prof_pause, self) : self;
737
+ }
738
+
739
+ /* call-seq:
740
+ stop -> self
741
+
742
+ Stops collecting profile data.*/
743
+ static VALUE prof_stop(VALUE self)
744
+ {
745
+ prof_profile_t* profile = prof_get_profile(self);
746
+
747
+ if (profile->running == Qfalse)
748
+ {
749
+ rb_raise(rb_eRuntimeError, "RubyProf.start was not yet called");
750
+ }
751
+
752
+ prof_remove_hook(self);
753
+
754
+ /* close trace file if open */
755
+ if (trace_file != NULL)
756
+ {
757
+ if (trace_file != stderr && trace_file != stdout)
758
+ {
759
+ fclose(trace_file);
760
+ }
761
+ trace_file = NULL;
762
+ }
763
+
764
+ prof_stop_threads(profile);
765
+
766
+ /* Unset the last_thread_data (very important!)
767
+ and the threads table */
768
+ profile->running = profile->paused = Qfalse;
769
+ profile->last_thread_data = NULL;
770
+
771
+ return self;
772
+ }
773
+
774
+ /* call-seq:
775
+ threads -> Array of RubyProf::Thread
776
+
777
+ Returns an array of RubyProf::Thread instances that were profiled. */
778
+ static VALUE prof_threads(VALUE self)
779
+ {
780
+ VALUE result = rb_ary_new();
781
+ prof_profile_t* profile = prof_get_profile(self);
782
+ rb_st_foreach(profile->threads_tbl, collect_threads, result);
783
+ return result;
784
+ }
785
+
786
+ /* Document-method: RubyProf::Profile#Profile
787
+ call-seq:
788
+ profile(&block) -> self
789
+
790
+ Profiles the specified block.
791
+
792
+ profile = RubyProf::Profile.new
793
+ profile.profile do
794
+ ..
795
+ end
796
+ */
797
+ static VALUE prof_profile_object(VALUE self)
798
+ {
799
+ int result;
800
+ prof_profile_t* profile = prof_get_profile(self);
801
+
802
+ if (!rb_block_given_p())
803
+ {
804
+ rb_raise(rb_eArgError, "A block must be provided to the profile method.");
805
+ }
806
+
807
+ prof_start(self);
808
+ rb_protect(rb_yield, self, &result);
809
+ self = prof_stop(self);
810
+
811
+ if (profile->allow_exceptions && result != 0)
812
+ {
813
+ rb_jump_tag(result);
814
+ }
815
+
816
+ return self;
817
+ }
818
+
819
+ /* Document-method: RubyProf::Profile::Profile
820
+ call-seq:
821
+ profile(&block) -> RubyProf::Profile
822
+ profile(options, &block) -> RubyProf::Profile
823
+
824
+ Profiles the specified block and returns a RubyProf::Profile
825
+ object. Arguments are passed to Profile initialize method.
826
+
827
+ profile = RubyProf::Profile.profile do
828
+ ..
829
+ end
830
+ */
831
+ static VALUE prof_profile_class(int argc, VALUE* argv, VALUE klass)
832
+ {
833
+ return prof_profile_object(rb_class_new_instance(argc, argv, cProfile));
834
+ }
835
+
836
+ /* call-seq:
837
+ exclude_method!(module, method_name) -> self
838
+
839
+ Excludes the method from profiling results.
840
+ */
841
+ static VALUE prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
842
+ {
843
+ prof_profile_t* profile = prof_get_profile(self);
844
+
845
+ if (profile->running == Qtrue)
846
+ {
847
+ rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
848
+ }
849
+
850
+ st_data_t key = method_key(klass, msym);
851
+ prof_method_t* method = method_table_lookup(profile->exclude_methods_tbl, key);
852
+
853
+ if (!method)
854
+ {
855
+ method = prof_method_create(self, klass, msym, Qnil, 0);
856
+ method_table_insert(profile->exclude_methods_tbl, method->key, method);
857
+ }
858
+
859
+ return self;
860
+ }
861
+
862
+ /* :nodoc: */
863
+ VALUE prof_profile_dump(VALUE self)
864
+ {
865
+ VALUE result = rb_hash_new();
866
+ rb_hash_aset(result, ID2SYM(rb_intern("threads")), prof_threads(self));
867
+ return result;
868
+ }
869
+
870
+ /* :nodoc: */
871
+ VALUE prof_profile_load(VALUE self, VALUE data)
872
+ {
873
+ prof_profile_t* profile = prof_get_profile(self);
874
+
875
+ VALUE threads = rb_hash_aref(data, ID2SYM(rb_intern("threads")));
876
+ for (int i = 0; i < rb_array_len(threads); i++)
877
+ {
878
+ VALUE thread = rb_ary_entry(threads, i);
879
+ thread_data_t* thread_data = prof_get_thread(thread);
880
+ rb_st_insert(profile->threads_tbl, (st_data_t)thread_data->fiber_id, (st_data_t)thread_data);
881
+ }
882
+
883
+ return data;
884
+ }
885
+
886
+ void rp_init_profile(void)
887
+ {
888
+ cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
889
+ rb_define_alloc_func(cProfile, prof_allocate);
890
+
891
+ rb_define_singleton_method(cProfile, "profile", prof_profile_class, -1);
892
+ rb_define_method(cProfile, "initialize", prof_initialize, -1);
893
+ rb_define_method(cProfile, "start", prof_start, 0);
894
+ rb_define_method(cProfile, "stop", prof_stop, 0);
895
+ rb_define_method(cProfile, "resume", prof_resume, 0);
896
+ rb_define_method(cProfile, "pause", prof_pause, 0);
897
+ rb_define_method(cProfile, "running?", prof_running, 0);
898
+ rb_define_method(cProfile, "paused?", prof_paused, 0);
899
+ rb_define_method(cProfile, "threads", prof_threads, 0);
900
+ rb_define_method(cProfile, "exclude_method!", prof_exclude_method, 2);
901
+ rb_define_method(cProfile, "profile", prof_profile_object, 0);
902
+
903
+ rb_define_method(cProfile, "measure_mode", prof_profile_measure_mode, 0);
904
+ rb_define_method(cProfile, "track_allocations?", prof_profile_track_allocations, 0);
905
+
906
+ rb_define_method(cProfile, "_dump_data", prof_profile_dump, 0);
907
+ rb_define_method(cProfile, "_load_data", prof_profile_load, 1);
908
+ }