ruby-prof 0.11.0.rc1 → 0.11.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGES +20 -5
  2. data/README.rdoc +10 -3
  3. data/ext/ruby_prof/rp_call_info.c +108 -79
  4. data/ext/ruby_prof/rp_call_info.h +1 -0
  5. data/ext/ruby_prof/rp_measure_cpu_time.c +111 -111
  6. data/ext/ruby_prof/rp_measure_gc_runs.c +1 -1
  7. data/ext/ruby_prof/rp_measure_memory.c +1 -1
  8. data/ext/ruby_prof/rp_measure_process_time.c +71 -71
  9. data/ext/ruby_prof/rp_measure_wall_time.c +1 -1
  10. data/ext/ruby_prof/rp_method.c +143 -73
  11. data/ext/ruby_prof/rp_method.h +7 -4
  12. data/ext/ruby_prof/rp_stack.c +16 -1
  13. data/ext/ruby_prof/rp_stack.h +4 -1
  14. data/ext/ruby_prof/rp_thread.c +165 -35
  15. data/ext/ruby_prof/rp_thread.h +8 -2
  16. data/ext/ruby_prof/ruby_prof.c +164 -171
  17. data/ext/ruby_prof/ruby_prof.h +53 -54
  18. data/ext/ruby_prof/vc/ruby_prof.sln +26 -0
  19. data/ext/ruby_prof/vc/ruby_prof.vcxproj +109 -0
  20. data/ext/ruby_prof/vc/ruby_prof_18.vcxproj +105 -0
  21. data/lib/ruby-prof/aggregate_call_info.rb +9 -7
  22. data/lib/ruby-prof/call_info.rb +2 -27
  23. data/lib/ruby-prof/call_info_visitor.rb +42 -0
  24. data/lib/ruby-prof/{empty.png → images/empty.png} +0 -0
  25. data/lib/ruby-prof/{minus.png → images/minus.png} +0 -0
  26. data/lib/ruby-prof/{plus.png → images/plus.png} +0 -0
  27. data/lib/ruby-prof/method_info.rb +13 -15
  28. data/lib/ruby-prof/{abstract_printer.rb → printers/abstract_printer.rb} +36 -2
  29. data/lib/ruby-prof/printers/call_info_printer.rb +40 -0
  30. data/lib/ruby-prof/{call_stack_printer.rb → printers/call_stack_printer.rb} +11 -16
  31. data/lib/ruby-prof/{call_tree_printer.rb → printers/call_tree_printer.rb} +4 -4
  32. data/lib/ruby-prof/{dot_printer.rb → printers/dot_printer.rb} +11 -31
  33. data/lib/ruby-prof/{flat_printer.rb → printers/flat_printer.rb} +26 -35
  34. data/lib/ruby-prof/{flat_printer_with_line_numbers.rb → printers/flat_printer_with_line_numbers.rb} +14 -25
  35. data/lib/ruby-prof/printers/graph_html_printer.rb +248 -0
  36. data/lib/ruby-prof/{graph_printer.rb → printers/graph_printer.rb} +31 -73
  37. data/lib/ruby-prof/{multi_printer.rb → printers/multi_printer.rb} +0 -0
  38. data/lib/ruby-prof/profile.rb +27 -22
  39. data/lib/ruby-prof/rack.rb +22 -12
  40. data/ruby-prof.gemspec +58 -0
  41. data/test/aggregate_test.rb +6 -6
  42. data/test/call_info_visitor_test.rb +31 -0
  43. data/test/duplicate_names_test.rb +1 -1
  44. data/test/dynamic_method_test.rb +1 -1
  45. data/test/enumerable_test.rb +1 -1
  46. data/test/exclude_threads_test.rb +2 -2
  47. data/test/gc_test.rb +35 -0
  48. data/test/line_number_test.rb +2 -2
  49. data/test/measure_cpu_time_test.rb +5 -5
  50. data/test/measure_process_time_test.rb +5 -5
  51. data/test/measure_wall_time_test.rb +5 -5
  52. data/test/method_elimination_test.rb +3 -3
  53. data/test/module_test.rb +1 -1
  54. data/test/no_method_class_test.rb +1 -1
  55. data/test/printers_test.rb +16 -8
  56. data/test/recursive_test.rb +115 -91
  57. data/test/stack_test.rb +1 -1
  58. data/test/start_stop_test.rb +13 -13
  59. data/test/summarize_test.rb +48 -0
  60. data/test/test_suite.rb +1 -0
  61. data/test/thread_test.rb +16 -12
  62. data/test/unique_call_path_test.rb +10 -10
  63. metadata +64 -85
  64. data/lib/1.8/ruby_prof.so +0 -0
  65. data/lib/1.9/ruby_prof.exp +0 -0
  66. data/lib/1.9/ruby_prof.ilk +0 -0
  67. data/lib/1.9/ruby_prof.lib +0 -0
  68. data/lib/1.9/ruby_prof.pdb +0 -0
  69. data/lib/1.9/ruby_prof.so +0 -0
  70. data/lib/ruby-prof.rb +0 -70
  71. data/lib/ruby-prof/graph_html_printer.rb +0 -286
  72. data/lib/ruby-prof/symbol_to_proc.rb +0 -10
  73. data/lib/ruby_prof.exp +0 -0
  74. data/lib/ruby_prof.ilk +0 -0
  75. data/lib/ruby_prof.lib +0 -0
  76. data/lib/ruby_prof.pdb +0 -0
  77. data/lib/ruby_prof.so +0 -0
  78. data/lib/unprof.rb +0 -10
  79. data/test/do_nothing.rb +0 -0
@@ -81,7 +81,7 @@ void rp_init_measure_gc_runs()
81
81
  {
82
82
  rb_define_const(mProf, "GC_RUNS", INT2NUM(MEASURE_GC_RUNS));
83
83
  rb_define_const(mProf, "GC_RUNS_ENABLED", MEASURE_GC_RUNS_ENABLED);
84
-
84
+
85
85
  cMeasureGcRuns = rb_define_class_under(mMeasure, "GcRuns", rb_cObject);
86
86
  rb_define_singleton_method(cMeasureGcRuns, "measure", prof_measure_gc_runs, 0);
87
87
  }
@@ -74,7 +74,7 @@ prof_measure_memory(VALUE self)
74
74
  void rp_init_measure_memory()
75
75
  {
76
76
  rb_define_const(mProf, "MEMORY", INT2NUM(MEASURE_MEMORY));
77
- rb_define_const(mProf, "MEMORY_ENABLED", MEASURE_MEMORY_ENABLED);
77
+ rb_define_const(mProf, "MEMORY_ENABLED", MEASURE_MEMORY_ENABLED);
78
78
 
79
79
  cMeasureMemory = rb_define_class_under(mMeasure, "Memory", rb_cObject);
80
80
  rb_define_singleton_method(cMeasureMemory, "measure", prof_measure_memory, 0);
@@ -1,71 +1,71 @@
1
- /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #include "ruby_prof.h"
5
- #include <time.h>
6
-
7
- static VALUE cMeasureProcessTime;
8
-
9
- static double
10
- measure_process_time()
11
- {
12
- #if defined(__linux__)
13
- struct timespec clock;
14
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
15
- return (clock.tv_sec * 1000000000 + clock.tv_nsec) / 1000000000.0;
16
- #elif defined(_win32)
17
- FILETIME createTime;
18
- FILETIME exitTime;
19
- FILETIME sysTime;
20
- FILETIME cpuTime;
21
-
22
- ULARGE_INTEGER sysTimeInt;
23
- ULARGE_INTEGER cpuTimeInt;
24
- ULONGLONG totalTime;
25
-
26
- GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
27
-
28
- /* Doing this based on MSFT's recommendation in the FILETIME structure documentation at
29
- http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx*/
30
-
31
- sysTimeInt.LowPart = sysTime.dwLowDateTime;
32
- sysTimeInt.HighPart = sysTime.dwHighDateTime;
33
- cpuTimeInt.LowPart = cpuTime.dwLowDateTime;
34
- cpuTimeInt.HighPart = cpuTime.dwHighDateTime;
35
-
36
- totalTime = sysTimeInt.QuadPart + cpuTimeInt.QuadPart;
37
-
38
- // Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
39
- return totalTime / 10000000.0;
40
- #else
41
- return ((double)clock()) / CLOCKS_PER_SEC;
42
- #endif
43
- }
44
-
45
- /* call-seq:
46
- measure_process_time -> float
47
-
48
- Returns the process time.*/
49
- static VALUE
50
- prof_measure_process_time(VALUE self)
51
- {
52
- return rb_float_new(measure_process_time());
53
- }
54
-
55
- prof_measurer_t* prof_measurer_process_time()
56
- {
57
- prof_measurer_t* measure = ALLOC(prof_measurer_t);
58
- measure->measure = measure_process_time;
59
- return measure;
60
- }
61
-
62
-
63
- void rp_init_measure_process_time()
64
- {
65
- rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
66
- rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
67
- rb_define_const(mProf, "PROCESS_TIME_ENABLED", Qtrue);
68
-
69
- cMeasureProcessTime = rb_define_class_under(mMeasure, "ProcessTime", rb_cObject);
70
- rb_define_singleton_method(cMeasureProcessTime, "measure", prof_measure_process_time, 0);
71
- }
1
+ /* Copyright (C) 2005-2011 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #include "ruby_prof.h"
5
+ #include <time.h>
6
+
7
+ static VALUE cMeasureProcessTime;
8
+
9
+ static double
10
+ measure_process_time()
11
+ {
12
+ #if defined(__linux__)
13
+ struct timespec clock;
14
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID , &clock);
15
+ return (clock.tv_sec * 1000000000 + clock.tv_nsec) / 1000000000.0;
16
+ #elif defined(_win32)
17
+ FILETIME createTime;
18
+ FILETIME exitTime;
19
+ FILETIME sysTime;
20
+ FILETIME cpuTime;
21
+
22
+ ULARGE_INTEGER sysTimeInt;
23
+ ULARGE_INTEGER cpuTimeInt;
24
+ ULONGLONG totalTime;
25
+
26
+ GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &cpuTime);
27
+
28
+ /* Doing this based on MSFT's recommendation in the FILETIME structure documentation at
29
+ http://msdn.microsoft.com/en-us/library/ms724284%28VS.85%29.aspx*/
30
+
31
+ sysTimeInt.LowPart = sysTime.dwLowDateTime;
32
+ sysTimeInt.HighPart = sysTime.dwHighDateTime;
33
+ cpuTimeInt.LowPart = cpuTime.dwLowDateTime;
34
+ cpuTimeInt.HighPart = cpuTime.dwHighDateTime;
35
+
36
+ totalTime = sysTimeInt.QuadPart + cpuTimeInt.QuadPart;
37
+
38
+ // Times are in 100-nanosecond time units. So instead of 10-9 use 10-7
39
+ return totalTime / 10000000.0;
40
+ #else
41
+ return ((double)clock()) / CLOCKS_PER_SEC;
42
+ #endif
43
+ }
44
+
45
+ /* call-seq:
46
+ measure_process_time -> float
47
+
48
+ Returns the process time.*/
49
+ static VALUE
50
+ prof_measure_process_time(VALUE self)
51
+ {
52
+ return rb_float_new(measure_process_time());
53
+ }
54
+
55
+ prof_measurer_t* prof_measurer_process_time()
56
+ {
57
+ prof_measurer_t* measure = ALLOC(prof_measurer_t);
58
+ measure->measure = measure_process_time;
59
+ return measure;
60
+ }
61
+
62
+
63
+ void rp_init_measure_process_time()
64
+ {
65
+ rb_define_const(mProf, "CLOCKS_PER_SEC", INT2NUM(CLOCKS_PER_SEC));
66
+ rb_define_const(mProf, "PROCESS_TIME", INT2NUM(MEASURE_PROCESS_TIME));
67
+ rb_define_const(mProf, "PROCESS_TIME_ENABLED", Qtrue);
68
+
69
+ cMeasureProcessTime = rb_define_class_under(mMeasure, "ProcessTime", rb_cObject);
70
+ rb_define_singleton_method(cMeasureProcessTime, "measure", prof_measure_process_time, 0);
71
+ }
@@ -37,6 +37,6 @@ void rp_init_measure_wall_time()
37
37
  rb_define_const(mProf, "WALL_TIME", INT2NUM(MEASURE_WALL_TIME));
38
38
  rb_define_const(mProf, "WALL_TIME_ENABLED", Qtrue);
39
39
 
40
- cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
40
+ cMeasureWallTime = rb_define_class_under(mMeasure, "WallTime", rb_cObject);
41
41
  rb_define_singleton_method(cMeasureWallTime, "measure", prof_measure_wall_time, 0);
42
42
  }
@@ -118,6 +118,129 @@ full_name(VALUE klass, ID mid)
118
118
  return result;
119
119
  }
120
120
 
121
+ void
122
+ method_key(prof_method_key_t* key, VALUE klass, ID mid)
123
+ {
124
+ /* Is this an include for a module? If so get the actual
125
+ module class since we want to combine all profiling
126
+ results for that module. */
127
+ if (klass != 0)
128
+ klass = (BUILTIN_TYPE(klass) == T_ICLASS ? RBASIC(klass)->klass : klass);
129
+
130
+ key->klass = klass;
131
+ key->mid = mid;
132
+ key->key = (klass << 4) + (mid << 2);
133
+ }
134
+
135
+ /* ================ prof_method_t =================*/
136
+ prof_method_t*
137
+ prof_method_create(VALUE klass, ID mid, const char* source_file, int line)
138
+ {
139
+ prof_method_t *result = ALLOC(prof_method_t);
140
+ result->object = Qnil;
141
+ result->call_infos = prof_call_infos_create();
142
+ result->call_infos2 = Qnil;
143
+
144
+ result->key = ALLOC(prof_method_key_t);
145
+ method_key(result->key, klass, mid);
146
+
147
+ //result->call_info_table = call_info_table_create();
148
+
149
+ if (source_file != NULL)
150
+ {
151
+ size_t len = strlen(source_file) + 1;
152
+ char *buffer = ALLOC_N(char, len);
153
+
154
+ MEMCPY(buffer, source_file, char, len);
155
+ result->source_file = buffer;
156
+ }
157
+ else
158
+ {
159
+ result->source_file = source_file;
160
+ }
161
+ result->line = line;
162
+
163
+ return result;
164
+ }
165
+
166
+ /* The underlying c structures are freed when the parent profile is freed.
167
+ However, on shutdown the Ruby GC frees objects in any will-nilly order.
168
+ That means the ruby thread object wrapping the c thread struct may
169
+ be freed before the parent profile. Thus we add in a free function
170
+ for the garbage collector so that if it does get called will nil
171
+ out our Ruby object reference.*/
172
+ static void
173
+ prof_method_ruby_gc_free(prof_method_t* method)
174
+ {
175
+ /* Has this thread object been accessed by Ruby? If
176
+ yes clean it up so to avoid a segmentation fault. */
177
+ if (method->object != Qnil)
178
+ {
179
+ RDATA(method->object)->data = NULL;
180
+ RDATA(method->object)->dfree = NULL;
181
+ RDATA(method->object)->dmark = NULL;
182
+ }
183
+ method->object = Qnil;
184
+ }
185
+
186
+ static void
187
+ prof_method_free(prof_method_t* method)
188
+ {
189
+ prof_method_ruby_gc_free(method);
190
+ prof_call_infos_free(method->call_infos);
191
+
192
+ xfree(method->key);
193
+ method->key = NULL;
194
+
195
+ xfree(method);
196
+ }
197
+
198
+ /*static int
199
+ mark_call_infos(st_data_t key, st_data_t value, st_data_t result)
200
+ {
201
+ prof_call_info_t *call_info = (prof_call_info_t *) value;
202
+ prof_call_info_mark(call_info);
203
+ return ST_CONTINUE;
204
+ }
205
+ */
206
+
207
+ void
208
+ prof_method_mark(prof_method_t *method)
209
+ {
210
+ if (method->object)
211
+ rb_gc_mark(method->object);
212
+
213
+ if (method->call_infos2)
214
+ rb_gc_mark(method->call_infos2);
215
+
216
+ if (method->call_infos->object)
217
+ rb_gc_mark(method->call_infos->object);
218
+
219
+ //st_foreach(method->call_info_table, mark_call_infos, NULL);
220
+ }
221
+
222
+ VALUE
223
+ prof_method_wrap(prof_method_t *result)
224
+ {
225
+ if (result->object == Qnil)
226
+ {
227
+ result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_ruby_gc_free, result);
228
+ }
229
+ return result->object;
230
+ }
231
+
232
+ static prof_method_t *
233
+ get_prof_method(VALUE self)
234
+ {
235
+ /* Can't use Data_Get_Struct because that triggers the event hook
236
+ ending up in endless recursion. */
237
+ prof_method_t* result = DATA_PTR(self);
238
+
239
+ if (!result)
240
+ rb_raise(rb_eRuntimeError, "This RubyProf::MethodInfo instance has already been freed, likely because its profile has been freed.");
241
+
242
+ return result;
243
+ }
121
244
 
122
245
  /* ================ Method Table =================*/
123
246
  int
@@ -143,6 +266,21 @@ method_table_create()
143
266
  return st_init_table(&type_method_hash);
144
267
  }
145
268
 
269
+ static int
270
+ method_table_free_iterator(st_data_t key, st_data_t value, st_data_t dummy)
271
+ {
272
+ prof_method_free((prof_method_t*)value);
273
+ return ST_CONTINUE;
274
+ }
275
+
276
+ void
277
+ method_table_free(st_table *table)
278
+ {
279
+ st_foreach(table, method_table_free_iterator, 0);
280
+ st_free_table(table);
281
+ }
282
+
283
+
146
284
  size_t
147
285
  method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val)
148
286
  {
@@ -163,15 +301,6 @@ method_table_lookup(st_table *table, const prof_method_key_t* key)
163
301
  }
164
302
  }
165
303
 
166
- void
167
- method_table_free(st_table *table)
168
- {
169
- /* Don't free the contents since they are wrapped by
170
- Ruby objects! */
171
- st_free_table(table);
172
- }
173
-
174
-
175
304
  /* ================ Method Info =================*/
176
305
  /* Document-class: RubyProf::MethodInfo
177
306
  The RubyProf::MethodInfo class stores profiling data for a method.
@@ -182,69 +311,6 @@ created. RubyProf::MethodInfo objects can be accessed via
182
311
  the RubyProf::Result object.
183
312
  */
184
313
 
185
- prof_method_t*
186
- prof_method_create(prof_method_key_t *key, const char* source_file, int line)
187
- {
188
- prof_method_t *result = ALLOC(prof_method_t);
189
- result->object = Qnil;
190
- result->key = ALLOC(prof_method_key_t);
191
- method_key(result->key, key->klass, key->mid);
192
-
193
- result->call_infos = prof_call_infos_create();
194
-
195
- if (source_file != NULL)
196
- {
197
- size_t len = strlen(source_file) + 1;
198
- char *buffer = ALLOC_N(char, len);
199
-
200
- MEMCPY(buffer, source_file, char, len);
201
- result->source_file = buffer;
202
- }
203
- else
204
- {
205
- result->source_file = source_file;
206
- }
207
- result->line = line;
208
-
209
- return result;
210
- }
211
-
212
- void
213
- prof_method_mark(prof_method_t *method)
214
- {
215
- rb_gc_mark(method->call_infos->object);
216
- rb_gc_mark(method->key->klass);
217
- }
218
-
219
- static void
220
- prof_method_free(prof_method_t *method)
221
- {
222
- if (method->source_file)
223
- {
224
- xfree((char*)method->source_file);
225
- }
226
-
227
- prof_call_infos_free(method->call_infos);
228
- xfree(method->key);
229
- xfree(method);
230
- }
231
-
232
- VALUE
233
- prof_method_wrap(prof_method_t *result)
234
- {
235
- if (result->object == Qnil)
236
- {
237
- result->object = Data_Wrap_Struct(cMethodInfo, prof_method_mark, prof_method_free, result);
238
- }
239
- return result->object;
240
- }
241
-
242
- static prof_method_t *
243
- get_prof_method(VALUE obj)
244
- {
245
- return (prof_method_t *) DATA_PTR(obj);
246
- }
247
-
248
314
  /* call-seq:
249
315
  line_no -> int
250
316
 
@@ -343,7 +409,11 @@ static VALUE
343
409
  prof_method_call_infos(VALUE self)
344
410
  {
345
411
  prof_method_t *method = get_prof_method(self);
346
- return prof_call_infos_wrap(method->call_infos);
412
+ if (method->call_infos2 == Qnil)
413
+ {
414
+ method->call_infos2 = prof_call_infos_wrap(method->call_infos);
415
+ }
416
+ return method->call_infos2;
347
417
  }
348
418
 
349
419
  void rp_init_method_info()
@@ -6,7 +6,7 @@
6
6
 
7
7
  #include <ruby.h>
8
8
 
9
- #ifndef RUBY_VM
9
+ #ifndef RUBY_VM
10
10
  #include <st.h>
11
11
  typedef st_data_t st_index_t;
12
12
  #endif
@@ -18,10 +18,10 @@ typedef struct
18
18
  {
19
19
  VALUE klass; /* The method's class. */
20
20
  ID mid; /* The method id. */
21
- int depth; /* The recursion depth. */
22
21
  st_index_t key; /* Cache calculated key */
23
22
  } prof_method_key_t;
24
23
 
24
+
25
25
  /* Forward declaration, see rp_call_info.h */
26
26
  struct prof_call_infos_t;
27
27
 
@@ -31,18 +31,21 @@ typedef struct
31
31
  prof_method_key_t *key; /* Method key */
32
32
  const char *source_file; /* The method's source file */
33
33
  int line; /* The method's line number. */
34
- struct prof_call_infos_t *call_infos; /* Call info objects for this method */
34
+ struct prof_call_infos_t *call_infos; /* Call info objects for this method */
35
35
  VALUE object; /* Cached ruby object */
36
+ VALUE call_infos2; /* Cached array of RubyProf::CallInfo */
36
37
  } prof_method_t;
37
38
 
38
39
  void rp_init_method_info(void);
39
40
 
41
+ void method_key(prof_method_key_t* key, VALUE klass, ID mid);
42
+
40
43
  st_table * method_table_create();
41
44
  prof_method_t * method_table_lookup(st_table *table, const prof_method_key_t* key);
42
45
  size_t method_table_insert(st_table *table, const prof_method_key_t *key, prof_method_t *val);
43
46
  void method_table_free(st_table *table);
44
47
 
45
- prof_method_t* prof_method_create(prof_method_key_t *key, const char* source_file, int line);
48
+ prof_method_t* prof_method_create(VALUE klass, ID mid, const char* source_file, int line);
46
49
  VALUE prof_method_wrap(prof_method_t *result);
47
50
  void prof_method_mark(prof_method_t *method);
48
51