ruby-prof 2.0.4 → 2.0.5

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -2
  3. data/lib/ruby-prof/printers/flame_graph_printer.rb +80 -78
  4. data/lib/ruby-prof/version.rb +1 -1
  5. metadata +4 -85
  6. data/Rakefile +0 -98
  7. data/docs/advanced-usage.md +0 -132
  8. data/docs/alternatives.md +0 -98
  9. data/docs/architecture.md +0 -304
  10. data/docs/best-practices.md +0 -27
  11. data/docs/getting-started.md +0 -130
  12. data/docs/history.md +0 -11
  13. data/docs/index.md +0 -45
  14. data/docs/profiling-rails.md +0 -64
  15. data/docs/public/examples/example.rb +0 -33
  16. data/docs/public/examples/generate_reports.rb +0 -92
  17. data/docs/public/examples/reports/call_info.txt +0 -27
  18. data/docs/public/examples/reports/call_stack.html +0 -835
  19. data/docs/public/examples/reports/callgrind.out +0 -150
  20. data/docs/public/examples/reports/flame_graph.html +0 -408
  21. data/docs/public/examples/reports/flat.txt +0 -45
  22. data/docs/public/examples/reports/graph.dot +0 -129
  23. data/docs/public/examples/reports/graph.html +0 -1319
  24. data/docs/public/examples/reports/graph.txt +0 -100
  25. data/docs/public/examples/reports/graphviz_viewer.html +0 -1
  26. data/docs/public/images/call_stack.png +0 -0
  27. data/docs/public/images/class_diagram.png +0 -0
  28. data/docs/public/images/dot_printer.png +0 -0
  29. data/docs/public/images/flame_graph.png +0 -0
  30. data/docs/public/images/flat.png +0 -0
  31. data/docs/public/images/graph.png +0 -0
  32. data/docs/public/images/graph_html.png +0 -0
  33. data/docs/public/images/ruby-prof-logo.svg +0 -1
  34. data/docs/reports.md +0 -151
  35. data/docs/stylesheets/extra.css +0 -80
  36. data/ruby-prof.gemspec +0 -66
  37. data/test/abstract_printer_test.rb +0 -25
  38. data/test/alias_test.rb +0 -203
  39. data/test/call_tree_builder.rb +0 -126
  40. data/test/call_tree_test.rb +0 -94
  41. data/test/call_tree_visitor_test.rb +0 -27
  42. data/test/call_trees_test.rb +0 -66
  43. data/test/duplicate_names_test.rb +0 -32
  44. data/test/dynamic_method_test.rb +0 -50
  45. data/test/enumerable_test.rb +0 -23
  46. data/test/exceptions_test.rb +0 -24
  47. data/test/exclude_methods_test.rb +0 -363
  48. data/test/exclude_threads_test.rb +0 -48
  49. data/test/fiber_test.rb +0 -195
  50. data/test/gc_test.rb +0 -104
  51. data/test/inverse_call_tree_test.rb +0 -174
  52. data/test/line_number_test.rb +0 -563
  53. data/test/marshal_test.rb +0 -144
  54. data/test/measure_allocations.rb +0 -26
  55. data/test/measure_allocations_test.rb +0 -1511
  56. data/test/measure_process_time_test.rb +0 -3286
  57. data/test/measure_times.rb +0 -56
  58. data/test/measure_wall_time_test.rb +0 -774
  59. data/test/measurement_test.rb +0 -82
  60. data/test/merge_test.rb +0 -146
  61. data/test/method_info_test.rb +0 -100
  62. data/test/multi_printer_test.rb +0 -52
  63. data/test/no_method_class_test.rb +0 -15
  64. data/test/pause_resume_test.rb +0 -171
  65. data/test/prime.rb +0 -54
  66. data/test/prime_script.rb +0 -6
  67. data/test/printer_call_stack_test.rb +0 -28
  68. data/test/printer_call_tree_test.rb +0 -30
  69. data/test/printer_flame_graph_test.rb +0 -82
  70. data/test/printer_flat_test.rb +0 -110
  71. data/test/printer_graph_html_test.rb +0 -62
  72. data/test/printer_graph_test.rb +0 -42
  73. data/test/printers_test.rb +0 -162
  74. data/test/printing_recursive_graph_test.rb +0 -81
  75. data/test/profile_test.rb +0 -101
  76. data/test/rack_test.rb +0 -103
  77. data/test/recursive_test.rb +0 -796
  78. data/test/scheduler.rb +0 -367
  79. data/test/singleton_test.rb +0 -39
  80. data/test/stack_printer_test.rb +0 -61
  81. data/test/start_stop_test.rb +0 -106
  82. data/test/test_helper.rb +0 -24
  83. data/test/thread_test.rb +0 -229
  84. data/test/unique_call_path_test.rb +0 -123
  85. data/test/yarv_test.rb +0 -56
data/test/scheduler.rb DELETED
@@ -1,367 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This is an example and simplified scheduler for test purposes.
4
- # It is not efficient for a large number of file descriptors as it uses IO.select().
5
- # Production Fiber schedulers should use epoll/kqueue/etc.
6
-
7
- require 'fiber'
8
- require 'socket'
9
-
10
- begin
11
- require 'io/nonblock'
12
- rescue LoadError
13
- # Ignore.
14
- end
15
-
16
- class Scheduler
17
- experimental = Warning[:experimental]
18
- begin
19
- Warning[:experimental] = false
20
- IO::Buffer.new(0)
21
- ensure
22
- Warning[:experimental] = experimental
23
- end
24
-
25
- def initialize
26
- @readable = {}
27
- @writable = {}
28
- @waiting = {}
29
-
30
- @closed = false
31
-
32
- @lock = Thread::Mutex.new
33
- @blocking = Hash.new.compare_by_identity
34
- @ready = []
35
-
36
- @urgent = IO.pipe
37
- end
38
-
39
- attr :readable
40
- attr :writable
41
- attr :waiting
42
-
43
- def next_timeout
44
- _fiber, timeout = @waiting.min_by{|key, value| value}
45
-
46
- if timeout
47
- offset = timeout - current_time
48
-
49
- if offset < 0
50
- return 0
51
- else
52
- return offset
53
- end
54
- end
55
- end
56
-
57
- def run
58
- # $stderr.puts [__method__, Fiber.current].inspect
59
-
60
- while @readable.any? or @writable.any? or @waiting.any? or @blocking.any?
61
- # Can only handle file descriptors up to 1024...
62
- readable, writable = IO.select(@readable.keys + [@urgent.first], @writable.keys, [], next_timeout)
63
-
64
- # puts "readable: #{readable}" if readable&.any?
65
- # puts "writable: #{writable}" if writable&.any?
66
-
67
- selected = {}
68
-
69
- readable&.each do |io|
70
- if fiber = @readable.delete(io)
71
- @writable.delete(io) if @writable[io] == fiber
72
- selected[fiber] = IO::READABLE
73
- elsif io == @urgent.first
74
- @urgent.first.read_nonblock(1024)
75
- end
76
- end
77
-
78
- writable&.each do |io|
79
- if fiber = @writable.delete(io)
80
- @readable.delete(io) if @readable[io] == fiber
81
- selected[fiber] = selected.fetch(fiber, 0) | IO::WRITABLE
82
- end
83
- end
84
-
85
- selected.each do |fiber, events|
86
- fiber.resume(events)
87
- end
88
-
89
- if @waiting.any?
90
- time = current_time
91
- waiting, @waiting = @waiting, {}
92
-
93
- waiting.each do |fiber, timeout|
94
- if fiber.alive?
95
- if timeout <= time
96
- fiber.resume
97
- else
98
- @waiting[fiber] = timeout
99
- end
100
- end
101
- end
102
- end
103
-
104
- if @ready.any?
105
- ready = nil
106
-
107
- @lock.synchronize do
108
- ready, @ready = @ready, []
109
- end
110
-
111
- ready.each do |fiber|
112
- fiber.resume
113
- end
114
- end
115
- end
116
- end
117
-
118
- def scheduler_close
119
- close(true)
120
- end
121
-
122
- def close(internal = false)
123
- # $stderr.puts [__method__, Fiber.current].inspect
124
-
125
- unless internal
126
- if Fiber.scheduler == self
127
- return Fiber.set_scheduler(nil)
128
- end
129
- end
130
-
131
- if @closed
132
- raise "Scheduler already closed!"
133
- end
134
-
135
- self.run
136
- ensure
137
- if @urgent
138
- @urgent.each(&:close)
139
- @urgent = nil
140
- end
141
-
142
- @closed ||= true
143
-
144
- # We freeze to detect any unintended modifications after the scheduler is closed:
145
- self.freeze
146
- end
147
-
148
- def closed?
149
- @closed
150
- end
151
-
152
- def current_time
153
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
154
- end
155
-
156
- def timeout_after(duration, klass, message, &block)
157
- fiber = Fiber.current
158
-
159
- self.fiber do
160
- sleep(duration)
161
-
162
- if fiber&.alive?
163
- fiber.raise(klass, message)
164
- end
165
- end
166
-
167
- begin
168
- yield(duration)
169
- ensure
170
- fiber = nil
171
- end
172
- end
173
-
174
- def process_wait(pid, flags)
175
- # $stderr.puts [__method__, pid, flags, Fiber.current].inspect
176
-
177
- # This is a very simple way to implement a non-blocking wait:
178
- Thread.new do
179
- Process::Status.wait(pid, flags)
180
- end.value
181
- end
182
-
183
- def io_wait(io, events, duration)
184
- # $stderr.puts [__method__, io, events, duration, Fiber.current].inspect
185
-
186
- unless (events & IO::READABLE).zero?
187
- @readable[io] = Fiber.current
188
- end
189
-
190
- unless (events & IO::WRITABLE).zero?
191
- @writable[io] = Fiber.current
192
- end
193
-
194
- Fiber.yield
195
- ensure
196
- @readable.delete(io)
197
- @writable.delete(io)
198
- end
199
-
200
- def io_select(...)
201
- # Emulate the operation using a non-blocking thread:
202
- Thread.new do
203
- IO.select(...)
204
- end.value
205
- end
206
-
207
- # Used for Kernel#sleep and Thread::Mutex#sleep
208
- def kernel_sleep(duration = nil)
209
- # $stderr.puts [__method__, duration, Fiber.current].inspect
210
-
211
- self.block(:sleep, duration)
212
-
213
- return true
214
- end
215
-
216
- # Used when blocking on synchronization (Thread::Mutex#lock,
217
- # Thread::Queue#pop, Thread::SizedQueue#push, ...)
218
- def block(blocker, timeout = nil)
219
- # $stderr.puts [__method__, blocker, timeout].inspect
220
-
221
- fiber = Fiber.current
222
-
223
- if timeout
224
- @waiting[fiber] = current_time + timeout
225
- begin
226
- Fiber.yield
227
- ensure
228
- # Remove from @waiting in the case #unblock was called before the timeout expired:
229
- @waiting.delete(fiber)
230
- end
231
- else
232
- @blocking[fiber] = true
233
- begin
234
- Fiber.yield
235
- ensure
236
- @blocking.delete(fiber)
237
- end
238
- end
239
- end
240
-
241
- # Used when synchronization wakes up a previously-blocked fiber
242
- # (Thread::Mutex#unlock, Thread::Queue#push, ...).
243
- # This might be called from another thread.
244
- def unblock(blocker, fiber)
245
- # $stderr.puts [__method__, blocker, fiber].inspect
246
- # $stderr.puts blocker.backtrace.inspect
247
- # $stderr.puts fiber.backtrace.inspect
248
-
249
- @lock.synchronize do
250
- @ready << fiber
251
- end
252
-
253
- io = @urgent.last
254
- io.write_nonblock('.')
255
- end
256
-
257
- def fiber(&block)
258
- fiber = Fiber.new(blocking: false, &block)
259
-
260
- fiber.resume
261
-
262
- return fiber
263
- end
264
-
265
- def fiber_interrupt(fiber, exception)
266
- fiber.raise(exception)
267
- end
268
-
269
- def address_resolve(hostname)
270
- Thread.new do
271
- Addrinfo.getaddrinfo(hostname, nil).map(&:ip_address).uniq
272
- end.value
273
- end
274
- end
275
-
276
- class IOBufferScheduler < Scheduler
277
- EAGAIN = -Errno::EAGAIN::Errno
278
-
279
- def io_read(io, buffer, length, offset)
280
- total = 0
281
- io.nonblock = true
282
-
283
- while true
284
- maximum_size = buffer.size - offset
285
- result = blocking{buffer.read(io, maximum_size, offset)}
286
-
287
- if result > 0
288
- total += result
289
- offset += result
290
- break if total >= length
291
- elsif result == 0
292
- break
293
- elsif result == EAGAIN
294
- if length > 0
295
- self.io_wait(io, IO::READABLE, nil)
296
- else
297
- return result
298
- end
299
- elsif result < 0
300
- return result
301
- end
302
- end
303
-
304
- return total
305
- end
306
-
307
- def io_write(io, buffer, length, offset)
308
- total = 0
309
- io.nonblock = true
310
-
311
- while true
312
- maximum_size = buffer.size - offset
313
- result = blocking{buffer.write(io, maximum_size, offset)}
314
-
315
- if result > 0
316
- total += result
317
- offset += result
318
- break if total >= length
319
- elsif result == 0
320
- break
321
- elsif result == EAGAIN
322
- if length > 0
323
- self.io_wait(io, IO::WRITABLE, nil)
324
- else
325
- return result
326
- end
327
- elsif result < 0
328
- return result
329
- end
330
- end
331
-
332
- return total
333
- end
334
-
335
- def blocking(&block)
336
- Fiber.blocking(&block)
337
- end
338
- end
339
-
340
- class BrokenUnblockScheduler < Scheduler
341
- def unblock(blocker, fiber)
342
- super
343
-
344
- raise "Broken unblock!"
345
- end
346
- end
347
-
348
- class SleepingUnblockScheduler < Scheduler
349
- # This method is invoked when the thread is exiting.
350
- def unblock(blocker, fiber)
351
- super
352
-
353
- # This changes the current thread state to `THREAD_RUNNING` which causes `thread_join_sleep` to hang.
354
- sleep(0.1)
355
- end
356
- end
357
-
358
- class SleepingBlockingScheduler < Scheduler
359
- def kernel_sleep(duration = nil)
360
- # Deliberaly sleep in a blocking state which can trigger a deadlock if the implementation is not correct.
361
- Fiber.blocking{sleep 0.0001}
362
-
363
- self.block(:sleep, duration)
364
-
365
- return true
366
- end
367
- end
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
-
4
- require File.expand_path('../test_helper', __FILE__)
5
- require 'stringio'
6
- require 'timeout'
7
-
8
- # -- Test for bug [#5657]
9
- # http://rubyforge.org/tracker/index.php?func=detail&aid=5657&group_id=1814&atid=7060
10
-
11
-
12
- class A
13
- attr_accessor :as
14
- def initialize
15
- @as = []
16
- class << @as
17
- def <<(an_a)
18
- super
19
- end
20
- end
21
- end
22
-
23
- def <<(an_a)
24
- @as << an_a
25
- end
26
- end
27
-
28
- class SingletonTest < TestCase
29
- def test_singleton
30
- result = RubyProf::Profile.profile do
31
- a = A.new
32
- a << :first_thing
33
- assert_equal(1, a.as.size)
34
- end
35
- printer = RubyProf::FlatPrinter.new(result)
36
- output = ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT'] == "1" ? STDOUT : StringIO.new
37
- printer.print(output)
38
- end
39
- end
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
-
4
- require File.expand_path('../test_helper', __FILE__)
5
-
6
- # Test data
7
- # A
8
- # / \
9
- # B C
10
- # \
11
- # B
12
-
13
- class STPT
14
- def a
15
- 100.times{b}
16
- 300.times{c}
17
- c;c;c
18
- end
19
-
20
- def b
21
- sleep 0
22
- end
23
-
24
- def c
25
- 5.times{b}
26
- end
27
- end
28
-
29
- class StackPrinterTest < TestCase
30
- def test_stack_can_be_printed
31
- start_time = Time.now
32
- result = RubyProf::Profile.profile(measure_mode: RubyProf::WALL_TIME) do
33
- 5.times{STPT.new.a}
34
- end
35
-
36
- end_time = Time.now
37
- expected_time = end_time - start_time
38
-
39
- file_contents = nil
40
- file_contents = print(result)
41
- re = /Thread: (\d+)(, Fiber: (\d+))? \([\.0-9]+.[\.0-9]+% ~ ([\.0-9]+)\)/
42
- assert_match(re, file_contents)
43
- file_contents =~ re
44
- actual_time = $4.to_f
45
- assert_in_delta(expected_time, actual_time, 0.1)
46
- end
47
-
48
- private
49
-
50
- def print(result)
51
- test = caller.first =~ /in `(.*)'/ ? $1 : "test"
52
- testfile_name = "#{Dir.tmpdir}/ruby_prof_#{test}.html"
53
- # puts "printing to #{testfile_name}"
54
- printer = RubyProf::CallStackPrinter.new(result)
55
- File.open(testfile_name, "w") {|f| printer.print(f, threshold: 0, min_percent: 0, title: "ruby_prof #{test}")}
56
- system("open '#{testfile_name}'") if RUBY_PLATFORM =~ /darwin/ && ENV['SHOW_RUBY_PROF_PRINTER_OUTPUT']=="1"
57
- assert File.exist?(testfile_name), "#{testfile_name} does not exist"
58
- assert File.readable?(testfile_name), "#{testfile_name} is no readable"
59
- File.read(testfile_name)
60
- end
61
- end
@@ -1,106 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
-
4
- require File.expand_path('../test_helper', __FILE__)
5
-
6
- class StartStopTest < TestCase
7
- def setup
8
- super
9
- # Need to use wall time for this test due to the sleep calls
10
- @profile = RubyProf::Profile.new(measure_mode: RubyProf::WALL_TIME)
11
- end
12
-
13
- def method1
14
- @profile.start
15
- method2
16
- end
17
-
18
- def method2
19
- method3
20
- end
21
-
22
- def method3
23
- sleep(2)
24
- @result = @profile.stop
25
- end
26
-
27
- def test_extra_stop_should_raise
28
- @profile.start
29
- assert_raises(RuntimeError) do
30
- @profile.start
31
- end
32
-
33
- @profile.stop # ok
34
- assert_raises(RuntimeError) do
35
- @profile.stop
36
- end
37
- end
38
-
39
- def test_different_methods
40
- method1
41
-
42
- # Ruby prof should be stopped
43
- assert_equal(false, @profile.running?)
44
-
45
- methods = @result.threads.first.methods.sort.reverse
46
- assert_equal(4, methods.length)
47
-
48
- method = methods[0]
49
- assert_equal('StartStopTest#method1', method.full_name)
50
- assert_equal(1, method.called)
51
- assert_in_delta(2, method.total_time, 0.05 * delta_multiplier)
52
- assert_in_delta(0, method.wait_time, 0.02 * delta_multiplier)
53
- assert_in_delta(0, method.self_time, 0.02 * delta_multiplier)
54
- assert_in_delta(2, method.children_time, 0.05 * delta_multiplier)
55
-
56
- assert_equal(1, method.call_trees.callees.length)
57
- call_tree = method.call_trees.callees[0]
58
- assert_equal('StartStopTest#method2', call_tree.target.full_name)
59
-
60
- method = methods[1]
61
- assert_equal('StartStopTest#method2', method.full_name)
62
- assert_equal(1, method.called)
63
- assert_in_delta(2, method.total_time, 0.05 * delta_multiplier)
64
- assert_in_delta(0, method.wait_time, 0.02 * delta_multiplier)
65
- assert_in_delta(0, method.self_time, 0.02 * delta_multiplier)
66
- assert_in_delta(2, method.children_time, 0.05 * delta_multiplier)
67
-
68
- assert_equal(1, method.call_trees.callers.length)
69
- call_tree = method.call_trees.callers[0]
70
- assert_equal('StartStopTest#method1', call_tree.parent.target.full_name)
71
-
72
- assert_equal(1, method.call_trees.callees.length)
73
- call_tree = method.call_trees.callees[0]
74
- assert_equal('StartStopTest#method3', call_tree.target.full_name)
75
-
76
- method = methods[2]
77
- assert_equal('StartStopTest#method3', method.full_name)
78
- assert_equal(1, method.called)
79
- assert_in_delta(2, method.total_time, 0.05 * delta_multiplier)
80
- assert_in_delta(0, method.wait_time, 0.02 * delta_multiplier)
81
- assert_in_delta(0, method.self_time, 0.02 * delta_multiplier)
82
- assert_in_delta(2, method.children_time, 0.05 * delta_multiplier)
83
-
84
- assert_equal(1, method.call_trees.callers.length)
85
- call_tree = method.call_trees.callers[0]
86
- assert_equal('StartStopTest#method2', call_tree.parent.target.full_name)
87
-
88
- assert_equal(1, method.call_trees.callees.length)
89
- call_tree = method.call_trees.callees[0]
90
- assert_equal('Kernel#sleep', call_tree.target.full_name)
91
-
92
- method = methods[3]
93
- assert_equal('Kernel#sleep', method.full_name)
94
- assert_equal(1, method.called)
95
- assert_in_delta(2, method.total_time, 0.05 * delta_multiplier)
96
- assert_in_delta(0, method.wait_time, 0.02 * delta_multiplier)
97
- assert_in_delta(2, method.self_time, 0.05 * delta_multiplier)
98
- assert_in_delta(0, method.children_time, 0.02 * delta_multiplier)
99
-
100
- assert_equal(1, method.call_trees.callers.length)
101
- call_tree = method.call_trees.callers[0]
102
- assert_equal('StartStopTest#method3', call_tree.parent.target.full_name)
103
-
104
- assert_equal(0, method.call_trees.callees.length)
105
- end
106
- end
data/test/test_helper.rb DELETED
@@ -1,24 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- # To make testing/debugging easier test within this source tree versus an installed gem
4
- require 'bundler/setup'
5
-
6
- # Add ext directory to load path to make it easier to test locally built extensions
7
- ext_path = File.expand_path(File.join(__dir__, '..', 'ext', 'ruby_prof'))
8
- $LOAD_PATH.unshift(ext_path)
9
-
10
- # Now load code
11
- require 'ruby-prof'
12
-
13
- # Disable minitest parallel tests. The problem is the thread switching will change test results
14
- # (self vs wait time)
15
- ENV["MT_CPU"] = "0" # New versions of minitest
16
- ENV["N"] = "0" # Older versions of minitest
17
-
18
- require 'minitest/autorun'
19
- Minitest.load_plugins
20
- class TestCase < Minitest::Test
21
- def delta_multiplier
22
- osx? && ENV["GITHUB_ACTIONS"] ? 6 : 1
23
- end
24
- end