ruby-prof 0.13.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }