ruby-prof 0.6.0-x86-mswin32-60

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 (50) hide show
  1. data/CHANGES +116 -0
  2. data/LICENSE +23 -0
  3. data/README +307 -0
  4. data/Rakefile +141 -0
  5. data/bin/ruby-prof +192 -0
  6. data/examples/flat.txt +55 -0
  7. data/examples/graph.html +823 -0
  8. data/examples/graph.txt +170 -0
  9. data/ext/extconf.rb +21 -0
  10. data/ext/extconf.rb.rej +13 -0
  11. data/ext/measure_allocations.h +43 -0
  12. data/ext/measure_cpu_time.h +138 -0
  13. data/ext/measure_memory.h +42 -0
  14. data/ext/measure_process_time.h +41 -0
  15. data/ext/measure_wall_time.h +42 -0
  16. data/ext/ruby_prof.c +1628 -0
  17. data/lib/ruby-prof.rb +43 -0
  18. data/lib/ruby-prof/abstract_printer.rb +42 -0
  19. data/lib/ruby-prof/call_tree_printer.rb +76 -0
  20. data/lib/ruby-prof/call_tree_printer.rb.rej +27 -0
  21. data/lib/ruby-prof/flat_printer.rb +79 -0
  22. data/lib/ruby-prof/graph_html_printer.rb +255 -0
  23. data/lib/ruby-prof/graph_printer.rb +163 -0
  24. data/lib/ruby-prof/profile_test_case.rb +80 -0
  25. data/lib/ruby-prof/task.rb +147 -0
  26. data/lib/ruby_prof.so +0 -0
  27. data/lib/unprof.rb +8 -0
  28. data/rails_plugin/ruby-prof/init.rb +8 -0
  29. data/rails_plugin/ruby-prof/lib/profiling.rb +57 -0
  30. data/test/basic_test.rb +190 -0
  31. data/test/duplicate_names_test.rb +33 -0
  32. data/test/line_number_test.rb +69 -0
  33. data/test/measure_mode_test.rb +79 -0
  34. data/test/module_test.rb +57 -0
  35. data/test/no_method_class_test.rb +14 -0
  36. data/test/prime.rb +60 -0
  37. data/test/prime1.rb +17 -0
  38. data/test/prime2.rb +26 -0
  39. data/test/prime3.rb +17 -0
  40. data/test/prime_test.rb +24 -0
  41. data/test/printers_test.rb +74 -0
  42. data/test/profile_unit_test.rb +24 -0
  43. data/test/recursive_test.rb +144 -0
  44. data/test/singleton_test.rb +38 -0
  45. data/test/start_test.rb +24 -0
  46. data/test/test_helper.rb +55 -0
  47. data/test/test_suite.rb +19 -0
  48. data/test/thread_test.rb +135 -0
  49. data/test/timing_test.rb +133 -0
  50. metadata +116 -0
@@ -0,0 +1,42 @@
1
+ /* :nodoc:
2
+ * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
3
+ * Charlie Savage <cfis@savagexi.com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions
8
+ * are met:
9
+ * 1. Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ * 2. Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ *
15
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ * SUCH DAMAGE. */
26
+
27
+
28
+ #define MEASURE_WALL_TIME 1
29
+
30
+ static prof_measure_t
31
+ measure_wall_time()
32
+ {
33
+ struct timeval tv;
34
+ gettimeofday(&tv, NULL);
35
+ return tv.tv_sec * 1000000 + tv.tv_usec;
36
+ }
37
+
38
+ static double
39
+ convert_wall_time(prof_measure_t c)
40
+ {
41
+ return (double) c / 1000000;
42
+ }
data/ext/ruby_prof.c ADDED
@@ -0,0 +1,1628 @@
1
+ /*
2
+ * Copyright (C) 2007 Shugo Maeda <shugo@ruby-lang.org>
3
+ * Charlie Savage <cfis@savagexi.com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions
8
+ * are met:
9
+ * 1. Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ * 2. Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ *
15
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
+ * SUCH DAMAGE.
26
+ */
27
+
28
+ /* ruby-prof tracks the time spent executing every method in ruby programming.
29
+ The main players are:
30
+
31
+ prof_result_t - Its one field, values, contains the overall results
32
+ thread_data_t - Stores data about a single thread.
33
+ prof_stack_t - The method call stack in a particular thread
34
+ prof_method_t - Profiling information for each method
35
+ prof_call_info_t - Keeps track a method's callers and callees.
36
+
37
+ The final resulut is a hash table of thread_data_t, keyed on the thread
38
+ id. Each thread has an hash a table of prof_method_t, keyed on the
39
+ method id. A hash table is used for quick look up when doing a profile.
40
+ However, it is exposed to Ruby as an array.
41
+
42
+ Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
43
+ These objects keep track of a method's callers (who called the method) and its
44
+ callees (who the method called). These are keyed the method id, but once again,
45
+ are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
46
+ caller or callee method, thereby making it easy to navigate through the call
47
+ hierarchy in ruby - which is very helpful for creating call graphs.
48
+ */
49
+
50
+
51
+ #include <stdio.h>
52
+
53
+ #include <ruby.h>
54
+ #ifndef RUBY_VM
55
+ #include <node.h>
56
+ #include <st.h>
57
+ typedef rb_event_t rb_event_flag_t;
58
+ #define rb_sourcefile() (node ? node->nd_file : 0)
59
+ #define rb_sourceline() (node ? nd_line(node) : 0)
60
+ #endif
61
+
62
+
63
+ /* ================ Constants =================*/
64
+ #define INITIAL_STACK_SIZE 8
65
+ #define PROF_VERSION "0.6.0"
66
+
67
+
68
+ /* ================ Measurement =================*/
69
+ #ifdef HAVE_LONG_LONG
70
+ typedef LONG_LONG prof_measure_t;
71
+ #else
72
+ typedef unsigned long prof_measure_t;
73
+ #endif
74
+
75
+ #include "measure_process_time.h"
76
+ #include "measure_wall_time.h"
77
+ #include "measure_cpu_time.h"
78
+ #include "measure_allocations.h"
79
+ #include "measure_memory.h"
80
+
81
+ static prof_measure_t (*get_measurement)() = measure_process_time;
82
+ static double (*convert_measurement)(prof_measure_t) = convert_process_time;
83
+
84
+ /* ================ DataTypes =================*/
85
+ static VALUE mProf;
86
+ static VALUE cResult;
87
+ static VALUE cMethodInfo;
88
+ static VALUE cCallInfo;
89
+
90
+ /* Profiling information for each method. */
91
+ typedef struct prof_method_t {
92
+ st_data_t key; /* Cache hash value for speed reasons. */
93
+ VALUE name; /* Name of the method. */
94
+ VALUE klass; /* The method's class. */
95
+ ID mid; /* The method id. */
96
+ int depth; /* The recursive depth this method was called at.*/
97
+ int called; /* Number of times called */
98
+ const char* source_file; /* The method's source file */
99
+ int line; /* The method's line number. */
100
+ prof_measure_t total_time; /* Total time spent in this method and children. */
101
+ prof_measure_t self_time; /* Total time spent in this method. */
102
+ prof_measure_t wait_time; /* Total time this method spent waiting for other threads. */
103
+ st_table *parents; /* The method's callers (prof_call_info_t). */
104
+ st_table *children; /* The method's callees (prof_call_info_t). */
105
+ int active_frame; /* # of active frames for this method. Used to detect
106
+ recursion. Stashed here to avoid extra lookups in
107
+ the hook method - so a bit hackey. */
108
+ struct prof_method_t *base; /* For recursion - this is the parent method */
109
+ } prof_method_t;
110
+
111
+
112
+ /* Callers and callee information for a method. */
113
+ typedef struct {
114
+ prof_method_t *target;
115
+ int called;
116
+ prof_measure_t total_time;
117
+ prof_measure_t self_time;
118
+ prof_measure_t wait_time;
119
+ int line;
120
+ } prof_call_info_t;
121
+
122
+
123
+ /* Temporary object that maintains profiling information
124
+ for active methods - there is one per method.*/
125
+ typedef struct {
126
+ /* Caching prof_method_t values significantly
127
+ increases performance. */
128
+ prof_method_t *method;
129
+ prof_measure_t start_time;
130
+ prof_measure_t wait_time;
131
+ prof_measure_t child_time;
132
+ unsigned int line;
133
+ } prof_frame_t;
134
+
135
+ /* Current stack of active methods.*/
136
+ typedef struct {
137
+ prof_frame_t *start;
138
+ prof_frame_t *end;
139
+ prof_frame_t *ptr;
140
+ } prof_stack_t;
141
+
142
+ /* Profiling information for a thread. */
143
+ typedef struct {
144
+ unsigned long thread_id; /* Thread id */
145
+ st_table* method_info_table; /* All called methods */
146
+ prof_stack_t* stack; /* Active methods */
147
+ prof_measure_t last_switch; /* Point of last context switch */
148
+ } thread_data_t;
149
+
150
+ typedef struct {
151
+ VALUE threads;
152
+ } prof_result_t;
153
+
154
+
155
+ /* ================ Variables =================*/
156
+ static int measure_mode;
157
+ static st_table *threads_tbl = NULL;
158
+ /* TODO - If Ruby become multi-threaded this has to turn into
159
+ a separate stack since this isn't thread safe! */
160
+ static thread_data_t* last_thread_data = NULL;
161
+
162
+
163
+ /* ================ Helper Functions =================*/
164
+ /* Helper method to get the id of a Ruby thread. */
165
+ static inline long
166
+ get_thread_id(VALUE thread)
167
+ {
168
+ //return NUM2ULONG(rb_obj_id(thread));
169
+ // From line 1997 in gc.c
170
+ return (long)thread;
171
+ }
172
+
173
+ static VALUE
174
+ figure_singleton_name(VALUE klass)
175
+ {
176
+ VALUE result = Qnil;
177
+
178
+ /* We have come across a singleton object. First
179
+ figure out what it is attached to.*/
180
+ VALUE attached = rb_iv_get(klass, "__attached__");
181
+
182
+ /* Is this a singleton class acting as a metaclass? */
183
+ if (BUILTIN_TYPE(attached) == T_CLASS)
184
+ {
185
+ result = rb_str_new2("<Class::");
186
+ rb_str_append(result, rb_inspect(attached));
187
+ rb_str_cat2(result, ">");
188
+ }
189
+
190
+ /* Is this for singleton methods on a module? */
191
+ else if (BUILTIN_TYPE(attached) == T_MODULE)
192
+ {
193
+ result = rb_str_new2("<Module::");
194
+ rb_str_append(result, rb_inspect(attached));
195
+ rb_str_cat2(result, ">");
196
+ }
197
+
198
+ /* Is this for singleton methods on an object? */
199
+ else if (BUILTIN_TYPE(attached) == T_OBJECT)
200
+ {
201
+ /* Make sure to get the super class so that we don't
202
+ mistakenly grab a T_ICLASS which would lead to
203
+ unknown method errors. */
204
+ #ifdef RCLASS_SUPER
205
+ VALUE super = rb_class_real(RCLASS_SUPER(klass));
206
+ #else
207
+ VALUE super = rb_class_real(RCLASS(klass)->super);
208
+ #endif
209
+ result = rb_str_new2("<Object::");
210
+ rb_str_append(result, rb_inspect(super));
211
+ rb_str_cat2(result, ">");
212
+ }
213
+
214
+ /* Ok, this could be other things like an array made put onto
215
+ a singleton object (yeah, it happens, see the singleton
216
+ objects test case). */
217
+ else
218
+ {
219
+ result = rb_inspect(klass);
220
+ }
221
+
222
+ return result;
223
+ }
224
+
225
+ static VALUE
226
+ klass_name(VALUE klass)
227
+ {
228
+ VALUE result = Qnil;
229
+
230
+ if (klass == 0 || klass == Qnil)
231
+ {
232
+ result = rb_str_new2("Global");
233
+ }
234
+ else if (BUILTIN_TYPE(klass) == T_MODULE)
235
+ {
236
+ result = rb_inspect(klass);
237
+ }
238
+ else if (BUILTIN_TYPE(klass) == T_CLASS && FL_TEST(klass, FL_SINGLETON))
239
+ {
240
+ result = figure_singleton_name(klass);
241
+ }
242
+ else if (BUILTIN_TYPE(klass) == T_CLASS)
243
+ {
244
+ result = rb_inspect(klass);
245
+ }
246
+ else
247
+ {
248
+ /* Should never happen. */
249
+ result = rb_str_new2("Unknown");
250
+ }
251
+
252
+ return result;
253
+ }
254
+
255
+ static VALUE
256
+ method_name(ID mid, int depth)
257
+ {
258
+ VALUE result;
259
+
260
+ if (mid == ID_ALLOCATOR)
261
+ result = rb_str_new2("allocate");
262
+ else if (mid == 0)
263
+ result = rb_str_new2("[No method]");
264
+ else
265
+ result = rb_String(ID2SYM(mid));
266
+
267
+ if (depth > 0)
268
+ {
269
+ char buffer[65];
270
+ sprintf(buffer, "%i", depth);
271
+ rb_str_cat2(result, "-");
272
+ rb_str_cat2(result, buffer);
273
+ }
274
+
275
+ return result;
276
+ }
277
+
278
+ static VALUE
279
+ full_name(VALUE klass, ID mid, int depth)
280
+ {
281
+ VALUE result = klass_name(klass);
282
+ rb_str_cat2(result, "#");
283
+ rb_str_append(result, method_name(mid, depth));
284
+
285
+ return result;
286
+ }
287
+
288
+
289
+ static inline st_data_t
290
+ method_key(VALUE klass, ID mid, int depth)
291
+ {
292
+ /* No idea if this is a unique key or not. Would be
293
+ best to use the method name, but we can't, since
294
+ that calls internal ruby functions which would
295
+ cause the hook method to recursively call itself.
296
+ And that is too much of a bother to deal with.
297
+ Plus of course, this is faster. */
298
+ return (klass * 100) + (mid * 10) + depth;
299
+ }
300
+
301
+ /* ================ Stack Handling =================*/
302
+
303
+ /* Creates a stack of prof_frame_t to keep track
304
+ of timings for active methods. */
305
+ static prof_stack_t *
306
+ stack_create()
307
+ {
308
+ prof_stack_t *stack = ALLOC(prof_stack_t);
309
+ stack->start = ALLOC_N(prof_frame_t, INITIAL_STACK_SIZE);
310
+ stack->ptr = stack->start;
311
+ stack->end = stack->start + INITIAL_STACK_SIZE;
312
+ return stack;
313
+ }
314
+
315
+ static void
316
+ stack_free(prof_stack_t *stack)
317
+ {
318
+ xfree(stack->start);
319
+ xfree(stack);
320
+ }
321
+
322
+ static inline prof_frame_t *
323
+ stack_push(prof_stack_t *stack)
324
+ {
325
+ /* Is there space on the stack? If not, double
326
+ its size. */
327
+ if (stack->ptr == stack->end)
328
+ {
329
+ size_t len = stack->ptr - stack->start;
330
+ size_t new_capacity = (stack->end - stack->start) * 2;
331
+ REALLOC_N(stack->start, prof_frame_t, new_capacity);
332
+ stack->ptr = stack->start + len;
333
+ stack->end = stack->start + new_capacity;
334
+ }
335
+ return stack->ptr++;
336
+ }
337
+
338
+ static inline prof_frame_t *
339
+ stack_pop(prof_stack_t *stack)
340
+ {
341
+ if (stack->ptr == stack->start)
342
+ return NULL;
343
+ else
344
+ return --stack->ptr;
345
+ }
346
+
347
+ static inline prof_frame_t *
348
+ stack_peek(prof_stack_t *stack)
349
+ {
350
+ if (stack->ptr == stack->start)
351
+ return NULL;
352
+ else
353
+ return stack->ptr - 1;
354
+ }
355
+
356
+ static inline size_t
357
+ stack_size(prof_stack_t *stack)
358
+ {
359
+ return stack->ptr - stack->start;
360
+ }
361
+
362
+ /* ================ Method Info Handling =================*/
363
+
364
+ /* --- Keeps track of the methods the current method calls */
365
+ static st_table *
366
+ method_info_table_create()
367
+ {
368
+ return st_init_numtable();
369
+ }
370
+
371
+ static inline size_t
372
+ method_info_table_insert(st_table *table, st_data_t key, prof_method_t *val)
373
+ {
374
+ return st_insert(table, key, (st_data_t) val);
375
+ }
376
+
377
+ static inline prof_method_t *
378
+ method_info_table_lookup(st_table *table, st_data_t key)
379
+ {
380
+ st_data_t val;
381
+ if (st_lookup(table, key, &val))
382
+ {
383
+ return (prof_method_t *) val;
384
+ }
385
+ else
386
+ {
387
+ return NULL;
388
+ }
389
+ }
390
+
391
+
392
+ static void
393
+ method_info_table_free(st_table *table)
394
+ {
395
+ /* Don't free the contents since they are wrapped by
396
+ Ruby objects! */
397
+ st_free_table(table);
398
+ }
399
+
400
+
401
+ /* ================ Call Info Handling =================*/
402
+
403
+ /* ---- Hash, keyed on class/method_id, that holds call_info objects ---- */
404
+ static st_table *
405
+ caller_table_create()
406
+ {
407
+ return st_init_numtable();
408
+ }
409
+
410
+ static inline size_t
411
+ caller_table_insert(st_table *table, st_data_t key, prof_call_info_t *val)
412
+ {
413
+ return st_insert(table, key, (st_data_t) val);
414
+ }
415
+
416
+ static inline prof_call_info_t *
417
+ caller_table_lookup(st_table *table, st_data_t key)
418
+ {
419
+ st_data_t val;
420
+ if (st_lookup(table, key, &val))
421
+ {
422
+ return (prof_call_info_t *) val;
423
+ }
424
+ else
425
+ {
426
+ return NULL;
427
+ }
428
+ }
429
+
430
+ static void
431
+ caller_table_free(st_table *table)
432
+ {
433
+ st_free_table(table);
434
+ }
435
+
436
+ /* Document-class: RubyProf::CallInfo
437
+ RubyProf::CallInfo is a helper class used by RubyProf::MethodInfo
438
+ to keep track of which child methods were called and how long
439
+ they took to execute. */
440
+
441
+ /* :nodoc: */
442
+ static prof_call_info_t *
443
+ call_info_create(prof_method_t* method)
444
+ {
445
+ prof_call_info_t *result;
446
+
447
+ result = ALLOC(prof_call_info_t);
448
+ result->target = method;
449
+ result->called = 0;
450
+ result->total_time = 0;
451
+ result->self_time = 0;
452
+ result->wait_time = 0;
453
+ return result;
454
+ }
455
+
456
+ static void
457
+ call_info_free(prof_call_info_t *call_info)
458
+ {
459
+ xfree(call_info);
460
+ }
461
+
462
+ static int
463
+ free_call_infos(st_data_t key, st_data_t value, st_data_t data)
464
+ {
465
+ prof_call_info_t* call_info = (prof_call_info_t*) value;
466
+ call_info_free(call_info);
467
+ return ST_CONTINUE;
468
+ }
469
+
470
+ static VALUE
471
+ call_info_new(prof_call_info_t *result)
472
+ {
473
+ /* We don't want Ruby freeing the underlying C structures, that
474
+ is done when the prof_method_t is freed. */
475
+ return Data_Wrap_Struct(cCallInfo, NULL, NULL, result);
476
+ }
477
+
478
+ static prof_call_info_t *
479
+ get_call_info_result(VALUE obj)
480
+ {
481
+ if (BUILTIN_TYPE(obj) != T_DATA)
482
+ {
483
+ /* Should never happen */
484
+ rb_raise(rb_eTypeError, "Not a call info object");
485
+ }
486
+ return (prof_call_info_t *) DATA_PTR(obj);
487
+ }
488
+
489
+
490
+ /* call-seq:
491
+ called -> MethodInfo
492
+
493
+ Returns the target method. */
494
+ static VALUE
495
+ call_info_target(VALUE self)
496
+ {
497
+ /* Target is a pointer to a method_info - so we have to be careful
498
+ about the GC. We will wrap the method_info but provide no
499
+ free method so the underlying object is not freed twice! */
500
+
501
+ prof_call_info_t *result = get_call_info_result(self);
502
+ return Data_Wrap_Struct(cMethodInfo, NULL, NULL, result->target);
503
+ }
504
+
505
+ /* call-seq:
506
+ called -> int
507
+
508
+ Returns the total amount of time this method was called. */
509
+ static VALUE
510
+ call_info_called(VALUE self)
511
+ {
512
+ prof_call_info_t *result = get_call_info_result(self);
513
+
514
+ return INT2NUM(result->called);
515
+ }
516
+
517
+ /* call-seq:
518
+ line_no -> int
519
+
520
+ returns the line number of the method */
521
+ static VALUE
522
+ call_info_line(VALUE self)
523
+ {
524
+ return rb_int_new(get_call_info_result(self)->line);
525
+ }
526
+
527
+ /* call-seq:
528
+ total_time -> float
529
+
530
+ Returns the total amount of time spent in this method and its children. */
531
+ static VALUE
532
+ call_info_total_time(VALUE self)
533
+ {
534
+ prof_call_info_t *result = get_call_info_result(self);
535
+
536
+ return rb_float_new(convert_measurement(result->total_time));
537
+ }
538
+
539
+ /* call-seq:
540
+ self_time -> float
541
+
542
+ Returns the total amount of time spent in this method. */
543
+ static VALUE
544
+ call_info_self_time(VALUE self)
545
+ {
546
+ prof_call_info_t *result = get_call_info_result(self);
547
+
548
+ return rb_float_new(convert_measurement(result->self_time));
549
+ }
550
+
551
+ /* call-seq:
552
+ wait_time -> float
553
+
554
+ Returns the total amount of time this method waited for other threads. */
555
+ static VALUE
556
+ call_info_wait_time(VALUE self)
557
+ {
558
+ prof_call_info_t *result = get_call_info_result(self);
559
+
560
+ return rb_float_new(convert_measurement(result->wait_time));
561
+ }
562
+
563
+ /* call-seq:
564
+ children_time -> float
565
+
566
+ Returns the total amount of time spent in this method's children. */
567
+ static VALUE
568
+ call_info_children_time(VALUE self)
569
+ {
570
+ prof_call_info_t *result = get_call_info_result(self);
571
+ prof_measure_t children_time = result->total_time - result->self_time - result->wait_time;
572
+ return rb_float_new(convert_measurement(children_time));
573
+ }
574
+
575
+
576
+ /* Document-class: RubyProf::MethodInfo
577
+ The RubyProf::MethodInfo class stores profiling data for a method.
578
+ One instance of the RubyProf::MethodInfo class is created per method
579
+ called per thread. Thus, if a method is called in two different
580
+ thread then there will be two RubyProf::MethodInfo objects
581
+ created. RubyProf::MethodInfo objects can be accessed via
582
+ the RubyProf::Result object.
583
+ */
584
+
585
+ /* :nodoc: */
586
+ static prof_method_t *
587
+ prof_method_create(st_data_t key, VALUE klass, ID mid, int depth,
588
+ const char* source_file, int line)
589
+ {
590
+ prof_method_t *result = ALLOC(prof_method_t);
591
+
592
+ result->klass = klass;
593
+ result->mid = mid;
594
+ result->key = key;
595
+ result->depth = depth;
596
+
597
+ result->called = 0;
598
+ result->total_time = 0;
599
+ result->self_time = 0;
600
+ result->wait_time = 0;
601
+ result->parents = caller_table_create();
602
+ result->children = caller_table_create();
603
+ result->active_frame = 0;
604
+ result->base = result;
605
+
606
+ result->source_file = source_file;
607
+ result->line = line;
608
+ return result;
609
+ }
610
+
611
+ static void
612
+ prof_method_mark(prof_method_t *data)
613
+ {
614
+ rb_gc_mark(data->klass);
615
+ }
616
+
617
+ static void
618
+ prof_method_free(prof_method_t *data)
619
+ {
620
+ st_foreach(data->parents, free_call_infos, 0);
621
+ caller_table_free(data->parents);
622
+
623
+ st_foreach(data->children, free_call_infos, 0);
624
+ caller_table_free(data->children);
625
+
626
+ xfree(data);
627
+ }
628
+
629
+ static VALUE
630
+ prof_method_new(prof_method_t *result)
631
+ {
632
+ return Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
633
+ }
634
+
635
+ static prof_method_t *
636
+ get_prof_method(VALUE obj)
637
+ {
638
+ return (prof_method_t *) DATA_PTR(obj);
639
+ }
640
+
641
+ /* call-seq:
642
+ called -> int
643
+
644
+ Returns the number of times this method was called. */
645
+ static VALUE
646
+ prof_method_called(VALUE self)
647
+ {
648
+ prof_method_t *result = get_prof_method(self);
649
+
650
+ return INT2NUM(result->called);
651
+ }
652
+
653
+
654
+ /* call-seq:
655
+ total_time -> float
656
+
657
+ Returns the total amount of time spent in this method and its children. */
658
+ static VALUE
659
+ prof_method_total_time(VALUE self)
660
+ {
661
+ prof_method_t *result = get_prof_method(self);
662
+
663
+ return rb_float_new(convert_measurement(result->total_time));
664
+ }
665
+
666
+ /* call-seq:
667
+ self_time -> float
668
+
669
+ Returns the total amount of time spent in this method. */
670
+ static VALUE
671
+ prof_method_self_time(VALUE self)
672
+ {
673
+ prof_method_t *result = get_prof_method(self);
674
+
675
+ return rb_float_new(convert_measurement(result->self_time));
676
+ }
677
+
678
+ /* call-seq:
679
+ wait_time -> float
680
+
681
+ Returns the total amount of time this method waited for other threads. */
682
+ static VALUE
683
+ prof_method_wait_time(VALUE self)
684
+ {
685
+ prof_method_t *result = get_prof_method(self);
686
+
687
+ return rb_float_new(convert_measurement(result->wait_time));
688
+ }
689
+
690
+ /* call-seq:
691
+ line_no -> int
692
+
693
+ returns the line number of the method */
694
+ static VALUE
695
+ prof_method_line(VALUE self)
696
+ {
697
+ return rb_int_new(get_prof_method(self)->line);
698
+ }
699
+
700
+ /* call-seq:
701
+ children_time -> float
702
+
703
+ Returns the total amount of time spent in this method's children. */
704
+ static VALUE
705
+ prof_method_children_time(VALUE self)
706
+ {
707
+ prof_method_t *result = get_prof_method(self);
708
+ prof_measure_t children_time = result->total_time - result->self_time - result->wait_time;
709
+ return rb_float_new(convert_measurement(children_time));
710
+ }
711
+
712
+ /* call-seq:
713
+ source_file => string
714
+
715
+ return the source file of the method
716
+ */
717
+ static VALUE prof_method_source_file(VALUE self)
718
+ {
719
+ const char* sf = get_prof_method(self)->source_file;
720
+ if(!sf)
721
+ {
722
+ return rb_str_new2("ruby_runtime");
723
+ }
724
+ else
725
+ {
726
+ return rb_str_new2(sf);
727
+ }
728
+ }
729
+
730
+
731
+ /* call-seq:
732
+ method_class -> klass
733
+
734
+ Returns the Ruby klass that owns this method. */
735
+ static VALUE
736
+ prof_method_klass(VALUE self)
737
+ {
738
+ prof_method_t *result = get_prof_method(self);
739
+
740
+ return result->klass;
741
+ }
742
+
743
+ /* call-seq:
744
+ method_id -> ID
745
+
746
+ Returns the id of this method. */
747
+ static VALUE
748
+ prof_method_id(VALUE self)
749
+ {
750
+ prof_method_t *result = get_prof_method(self);
751
+
752
+ return ID2SYM(result->mid);
753
+ }
754
+
755
+ /* call-seq:
756
+ klass_name -> string
757
+
758
+ Returns the name of this method's class. Singleton classes
759
+ will have the form <Object::Object>. */
760
+
761
+ static VALUE
762
+ prof_klass_name(VALUE self)
763
+ {
764
+ prof_method_t *method = get_prof_method(self);
765
+ return klass_name(method->klass);
766
+ }
767
+
768
+ /* call-seq:
769
+ method_name -> string
770
+
771
+ Returns the name of this method in the format Object#method. Singletons
772
+ methods will be returned in the format <Object::Object>#method.*/
773
+
774
+ static VALUE
775
+ prof_method_name(VALUE self)
776
+ {
777
+ prof_method_t *method = get_prof_method(self);
778
+ return method_name(method->mid, method->depth);
779
+ }
780
+
781
+ /* call-seq:
782
+ full_name -> string
783
+
784
+ Returns the full name of this method in the format Object#method.*/
785
+
786
+ static VALUE
787
+ prof_full_name(VALUE self)
788
+ {
789
+ prof_method_t *method = get_prof_method(self);
790
+ return full_name(method->klass, method->mid, method->depth);
791
+ }
792
+
793
+ /* call-seq:
794
+ called -> MethodInfo
795
+
796
+ For recursively called methods, returns the base method. Otherwise,
797
+ returns self. */
798
+ static VALUE
799
+ prof_method_base(VALUE self)
800
+ {
801
+ prof_method_t *method = get_prof_method(self);
802
+
803
+ if (method == method->base)
804
+ return self;
805
+ else
806
+ /* Target is a pointer to a method_info - so we have to be careful
807
+ about the GC. We will wrap the method_info but provide no
808
+ free method so the underlying object is not freed twice! */
809
+ return Data_Wrap_Struct(cMethodInfo, NULL, NULL, method->base);
810
+ }
811
+
812
+ static int
813
+ prof_method_collect_call_infos(st_data_t key, st_data_t value, st_data_t result)
814
+ {
815
+ /* Create a new Ruby CallInfo object and store it into the hash
816
+ keyed on the parent's name. We use the parent's name because
817
+ we want to see that printed out for child records in
818
+ a call graph. */
819
+ prof_call_info_t *call_info = (prof_call_info_t *) value;
820
+ VALUE arr = (VALUE) result;
821
+ rb_ary_push(arr, call_info_new(call_info));
822
+ return ST_CONTINUE;
823
+ }
824
+
825
+ /* call-seq:
826
+ children -> hash
827
+
828
+ Returns an array of call info objects of methods that this method
829
+ was called by (ie, parents).*/
830
+ static VALUE
831
+ prof_method_parents(VALUE self)
832
+ {
833
+ /* Returns an array of call info objects for this
834
+ method's callers (the methods this method called). */
835
+
836
+ VALUE children = rb_ary_new();
837
+ prof_method_t *result = get_prof_method(self);
838
+ st_foreach(result->parents, prof_method_collect_call_infos, children);
839
+ return children;
840
+ }
841
+
842
+
843
+ /* call-seq:
844
+ children -> hash
845
+
846
+ Returns an array of call info objects of methods that this method
847
+ called (ie, children).*/
848
+ static VALUE
849
+ prof_method_children(VALUE self)
850
+ {
851
+ /* Returns an array of call info objects for this
852
+ method's callees (the methods this method called). */
853
+
854
+ VALUE children = rb_ary_new();
855
+ prof_method_t *result = get_prof_method(self);
856
+ st_foreach(result->children, prof_method_collect_call_infos, children);
857
+ return children;
858
+ }
859
+
860
+ /* :nodoc: */
861
+ static VALUE
862
+ prof_method_cmp(VALUE self, VALUE other)
863
+ {
864
+ /* For call graphs we want to sort methods by
865
+ their total time, not self time. */
866
+ prof_method_t *x = get_prof_method(self);
867
+ prof_method_t *y = get_prof_method(other);
868
+
869
+ if (x->called == 0 && y->called == 0)
870
+ return INT2FIX(0);
871
+ else if (x->called == 0)
872
+ return INT2FIX(1);
873
+ else if (y->called == 0)
874
+ return INT2FIX(-1);
875
+ else
876
+ return rb_dbl_cmp(x->total_time, y->total_time);
877
+ }
878
+
879
+ static int
880
+ collect_methods(st_data_t key, st_data_t value, st_data_t result)
881
+ {
882
+ /* Called for each method stored in a thread's method table.
883
+ We want to store the method info information into an array.*/
884
+ VALUE methods = (VALUE) result;
885
+ prof_method_t *method = (prof_method_t *) value;
886
+ rb_ary_push(methods, prof_method_new(method));
887
+
888
+ return ST_CONTINUE;
889
+ }
890
+
891
+
892
+ /* ================ Thread Handling =================*/
893
+
894
+ /* ---- Keeps track of thread's stack and methods ---- */
895
+ static thread_data_t*
896
+ thread_data_create()
897
+ {
898
+ thread_data_t* result = ALLOC(thread_data_t);
899
+ result->stack = stack_create();
900
+ result->method_info_table = method_info_table_create();
901
+ result->last_switch = 0;
902
+ return result;
903
+ }
904
+
905
+ static void
906
+ thread_data_free(thread_data_t* thread_data)
907
+ {
908
+ stack_free(thread_data->stack);
909
+ method_info_table_free(thread_data->method_info_table);
910
+ xfree(thread_data);
911
+ }
912
+
913
+
914
+ /* ---- Hash, keyed on thread, that stores thread's stack
915
+ and methods---- */
916
+
917
+ static st_table *
918
+ threads_table_create()
919
+ {
920
+ return st_init_numtable();
921
+ }
922
+
923
+ static inline size_t
924
+ threads_table_insert(st_table *table, VALUE thread, thread_data_t *thread_data)
925
+ {
926
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
927
+ return st_insert(table, (st_data_t ) thread, (st_data_t) thread_data);
928
+ }
929
+
930
+ static inline thread_data_t *
931
+ threads_table_lookup(st_table *table, long thread_id)
932
+ {
933
+ thread_data_t* result;
934
+ st_data_t val;
935
+
936
+ /* Its too slow to key on the real thread id so just typecast thread instead. */
937
+ if (st_lookup(table, (st_data_t) thread_id, &val))
938
+ {
939
+ result = (thread_data_t *) val;
940
+ }
941
+ else
942
+ {
943
+ result = thread_data_create();
944
+ result->thread_id = thread_id;
945
+
946
+ /* Insert the table */
947
+ threads_table_insert(threads_tbl, thread_id, result);
948
+ }
949
+ return result;
950
+ }
951
+
952
+ static int
953
+ free_thread_data(st_data_t key, st_data_t value, st_data_t dummy)
954
+ {
955
+ thread_data_free((thread_data_t*)value);
956
+ return ST_CONTINUE;
957
+ }
958
+
959
+
960
+ static void
961
+ threads_table_free(st_table *table)
962
+ {
963
+ st_foreach(table, free_thread_data, 0);
964
+ st_free_table(table);
965
+ }
966
+
967
+
968
+ static int
969
+ collect_threads(st_data_t key, st_data_t value, st_data_t result)
970
+ {
971
+ /* Although threads are keyed on an id, that is actually a
972
+ pointer to the VALUE object of the thread. So its bogus.
973
+ However, in thread_data is the real thread id stored
974
+ as an int. */
975
+ thread_data_t* thread_data = (thread_data_t*) value;
976
+ VALUE threads_hash = (VALUE) result;
977
+
978
+ VALUE methods = rb_ary_new();
979
+
980
+ /* Now collect an array of all the called methods */
981
+ st_foreach(thread_data->method_info_table, collect_methods, methods);
982
+
983
+ /* Store the results in the threads hash keyed on the thread id. */
984
+ rb_hash_aset(threads_hash, ULONG2NUM(thread_data->thread_id), methods);
985
+
986
+ return ST_CONTINUE;
987
+ }
988
+
989
+
990
+ /* ================ Profiling =================*/
991
+ /* Copied from eval.c */
992
+ static char *
993
+ get_event_name(rb_event_flag_t event)
994
+ {
995
+ switch (event) {
996
+ case RUBY_EVENT_LINE:
997
+ return "line";
998
+ case RUBY_EVENT_CLASS:
999
+ return "class";
1000
+ case RUBY_EVENT_END:
1001
+ return "end";
1002
+ case RUBY_EVENT_CALL:
1003
+ return "call";
1004
+ case RUBY_EVENT_RETURN:
1005
+ return "return";
1006
+ case RUBY_EVENT_C_CALL:
1007
+ return "c-call";
1008
+ case RUBY_EVENT_C_RETURN:
1009
+ return "c-return";
1010
+ case RUBY_EVENT_RAISE:
1011
+ return "raise";
1012
+ default:
1013
+ return "unknown";
1014
+ }
1015
+ }
1016
+
1017
+ static void
1018
+ update_result(thread_data_t* thread_data,
1019
+ prof_measure_t total_time,
1020
+ prof_frame_t *parent_frame, prof_frame_t *child_frame)
1021
+ {
1022
+ prof_method_t *parent = NULL;
1023
+ prof_method_t *child = child_frame->method;
1024
+ prof_call_info_t *parent_call_info = NULL;
1025
+ prof_call_info_t *child_call_info = NULL;
1026
+
1027
+ prof_measure_t wait_time = child_frame->wait_time;
1028
+ prof_measure_t self_time = total_time - child_frame->child_time - wait_time;
1029
+
1030
+ /* Update information about the child (ie, the current method) */
1031
+ child->called++;
1032
+ child->total_time += total_time;
1033
+ child->self_time += self_time;
1034
+ child->wait_time += wait_time;
1035
+
1036
+ if (!parent_frame) return;
1037
+
1038
+ parent = parent_frame->method;
1039
+
1040
+ child_call_info = caller_table_lookup(parent->children, child->key);
1041
+ if (child_call_info == NULL)
1042
+ {
1043
+ child_call_info = call_info_create(child);
1044
+ caller_table_insert(parent->children, child->key, child_call_info);
1045
+ }
1046
+
1047
+ child_call_info->called++;
1048
+ child_call_info->total_time += total_time;
1049
+ child_call_info->self_time += self_time;
1050
+ child_call_info->wait_time += wait_time;
1051
+ child_call_info->line = parent_frame->line;
1052
+
1053
+ /* Update child's parent information */
1054
+ parent_call_info = caller_table_lookup(child->parents, parent->key);
1055
+ if (parent_call_info == NULL)
1056
+ {
1057
+ parent_call_info = call_info_create(parent);
1058
+ caller_table_insert(child->parents, parent->key, parent_call_info);
1059
+ }
1060
+
1061
+ parent_call_info->called++;
1062
+ parent_call_info->total_time += total_time;
1063
+ parent_call_info->self_time += self_time;
1064
+ parent_call_info->wait_time += wait_time;
1065
+ parent_call_info->line = (parent_frame ? parent_frame->line : 0);
1066
+
1067
+
1068
+ /* If the caller is the top of the stack, the merge in
1069
+ all the child results. We have to do this because
1070
+ the top method is never popped since sooner or later
1071
+ the user has to call RubyProf::stop.*/
1072
+
1073
+ if (stack_size(thread_data->stack) == 1)
1074
+ {
1075
+ parent->total_time += total_time;
1076
+ parent->wait_time += wait_time;
1077
+ }
1078
+ }
1079
+
1080
+
1081
+ #ifdef RUBY_VM
1082
+ static void
1083
+ prof_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
1084
+ #else
1085
+ static void
1086
+ prof_event_hook(rb_event_flag_t event, NODE *node, VALUE self, ID mid, VALUE klass)
1087
+ #endif
1088
+ {
1089
+
1090
+ VALUE thread;
1091
+ prof_measure_t now = 0;
1092
+ thread_data_t* thread_data = NULL;
1093
+ long thread_id = 0;
1094
+ prof_frame_t *frame = NULL;
1095
+ #ifdef RUBY_VM
1096
+
1097
+ if (event != RUBY_EVENT_C_CALL &&
1098
+ event != RUBY_EVENT_C_RETURN) {
1099
+ VALUE thread = rb_thread_current();
1100
+ rb_frame_method_id_and_class(&mid, &klass);
1101
+ }
1102
+ #endif
1103
+ /* This code is here for debug purposes - uncomment it out
1104
+ when debugging to see a print out of exactly what the
1105
+ profiler is tracing.
1106
+ {
1107
+ st_data_t key = 0;
1108
+ static unsigned long last_thread_id = 0;
1109
+
1110
+ VALUE thread = rb_thread_current();
1111
+ unsigned long thread_id = get_thread_id(thread);
1112
+ char* class_name = rb_obj_classname(klass);
1113
+ char* method_name = rb_id2name(mid);
1114
+ char* source_file = node ? node->nd_file : 0;
1115
+ unsigned int source_line = node ? nd_line(node) : 0;
1116
+ char* event_name = get_event_name(event);
1117
+
1118
+ if (last_thread_id != thread_id)
1119
+ printf("\n");
1120
+
1121
+ if (klass != 0)
1122
+ klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1123
+ key = method_key(klass, mid, 0);
1124
+ printf("%2u: %-8s :%2d %s#%s (%u)\n",
1125
+ thread_id, event_name, source_line, class_name, method_name, key);
1126
+ last_thread_id = thread_id;
1127
+ } */
1128
+
1129
+ /* Special case - skip any methods from the mProf
1130
+ module, such as Prof.stop, since they clutter
1131
+ the results but aren't important to them results. */
1132
+ if (self == mProf) return;
1133
+
1134
+ /* Get current measurement*/
1135
+ now = get_measurement();
1136
+
1137
+ /* Get the current thread information. */
1138
+ thread = rb_thread_current();
1139
+ thread_id = get_thread_id(thread);
1140
+
1141
+ /* Was there a context switch? */
1142
+ if (!last_thread_data || last_thread_data->thread_id != thread_id)
1143
+ {
1144
+ prof_measure_t wait_time = 0;
1145
+
1146
+ /* Get new thread information. */
1147
+ thread_data = threads_table_lookup(threads_tbl, thread_id);
1148
+
1149
+ /* How long has this thread been waiting? */
1150
+ wait_time = now - thread_data->last_switch;
1151
+ thread_data->last_switch = 0;
1152
+
1153
+ /* Get the frame at the top of the stack. This may represent
1154
+ the current method (EVENT_LINE, EVENT_RETURN) or the
1155
+ previous method (EVENT_CALL).*/
1156
+ frame = stack_peek(thread_data->stack);
1157
+
1158
+ if (frame)
1159
+ frame->wait_time += wait_time;
1160
+
1161
+ /* Save on the last thread the time of the context switch
1162
+ and reset this thread's last context switch to 0.*/
1163
+ if (last_thread_data)
1164
+ last_thread_data->last_switch = now;
1165
+
1166
+ last_thread_data = thread_data;
1167
+ }
1168
+ else
1169
+ {
1170
+ thread_data = last_thread_data;
1171
+ frame = stack_peek(thread_data->stack);
1172
+ }
1173
+
1174
+ switch (event) {
1175
+ case RUBY_EVENT_LINE:
1176
+ {
1177
+ /* Keep track of the current line number in this method. When
1178
+ a new method is called, we know what line number it was
1179
+ called from. */
1180
+ if (frame)
1181
+ {
1182
+ #ifdef RUBY_VM
1183
+ frame->line = rb_sourceline();
1184
+ #else
1185
+ if (node)
1186
+ frame->line = nd_line(node);
1187
+ #endif
1188
+ break;
1189
+ }
1190
+ /* If we get here there was no frame, which means this is
1191
+ the first method seen for this thread, so fall through
1192
+ to below to create it. */
1193
+ }
1194
+ case RUBY_EVENT_CALL:
1195
+ case RUBY_EVENT_C_CALL:
1196
+ {
1197
+ int depth = 0;
1198
+ st_data_t key = 0;
1199
+ prof_method_t *method = NULL;
1200
+
1201
+ /* Is this an include for a module? If so get the actual
1202
+ module class since we want to combine all profiling
1203
+ results for that module. */
1204
+
1205
+ if (klass != 0)
1206
+ klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
1207
+
1208
+ key = method_key(klass, mid, 0);
1209
+
1210
+ method = method_info_table_lookup(thread_data->method_info_table, key);
1211
+
1212
+ if (!method)
1213
+ {
1214
+ const char* source_file = rb_sourcefile();
1215
+ int line = rb_sourceline();
1216
+
1217
+ /* Line numbers are not accurate for c method calls */
1218
+ if (event == RUBY_EVENT_C_CALL)
1219
+ {
1220
+ line = 0;
1221
+ source_file = NULL;
1222
+ }
1223
+
1224
+ method = prof_method_create(key, klass, mid, depth, source_file, line);
1225
+ method_info_table_insert(thread_data->method_info_table, key, method);
1226
+ }
1227
+
1228
+ depth = method->active_frame;
1229
+ method->active_frame++;
1230
+
1231
+ if (depth > 0)
1232
+ {
1233
+ prof_method_t *base_method = method;
1234
+ key = method_key(klass, mid, depth);
1235
+ method = method_info_table_lookup(thread_data->method_info_table, key);
1236
+
1237
+ if (!method)
1238
+ {
1239
+ const char* source_file = rb_sourcefile();
1240
+ int line = rb_sourceline();
1241
+
1242
+ /* Line numbers are not accurate for c method calls */
1243
+ if (event == RUBY_EVENT_C_CALL)
1244
+ {
1245
+ line = 0;
1246
+ source_file = NULL;
1247
+ }
1248
+
1249
+ method = prof_method_create(key, klass, mid, depth, source_file, line);
1250
+ method->base = base_method;
1251
+ method_info_table_insert(thread_data->method_info_table, key, method);
1252
+ }
1253
+ }
1254
+
1255
+ /* Push a new frame onto the stack */
1256
+ frame = stack_push(thread_data->stack);
1257
+ frame->method = method;
1258
+ frame->start_time = now;
1259
+ frame->wait_time = 0;
1260
+ frame->child_time = 0;
1261
+ frame->line = rb_sourceline();
1262
+
1263
+ break;
1264
+ }
1265
+ case RUBY_EVENT_RETURN:
1266
+ case RUBY_EVENT_C_RETURN:
1267
+ {
1268
+ prof_frame_t* caller_frame = NULL;
1269
+
1270
+ prof_measure_t total_time;
1271
+
1272
+ frame = stack_pop(thread_data->stack);
1273
+ caller_frame = stack_peek(thread_data->stack);
1274
+
1275
+ /* Frame can be null. This can happen if RubProf.start is called from
1276
+ a method that exits. And it can happen if an exception is raised
1277
+ in code that is being profiled and the stack unwinds (RubProf is
1278
+ not notified of that by the ruby runtime. */
1279
+ if (frame == NULL) return;
1280
+
1281
+ total_time = now - frame->start_time;
1282
+
1283
+ if (caller_frame)
1284
+ {
1285
+ caller_frame->child_time += total_time;
1286
+ }
1287
+
1288
+ frame->method->base->active_frame--;
1289
+
1290
+ update_result(thread_data, total_time, caller_frame, frame);
1291
+ break;
1292
+ }
1293
+ }
1294
+ }
1295
+
1296
+
1297
+ /* ======== ProfResult ============== */
1298
+
1299
+ /* Document-class: RubyProf::Result
1300
+ The RubyProf::Result class is used to store the results of a
1301
+ profiling run. And instace of the class is returned from
1302
+ the methods RubyProf#stop and RubyProf#profile.
1303
+
1304
+ RubyProf::Result has one field, called threads, which is a hash
1305
+ table keyed on thread ID. For each thread id, the hash table
1306
+ stores another hash table that contains profiling information
1307
+ for each method called during the threads execution. That
1308
+ hash table is keyed on method name and contains
1309
+ RubyProf::MethodInfo objects. */
1310
+
1311
+
1312
+ static void
1313
+ prof_result_mark(prof_result_t *prof_result)
1314
+ {
1315
+ VALUE threads = prof_result->threads;
1316
+ rb_gc_mark(threads);
1317
+ }
1318
+
1319
+ static void
1320
+ prof_result_free(prof_result_t *prof_result)
1321
+ {
1322
+ prof_result->threads = Qnil;
1323
+ xfree(prof_result);
1324
+ }
1325
+
1326
+ static VALUE
1327
+ prof_result_new()
1328
+ {
1329
+ prof_result_t *prof_result = ALLOC(prof_result_t);
1330
+
1331
+ /* Wrap threads in Ruby regular Ruby hash table. */
1332
+ prof_result->threads = rb_hash_new();
1333
+ st_foreach(threads_tbl, collect_threads, prof_result->threads);
1334
+
1335
+ return Data_Wrap_Struct(cResult, prof_result_mark, prof_result_free, prof_result);
1336
+ }
1337
+
1338
+
1339
+ static prof_result_t *
1340
+ get_prof_result(VALUE obj)
1341
+ {
1342
+ if (BUILTIN_TYPE(obj) != T_DATA ||
1343
+ RDATA(obj)->dfree != (RUBY_DATA_FUNC) prof_result_free)
1344
+ {
1345
+ /* Should never happen */
1346
+ rb_raise(rb_eTypeError, "wrong result object");
1347
+ }
1348
+ return (prof_result_t *) DATA_PTR(obj);
1349
+ }
1350
+
1351
+ /* call-seq:
1352
+ threads -> Hash
1353
+
1354
+ Returns a hash table keyed on thread ID. For each thread id,
1355
+ the hash table stores another hash table that contains profiling
1356
+ information for each method called during the threads execution.
1357
+ That hash table is keyed on method name and contains
1358
+ RubyProf::MethodInfo objects. */
1359
+ static VALUE
1360
+ prof_result_threads(VALUE self)
1361
+ {
1362
+ prof_result_t *prof_result = get_prof_result(self);
1363
+ return prof_result->threads;
1364
+ }
1365
+
1366
+
1367
+
1368
+ /* call-seq:
1369
+ measure_mode -> measure_mode
1370
+
1371
+ Returns what ruby-prof is measuring. Valid values include:
1372
+
1373
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1374
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1375
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1376
+ *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1377
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.*/
1378
+ static VALUE
1379
+ prof_get_measure_mode(VALUE self)
1380
+ {
1381
+ return INT2NUM(measure_mode);
1382
+ }
1383
+
1384
+ /* call-seq:
1385
+ measure_mode=value -> void
1386
+
1387
+ Specifies what ruby-prof should measure. Valid values include:
1388
+
1389
+ *RubyProf::PROCESS_TIME - Measure process time. This is default. It is implemented using the clock functions in the C Runtime library.
1390
+ *RubyProf::WALL_TIME - Measure wall time using gettimeofday on Linx and GetLocalTime on Windows
1391
+ *RubyProf::CPU_TIME - Measure time using the CPU clock counter. This mode is only supported on Pentium or PowerPC platforms.
1392
+ *RubyProf::ALLOCATIONS - Measure object allocations. This requires a patched Ruby interpreter.
1393
+ *RubyProf::MEMORY - Measure memory size. This requires a patched Ruby interpreter.*/
1394
+ static VALUE
1395
+ prof_set_measure_mode(VALUE self, VALUE val)
1396
+ {
1397
+ long mode = NUM2LONG(val);
1398
+
1399
+ if (threads_tbl)
1400
+ {
1401
+ rb_raise(rb_eRuntimeError, "can't set measure_mode while profiling");
1402
+ }
1403
+
1404
+ switch (mode) {
1405
+ case MEASURE_PROCESS_TIME:
1406
+ get_measurement = measure_process_time;
1407
+ convert_measurement = convert_process_time;
1408
+ break;
1409
+
1410
+ case MEASURE_WALL_TIME:
1411
+ get_measurement = measure_wall_time;
1412
+ convert_measurement = convert_wall_time;
1413
+ break;
1414
+
1415
+ #if defined(MEASURE_CPU_TIME)
1416
+ case MEASURE_CPU_TIME:
1417
+ if (cpu_frequency == 0)
1418
+ cpu_frequency = measure_cpu_time();
1419
+ get_measurement = measure_cpu_time;
1420
+ convert_measurement = convert_cpu_time;
1421
+ break;
1422
+ #endif
1423
+
1424
+ #if defined(MEASURE_ALLOCATIONS)
1425
+ case MEASURE_ALLOCATIONS:
1426
+ get_measurement = measure_allocations;
1427
+ convert_measurement = convert_allocations;
1428
+ break;
1429
+ #endif
1430
+
1431
+ #if defined(MEASURE_MEMORY)
1432
+ case MEASURE_MEMORY:
1433
+ get_measurement = measure_memory;
1434
+ convert_measurement = convert_memory;
1435
+ break;
1436
+ #endif
1437
+
1438
+ default:
1439
+ rb_raise(rb_eArgError, "invalid mode: %d", mode);
1440
+ break;
1441
+ }
1442
+
1443
+ measure_mode = mode;
1444
+ return val;
1445
+ }
1446
+
1447
+ /* ========= Profiling ============= */
1448
+
1449
+
1450
+ /* call-seq:
1451
+ running? -> boolean
1452
+
1453
+ Returns whether a profile is currently running.*/
1454
+ static VALUE
1455
+ prof_running(VALUE self)
1456
+ {
1457
+ if (threads_tbl != NULL)
1458
+ return Qtrue;
1459
+ else
1460
+ return Qfalse;
1461
+ }
1462
+
1463
+ /* call-seq:
1464
+ start -> void
1465
+
1466
+ Starts recording profile data.*/
1467
+ static VALUE
1468
+ prof_start(VALUE self)
1469
+ {
1470
+ if (threads_tbl != NULL)
1471
+ {
1472
+ rb_raise(rb_eRuntimeError, "RubyProf.start was already called");
1473
+ }
1474
+
1475
+ /* Setup globals */
1476
+ last_thread_data = NULL;
1477
+ threads_tbl = threads_table_create();
1478
+
1479
+ #ifdef RUBY_VM
1480
+ rb_add_event_hook(prof_event_hook,
1481
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1482
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1483
+ | RUBY_EVENT_LINE, Qnil);
1484
+ #else
1485
+ rb_add_event_hook(prof_event_hook,
1486
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
1487
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN
1488
+ | RUBY_EVENT_LINE);
1489
+ #endif
1490
+
1491
+ #if defined(MEASURE_MEMORY)
1492
+ rb_gc_enable_stats();
1493
+ #endif
1494
+
1495
+ return Qnil;
1496
+ }
1497
+
1498
+
1499
+ /* call-seq:
1500
+ stop -> RubyProf::Result
1501
+
1502
+ Stops collecting profile data and returns a RubyProf::Result object. */
1503
+ static VALUE
1504
+ prof_stop(VALUE self)
1505
+ {
1506
+ #if defined(MEASURE_MEMORY)
1507
+ rb_gc_disable_stats();
1508
+ #endif
1509
+
1510
+ VALUE result = Qnil;
1511
+
1512
+ if (threads_tbl == NULL)
1513
+ {
1514
+ rb_raise(rb_eRuntimeError, "RubyProf is not running.");
1515
+ }
1516
+
1517
+ /* Now unregister from event */
1518
+ rb_remove_event_hook(prof_event_hook);
1519
+
1520
+ /* Create the result */
1521
+ result = prof_result_new();
1522
+
1523
+ /* Unset the last_thread_data (very important!)
1524
+ and the threads table */
1525
+ last_thread_data = NULL;
1526
+ threads_table_free(threads_tbl);
1527
+ threads_tbl = NULL;
1528
+
1529
+ return result;
1530
+ }
1531
+
1532
+
1533
+ /* call-seq:
1534
+ profile {block} -> RubyProf::Result
1535
+
1536
+ Profiles the specified block and returns a RubyProf::Result object. */
1537
+ static VALUE
1538
+ prof_profile(VALUE self)
1539
+ {
1540
+ if (!rb_block_given_p())
1541
+ {
1542
+ rb_raise(rb_eArgError, "A block must be provided to the profile method.");
1543
+ }
1544
+
1545
+ prof_start(self);
1546
+ rb_yield(Qnil);
1547
+ return prof_stop(self);
1548
+ }
1549
+
1550
+
1551
+ #if defined(_WIN32)
1552
+ __declspec(dllexport)
1553
+ #endif
1554
+ void
1555
+
1556
+ Init_ruby_prof()
1557
+ {
1558
+ mProf = rb_define_module("RubyProf");
1559
+ rb_define_const(mProf, "VERSION", rb_str_new2(PROF_VERSION));
1560
+ rb_define_module_function(mProf, "start", prof_start, 0);
1561
+ rb_define_module_function(mProf, "stop", prof_stop, 0);
1562
+ rb_define_module_function(mProf, "running?", prof_running, 0);
1563
+ rb_define_module_function(mProf, "profile", prof_profile, 0);
1564
+
1565
+ rb_define_singleton_method(mProf, "measure_mode", prof_get_measure_mode, 0);
1566
+ rb_define_singleton_method(mProf, "measure_mode=", prof_set_measure_mode, 1);
1567
+
1568
+ rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
1569
+ rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
1570
+ rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
1571
+
1572
+ #ifndef MEASURE_CPU_TIME
1573
+ rb_define_const(mProf, "CPU_TIME", Qnil);
1574
+ #else
1575
+ rb_define_const(mProf, "CPU_TIME", INT2NUM(MEASURE_CPU_TIME));
1576
+ rb_define_singleton_method(mProf, "cpu_frequency", prof_get_cpu_frequency, 0); /* in measure_cpu_time.h */
1577
+ rb_define_singleton_method(mProf, "cpu_frequency=", prof_set_cpu_frequency, 1); /* in measure_cpu_time.h */
1578
+ #endif
1579
+
1580
+ #ifndef MEASURE_ALLOCATIONS
1581
+ rb_define_const(mProf, "ALLOCATIONS", Qnil);
1582
+ #else
1583
+ rb_define_const(mProf, "ALLOCATIONS", INT2NUM(MEASURE_ALLOCATIONS));
1584
+ #endif
1585
+
1586
+ #ifndef MEASURE_MEMORY
1587
+ rb_define_const(mProf, "MEMORY", Qnil);
1588
+ #else
1589
+ rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
1590
+ #endif
1591
+
1592
+ cResult = rb_define_class_under(mProf, "Result", rb_cObject);
1593
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1594
+ rb_define_method(cResult, "threads", prof_result_threads, 0);
1595
+
1596
+ cMethodInfo = rb_define_class_under(mProf, "MethodInfo", rb_cObject);
1597
+ rb_include_module(cMethodInfo, rb_mComparable);
1598
+ rb_undef_method(CLASS_OF(cMethodInfo), "new");
1599
+
1600
+ rb_define_method(cMethodInfo, "klass", prof_method_klass, 0);
1601
+ rb_define_method(cMethodInfo, "klass_name", prof_klass_name, 0);
1602
+ rb_define_method(cMethodInfo, "method_name", prof_method_name, 0);
1603
+ rb_define_method(cMethodInfo, "full_name", prof_full_name, 0);
1604
+ rb_define_method(cMethodInfo, "method_id", prof_method_id, 0);
1605
+ rb_define_method(cMethodInfo, "base", prof_method_base, 0);
1606
+
1607
+ rb_define_method(cMethodInfo, "parents", prof_method_parents, 0);
1608
+ rb_define_method(cMethodInfo, "children", prof_method_children, 0);
1609
+ rb_define_method(cMethodInfo, "<=>", prof_method_cmp, 1);
1610
+ rb_define_method(cMethodInfo, "source_file", prof_method_source_file,0);
1611
+ rb_define_method(cMethodInfo, "line", prof_method_line, 0);
1612
+ rb_define_method(cMethodInfo, "called", prof_method_called, 0);
1613
+ rb_define_method(cMethodInfo, "total_time", prof_method_total_time, 0);
1614
+ rb_define_method(cMethodInfo, "self_time", prof_method_self_time, 0);
1615
+ rb_define_method(cMethodInfo, "wait_time", prof_method_wait_time, 0);
1616
+ rb_define_method(cMethodInfo, "children_time", prof_method_children_time, 0);
1617
+
1618
+ cCallInfo = rb_define_class_under(mProf, "CallInfo", rb_cObject);
1619
+ rb_undef_method(CLASS_OF(cCallInfo), "new");
1620
+ rb_define_method(cCallInfo, "target", call_info_target, 0);
1621
+ rb_define_method(cCallInfo, "called", call_info_called, 0);
1622
+ rb_define_method(cCallInfo, "total_time", call_info_total_time, 0);
1623
+ rb_define_method(cCallInfo, "self_time", call_info_self_time, 0);
1624
+ rb_define_method(cCallInfo, "wait_time", call_info_wait_time, 0);
1625
+ rb_define_method(cCallInfo, "line", call_info_line, 0);
1626
+ rb_define_method(cCallInfo, "children_time", call_info_children_time, 0);
1627
+ }
1628
+