ruby-prof 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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