ruby-prof 1.4.4-x64-mingw-ucrt

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +608 -0
  3. data/LICENSE +25 -0
  4. data/README.md +5 -0
  5. data/Rakefile +98 -0
  6. data/bin/ruby-prof +328 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +22 -0
  9. data/ext/ruby_prof/rp_aggregate_call_tree.c +59 -0
  10. data/ext/ruby_prof/rp_aggregate_call_tree.h +13 -0
  11. data/ext/ruby_prof/rp_allocation.c +287 -0
  12. data/ext/ruby_prof/rp_allocation.h +31 -0
  13. data/ext/ruby_prof/rp_call_tree.c +367 -0
  14. data/ext/ruby_prof/rp_call_tree.h +43 -0
  15. data/ext/ruby_prof/rp_call_trees.c +288 -0
  16. data/ext/ruby_prof/rp_call_trees.h +28 -0
  17. data/ext/ruby_prof/rp_measure_allocations.c +47 -0
  18. data/ext/ruby_prof/rp_measure_memory.c +46 -0
  19. data/ext/ruby_prof/rp_measure_process_time.c +66 -0
  20. data/ext/ruby_prof/rp_measure_wall_time.c +64 -0
  21. data/ext/ruby_prof/rp_measurement.c +237 -0
  22. data/ext/ruby_prof/rp_measurement.h +50 -0
  23. data/ext/ruby_prof/rp_method.c +491 -0
  24. data/ext/ruby_prof/rp_method.h +62 -0
  25. data/ext/ruby_prof/rp_profile.c +915 -0
  26. data/ext/ruby_prof/rp_profile.h +35 -0
  27. data/ext/ruby_prof/rp_stack.c +212 -0
  28. data/ext/ruby_prof/rp_stack.h +53 -0
  29. data/ext/ruby_prof/rp_thread.c +362 -0
  30. data/ext/ruby_prof/rp_thread.h +39 -0
  31. data/ext/ruby_prof/ruby_prof.c +52 -0
  32. data/ext/ruby_prof/ruby_prof.h +26 -0
  33. data/ext/ruby_prof/vc/ruby_prof.sln +39 -0
  34. data/ext/ruby_prof/vc/ruby_prof.vcxproj +160 -0
  35. data/lib/3.1/ruby_prof.so +0 -0
  36. data/lib/ruby-prof/assets/call_stack_printer.html.erb +711 -0
  37. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  38. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -0
  39. data/lib/ruby-prof/call_tree.rb +57 -0
  40. data/lib/ruby-prof/call_tree_visitor.rb +36 -0
  41. data/lib/ruby-prof/compatibility.rb +99 -0
  42. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  43. data/lib/ruby-prof/measurement.rb +17 -0
  44. data/lib/ruby-prof/method_info.rb +78 -0
  45. data/lib/ruby-prof/printers/abstract_printer.rb +137 -0
  46. data/lib/ruby-prof/printers/call_info_printer.rb +53 -0
  47. data/lib/ruby-prof/printers/call_stack_printer.rb +180 -0
  48. data/lib/ruby-prof/printers/call_tree_printer.rb +147 -0
  49. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  50. data/lib/ruby-prof/printers/flat_printer.rb +53 -0
  51. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -0
  52. data/lib/ruby-prof/printers/graph_printer.rb +113 -0
  53. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  54. data/lib/ruby-prof/profile.rb +37 -0
  55. data/lib/ruby-prof/rack.rb +95 -0
  56. data/lib/ruby-prof/task.rb +147 -0
  57. data/lib/ruby-prof/thread.rb +20 -0
  58. data/lib/ruby-prof/version.rb +3 -0
  59. data/lib/ruby-prof.rb +52 -0
  60. data/lib/unprof.rb +10 -0
  61. data/ruby-prof.gemspec +64 -0
  62. data/test/abstract_printer_test.rb +26 -0
  63. data/test/alias_test.rb +122 -0
  64. data/test/basic_test.rb +43 -0
  65. data/test/call_tree_visitor_test.rb +32 -0
  66. data/test/call_trees_test.rb +66 -0
  67. data/test/duplicate_names_test.rb +32 -0
  68. data/test/dynamic_method_test.rb +67 -0
  69. data/test/enumerable_test.rb +21 -0
  70. data/test/exceptions_test.rb +24 -0
  71. data/test/exclude_methods_test.rb +151 -0
  72. data/test/exclude_threads_test.rb +53 -0
  73. data/test/fiber_test.rb +129 -0
  74. data/test/gc_test.rb +100 -0
  75. data/test/inverse_call_tree_test.rb +175 -0
  76. data/test/line_number_test.rb +158 -0
  77. data/test/marshal_test.rb +145 -0
  78. data/test/measure_allocations.rb +26 -0
  79. data/test/measure_allocations_test.rb +333 -0
  80. data/test/measure_memory_test.rb +688 -0
  81. data/test/measure_process_time_test.rb +1614 -0
  82. data/test/measure_times.rb +56 -0
  83. data/test/measure_wall_time_test.rb +426 -0
  84. data/test/multi_printer_test.rb +71 -0
  85. data/test/no_method_class_test.rb +15 -0
  86. data/test/pause_resume_test.rb +175 -0
  87. data/test/prime.rb +54 -0
  88. data/test/prime_script.rb +6 -0
  89. data/test/printer_call_stack_test.rb +27 -0
  90. data/test/printer_call_tree_test.rb +30 -0
  91. data/test/printer_flat_test.rb +99 -0
  92. data/test/printer_graph_html_test.rb +59 -0
  93. data/test/printer_graph_test.rb +40 -0
  94. data/test/printers_test.rb +141 -0
  95. data/test/printing_recursive_graph_test.rb +81 -0
  96. data/test/profile_test.rb +16 -0
  97. data/test/rack_test.rb +93 -0
  98. data/test/recursive_test.rb +430 -0
  99. data/test/singleton_test.rb +38 -0
  100. data/test/stack_printer_test.rb +64 -0
  101. data/test/start_stop_test.rb +109 -0
  102. data/test/test_helper.rb +13 -0
  103. data/test/thread_test.rb +144 -0
  104. data/test/unique_call_path_test.rb +136 -0
  105. data/test/yarv_test.rb +60 -0
  106. metadata +187 -0
@@ -0,0 +1,35 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_PROFILE_H__
5
+ #define __RP_PROFILE_H__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_measurement.h"
9
+ #include "rp_thread.h"
10
+
11
+ extern VALUE cProfile;
12
+
13
+ typedef struct prof_profile_t
14
+ {
15
+ VALUE running;
16
+ VALUE paused;
17
+
18
+ prof_measurer_t* measurer;
19
+
20
+ VALUE tracepoints;
21
+
22
+ st_table* threads_tbl;
23
+ st_table* exclude_threads_tbl;
24
+ st_table* include_threads_tbl;
25
+ st_table* exclude_methods_tbl;
26
+ thread_data_t* last_thread_data;
27
+ double measurement_at_pause_resume;
28
+ bool allow_exceptions;
29
+ } prof_profile_t;
30
+
31
+ void rp_init_profile(void);
32
+ prof_profile_t* prof_get_profile(VALUE self);
33
+
34
+
35
+ #endif //__RP_PROFILE_H__
@@ -0,0 +1,212 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "rp_stack.h"
5
+
6
+ #define INITIAL_STACK_SIZE 16
7
+
8
+ // Creates a stack of prof_frame_t to keep track of timings for active methods.
9
+ prof_stack_t* prof_stack_create()
10
+ {
11
+ prof_stack_t* stack = ALLOC(prof_stack_t);
12
+ stack->start = ZALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
13
+ stack->ptr = stack->start;
14
+ stack->end = stack->start + INITIAL_STACK_SIZE;
15
+
16
+ return stack;
17
+ }
18
+
19
+ void prof_stack_free(prof_stack_t* stack)
20
+ {
21
+ xfree(stack->start);
22
+ xfree(stack);
23
+ }
24
+
25
+ prof_frame_t* prof_stack_parent(prof_stack_t* stack)
26
+ {
27
+ if (stack->ptr == stack->start || stack->ptr - 1 == stack->start)
28
+ return NULL;
29
+ else
30
+ return stack->ptr - 2;
31
+ }
32
+
33
+ prof_frame_t* prof_stack_last(prof_stack_t* stack)
34
+ {
35
+ if (stack->ptr == stack->start)
36
+ return NULL;
37
+ else
38
+ return stack->ptr - 1;
39
+ }
40
+
41
+ void prof_stack_verify_size(prof_stack_t* stack)
42
+ {
43
+ // Is there space on the stack? If not, double its size.
44
+ if (stack->ptr == stack->end)
45
+ {
46
+ size_t len = stack->ptr - stack->start;
47
+ size_t new_capacity = (stack->end - stack->start) * 2;
48
+ REALLOC_N(stack->start, prof_frame_t, new_capacity);
49
+
50
+ /* Memory just got moved, reset pointers */
51
+ stack->ptr = stack->start + len;
52
+ stack->end = stack->start + new_capacity;
53
+ }
54
+ }
55
+
56
+ prof_frame_t* prof_stack_push(prof_stack_t* stack)
57
+ {
58
+ prof_stack_verify_size(stack);
59
+
60
+ prof_frame_t* result = stack->ptr;
61
+ stack->ptr++;
62
+ return result;
63
+ }
64
+
65
+ prof_frame_t* prof_stack_pop(prof_stack_t* stack)
66
+ {
67
+ prof_frame_t* result = prof_stack_last(stack);
68
+ if (result)
69
+ stack->ptr--;
70
+
71
+ return result;
72
+ }
73
+
74
+ // ---------------- Frame Methods ----------------------------
75
+ void prof_frame_pause(prof_frame_t* frame, double current_measurement)
76
+ {
77
+ if (frame && prof_frame_is_unpaused(frame))
78
+ frame->pause_time = current_measurement;
79
+ }
80
+
81
+ void prof_frame_unpause(prof_frame_t* frame, double current_measurement)
82
+ {
83
+ if (prof_frame_is_paused(frame))
84
+ {
85
+ frame->dead_time += (current_measurement - frame->pause_time);
86
+ frame->pause_time = -1;
87
+ }
88
+ }
89
+
90
+ prof_frame_t* prof_frame_current(prof_stack_t* stack)
91
+ {
92
+ return prof_stack_last(stack);
93
+ }
94
+
95
+ prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree, double measurement, bool paused)
96
+ {
97
+ prof_frame_t* result = prof_stack_push(stack);
98
+ prof_frame_t* parent_frame = prof_stack_parent(stack);
99
+
100
+ result->call_tree = call_tree;
101
+
102
+ result->start_time = measurement;
103
+ result->pause_time = -1; // init as not paused.
104
+ result->switch_time = 0;
105
+ result->wait_time = 0;
106
+ result->child_time = 0;
107
+ result->dead_time = 0;
108
+ result->source_file = Qnil;
109
+ result->source_line = 0;
110
+
111
+ call_tree->measurement->called++;
112
+ call_tree->visits++;
113
+
114
+ if (call_tree->method->visits > 0)
115
+ {
116
+ call_tree->method->recursive = true;
117
+ }
118
+ call_tree->method->measurement->called++;
119
+ call_tree->method->visits++;
120
+
121
+ // Unpause the parent frame, if it exists.
122
+ // If currently paused then:
123
+ // 1) The child frame will begin paused.
124
+ // 2) The parent will inherit the child's dead time.
125
+ if (parent_frame)
126
+ prof_frame_unpause(parent_frame, measurement);
127
+
128
+ if (paused)
129
+ {
130
+ prof_frame_pause(result, measurement);
131
+ }
132
+
133
+ // Return the result
134
+ return result;
135
+ }
136
+
137
+ 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)
138
+ {
139
+ if (prof_stack_last(stack))
140
+ rb_raise(rb_eRuntimeError, "Stack unshift can only be called with an empty stack");
141
+
142
+ parent_call_tree->measurement->total_time = call_tree->measurement->total_time;
143
+ parent_call_tree->measurement->self_time = 0;
144
+ parent_call_tree->measurement->wait_time = call_tree->measurement->wait_time;
145
+
146
+ parent_call_tree->method->measurement->total_time += call_tree->measurement->total_time;
147
+ parent_call_tree->method->measurement->wait_time += call_tree->measurement->wait_time;
148
+
149
+ return prof_frame_push(stack, parent_call_tree, measurement, false);
150
+ }
151
+
152
+ prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement)
153
+ {
154
+ prof_frame_t* frame = prof_stack_pop(stack);
155
+
156
+ if (!frame)
157
+ return NULL;
158
+
159
+ /* Calculate the total time this method took */
160
+ prof_frame_unpause(frame, measurement);
161
+
162
+ double total_time = measurement - frame->start_time - frame->dead_time;
163
+ double self_time = total_time - frame->child_time - frame->wait_time;
164
+
165
+ /* Update information about the current method */
166
+ prof_call_tree_t* call_tree = frame->call_tree;
167
+
168
+ // Update method measurement
169
+ call_tree->method->measurement->self_time += self_time;
170
+ call_tree->method->measurement->wait_time += frame->wait_time;
171
+ if (call_tree->method->visits == 1)
172
+ call_tree->method->measurement->total_time += total_time;
173
+
174
+ call_tree->method->visits--;
175
+
176
+ // Update method measurement
177
+ call_tree->measurement->self_time += self_time;
178
+ call_tree->measurement->wait_time += frame->wait_time;
179
+ if (call_tree->visits == 1)
180
+ call_tree->measurement->total_time += total_time;
181
+
182
+ call_tree->visits--;
183
+
184
+ prof_frame_t* parent_frame = prof_stack_last(stack);
185
+ if (parent_frame)
186
+ {
187
+ parent_frame->child_time += total_time;
188
+ parent_frame->dead_time += frame->dead_time;
189
+ }
190
+
191
+ frame->source_file = Qnil;
192
+
193
+ return frame;
194
+ }
195
+
196
+ prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
197
+ {
198
+ prof_frame_t* frame = prof_stack_last(stack);
199
+ while (frame >= stack->start)
200
+ {
201
+ if (!frame->call_tree)
202
+ return NULL;
203
+
204
+ if (rb_str_equal(source_file, frame->call_tree->method->source_file) &&
205
+ source_line >= frame->call_tree->method->source_line)
206
+ {
207
+ return frame->call_tree->method;
208
+ }
209
+ frame--;
210
+ }
211
+ return NULL;
212
+ }
@@ -0,0 +1,53 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_STACK__
5
+ #define __RP_STACK__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_call_tree.h"
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. */
13
+ typedef struct prof_frame_t
14
+ {
15
+ /* Caching prof_method_t values significantly
16
+ increases performance. */
17
+ prof_call_tree_t* call_tree;
18
+
19
+ VALUE source_file;
20
+ unsigned int source_line;
21
+
22
+ double start_time;
23
+ double switch_time; /* Time at switch to different thread */
24
+ double wait_time;
25
+ double child_time;
26
+ double pause_time; // Time pause() was initiated
27
+ double dead_time; // Time to ignore (i.e. total amount of time between pause/resume blocks)
28
+ } prof_frame_t;
29
+
30
+ #define prof_frame_is_paused(f) (f->pause_time >= 0)
31
+ #define prof_frame_is_unpaused(f) (f->pause_time < 0)
32
+
33
+ void prof_frame_pause(prof_frame_t*, double current_measurement);
34
+ void prof_frame_unpause(prof_frame_t*, double current_measurement);
35
+
36
+ /* Current stack of active methods.*/
37
+ typedef struct prof_stack_t
38
+ {
39
+ prof_frame_t* start;
40
+ prof_frame_t* end;
41
+ prof_frame_t* ptr;
42
+ } prof_stack_t;
43
+
44
+ prof_stack_t* prof_stack_create(void);
45
+ void prof_stack_free(prof_stack_t* stack);
46
+
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);
52
+
53
+ #endif //__RP_STACK__
@@ -0,0 +1,362 @@
1
+ /* Copyright (C) 2005-2013 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ /* Document-class: RubyProf::Thread
5
+
6
+ The Thread class contains profile results for a single fiber (note a Ruby thread can run multiple fibers).
7
+ You cannot create an instance of RubyProf::Thread, instead you access it from a RubyProf::Profile object.
8
+
9
+ profile = RubyProf::Profile.profile do
10
+ ...
11
+ end
12
+
13
+ profile.threads.each do |thread|
14
+ thread.root_methods.sort.each do |method|
15
+ puts method.total_time
16
+ end
17
+ end */
18
+
19
+ #include "rp_thread.h"
20
+ #include "rp_profile.h"
21
+
22
+ VALUE cRpThread;
23
+
24
+ // ====== thread_data_t ======
25
+ thread_data_t* thread_data_create(void)
26
+ {
27
+ thread_data_t* result = ALLOC(thread_data_t);
28
+ result->stack = prof_stack_create();
29
+ result->method_table = method_table_create();
30
+ result->call_tree = NULL;
31
+ result->object = Qnil;
32
+ result->methods = Qnil;
33
+ result->fiber_id = Qnil;
34
+ result->thread_id = Qnil;
35
+ result->trace = true;
36
+ result->fiber = Qnil;
37
+ return result;
38
+ }
39
+
40
+ static int mark_methods(st_data_t key, st_data_t value, st_data_t result)
41
+ {
42
+ prof_method_t* method = (prof_method_t*)value;
43
+ prof_method_mark(method);
44
+ return ST_CONTINUE;
45
+ }
46
+
47
+ size_t prof_thread_size(const void* data)
48
+ {
49
+ return sizeof(thread_data_t);
50
+ }
51
+
52
+ void prof_thread_mark(void* data)
53
+ {
54
+ if (!data)
55
+ return;
56
+
57
+ thread_data_t* thread = (thread_data_t*)data;
58
+
59
+ if (thread->object != Qnil)
60
+ rb_gc_mark(thread->object);
61
+
62
+ rb_gc_mark(thread->fiber);
63
+
64
+ if (thread->methods != Qnil)
65
+ rb_gc_mark(thread->methods);
66
+
67
+ if (thread->fiber_id != Qnil)
68
+ rb_gc_mark(thread->fiber_id);
69
+
70
+ if (thread->thread_id != Qnil)
71
+ rb_gc_mark(thread->thread_id);
72
+
73
+ if (thread->call_tree)
74
+ prof_call_tree_mark(thread->call_tree);
75
+
76
+ rb_st_foreach(thread->method_table, mark_methods, 0);
77
+ }
78
+
79
+ void prof_thread_ruby_gc_free(void* data)
80
+ {
81
+ if (data)
82
+ {
83
+ thread_data_t* thread_data = (thread_data_t*)data;
84
+ thread_data->object = Qnil;
85
+ }
86
+ }
87
+
88
+ static void prof_thread_free(thread_data_t* thread_data)
89
+ {
90
+ /* Has this method object been accessed by Ruby? If
91
+ yes then set its data to nil to avoid a segmentation fault on the next mark and sweep. */
92
+ if (thread_data->object != Qnil)
93
+ {
94
+ RTYPEDDATA(thread_data->object)->data = NULL;
95
+ thread_data->object = Qnil;
96
+ }
97
+
98
+ method_table_free(thread_data->method_table);
99
+
100
+ if (thread_data->call_tree)
101
+ prof_call_tree_free(thread_data->call_tree);
102
+
103
+ prof_stack_free(thread_data->stack);
104
+
105
+ xfree(thread_data);
106
+ }
107
+
108
+ static const rb_data_type_t thread_type =
109
+ {
110
+ .wrap_struct_name = "ThreadInfo",
111
+ .function =
112
+ {
113
+ .dmark = prof_thread_mark,
114
+ .dfree = prof_thread_ruby_gc_free,
115
+ .dsize = prof_thread_size,
116
+ },
117
+ .data = NULL,
118
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
119
+ };
120
+
121
+ VALUE prof_thread_wrap(thread_data_t* thread)
122
+ {
123
+ if (thread->object == Qnil)
124
+ {
125
+ thread->object = TypedData_Wrap_Struct(cRpThread, &thread_type, thread);
126
+ }
127
+ return thread->object;
128
+ }
129
+
130
+ static VALUE prof_thread_allocate(VALUE klass)
131
+ {
132
+ thread_data_t* thread_data = thread_data_create();
133
+ thread_data->object = prof_thread_wrap(thread_data);
134
+ return thread_data->object;
135
+ }
136
+
137
+ thread_data_t* prof_get_thread(VALUE self)
138
+ {
139
+ /* Can't use Data_Get_Struct because that triggers the event hook
140
+ ending up in endless recursion. */
141
+ thread_data_t* result = RTYPEDDATA_DATA(self);
142
+ if (!result)
143
+ rb_raise(rb_eRuntimeError, "This RubyProf::Thread instance has already been freed, likely because its profile has been freed.");
144
+
145
+ return result;
146
+ }
147
+
148
+ // ====== Thread Table ======
149
+ // The thread table is hash keyed on ruby fiber_id that stores instances of thread_data_t.
150
+
151
+ st_table* threads_table_create()
152
+ {
153
+ return rb_st_init_numtable();
154
+ }
155
+
156
+ static int thread_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
157
+ {
158
+ prof_thread_free((thread_data_t*)value);
159
+ return ST_CONTINUE;
160
+ }
161
+
162
+ void threads_table_free(st_table* table)
163
+ {
164
+ rb_st_foreach(table, thread_table_free_iterator, 0);
165
+ rb_st_free_table(table);
166
+ }
167
+
168
+ thread_data_t* threads_table_lookup(void* prof, VALUE fiber)
169
+ {
170
+ prof_profile_t* profile = prof;
171
+ thread_data_t* result = NULL;
172
+ st_data_t val;
173
+
174
+ if (rb_st_lookup(profile->threads_tbl, fiber, &val))
175
+ {
176
+ result = (thread_data_t*)val;
177
+ }
178
+
179
+ return result;
180
+ }
181
+
182
+ thread_data_t* threads_table_insert(void* prof, VALUE fiber)
183
+ {
184
+ prof_profile_t* profile = prof;
185
+ thread_data_t* result = thread_data_create();
186
+ VALUE thread = rb_thread_current();
187
+
188
+ result->fiber = fiber;
189
+ result->fiber_id = rb_obj_id(fiber);
190
+ result->thread_id = rb_obj_id(thread);
191
+ rb_st_insert(profile->threads_tbl, (st_data_t)fiber, (st_data_t)result);
192
+
193
+ // Are we tracing this thread?
194
+ if (profile->include_threads_tbl && !rb_st_lookup(profile->include_threads_tbl, thread, 0))
195
+ {
196
+ result->trace = false;
197
+ }
198
+ else if (profile->exclude_threads_tbl && rb_st_lookup(profile->exclude_threads_tbl, thread, 0))
199
+ {
200
+ result->trace = false;
201
+ }
202
+ else
203
+ {
204
+ result->trace = true;
205
+ }
206
+
207
+ return result;
208
+ }
209
+
210
+ // ====== Profiling Methods ======
211
+ void switch_thread(void* prof, thread_data_t* thread_data, double measurement)
212
+ {
213
+ prof_profile_t* profile = prof;
214
+
215
+ /* Get current frame for this thread */
216
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
217
+ if (frame)
218
+ {
219
+ frame->wait_time += measurement - frame->switch_time;
220
+ frame->switch_time = 0;
221
+ }
222
+
223
+ /* Save on the last thread the time of the context switch
224
+ and reset this thread's last context switch to 0.*/
225
+ if (profile->last_thread_data)
226
+ {
227
+ prof_frame_t* last_frame = prof_frame_current(profile->last_thread_data->stack);
228
+ if (last_frame)
229
+ last_frame->switch_time = measurement;
230
+ }
231
+
232
+ profile->last_thread_data = thread_data;
233
+ }
234
+
235
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data)
236
+ {
237
+ thread_data_t* thread_data = (thread_data_t*)value;
238
+ prof_profile_t* profile = (prof_profile_t*)data;
239
+
240
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
241
+ prof_frame_pause(frame, profile->measurement_at_pause_resume);
242
+
243
+ return ST_CONTINUE;
244
+ }
245
+
246
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data)
247
+ {
248
+ thread_data_t* thread_data = (thread_data_t*)value;
249
+ prof_profile_t* profile = (prof_profile_t*)data;
250
+
251
+ prof_frame_t* frame = prof_frame_current(thread_data->stack);
252
+ prof_frame_unpause(frame, profile->measurement_at_pause_resume);
253
+
254
+ return ST_CONTINUE;
255
+ }
256
+
257
+ // ====== Helper Methods ======
258
+ static int collect_methods(st_data_t key, st_data_t value, st_data_t result)
259
+ {
260
+ /* Called for each method stored in a thread's method table.
261
+ We want to store the method info information into an array.*/
262
+ VALUE methods = (VALUE)result;
263
+ prof_method_t* method = (prof_method_t*)value;
264
+ rb_ary_push(methods, prof_method_wrap(method));
265
+
266
+ return ST_CONTINUE;
267
+ }
268
+
269
+ // ====== RubyProf::Thread ======
270
+ /* call-seq:
271
+ id -> number
272
+
273
+ Returns the thread id of this thread. */
274
+ static VALUE prof_thread_id(VALUE self)
275
+ {
276
+ thread_data_t* thread = prof_get_thread(self);
277
+ return thread->thread_id;
278
+ }
279
+
280
+ /* call-seq:
281
+ fiber_id -> number
282
+
283
+ Returns the fiber id of this thread. */
284
+ static VALUE prof_fiber_id(VALUE self)
285
+ {
286
+ thread_data_t* thread = prof_get_thread(self);
287
+ return thread->fiber_id;
288
+ }
289
+
290
+ /* call-seq:
291
+ call_tree -> CallTree
292
+
293
+ Returns the root of the call tree. */
294
+ static VALUE prof_call_tree(VALUE self)
295
+ {
296
+ thread_data_t* thread = prof_get_thread(self);
297
+ return prof_call_tree_wrap(thread->call_tree);
298
+ }
299
+
300
+ /* call-seq:
301
+ methods -> [RubyProf::MethodInfo]
302
+
303
+ Returns an array of methods that were called from this
304
+ thread during program execution. */
305
+ static VALUE prof_thread_methods(VALUE self)
306
+ {
307
+ thread_data_t* thread = prof_get_thread(self);
308
+ if (thread->methods == Qnil)
309
+ {
310
+ thread->methods = rb_ary_new();
311
+ rb_st_foreach(thread->method_table, collect_methods, thread->methods);
312
+ }
313
+ return thread->methods;
314
+ }
315
+
316
+ /* :nodoc: */
317
+ static VALUE prof_thread_dump(VALUE self)
318
+ {
319
+ thread_data_t* thread_data = RTYPEDDATA_DATA(self);
320
+
321
+ VALUE result = rb_hash_new();
322
+ rb_hash_aset(result, ID2SYM(rb_intern("fiber_id")), thread_data->fiber_id);
323
+ rb_hash_aset(result, ID2SYM(rb_intern("methods")), prof_thread_methods(self));
324
+ rb_hash_aset(result, ID2SYM(rb_intern("call_tree")), prof_call_tree(self));
325
+
326
+ return result;
327
+ }
328
+
329
+ /* :nodoc: */
330
+ static VALUE prof_thread_load(VALUE self, VALUE data)
331
+ {
332
+ thread_data_t* thread_data = RTYPEDDATA_DATA(self);
333
+
334
+ VALUE call_tree = rb_hash_aref(data, ID2SYM(rb_intern("call_tree")));
335
+ thread_data->call_tree = prof_get_call_tree(call_tree);
336
+
337
+ thread_data->fiber_id = rb_hash_aref(data, ID2SYM(rb_intern("fiber_id")));
338
+
339
+ VALUE methods = rb_hash_aref(data, ID2SYM(rb_intern("methods")));
340
+ for (int i = 0; i < rb_array_len(methods); i++)
341
+ {
342
+ VALUE method = rb_ary_entry(methods, i);
343
+ prof_method_t* method_data = RTYPEDDATA_DATA(method);
344
+ method_table_insert(thread_data->method_table, method_data->key, method_data);
345
+ }
346
+
347
+ return data;
348
+ }
349
+
350
+ void rp_init_thread(void)
351
+ {
352
+ cRpThread = rb_define_class_under(mProf, "Thread", rb_cObject);
353
+ rb_undef_method(CLASS_OF(cRpThread), "new");
354
+ rb_define_alloc_func(cRpThread, prof_thread_allocate);
355
+
356
+ rb_define_method(cRpThread, "id", prof_thread_id, 0);
357
+ rb_define_method(cRpThread, "call_tree", prof_call_tree, 0);
358
+ rb_define_method(cRpThread, "fiber_id", prof_fiber_id, 0);
359
+ rb_define_method(cRpThread, "methods", prof_thread_methods, 0);
360
+ rb_define_method(cRpThread, "_dump_data", prof_thread_dump, 0);
361
+ rb_define_method(cRpThread, "_load_data", prof_thread_load, 1);
362
+ }
@@ -0,0 +1,39 @@
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_THREAD__
5
+ #define __RP_THREAD__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_stack.h"
9
+
10
+ /* Profiling information for a thread. */
11
+ typedef struct thread_data_t
12
+ {
13
+ // Runtime
14
+ VALUE object; /* Cache to wrapped object */
15
+ VALUE fiber; /* Fiber */
16
+ prof_stack_t* stack; /* Stack of frames */
17
+ bool trace; /* Are we tracking this thread */
18
+ prof_call_tree_t* call_tree; /* The root of the call tree*/
19
+ VALUE thread_id; /* Thread id */
20
+ VALUE fiber_id; /* Fiber id */
21
+ VALUE methods; /* Array of RubyProf::MethodInfo */
22
+ st_table* method_table; /* Methods called in the thread */
23
+ } thread_data_t;
24
+
25
+ void rp_init_thread(void);
26
+ st_table* threads_table_create(void);
27
+ thread_data_t* threads_table_lookup(void* profile, VALUE fiber);
28
+ thread_data_t* threads_table_insert(void* profile, VALUE fiber);
29
+ void threads_table_free(st_table* table);
30
+
31
+ thread_data_t* prof_get_thread(VALUE self);
32
+ VALUE prof_thread_wrap(thread_data_t* thread);
33
+ void prof_thread_mark(void* data);
34
+
35
+ void switch_thread(void* profile, thread_data_t* thread_data, double measurement);
36
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data);
37
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
38
+
39
+ #endif //__RP_THREAD__