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
@@ -16,7 +16,6 @@ typedef struct
16
16
  VALUE paused;
17
17
 
18
18
  prof_measurer_t* measurer;
19
- VALUE threads;
20
19
 
21
20
  VALUE tracepoints;
22
21
 
@@ -3,31 +3,12 @@
3
3
 
4
4
  #include "rp_stack.h"
5
5
 
6
- #define INITIAL_STACK_SIZE 32
6
+ #define INITIAL_STACK_SIZE 16
7
7
 
8
- void
9
- prof_frame_pause(prof_frame_t *frame, double current_measurement)
8
+ // Creates a stack of prof_frame_t to keep track of timings for active methods.
9
+ prof_stack_t* prof_stack_create()
10
10
  {
11
- if (frame && prof_frame_is_unpaused(frame))
12
- frame->pause_time = current_measurement;
13
- }
14
-
15
- void
16
- prof_frame_unpause(prof_frame_t *frame, double current_measurement)
17
- {
18
- if (frame && prof_frame_is_paused(frame))
19
- {
20
- frame->dead_time += (current_measurement - frame->pause_time);
21
- frame->pause_time = -1;
22
- }
23
- }
24
-
25
- /* Creates a stack of prof_frame_t to keep track
26
- of timings for active methods. */
27
- prof_stack_t *
28
- prof_stack_create()
29
- {
30
- prof_stack_t *stack = ALLOC(prof_stack_t);
11
+ prof_stack_t* stack = ALLOC(prof_stack_t);
31
12
  stack->start = ZALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
32
13
  stack->ptr = stack->start;
33
14
  stack->end = stack->start + INITIAL_STACK_SIZE;
@@ -35,22 +16,24 @@ prof_stack_create()
35
16
  return stack;
36
17
  }
37
18
 
38
- void
39
- prof_stack_free(prof_stack_t *stack)
19
+ void prof_stack_free(prof_stack_t* stack)
40
20
  {
41
21
  xfree(stack->start);
42
22
  xfree(stack);
43
23
  }
44
24
 
45
- prof_frame_t *
46
- prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measurement, int paused)
25
+ prof_frame_t* prof_stack_last(prof_stack_t* stack)
47
26
  {
48
- prof_frame_t *result;
49
- prof_frame_t* parent_frame;
27
+ if (stack->ptr == stack->start)
28
+ return NULL;
29
+ else
30
+ return stack->ptr - 1;
31
+ }
50
32
 
51
- /* Is there space on the stack? If not, double
52
- its size. */
53
- if (stack->ptr == stack->end - 1)
33
+ void prof_stack_verify_size(prof_stack_t* stack)
34
+ {
35
+ // Is there space on the stack? If not, double its size.
36
+ if (stack->ptr == stack->end)
54
37
  {
55
38
  size_t len = stack->ptr - stack->start;
56
39
  size_t new_capacity = (stack->end - stack->start) * 2;
@@ -60,14 +43,53 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
60
43
  stack->ptr = stack->start + len;
61
44
  stack->end = stack->start + new_capacity;
62
45
  }
46
+ }
47
+
48
+ prof_frame_t* prof_stack_push(prof_stack_t* stack)
49
+ {
50
+ prof_stack_verify_size(stack);
63
51
 
64
- parent_frame = stack->ptr;
52
+ prof_frame_t* result = stack->ptr;
65
53
  stack->ptr++;
54
+ return result;
55
+ }
56
+
57
+ prof_frame_t* prof_stack_pop(prof_stack_t* stack)
58
+ {
59
+ prof_frame_t* result = prof_stack_last(stack);
60
+ if (result)
61
+ stack->ptr--;
62
+
63
+ return result;
64
+ }
65
+
66
+ // ---------------- Frame Methods ----------------------------
67
+ void prof_frame_pause(prof_frame_t* frame, double current_measurement)
68
+ {
69
+ if (frame && prof_frame_is_unpaused(frame))
70
+ frame->pause_time = current_measurement;
71
+ }
72
+
73
+ void prof_frame_unpause(prof_frame_t* frame, double current_measurement)
74
+ {
75
+ if (prof_frame_is_paused(frame))
76
+ {
77
+ frame->dead_time += (current_measurement - frame->pause_time);
78
+ frame->pause_time = -1;
79
+ }
80
+ }
81
+
82
+ prof_frame_t* prof_frame_current(prof_stack_t* stack)
83
+ {
84
+ return prof_stack_last(stack);
85
+ }
66
86
 
67
- result = stack->ptr;
68
- result->call_info = call_info;
69
- result->call_info->depth = (int)(stack->ptr - stack->start); // shortening of 64 bit into 32;
70
- result->passes = 0;
87
+ prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree, double measurement, bool paused)
88
+ {
89
+ prof_frame_t* parent_frame = prof_stack_last(stack);
90
+ prof_frame_t* result = prof_stack_push(stack);
91
+
92
+ result->call_tree = call_tree;
71
93
 
72
94
  result->start_time = measurement;
73
95
  result->pause_time = -1; // init as not paused.
@@ -78,21 +100,22 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
78
100
  result->source_file = Qnil;
79
101
  result->source_line = 0;
80
102
 
81
- call_info->measurement->called++;
82
- call_info->visits++;
103
+ call_tree->measurement->called++;
104
+ call_tree->visits++;
83
105
 
84
- if (call_info->method->visits > 0)
106
+ if (call_tree->method->visits > 0)
85
107
  {
86
- call_info->method->recursive = true;
108
+ call_tree->method->recursive = true;
87
109
  }
88
- call_info->method->measurement->called++;
89
- call_info->method->visits++;
110
+ call_tree->method->measurement->called++;
111
+ call_tree->method->visits++;
90
112
 
91
113
  // Unpause the parent frame, if it exists.
92
114
  // If currently paused then:
93
115
  // 1) The child frame will begin paused.
94
116
  // 2) The parent will inherit the child's dead time.
95
- prof_frame_unpause(parent_frame, measurement);
117
+ if (parent_frame)
118
+ prof_frame_unpause(parent_frame, measurement);
96
119
 
97
120
  if (paused)
98
121
  {
@@ -103,92 +126,77 @@ prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measure
103
126
  return result;
104
127
  }
105
128
 
106
- prof_frame_t *
107
- prof_stack_pop(prof_stack_t *stack, double measurement)
129
+ prof_frame_t* prof_frame_unshift(prof_stack_t* stack, prof_call_tree_t* parent_call_tree, prof_call_tree_t* call_tree, double measurement)
108
130
  {
109
- prof_frame_t *frame;
110
- prof_frame_t *parent_frame;
111
- prof_call_info_t *call_info;
131
+ if (prof_stack_last(stack))
132
+ rb_raise(rb_eRuntimeError, "Stach unshift can only be called with an empty stack");
112
133
 
113
- double total_time;
114
- double self_time;
134
+ parent_call_tree->measurement->total_time = call_tree->measurement->total_time;
135
+ parent_call_tree->measurement->self_time = 0;
136
+ parent_call_tree->measurement->wait_time = call_tree->measurement->wait_time;
115
137
 
116
- if (stack->ptr == stack->start)
117
- return NULL;
138
+ parent_call_tree->method->measurement->total_time += call_tree->measurement->total_time;
139
+ parent_call_tree->method->measurement->wait_time += call_tree->measurement->wait_time;
118
140
 
119
- frame = stack->ptr;
120
-
121
- /* Match passes until we reach the frame itself. */
122
- if (prof_frame_is_pass(frame))
123
- {
124
- frame->passes--;
125
- /* Additional frames can be consumed. See pop_frames(). */
126
- return frame;
127
- }
128
-
129
- /* Consume this frame. */
130
- stack->ptr--;
131
-
132
- parent_frame = stack->ptr;
141
+ return prof_frame_push(stack, parent_call_tree, measurement, false);
142
+ }
133
143
 
134
- /* Calculate the total time this method took */
135
- prof_frame_unpause(frame, measurement);
136
-
137
- total_time = measurement - frame->start_time - frame->dead_time;
138
- self_time = total_time - frame->child_time - frame->wait_time;
144
+ prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement)
145
+ {
146
+ prof_frame_t* frame = prof_stack_pop(stack);
139
147
 
140
- /* Update information about the current method */
141
- call_info = frame->call_info;
148
+ if (!frame)
149
+ return NULL;
142
150
 
143
- // Update method measurement
144
- call_info->method->measurement->self_time += self_time;
145
- call_info->method->measurement->wait_time += frame->wait_time;
146
- if (call_info->method->visits == 1)
147
- call_info->method->measurement->total_time += total_time;
151
+ /* Calculate the total time this method took */
152
+ prof_frame_unpause(frame, measurement);
148
153
 
149
- call_info->method->visits--;
154
+ double total_time = measurement - frame->start_time - frame->dead_time;
155
+ double self_time = total_time - frame->child_time - frame->wait_time;
150
156
 
151
- // Update method measurement
152
- call_info->measurement->self_time += self_time;
153
- call_info->measurement->wait_time += frame->wait_time;
154
- if (call_info->visits == 1)
155
- call_info->measurement->total_time += total_time;
157
+ /* Update information about the current method */
158
+ prof_call_tree_t* call_tree = frame->call_tree;
156
159
 
157
- call_info->visits--;
160
+ // Update method measurement
161
+ call_tree->method->measurement->self_time += self_time;
162
+ call_tree->method->measurement->wait_time += frame->wait_time;
163
+ if (call_tree->method->visits == 1)
164
+ call_tree->method->measurement->total_time += total_time;
158
165
 
159
- if (parent_frame)
160
- {
161
- parent_frame->child_time += total_time;
162
- parent_frame->dead_time += frame->dead_time;
163
- }
166
+ call_tree->method->visits--;
164
167
 
165
- return frame;
166
- }
168
+ // Update method measurement
169
+ call_tree->measurement->self_time += self_time;
170
+ call_tree->measurement->wait_time += frame->wait_time;
171
+ if (call_tree->visits == 1)
172
+ call_tree->measurement->total_time += total_time;
167
173
 
168
- prof_frame_t *
169
- prof_stack_pass(prof_stack_t *stack)
170
- {
171
- prof_frame_t* frame = stack->ptr;
172
- if (frame)
174
+ call_tree->visits--;
175
+
176
+ prof_frame_t* parent_frame = prof_stack_last(stack);
177
+ if (parent_frame)
173
178
  {
174
- frame->passes++;
179
+ parent_frame->child_time += total_time;
180
+ parent_frame->dead_time += frame->dead_time;
175
181
  }
182
+
183
+ frame->source_file = Qnil;
184
+
176
185
  return frame;
177
186
  }
178
187
 
179
- prof_method_t*
180
- prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
188
+ prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
181
189
  {
182
190
  prof_frame_t* frame = stack->ptr;
183
191
  while (frame >= stack->start)
184
192
  {
185
- if (!frame->call_info)
193
+ if (!frame->call_tree)
186
194
  return NULL;
187
195
 
188
- if (rb_str_equal(source_file, frame->call_info->method->source_file) &&
189
- source_line >= frame->call_info->method->source_line)
196
+ if (rb_str_equal(source_file, frame->call_tree->method->source_file) &&
197
+ source_line >= frame->call_tree->method->source_line)
190
198
  {
191
- return frame->call_info->method;
199
+ return frame->call_tree->method;
192
200
  }
193
201
  frame--;
194
202
  }
@@ -5,20 +5,19 @@
5
5
  #define __RP_STACK__
6
6
 
7
7
  #include "ruby_prof.h"
8
- #include "rp_call_info.h"
8
+ #include "rp_call_tree.h"
9
9
 
10
- /* Temporary object that maintains profiling information
11
- for active methods. They are created and destroyed
12
- as the program moves up and down its stack. */
10
+ /* Temporary object that maintains profiling information
11
+ for active methods. They are created and destroyed
12
+ as the program moves up and down its stack. */
13
13
  typedef struct
14
14
  {
15
15
  /* Caching prof_method_t values significantly
16
16
  increases performance. */
17
- prof_call_info_t *call_info;
17
+ prof_call_tree_t* call_tree;
18
18
 
19
19
  VALUE source_file;
20
20
  unsigned int source_line;
21
- unsigned int passes; /* Count of "pass" frames, _after_ this one. */
22
21
 
23
22
  double start_time;
24
23
  double switch_time; /* Time at switch to different thread */
@@ -28,9 +27,6 @@ typedef struct
28
27
  double dead_time; // Time to ignore (i.e. total amount of time between pause/resume blocks)
29
28
  } prof_frame_t;
30
29
 
31
- #define prof_frame_is_real(f) ((f)->passes == 0)
32
- #define prof_frame_is_pass(f) ((f)->passes > 0)
33
-
34
30
  #define prof_frame_is_paused(f) (f->pause_time >= 0)
35
31
  #define prof_frame_is_unpaused(f) (f->pause_time < 0)
36
32
 
@@ -40,17 +36,18 @@ void prof_frame_unpause(prof_frame_t*, double current_measurement);
40
36
  /* Current stack of active methods.*/
41
37
  typedef struct
42
38
  {
43
- prof_frame_t *start;
44
- prof_frame_t *end;
45
- prof_frame_t *ptr;
39
+ prof_frame_t* start;
40
+ prof_frame_t* end;
41
+ prof_frame_t* ptr;
46
42
  } prof_stack_t;
47
43
 
48
- prof_stack_t *prof_stack_create(void);
49
- void prof_stack_free(prof_stack_t *stack);
44
+ prof_stack_t* prof_stack_create(void);
45
+ void prof_stack_free(prof_stack_t* stack);
50
46
 
51
- prof_frame_t *prof_stack_push(prof_stack_t *stack, prof_call_info_t *call_info, double measurement, int paused);
52
- prof_frame_t *prof_stack_pop(prof_stack_t *stack, double measurement);
53
- prof_frame_t *prof_stack_pass(prof_stack_t *stack);
54
- prof_method_t *prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line);
47
+ prof_frame_t* prof_frame_current(prof_stack_t* stack);
48
+ prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree, double measurement, bool paused);
49
+ prof_frame_t* prof_frame_unshift(prof_stack_t* stack, prof_call_tree_t* parent_call_tree, prof_call_tree_t* call_tree, double measurement);
50
+ prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement);
51
+ prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line);
55
52
 
56
53
  #endif //__RP_STACK__
@@ -14,21 +14,20 @@ You cannot create an instance of RubyProf::Thread, instead you access it from a
14
14
  thread.root_methods.sort.each do |method|
15
15
  puts method.total_time
16
16
  end
17
- end
18
- */
17
+ end */
19
18
 
20
19
  #include "rp_thread.h"
21
20
  #include "rp_profile.h"
22
21
 
23
22
  VALUE cRpThread;
24
23
 
25
- /* ====== thread_data_t ====== */
26
- thread_data_t*
27
- thread_data_create(void)
24
+ // ====== thread_data_t ======
25
+ thread_data_t* thread_data_create(void)
28
26
  {
29
27
  thread_data_t* result = ALLOC(thread_data_t);
30
28
  result->stack = prof_stack_create();
31
29
  result->method_table = method_table_create();
30
+ result->call_tree = NULL;
32
31
  result->object = Qnil;
33
32
  result->methods = Qnil;
34
33
  result->fiber_id = Qnil;
@@ -38,81 +37,87 @@ thread_data_create(void)
38
37
  return result;
39
38
  }
40
39
 
41
- static void
42
- prof_thread_free(thread_data_t* thread_data)
40
+ static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
43
41
  {
44
- /* Has this method object been accessed by Ruby? If
45
- yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
46
- if (thread_data->object != Qnil)
47
- {
48
- RDATA(thread_data->object)->dmark = NULL;
49
- RDATA(thread_data->object)->dfree = NULL;
50
- RDATA(thread_data->object)->data = NULL;
51
- thread_data->object = Qnil;
52
- }
53
-
54
- method_table_free(thread_data->method_table);
55
- prof_stack_free(thread_data->stack);
56
-
57
-
58
- xfree(thread_data);
59
- }
60
-
61
- static int
62
- mark_methods(st_data_t key, st_data_t value, st_data_t result)
63
- {
64
- prof_method_t *method = (prof_method_t *) value;
42
+ prof_method_t* method = (prof_method_t*)value;
65
43
  prof_method_mark(method);
66
44
  return ST_CONTINUE;
67
45
  }
68
46
 
69
- size_t
70
- prof_thread_size(const void *data)
47
+ size_t prof_thread_size(const void* data)
71
48
  {
72
- return sizeof(prof_call_info_t);
49
+ return sizeof(prof_call_tree_t);
73
50
  }
74
51
 
75
- void
76
- prof_thread_mark(void *data)
52
+ void prof_thread_mark(void* data)
77
53
  {
78
- thread_data_t *thread = (thread_data_t*)data;
79
-
54
+ thread_data_t* thread = (thread_data_t*)data;
55
+
80
56
  if (thread->object != Qnil)
81
57
  rb_gc_mark(thread->object);
82
-
58
+
59
+ rb_gc_mark(thread->fiber);
60
+
83
61
  if (thread->methods != Qnil)
84
62
  rb_gc_mark(thread->methods);
85
-
63
+
86
64
  if (thread->fiber_id != Qnil)
87
65
  rb_gc_mark(thread->fiber_id);
88
-
66
+
89
67
  if (thread->thread_id != Qnil)
90
68
  rb_gc_mark(thread->thread_id);
91
-
92
- st_foreach(thread->method_table, mark_methods, 0);
69
+
70
+ if (thread->call_tree)
71
+ prof_call_tree_mark(thread->call_tree);
72
+
73
+ rb_st_foreach(thread->method_table, mark_methods, 0);
93
74
  }
94
75
 
76
+ void prof_thread_ruby_gc_free(void* data)
77
+ {
78
+ thread_data_t* thread_data = (thread_data_t*)data;
79
+ thread_data->object = Qnil;
80
+ }
81
+
82
+ static void prof_thread_free(thread_data_t* thread_data)
83
+ {
84
+ /* Has this method object been accessed by Ruby? If
85
+ yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
86
+ if (thread_data->object != Qnil)
87
+ {
88
+ RDATA(thread_data->object)->dmark = NULL;
89
+ RDATA(thread_data->object)->dfree = NULL;
90
+ RDATA(thread_data->object)->data = NULL;
91
+ thread_data->object = Qnil;
92
+ }
93
+
94
+ method_table_free(thread_data->method_table);
95
+
96
+ if (thread_data->call_tree)
97
+ prof_call_tree_free(thread_data->call_tree);
98
+
99
+ prof_stack_free(thread_data->stack);
100
+
101
+ xfree(thread_data);
102
+ }
95
103
 
96
- VALUE
97
- prof_thread_wrap(thread_data_t *thread)
104
+ VALUE prof_thread_wrap(thread_data_t* thread)
98
105
  {
99
106
  if (thread->object == Qnil)
100
107
  {
101
- thread->object = Data_Wrap_Struct(cRpThread, prof_thread_mark, NULL, thread);
108
+ thread->object = Data_Wrap_Struct(cRpThread, prof_thread_mark, prof_thread_ruby_gc_free, thread);
102
109
  }
103
110
  return thread->object;
104
111
  }
105
112
 
106
- static VALUE
107
- prof_thread_allocate(VALUE klass)
113
+ static VALUE prof_thread_allocate(VALUE klass)
108
114
  {
109
115
  thread_data_t* thread_data = thread_data_create();
110
116
  thread_data->object = prof_thread_wrap(thread_data);
111
117
  return thread_data->object;
112
118
  }
113
119
 
114
- static thread_data_t*
115
- prof_get_thread(VALUE self)
120
+ static thread_data_t* prof_get_thread(VALUE self)
116
121
  {
117
122
  /* Can't use Data_Get_Struct because that triggers the event hook
118
123
  ending up in endless recursion. */
@@ -123,38 +128,33 @@ prof_get_thread(VALUE self)
123
128
  return result;
124
129
  }
125
130
 
126
- /* ====== Thread Table ====== */
127
- /* The thread table is hash keyed on ruby thread_id that stores instances
128
- of thread_data_t. */
131
+ // ====== Thread Table ======
132
+ // The thread table is hash keyed on ruby fiber_id that stores instances of thread_data_t.
129
133
 
130
- st_table *
131
- threads_table_create()
134
+ st_table* threads_table_create()
132
135
  {
133
- return st_init_numtable();
136
+ return rb_st_init_numtable();
134
137
  }
135
138
 
136
- static int
137
- thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
139
+ static int thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
138
140
  {
139
141
  prof_thread_free((thread_data_t*)value);
140
142
  return ST_CONTINUE;
141
143
  }
142
144
 
143
- void
144
- threads_table_free(st_table *table)
145
+ void threads_table_free(st_table* table)
145
146
  {
146
- st_foreach(table, thread_table_free_iterator, 0);
147
- st_free_table(table);
147
+ rb_st_foreach(table, thread_table_free_iterator, 0);
148
+ rb_st_free_table(table);
148
149
  }
149
150
 
150
- thread_data_t *
151
- threads_table_lookup(void *prof, VALUE fiber)
151
+ thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
152
152
  {
153
- prof_profile_t *profile = prof;
153
+ prof_profile_t* profile = prof;
154
154
  thread_data_t* result = NULL;
155
155
  st_data_t val;
156
156
 
157
- if (st_lookup(profile->threads_tbl, fiber, &val))
157
+ if (rb_st_lookup(profile->threads_tbl, fiber, &val))
158
158
  {
159
159
  result = (thread_data_t*)val;
160
160
  }
@@ -162,24 +162,23 @@ threads_table_lookup(void *prof, VALUE fiber)
162
162
  return result;
163
163
  }
164
164
 
165
- thread_data_t*
166
- threads_table_insert(void *prof, VALUE fiber)
165
+ thread_data_t* threads_table_insert(void* prof, VALUE fiber)
167
166
  {
168
- prof_profile_t *profile = prof;
169
- thread_data_t *result = thread_data_create();
167
+ prof_profile_t* profile = prof;
168
+ thread_data_t* result = thread_data_create();
170
169
  VALUE thread = rb_thread_current();
171
170
 
172
171
  result->fiber = fiber;
173
172
  result->fiber_id = rb_obj_id(fiber);
174
173
  result->thread_id = rb_obj_id(thread);
175
- st_insert(profile->threads_tbl, (st_data_t)fiber, (st_data_t)result);
174
+ rb_st_insert(profile->threads_tbl, (st_data_t)fiber, (st_data_t)result);
176
175
 
177
176
  // Are we tracing this thread?
178
- if (profile->include_threads_tbl && !st_lookup(profile->include_threads_tbl, thread, 0))
177
+ if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
179
178
  {
180
- result->trace= false;
179
+ result->trace = false;
181
180
  }
182
- else if (profile->exclude_threads_tbl && st_lookup(profile->exclude_threads_tbl, thread, 0))
181
+ else if (profile->exclude_threads_tbl && rb_st_lookup(profile->exclude_threads_tbl, thread, 0))
183
182
  {
184
183
  result->trace = false;
185
184
  }
@@ -187,25 +186,28 @@ threads_table_insert(void *prof, VALUE fiber)
187
186
  {
188
187
  result->trace = true;
189
188
  }
190
-
189
+
191
190
  return result;
192
191
  }
193
192
 
194
- void
195
- switch_thread(void *prof, thread_data_t *thread_data, double measurement)
193
+ // ====== Profiling Methods ======
194
+ void switch_thread(void* prof, thread_data_t* thread_data, double measurement)
196
195
  {
197
- prof_profile_t *profile = prof;
196
+ prof_profile_t* profile = prof;
198
197
 
199
198
  /* Get current frame for this thread */
200
- prof_frame_t* frame = thread_data->stack->ptr;
201
- frame->wait_time += measurement - frame->switch_time;
202
- frame->switch_time = measurement;
203
-
199
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
200
+ if (frame)
201
+ {
202
+ frame->wait_time += measurement - frame->switch_time;
203
+ frame->switch_time = 0;
204
+ }
205
+
204
206
  /* Save on the last thread the time of the context switch
205
207
  and reset this thread's last context switch to 0.*/
206
208
  if (profile->last_thread_data)
207
209
  {
208
- prof_frame_t* last_frame = profile->last_thread_data->stack->ptr;
210
+ prof_frame_t* last_frame = prof_frame_current(profile->last_thread_data->stack);
209
211
  if (last_frame)
210
212
  last_frame->switch_time = measurement;
211
213
  }
@@ -215,10 +217,10 @@ switch_thread(void *prof, thread_data_t *thread_data, double measurement)
215
217
 
216
218
  int pause_thread(st_data_t key, st_data_t value, st_data_t data)
217
219
  {
218
- thread_data_t* thread_data = (thread_data_t *) value;
220
+ thread_data_t* thread_data = (thread_data_t*)value;
219
221
  prof_profile_t* profile = (prof_profile_t*)data;
220
222
 
221
- prof_frame_t* frame = thread_data->stack->ptr;
223
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
222
224
  prof_frame_pause(frame, profile->measurement_at_pause_resume);
223
225
 
224
226
  return ST_CONTINUE;
@@ -226,37 +228,33 @@ int pause_thread(st_data_t key, st_data_t value, st_data_t data)
226
228
 
227
229
  int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
228
230
  {
229
- thread_data_t* thread_data = (thread_data_t *) value;
231
+ thread_data_t* thread_data = (thread_data_t*)value;
230
232
  prof_profile_t* profile = (prof_profile_t*)data;
231
233
 
232
- prof_frame_t* frame = thread_data->stack->ptr;
234
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
233
235
  prof_frame_unpause(frame, profile->measurement_at_pause_resume);
234
236
 
235
237
  return ST_CONTINUE;
236
238
  }
237
239
 
238
- static int
239
- collect_methods(st_data_t key, st_data_t value, st_data_t result)
240
+ // ====== Helper Methods ======
241
+ static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
240
242
  {
241
243
  /* Called for each method stored in a thread's method table.
242
244
  We want to store the method info information into an array.*/
243
- VALUE methods = (VALUE) result;
244
- prof_method_t *method = (prof_method_t *) value;
245
-
246
- if (!method->excluded)
247
- {
248
- rb_ary_push(methods, prof_method_wrap(method));
249
- }
245
+ VALUE methods = (VALUE)result;
246
+ prof_method_t* method = (prof_method_t*)value;
247
+ rb_ary_push(methods, prof_method_wrap(method));
250
248
 
251
249
  return ST_CONTINUE;
252
250
  }
253
251
 
252
+ // ====== RubyProf::Thread ======
254
253
  /* call-seq:
255
254
  id -> number
256
255
 
257
256
  Returns the thread id of this thread. */
258
- static VALUE
259
- prof_thread_id(VALUE self)
257
+ static VALUE prof_thread_id(VALUE self)
260
258
  {
261
259
  thread_data_t* thread = prof_get_thread(self);
262
260
  return thread->thread_id;
@@ -266,57 +264,66 @@ prof_thread_id(VALUE self)
266
264
  fiber_id -> number
267
265
 
268
266
  Returns the fiber id of this thread. */
269
- static VALUE
270
- prof_fiber_id(VALUE self)
267
+ static VALUE prof_fiber_id(VALUE self)
271
268
  {
272
269
  thread_data_t* thread = prof_get_thread(self);
273
270
  return thread->fiber_id;
274
271
  }
275
272
 
273
+ /* call-seq:
274
+ call_tree -> CallTree
275
+
276
+ Returns the root of the call tree. */
277
+ static VALUE prof_call_tree(VALUE self)
278
+ {
279
+ thread_data_t* thread = prof_get_thread(self);
280
+ return prof_call_tree_wrap(thread->call_tree);
281
+ }
282
+
276
283
  /* call-seq:
277
284
  methods -> [RubyProf::MethodInfo]
278
285
 
279
286
  Returns an array of methods that were called from this
280
287
  thread during program execution. */
281
- static VALUE
282
- prof_thread_methods(VALUE self)
288
+ static VALUE prof_thread_methods(VALUE self)
283
289
  {
284
290
  thread_data_t* thread = prof_get_thread(self);
285
291
  if (thread->methods == Qnil)
286
292
  {
287
293
  thread->methods = rb_ary_new();
288
- st_foreach(thread->method_table, collect_methods, thread->methods);
294
+ rb_st_foreach(thread->method_table, collect_methods, thread->methods);
289
295
  }
290
296
  return thread->methods;
291
297
  }
292
298
 
293
299
  /* :nodoc: */
294
- static VALUE
295
- prof_thread_dump(VALUE self)
300
+ static VALUE prof_thread_dump(VALUE self)
296
301
  {
297
302
  thread_data_t* thread_data = DATA_PTR(self);
298
303
 
299
304
  VALUE result = rb_hash_new();
300
305
  rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
301
306
  rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
307
+ rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
302
308
 
303
309
  return result;
304
310
  }
305
311
 
306
312
  /* :nodoc: */
307
- static VALUE
308
- prof_thread_load(VALUE self, VALUE data)
313
+ static VALUE prof_thread_load(VALUE self, VALUE data)
309
314
  {
310
315
  thread_data_t* thread_data = DATA_PTR(self);
311
- thread_data->object = self;
316
+
317
+ VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
318
+ thread_data->call_tree = prof_get_call_tree(call_tree);
312
319
 
313
320
  thread_data->fiber_id = rb_hash_aref(data, ID2SYM(rb_intern("fiber_id")));
314
- VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
315
321
 
322
+ VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
316
323
  for (int i = 0; i < rb_array_len(methods); i++)
317
324
  {
318
325
  VALUE method = rb_ary_entry(methods, i);
319
- prof_method_t *method_data = DATA_PTR(method);
326
+ prof_method_t* method_data = DATA_PTR(method);
320
327
  method_table_insert(thread_data->method_table, method_data->key, method_data);
321
328
  }
322
329
 
@@ -330,6 +337,7 @@ void rp_init_thread(void)
330
337
  rb_define_alloc_func(cRpThread, prof_thread_allocate);
331
338
 
332
339
  rb_define_method(cRpThread, "id", prof_thread_id, 0);
340
+ rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
333
341
  rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
334
342
  rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
335
343
  rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);