ruby-prof 0.6.0-x86-mswin32-60

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