ruby-prof 1.1.0 → 1.2.0

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +12 -1
  3. data/bin/ruby-prof +100 -152
  4. data/ext/ruby_prof/rp_aggregate_call_tree.c +41 -0
  5. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  6. data/ext/ruby_prof/rp_allocation.c +31 -51
  7. data/ext/ruby_prof/rp_allocation.h +2 -2
  8. data/ext/ruby_prof/rp_call_tree.c +353 -0
  9. data/ext/ruby_prof/rp_call_tree.h +43 -0
  10. data/ext/ruby_prof/rp_call_trees.c +266 -0
  11. data/ext/ruby_prof/rp_call_trees.h +29 -0
  12. data/ext/ruby_prof/rp_measure_allocations.c +11 -13
  13. data/ext/ruby_prof/rp_measure_process_time.c +11 -13
  14. data/ext/ruby_prof/rp_measure_wall_time.c +17 -15
  15. data/ext/ruby_prof/rp_measurement.c +27 -36
  16. data/ext/ruby_prof/rp_measurement.h +6 -6
  17. data/ext/ruby_prof/rp_method.c +88 -248
  18. data/ext/ruby_prof/rp_method.h +12 -19
  19. data/ext/ruby_prof/rp_profile.c +277 -270
  20. data/ext/ruby_prof/rp_profile.h +0 -1
  21. data/ext/ruby_prof/rp_stack.c +113 -105
  22. data/ext/ruby_prof/rp_stack.h +15 -18
  23. data/ext/ruby_prof/rp_thread.c +115 -107
  24. data/ext/ruby_prof/rp_thread.h +9 -8
  25. data/ext/ruby_prof/ruby_prof.c +27 -23
  26. data/ext/ruby_prof/ruby_prof.h +9 -0
  27. data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -7
  28. data/lib/ruby-prof.rb +2 -3
  29. data/lib/ruby-prof/assets/call_stack_printer.html.erb +4 -7
  30. data/lib/ruby-prof/assets/graph_printer.html.erb +5 -6
  31. data/lib/ruby-prof/{call_info.rb → call_tree.rb} +6 -6
  32. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  33. data/lib/ruby-prof/measurement.rb +5 -2
  34. data/lib/ruby-prof/method_info.rb +3 -15
  35. data/lib/ruby-prof/printers/call_info_printer.rb +12 -10
  36. data/lib/ruby-prof/printers/call_stack_printer.rb +19 -22
  37. data/lib/ruby-prof/printers/call_tree_printer.rb +1 -1
  38. data/lib/ruby-prof/printers/dot_printer.rb +3 -3
  39. data/lib/ruby-prof/printers/graph_printer.rb +3 -4
  40. data/lib/ruby-prof/printers/multi_printer.rb +2 -2
  41. data/lib/ruby-prof/rack.rb +3 -0
  42. data/lib/ruby-prof/thread.rb +3 -18
  43. data/lib/ruby-prof/version.rb +1 -1
  44. data/ruby-prof.gemspec +7 -0
  45. data/test/alias_test.rb +42 -45
  46. data/test/basic_test.rb +0 -86
  47. data/test/{call_info_visitor_test.rb → call_tree_visitor_test.rb} +6 -5
  48. data/test/call_trees_test.rb +66 -0
  49. data/test/exclude_methods_test.rb +17 -12
  50. data/test/fiber_test.rb +203 -6
  51. data/test/gc_test.rb +32 -23
  52. data/test/inverse_call_tree_test.rb +175 -0
  53. data/test/line_number_test.rb +64 -67
  54. data/test/marshal_test.rb +7 -11
  55. data/test/measure_allocations_test.rb +224 -234
  56. data/test/measure_allocations_trace_test.rb +224 -234
  57. data/test/measure_memory_trace_test.rb +814 -469
  58. data/test/measure_process_time_test.rb +0 -64
  59. data/test/measure_times.rb +2 -0
  60. data/test/measure_wall_time_test.rb +34 -58
  61. data/test/pause_resume_test.rb +19 -10
  62. data/test/prime.rb +1 -3
  63. data/test/prime_script.rb +6 -0
  64. data/test/printers_test.rb +1 -1
  65. data/test/recursive_test.rb +50 -54
  66. data/test/start_stop_test.rb +19 -19
  67. data/test/test_helper.rb +3 -15
  68. data/test/thread_test.rb +11 -11
  69. data/test/unique_call_path_test.rb +25 -95
  70. metadata +19 -9
  71. data/ext/ruby_prof/rp_call_info.c +0 -271
  72. data/ext/ruby_prof/rp_call_info.h +0 -35
  73. data/lib/ruby-prof/call_info_visitor.rb +0 -38
  74. data/test/parser_timings.rb +0 -24
@@ -18,20 +18,15 @@ enum {
18
18
  kOtherSingleton = 0x10 /* Singleton of unkown object */
19
19
  };
20
20
 
21
- /* Forward declaration, see rp_call_info.h */
22
- struct prof_call_infos_t;
23
-
24
21
  /* Profiling information for each method. */
25
- /* Excluded methods have no call_infos, source_klass, or source_file. */
22
+ /* Excluded methods have no call_trees, source_klass, or source_file. */
26
23
  typedef struct
27
24
  {
28
25
  st_data_t key; /* Table key */
29
26
 
30
27
  int visits; /* Current visits on the stack */
31
- bool excluded; /* Exclude from profile? */
32
28
 
33
- st_table* parent_call_infos; /* Call infos that call this method */
34
- st_table* child_call_infos; /* Call infos that this method calls */
29
+ struct prof_call_trees_t* call_trees; /* Call infos that call this method */
35
30
  st_table* allocations_table; /* Tracks object allocations */
36
31
 
37
32
  unsigned int klass_flags; /* Information about the type of class */
@@ -41,30 +36,28 @@ typedef struct
41
36
 
42
37
  VALUE object; /* Cached ruby object */
43
38
 
44
- bool root; /* Is this a root method */
45
39
  bool recursive;
46
40
  VALUE source_file; /* Source file */
47
41
  int source_line; /* Line number */
48
42
 
49
- prof_measurement_t *measurement;
43
+ prof_measurement_t* measurement;
50
44
  } prof_method_t;
51
45
 
52
46
  void rp_init_method_info(void);
53
47
 
54
48
  st_data_t method_key(VALUE klass, VALUE msym);
55
49
 
56
- st_table *method_table_create(void);
57
- prof_method_t* prof_method_create_excluded(VALUE klass, VALUE msym);
58
- prof_method_t *method_table_lookup(st_table *table, st_data_t key);
59
- size_t method_table_insert(st_table *table, st_data_t key, prof_method_t *val);
60
- void method_table_free(st_table *table);
61
- prof_method_t *prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line);
62
- prof_method_t *prof_method_get(VALUE self);
50
+ st_table* method_table_create(void);
51
+ prof_method_t* method_table_lookup(st_table* table, st_data_t key);
52
+ size_t method_table_insert(st_table* table, st_data_t key, prof_method_t* val);
53
+ void method_table_free(st_table* table);
54
+ prof_method_t* prof_method_create(VALUE klass, VALUE msym, VALUE source_file, int source_line);
55
+ prof_method_t* prof_get_method(VALUE self);
63
56
 
64
- VALUE prof_method_wrap(prof_method_t *result);
65
- void prof_method_mark(void *data);
57
+ VALUE prof_method_wrap(prof_method_t* result);
58
+ void prof_method_mark(void* data);
66
59
 
67
- VALUE resolve_klass(VALUE klass, unsigned int *klass_flags);
60
+ VALUE resolve_klass(VALUE klass, unsigned int* klass_flags);
68
61
  VALUE resolve_klass_name(VALUE klass, unsigned int* klass_flags);
69
62
 
70
63
  #endif //__RP_METHOD_INFO__
@@ -1,28 +1,29 @@
1
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
- /* Document-class: RubyProf::Profile
4
+ /* Document-class: RubyProf::Profile
5
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.
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
9
 
10
- profile = RubyProf::Profile.new
11
- profile.start
12
- ...
13
- result = profile.stop
10
+ profile = RubyProf::Profile.new
11
+ profile.start
12
+ ...
13
+ result = profile.stop
14
14
 
15
- Alternatively, you can use the block syntax:
15
+ Alternatively, you can use the block syntax:
16
16
 
17
- profile = RubyProf::Profile.profile do
18
- ...
19
- end
20
- */
17
+ profile = RubyProf::Profile.profile do
18
+ ...
19
+ end
20
+ */
21
21
 
22
22
  #include <assert.h>
23
23
 
24
24
  #include "rp_allocation.h"
25
- #include "rp_call_info.h"
25
+ #include "rp_call_trees.h"
26
+ #include "rp_call_tree.h"
26
27
  #include "rp_profile.h"
27
28
  #include "rp_method.h"
28
29
 
@@ -35,69 +36,37 @@ VALUE cProfile;
35
36
  */
36
37
  FILE* trace_file = NULL;
37
38
 
38
- static int
39
- excludes_method(st_data_t key, prof_profile_t* profile)
40
- {
41
- return (profile->exclude_methods_tbl &&
42
- method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
43
- }
44
-
45
- static prof_method_t*
46
- create_method(prof_profile_t* profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
47
- {
48
- prof_method_t* result = NULL;
49
-
50
- if (excludes_method(key, profile))
51
- {
52
- /* We found a exclusion sentinel so propagate it into the thread's local hash table. */
53
- /* TODO(nelgau): Is there a way to avoid this allocation completely so that all these
54
- tables share the same exclusion method struct? The first attempt failed due to my
55
- ignorance of the whims of the GC. */
56
- result = prof_method_create_excluded(klass, msym);
57
- }
58
- else
59
- {
60
- result = prof_method_create(klass, msym, source_file, source_line);
61
- }
62
-
63
- /* Insert the newly created method, or the exlcusion sentinel. */
64
- method_table_insert(profile->last_thread_data->method_table, result->key, result);
65
-
66
- return result;
67
- }
68
-
69
- static const char *
70
- get_event_name(rb_event_flag_t event)
39
+ static const char* get_event_name(rb_event_flag_t event)
71
40
  {
72
41
  switch (event) {
73
- case RUBY_EVENT_LINE:
74
- return "line";
75
- case RUBY_EVENT_CLASS:
76
- return "class";
77
- case RUBY_EVENT_END:
78
- return "end";
79
- case RUBY_EVENT_CALL:
80
- return "call";
81
- case RUBY_EVENT_RETURN:
82
- return "return";
83
- case RUBY_EVENT_B_CALL:
84
- return "b-call";
85
- case RUBY_EVENT_B_RETURN:
86
- return "b-return";
87
- case RUBY_EVENT_C_CALL:
88
- return "c-call";
89
- case RUBY_EVENT_C_RETURN:
90
- return "c-return";
91
- case RUBY_EVENT_THREAD_BEGIN:
92
- return "thread-begin";
93
- case RUBY_EVENT_THREAD_END:
94
- return "thread-end";
95
- case RUBY_EVENT_FIBER_SWITCH:
96
- return "fiber-switch";
97
- case RUBY_EVENT_RAISE:
98
- return "raise";
99
- default:
100
- return "unknown";
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
+ default:
69
+ return "unknown";
101
70
  }
102
71
  }
103
72
 
@@ -108,14 +77,14 @@ VALUE get_fiber(prof_profile_t* profile)
108
77
  else
109
78
  return rb_fiber_current();
110
79
  }
111
- /* =========== Profiling ================= */
112
- thread_data_t* check_fiber(prof_profile_t *profile, double measurement)
80
+
81
+ thread_data_t* check_fiber(prof_profile_t* profile, double measurement)
113
82
  {
114
83
  thread_data_t* result = NULL;
115
-
84
+
116
85
  /* Get the current thread and fiber information. */
117
86
  VALUE fiber = get_fiber(profile);
118
-
87
+
119
88
  /* We need to switch the profiling context if we either had none before,
120
89
  we don't merge fibers and the fiber ids differ, or the thread ids differ. */
121
90
  if (profile->last_thread_data->fiber != fiber)
@@ -134,160 +103,210 @@ thread_data_t* check_fiber(prof_profile_t *profile, double measurement)
134
103
  return result;
135
104
  }
136
105
 
137
- static void
138
- prof_trace(prof_profile_t* profile, rb_trace_arg_t *trace_arg, double measurement)
106
+ static int excludes_method(st_data_t key, prof_profile_t* profile)
107
+ {
108
+ return (profile->exclude_methods_tbl &&
109
+ method_table_lookup(profile->exclude_methods_tbl, key) != NULL);
110
+ }
111
+
112
+ static prof_method_t* create_method(prof_profile_t* profile, st_data_t key, VALUE klass, VALUE msym, VALUE source_file, int source_line)
113
+ {
114
+ prof_method_t* result = prof_method_create(klass, msym, source_file, source_line);
115
+ method_table_insert(profile->last_thread_data->method_table, result->key, result);
116
+
117
+ return result;
118
+ }
119
+
120
+ static prof_method_t* check_parent_method(prof_profile_t* profile, thread_data_t* thread_data)
121
+ {
122
+ VALUE msym = ID2SYM(rb_intern("_inserted_parent_"));
123
+ st_data_t key = method_key(cProfile, msym);
124
+
125
+ prof_method_t* result = method_table_lookup(thread_data->method_table, key);
126
+
127
+ if (!result)
128
+ {
129
+ result = create_method(profile, key, cProfile, msym, Qnil, 0);
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ prof_method_t* check_method(prof_profile_t* profile, rb_trace_arg_t* trace_arg, rb_event_flag_t event, thread_data_t* thread_data)
136
+ {
137
+ VALUE klass = rb_tracearg_defined_class(trace_arg);
138
+
139
+ /* Special case - skip any methods from the mProf
140
+ module or cProfile class since they clutter
141
+ the results but aren't important to them results. */
142
+ if (klass == cProfile)
143
+ return NULL;
144
+
145
+ #ifdef HAVE_RB_TRACEARG_CALLEE_ID
146
+ VALUE msym = rb_tracearg_callee_id(trace_arg);
147
+ #else
148
+ VALUE msym = rb_tracearg_method_id(trace_arg);
149
+ #endif
150
+
151
+ st_data_t key = method_key(klass, msym);
152
+
153
+ if (excludes_method(key, profile))
154
+ return NULL;
155
+
156
+ prof_method_t* result = method_table_lookup(thread_data->method_table, key);
157
+
158
+ if (!result)
159
+ {
160
+ VALUE source_file = (event != RUBY_EVENT_C_CALL ? rb_tracearg_path(trace_arg) : Qnil);
161
+ int source_line = (event != RUBY_EVENT_C_CALL ? FIX2INT(rb_tracearg_lineno(trace_arg)) : 0);
162
+ result = create_method(profile, key, klass, msym, source_file, source_line);
163
+ }
164
+
165
+ return result;
166
+ }
167
+
168
+ /* =========== Profiling ================= */
169
+ static void prof_trace(prof_profile_t* profile, rb_trace_arg_t* trace_arg, double measurement)
139
170
  {
140
171
  static VALUE last_fiber = Qnil;
141
172
  VALUE fiber = get_fiber(profile);
142
-
173
+
143
174
  rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
144
175
  const char* event_name = get_event_name(event);
145
-
176
+
146
177
  VALUE source_file = rb_tracearg_path(trace_arg);
147
178
  int source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
148
-
149
- #ifdef HAVE_RB_TRACEARG_CALLEE_ID
179
+
180
+ #ifdef HAVE_RB_TRACEARG_CALLEE_ID
150
181
  VALUE msym = rb_tracearg_callee_id(trace_arg);
151
- #else
182
+ #else
152
183
  VALUE msym = rb_tracearg_method_id(trace_arg);
153
- #endif
154
-
184
+ #endif
185
+
155
186
  unsigned int klass_flags;
156
187
  VALUE klass = rb_tracearg_defined_class(trace_arg);
157
188
  VALUE resolved_klass = resolve_klass(klass, &klass_flags);
158
189
  const char* class_name = "";
159
-
190
+
160
191
  if (resolved_klass != Qnil)
161
192
  class_name = rb_class2name(resolved_klass);
162
-
193
+
163
194
  if (last_fiber != fiber)
164
195
  {
165
196
  fprintf(trace_file, "\n");
166
197
  }
167
-
198
+
168
199
  const char* method_name_char = (msym != Qnil ? rb_id2name(SYM2ID(msym)) : "");
169
200
  const char* source_file_char = (source_file != Qnil ? StringValuePtr(source_file) : "");
170
-
201
+
171
202
  fprintf(trace_file, "%2lu:%2f %-8s %s#%s %s:%2d\n",
172
- FIX2ULONG(fiber), (double) measurement,
203
+ FIX2ULONG(fiber), (double)measurement,
173
204
  event_name, class_name, method_name_char, source_file_char, source_line);
174
205
  fflush(trace_file);
175
206
  last_fiber = fiber;
176
207
  }
177
208
 
178
- static void
179
- prof_event_hook(VALUE trace_point, void* data)
209
+ static void prof_event_hook(VALUE trace_point, void* data)
180
210
  {
181
211
  prof_profile_t* profile = (prof_profile_t*)data;
182
- thread_data_t* thread_data = NULL;
183
- prof_frame_t *frame = NULL;
184
212
  rb_trace_arg_t* trace_arg = rb_tracearg_from_tracepoint(trace_point);
185
213
  double measurement = prof_measure(profile->measurer, trace_arg);
186
214
  rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
187
215
  VALUE self = rb_tracearg_self(trace_arg);
188
-
216
+
189
217
  if (trace_file != NULL)
190
218
  {
191
219
  prof_trace(profile, trace_arg, measurement);
192
220
  }
193
-
221
+
194
222
  /* Special case - skip any methods from the mProf
195
223
  module since they clutter the results but aren't important to them results. */
196
224
  if (self == mProf)
197
225
  return;
198
-
199
- thread_data = check_fiber(profile, measurement);
200
-
226
+
227
+ thread_data_t* thread_data = check_fiber(profile, measurement);
228
+
201
229
  if (!thread_data->trace)
202
230
  return;
203
-
204
- /* Get the current frame for the current thread. */
205
- frame = thread_data->stack->ptr;
206
-
231
+
207
232
  switch (event)
208
233
  {
209
234
  case RUBY_EVENT_LINE:
210
235
  {
211
- /* Keep track of the current line number in this method. When
212
- a new method is called, we know what line number it was
213
- called from. */
214
- if (frame->call_info)
236
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
237
+
238
+ if (!frame)
215
239
  {
216
- if (prof_frame_is_real(frame))
240
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
241
+
242
+ if (!method)
243
+ break;
244
+
245
+ prof_call_tree_t* call_tree = prof_call_tree_create(method, NULL, method->source_file, method->source_line);
246
+ prof_add_call_tree(method->call_trees, call_tree);
247
+
248
+ if (thread_data->call_tree)
217
249
  {
218
- frame->source_file = rb_tracearg_path(trace_arg);
219
- frame->source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
250
+ prof_call_tree_add_parent(thread_data->call_tree, call_tree);
251
+ frame = prof_frame_unshift(thread_data->stack, call_tree, thread_data->call_tree, measurement);
220
252
  }
221
- break;
253
+ else
254
+ {
255
+ frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile->paused));
256
+ }
257
+
258
+ thread_data->call_tree = call_tree;
222
259
  }
223
260
 
224
- /* If we get here there was no frame, which means this is
225
- the first method seen for this thread, so fall through
226
- to below to create it. */
261
+ frame->source_file = rb_tracearg_path(trace_arg);
262
+ frame->source_line = FIX2INT(rb_tracearg_lineno(trace_arg));
263
+
264
+ break;
227
265
  }
228
266
  case RUBY_EVENT_CALL:
229
267
  case RUBY_EVENT_C_CALL:
230
268
  {
231
- prof_frame_t* next_frame;
232
- prof_call_info_t* call_info;
233
- prof_method_t* method;
269
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
234
270
 
235
- /* Get current measurement */
236
- measurement = prof_measure(profile->measurer, trace_arg);
271
+ if (!method)
272
+ break;
237
273
 
238
- VALUE klass = rb_tracearg_defined_class(trace_arg);
239
-
240
- /* Special case - skip any methods from the mProf
241
- module or cProfile class since they clutter
242
- the results but aren't important to them results. */
243
- if (klass == cProfile)
244
- return;
245
-
246
- #ifdef HAVE_RB_TRACEARG_CALLEE_ID
247
- VALUE msym = rb_tracearg_callee_id(trace_arg);
248
- #else
249
- VALUE msym = rb_tracearg_method_id(trace_arg);
250
- #endif
274
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
275
+ prof_call_tree_t* parent_call_tree = NULL;
276
+ prof_call_tree_t* call_tree = NULL;
251
277
 
252
- st_data_t key = method_key(klass, msym);
253
-
254
- method = method_table_lookup(thread_data->method_table, key);
255
-
256
- if (!method)
278
+ // Frame can be NULL if we are switching from one fiber to another (see FiberTest#fiber_test)
279
+ if (frame)
257
280
  {
258
- VALUE source_file = (event != RUBY_EVENT_C_CALL ? rb_tracearg_path(trace_arg) : Qnil);
259
- int source_line = (event != RUBY_EVENT_C_CALL ? FIX2INT(rb_tracearg_lineno(trace_arg)) : 0);
260
- method = create_method(profile, key, klass, msym, source_file, source_line);
281
+ parent_call_tree = frame->call_tree;
282
+ call_tree = call_tree_table_lookup(parent_call_tree->children, method->key);
261
283
  }
262
-
263
- if (method->excluded)
284
+ else if (!frame && thread_data->call_tree)
264
285
  {
265
- prof_stack_pass(thread_data->stack);
266
- break;
286
+ // There is no current parent - likely we have returned out of the highest level method we have profiled so far.
287
+ // This can happen with enumerators (see fiber_test.rb). So create a new dummy parent.
288
+ prof_method_t* parent_method = check_parent_method(profile, thread_data);
289
+ parent_call_tree = prof_call_tree_create(parent_method, NULL, Qnil, 0);
290
+ prof_add_call_tree(parent_method->call_trees, parent_call_tree);
291
+ prof_call_tree_add_parent(thread_data->call_tree, parent_call_tree);
292
+ frame = prof_frame_unshift(thread_data->stack, parent_call_tree, thread_data->call_tree, measurement);
293
+ thread_data->call_tree = parent_call_tree;
267
294
  }
268
-
269
- if (!frame->call_info)
270
- {
271
- method->root = true;
272
- call_info = prof_call_info_create(method, NULL, method->source_file, method->source_line);
273
- st_insert(method->parent_call_infos, (st_data_t)&key, (st_data_t)call_info);
274
- }
275
- else
295
+
296
+ if (!call_tree)
276
297
  {
277
- call_info = call_info_table_lookup(method->parent_call_infos, frame->call_info->method->key);
278
-
279
- if (!call_info)
280
- {
281
- /* This call info does not yet exist. So create it, then add
282
- it to previous callinfo's children and to the current method .*/
283
- call_info = prof_call_info_create(method, frame->call_info->method, frame->source_file, frame->source_line);
284
- call_info_table_insert(method->parent_call_infos, frame->call_info->method->key, call_info);
285
- call_info_table_insert(frame->call_info->method->child_call_infos, method->key, call_info);
286
- }
298
+ // This call info does not yet exist. So create it and add it to previous CallTree's children and the current method.
299
+ call_tree = prof_call_tree_create(method, parent_call_tree, frame ? frame->source_file : Qnil, frame? frame->source_line : 0);
300
+ prof_add_call_tree(method->call_trees, call_tree);
301
+ if (parent_call_tree)
302
+ prof_call_tree_add_child(parent_call_tree, call_tree);
287
303
  }
288
304
 
289
- /* Push a new frame onto the stack for a new c-call or ruby call (into a method) */
290
- next_frame = prof_stack_push(thread_data->stack, call_info, measurement, RTEST(profile->paused));
305
+ if (!thread_data->call_tree)
306
+ thread_data->call_tree = call_tree;
307
+
308
+ // Push a new frame onto the stack for a new c-call or ruby call (into a method)
309
+ prof_frame_t* next_frame = prof_frame_push(thread_data->stack, call_tree, measurement, RTEST(profile->paused));
291
310
  next_frame->source_file = method->source_file;
292
311
  next_frame->source_line = method->source_line;
293
312
  break;
@@ -295,8 +314,13 @@ prof_event_hook(VALUE trace_point, void* data)
295
314
  case RUBY_EVENT_RETURN:
296
315
  case RUBY_EVENT_C_RETURN:
297
316
  {
298
- /* Get current measurement */
299
- prof_stack_pop(thread_data->stack, measurement);
317
+ // We need to check for excluded methods so that we don't pop them off the stack
318
+ prof_method_t* method = check_method(profile, trace_arg, event, thread_data);
319
+
320
+ if (!method)
321
+ break;
322
+
323
+ prof_frame_pop(thread_data->stack, measurement);
300
324
  break;
301
325
  }
302
326
  case RUBY_INTERNAL_EVENT_NEWOBJ:
@@ -315,35 +339,33 @@ prof_event_hook(VALUE trace_point, void* data)
315
339
  }
316
340
  }
317
341
 
318
- void
319
- prof_install_hook(VALUE self)
342
+ void prof_install_hook(VALUE self)
320
343
  {
321
344
  prof_profile_t* profile = prof_get_profile(self);
322
-
345
+
323
346
  VALUE event_tracepoint = rb_tracepoint_new(Qnil,
324
347
  RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
325
348
  RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
326
349
  RUBY_EVENT_LINE,
327
350
  prof_event_hook, profile);
328
351
  rb_ary_push(profile->tracepoints, event_tracepoint);
329
-
352
+
330
353
  if (profile->measurer->track_allocations)
331
354
  {
332
355
  VALUE allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, prof_event_hook, profile);
333
356
  rb_ary_push(profile->tracepoints, allocation_tracepoint);
334
357
  }
335
-
358
+
336
359
  for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
337
360
  {
338
361
  rb_tracepoint_enable(rb_ary_entry(profile->tracepoints, i));
339
362
  }
340
363
  }
341
364
 
342
- void
343
- prof_remove_hook(VALUE self)
365
+ void prof_remove_hook(VALUE self)
344
366
  {
345
367
  prof_profile_t* profile = prof_get_profile(self);
346
-
368
+
347
369
  for (int i = 0; i < RARRAY_LEN(profile->tracepoints); i++)
348
370
  {
349
371
  rb_tracepoint_disable(rb_ary_entry(profile->tracepoints, i));
@@ -351,18 +373,16 @@ prof_remove_hook(VALUE self)
351
373
  rb_ary_clear(profile->tracepoints);
352
374
  }
353
375
 
354
- prof_profile_t*
355
- prof_get_profile(VALUE self)
376
+ prof_profile_t* prof_get_profile(VALUE self)
356
377
  {
357
378
  /* Can't use Data_Get_Struct because that triggers the event hook
358
379
  ending up in endless recursion. */
359
380
  return DATA_PTR(self);
360
381
  }
361
382
 
362
- static int
363
- collect_threads(st_data_t key, st_data_t value, st_data_t result)
383
+ static int collect_threads(st_data_t key, st_data_t value, st_data_t result)
364
384
  {
365
- thread_data_t* thread_data = (thread_data_t*) value;
385
+ thread_data_t* thread_data = (thread_data_t*)value;
366
386
  if (thread_data->trace)
367
387
  {
368
388
  VALUE threads_array = (VALUE)result;
@@ -372,35 +392,40 @@ collect_threads(st_data_t key, st_data_t value, st_data_t result)
372
392
  }
373
393
 
374
394
  /* ======== Profile Class ====== */
375
- static int
376
- mark_threads(st_data_t key, st_data_t value, st_data_t result)
395
+ static int mark_threads(st_data_t key, st_data_t value, st_data_t result)
377
396
  {
378
- thread_data_t *thread = (thread_data_t *) value;
397
+ thread_data_t* thread = (thread_data_t*)value;
379
398
  prof_thread_mark(thread);
380
399
  return ST_CONTINUE;
381
400
  }
382
401
 
383
- static int
384
- mark_methods(st_data_t key, st_data_t value, st_data_t result)
402
+ static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
385
403
  {
386
- prof_method_t *method = (prof_method_t *) value;
404
+ prof_method_t* method = (prof_method_t*)value;
387
405
  prof_method_mark(method);
388
406
  return ST_CONTINUE;
389
407
  }
390
408
 
391
- static void
392
- prof_mark(prof_profile_t *profile)
409
+ static void prof_mark(prof_profile_t* profile)
393
410
  {
394
411
  rb_gc_mark(profile->tracepoints);
395
- st_foreach(profile->threads_tbl, mark_threads, 0);
396
- st_foreach(profile->exclude_methods_tbl, mark_methods, 0);
412
+ rb_gc_mark(profile->running);
413
+ rb_gc_mark(profile->paused);
414
+ rb_gc_mark(profile->tracepoints);
415
+
416
+ // If GC stress is true (useful for debugging), when threads_table_create is called in the
417
+ // allocate method Ruby will immediately call this mark method. Thus the threads_tbl will be NULL.
418
+ if (profile->threads_tbl)
419
+ rb_st_foreach(profile->threads_tbl, mark_threads, 0);
420
+
421
+ if (profile->exclude_methods_tbl)
422
+ rb_st_foreach(profile->exclude_methods_tbl, mark_methods, 0);
397
423
  }
398
424
 
399
425
  /* Freeing the profile creates a cascade of freeing.
400
426
  It fress the thread table, which frees its methods,
401
427
  which frees its call infos. */
402
- static void
403
- prof_free(prof_profile_t *profile)
428
+ static void prof_free(prof_profile_t* profile)
404
429
  {
405
430
  profile->last_thread_data = NULL;
406
431
 
@@ -409,13 +434,13 @@ prof_free(prof_profile_t *profile)
409
434
 
410
435
  if (profile->exclude_threads_tbl)
411
436
  {
412
- st_free_table(profile->exclude_threads_tbl);
437
+ rb_st_free_table(profile->exclude_threads_tbl);
413
438
  profile->exclude_threads_tbl = NULL;
414
439
  }
415
440
 
416
441
  if (profile->include_threads_tbl)
417
442
  {
418
- st_free_table(profile->include_threads_tbl);
443
+ rb_st_free_table(profile->include_threads_tbl);
419
444
  profile->include_threads_tbl = NULL;
420
445
  }
421
446
 
@@ -429,8 +454,7 @@ prof_free(prof_profile_t *profile)
429
454
  xfree(profile);
430
455
  }
431
456
 
432
- static VALUE
433
- prof_allocate(VALUE klass)
457
+ static VALUE prof_allocate(VALUE klass)
434
458
  {
435
459
  VALUE result;
436
460
  prof_profile_t* profile;
@@ -447,14 +471,12 @@ prof_allocate(VALUE klass)
447
471
  return result;
448
472
  }
449
473
 
450
- static void
451
- prof_exclude_common_methods(VALUE profile)
474
+ static void prof_exclude_common_methods(VALUE profile)
452
475
  {
453
476
  rb_funcall(profile, rb_intern("exclude_common_methods!"), 0);
454
477
  }
455
478
 
456
- static int
457
- pop_frames(VALUE key, st_data_t value, st_data_t data)
479
+ static int pop_frames(VALUE key, st_data_t value, st_data_t data)
458
480
  {
459
481
  thread_data_t* thread_data = (thread_data_t*)value;
460
482
  prof_profile_t* profile = (prof_profile_t*)data;
@@ -463,7 +485,7 @@ pop_frames(VALUE key, st_data_t value, st_data_t data)
463
485
  if (profile->last_thread_data->fiber != thread_data->fiber)
464
486
  switch_thread(profile, thread_data, measurement);
465
487
 
466
- while (prof_stack_pop(thread_data->stack, measurement));
488
+ while (prof_frame_pop(thread_data->stack, measurement));
467
489
 
468
490
  return ST_CONTINUE;
469
491
  }
@@ -471,7 +493,7 @@ pop_frames(VALUE key, st_data_t value, st_data_t data)
471
493
  static void
472
494
  prof_stop_threads(prof_profile_t* profile)
473
495
  {
474
- st_foreach(profile->threads_tbl, pop_frames, (st_data_t)profile);
496
+ rb_st_foreach(profile->threads_tbl, pop_frames, (st_data_t)profile);
475
497
  }
476
498
 
477
499
  /* call-seq:
@@ -480,19 +502,19 @@ prof_stop_threads(prof_profile_t* profile)
480
502
 
481
503
  Returns a new profiler. Possible options for the options hash are:
482
504
 
483
- measure_mode:: Measure mode. Specifies the profile measure mode.
505
+ measure_mode: Measure mode. Specifies the profile measure mode.
484
506
  If not specified, defaults to RubyProf::WALL_TIME.
485
- exclude_threads:: Threads to exclude from the profiling results.
486
- include_threads:: Focus profiling on only the given threads. This will ignore
487
- all other threads.
488
- allow_exceptions:: Whether to raise exceptions encountered during profiling,
507
+ allow_exceptions: Whether to raise exceptions encountered during profiling,
489
508
  or to suppress all exceptions during profiling
490
- merge_fibers:: Whether profiling data for a given thread's fibers should all be
509
+ merge_fibers: Whether profiling data for a given thread's fibers should all be
491
510
  subsumed under a single entry. Basically only useful to produce
492
511
  callgrind profiles.
493
- */
494
- static VALUE
495
- prof_initialize(int argc, VALUE *argv, VALUE self)
512
+ track_allocations: Whether to track object allocations while profiling. True or false.
513
+ exclude_common: Exclude common methods from the profile. True or false.
514
+ exclude_threads: Threads to exclude from the profiling results.
515
+ include_threads: Focus profiling on only the given threads. This will ignore
516
+ all other threads. */
517
+ static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
496
518
  {
497
519
  prof_profile_t* profile = prof_get_profile(self);
498
520
  VALUE mode_or_options;
@@ -552,7 +574,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
552
574
  for (i = 0; i < RARRAY_LEN(exclude_threads); i++)
553
575
  {
554
576
  VALUE thread = rb_ary_entry(exclude_threads, i);
555
- st_insert(profile->exclude_threads_tbl, thread, Qtrue);
577
+ rb_st_insert(profile->exclude_threads_tbl, thread, Qtrue);
556
578
  }
557
579
  }
558
580
 
@@ -564,7 +586,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
564
586
  for (i = 0; i < RARRAY_LEN(include_threads); i++)
565
587
  {
566
588
  VALUE thread = rb_ary_entry(include_threads, i);
567
- st_insert(profile->include_threads_tbl, thread, Qtrue);
589
+ rb_st_insert(profile->include_threads_tbl, thread, Qtrue);
568
590
  }
569
591
  }
570
592
 
@@ -580,8 +602,7 @@ prof_initialize(int argc, VALUE *argv, VALUE self)
580
602
  paused? -> boolean
581
603
 
582
604
  Returns whether a profile is currently paused.*/
583
- static VALUE
584
- prof_paused(VALUE self)
605
+ static VALUE prof_paused(VALUE self)
585
606
  {
586
607
  prof_profile_t* profile = prof_get_profile(self);
587
608
  return profile->paused;
@@ -602,8 +623,7 @@ prof_running(VALUE self)
602
623
  mode -> measure_mode
603
624
 
604
625
  Returns the measure mode used in this profile.*/
605
- static VALUE
606
- prof_profile_measure_mode(VALUE self)
626
+ static VALUE prof_profile_measure_mode(VALUE self)
607
627
  {
608
628
  prof_profile_t* profile = prof_get_profile(self);
609
629
  return INT2NUM(profile->measurer->mode);
@@ -613,8 +633,7 @@ prof_profile_measure_mode(VALUE self)
613
633
  track_allocations -> boolean
614
634
 
615
635
  Returns if object allocations were tracked in this profile.*/
616
- static VALUE
617
- prof_profile_track_allocations(VALUE self)
636
+ static VALUE prof_profile_track_allocations(VALUE self)
618
637
  {
619
638
  prof_profile_t* profile = prof_get_profile(self);
620
639
  return profile->measurer->track_allocations ? Qtrue : Qfalse;
@@ -624,8 +643,7 @@ prof_profile_track_allocations(VALUE self)
624
643
  start -> self
625
644
 
626
645
  Starts recording profile data.*/
627
- static VALUE
628
- prof_start(VALUE self)
646
+ static VALUE prof_start(VALUE self)
629
647
  {
630
648
  char* trace_file_name;
631
649
 
@@ -642,20 +660,21 @@ prof_start(VALUE self)
642
660
 
643
661
  /* open trace file if environment wants it */
644
662
  trace_file_name = getenv("RUBY_PROF_TRACE");
645
- if (trace_file_name != NULL)
646
- {
647
- if (strcmp(trace_file_name, "stdout") == 0)
648
- {
649
- trace_file = stdout;
650
- }
651
- else if (strcmp(trace_file_name, "stderr") == 0)
652
- {
653
- trace_file = stderr;
654
- }
655
- else
656
- {
657
- trace_file = fopen(trace_file_name, "w");
658
- }
663
+
664
+ if (trace_file_name != NULL)
665
+ {
666
+ if (strcmp(trace_file_name, "stdout") == 0)
667
+ {
668
+ trace_file = stdout;
669
+ }
670
+ else if (strcmp(trace_file_name, "stderr") == 0)
671
+ {
672
+ trace_file = stderr;
673
+ }
674
+ else
675
+ {
676
+ trace_file = fopen(trace_file_name, "w");
677
+ }
659
678
  }
660
679
 
661
680
  prof_install_hook(self);
@@ -666,8 +685,7 @@ prof_start(VALUE self)
666
685
  pause -> self
667
686
 
668
687
  Pauses collecting profile data. */
669
- static VALUE
670
- prof_pause(VALUE self)
688
+ static VALUE prof_pause(VALUE self)
671
689
  {
672
690
  prof_profile_t* profile = prof_get_profile(self);
673
691
  if (profile->running == Qfalse)
@@ -679,7 +697,7 @@ prof_pause(VALUE self)
679
697
  {
680
698
  profile->paused = Qtrue;
681
699
  profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
682
- st_foreach(profile->threads_tbl, pause_thread, (st_data_t) profile);
700
+ rb_st_foreach(profile->threads_tbl, pause_thread, (st_data_t)profile);
683
701
  }
684
702
 
685
703
  return self;
@@ -690,8 +708,7 @@ prof_pause(VALUE self)
690
708
  resume(&block) -> self
691
709
 
692
710
  Resumes recording profile data.*/
693
- static VALUE
694
- prof_resume(VALUE self)
711
+ static VALUE prof_resume(VALUE self)
695
712
  {
696
713
  prof_profile_t* profile = prof_get_profile(self);
697
714
  if (profile->running == Qfalse)
@@ -703,7 +720,7 @@ prof_resume(VALUE self)
703
720
  {
704
721
  profile->paused = Qfalse;
705
722
  profile->measurement_at_pause_resume = prof_measure(profile->measurer, NULL);
706
- st_foreach(profile->threads_tbl, unpause_thread, (st_data_t) profile);
723
+ rb_st_foreach(profile->threads_tbl, unpause_thread, (st_data_t)profile);
707
724
  }
708
725
 
709
726
  return rb_block_given_p() ? rb_ensure(rb_yield, self, prof_pause, self) : self;
@@ -713,8 +730,7 @@ prof_resume(VALUE self)
713
730
  stop -> self
714
731
 
715
732
  Stops collecting profile data.*/
716
- static VALUE
717
- prof_stop(VALUE self)
733
+ static VALUE prof_stop(VALUE self)
718
734
  {
719
735
  prof_profile_t* profile = prof_get_profile(self);
720
736
 
@@ -728,15 +744,15 @@ prof_stop(VALUE self)
728
744
  /* close trace file if open */
729
745
  if (trace_file != NULL)
730
746
  {
731
- if (trace_file !=stderr && trace_file != stdout)
732
- {
747
+ if (trace_file != stderr && trace_file != stdout)
748
+ {
733
749
  #ifdef _MSC_VER
734
- _fcloseall();
750
+ _fcloseall();
735
751
  #else
736
- fclose(trace_file);
752
+ fclose(trace_file);
737
753
  #endif
738
- }
739
- trace_file = NULL;
754
+ }
755
+ trace_file = NULL;
740
756
  }
741
757
 
742
758
  prof_stop_threads(profile);
@@ -753,12 +769,11 @@ prof_stop(VALUE self)
753
769
  threads -> Array of RubyProf::Thread
754
770
 
755
771
  Returns an array of RubyProf::Thread instances that were profiled. */
756
- static VALUE
757
- prof_threads(VALUE self)
772
+ static VALUE prof_threads(VALUE self)
758
773
  {
759
774
  VALUE result = rb_ary_new();
760
775
  prof_profile_t* profile = prof_get_profile(self);
761
- st_foreach(profile->threads_tbl, collect_threads, result);
776
+ rb_st_foreach(profile->threads_tbl, collect_threads, result);
762
777
  return result;
763
778
  }
764
779
 
@@ -773,8 +788,7 @@ prof_threads(VALUE self)
773
788
  ..
774
789
  end
775
790
  */
776
- static VALUE
777
- prof_profile_object(VALUE self)
791
+ static VALUE prof_profile_object(VALUE self)
778
792
  {
779
793
  int result;
780
794
  prof_profile_t* profile = prof_get_profile(self);
@@ -794,7 +808,6 @@ prof_profile_object(VALUE self)
794
808
  }
795
809
 
796
810
  return self;
797
-
798
811
  }
799
812
 
800
813
  /* Document-method: RubyProf::Profile::Profile
@@ -809,8 +822,7 @@ prof_profile_object(VALUE self)
809
822
  ..
810
823
  end
811
824
  */
812
- static VALUE
813
- prof_profile_class(int argc, VALUE *argv, VALUE klass)
825
+ static VALUE prof_profile_class(int argc, VALUE* argv, VALUE klass)
814
826
  {
815
827
  return prof_profile_object(rb_class_new_instance(argc, argv, cProfile));
816
828
  }
@@ -820,25 +832,22 @@ prof_profile_class(int argc, VALUE *argv, VALUE klass)
820
832
 
821
833
  Excludes the method from profiling results.
822
834
  */
823
- static VALUE
824
- prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
835
+ static VALUE prof_exclude_method(VALUE self, VALUE klass, VALUE msym)
825
836
  {
826
837
  prof_profile_t* profile = prof_get_profile(self);
827
838
 
828
- st_data_t key = method_key(klass, msym);
829
- prof_method_t *method;
830
-
831
839
  if (profile->running == Qtrue)
832
840
  {
833
841
  rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
834
842
  }
835
843
 
836
- method = method_table_lookup(profile->exclude_methods_tbl, key);
844
+ st_data_t key = method_key(klass, msym);
845
+ prof_method_t* method = method_table_lookup(profile->exclude_methods_tbl, key);
837
846
 
838
847
  if (!method)
839
848
  {
840
- method = prof_method_create_excluded(klass, msym);
841
- method_table_insert(profile->exclude_methods_tbl, method->key, method);
849
+ method = prof_method_create(klass, msym, Qnil, 0);
850
+ method_table_insert(profile->exclude_methods_tbl, method->key, method);
842
851
  }
843
852
 
844
853
  return self;
@@ -862,7 +871,7 @@ VALUE prof_profile_load(VALUE self, VALUE data)
862
871
  {
863
872
  VALUE thread = rb_ary_entry(threads, i);
864
873
  thread_data_t* thread_data = DATA_PTR(thread);
865
- st_insert(profile->threads_tbl, (st_data_t)thread_data->fiber_id, (st_data_t)thread_data);
874
+ rb_st_insert(profile->threads_tbl, (st_data_t)thread_data->fiber_id, (st_data_t)thread_data);
866
875
  }
867
876
 
868
877
  return data;
@@ -870,8 +879,6 @@ VALUE prof_profile_load(VALUE self, VALUE data)
870
879
 
871
880
  void rp_init_profile(void)
872
881
  {
873
- mProf = rb_define_module("RubyProf");
874
-
875
882
  cProfile = rb_define_class_under(mProf, "Profile", rb_cObject);
876
883
  rb_define_alloc_func(cProfile, prof_allocate);
877
884