ruby-prof 1.7.2 → 2.0.1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/{CHANGES → CHANGELOG.md} +115 -178
  3. data/README.md +5 -5
  4. data/bin/ruby-prof +1 -4
  5. data/bin/ruby-prof-check-trace +45 -45
  6. data/docs/advanced-usage.md +132 -0
  7. data/docs/alternatives.md +98 -0
  8. data/docs/architecture.md +122 -0
  9. data/docs/best-practices.md +27 -0
  10. data/docs/getting-started.md +130 -0
  11. data/docs/history.md +11 -0
  12. data/docs/index.md +45 -0
  13. data/docs/profiling-rails.md +64 -0
  14. data/docs/public/examples/example.rb +33 -0
  15. data/docs/public/examples/generate_reports.rb +92 -0
  16. data/docs/public/examples/reports/call_info.txt +27 -0
  17. data/docs/public/examples/reports/call_stack.html +835 -0
  18. data/docs/public/examples/reports/callgrind.out +150 -0
  19. data/docs/public/examples/reports/flame_graph.html +408 -0
  20. data/docs/public/examples/reports/flat.txt +45 -0
  21. data/docs/public/examples/reports/graph.dot +129 -0
  22. data/docs/public/examples/reports/graph.html +1319 -0
  23. data/docs/public/examples/reports/graph.txt +100 -0
  24. data/docs/public/examples/reports/graphviz_viewer.html +1 -0
  25. data/docs/public/images/call_stack.png +0 -0
  26. data/docs/public/images/class_diagram.png +0 -0
  27. data/docs/public/images/dot_printer.png +0 -0
  28. data/docs/public/images/flame_graph.png +0 -0
  29. data/docs/public/images/flat.png +0 -0
  30. data/docs/public/images/graph.png +0 -0
  31. data/docs/public/images/graph_html.png +0 -0
  32. data/docs/public/images/ruby-prof-logo.svg +1 -0
  33. data/docs/reports.md +150 -0
  34. data/docs/stylesheets/extra.css +80 -0
  35. data/ext/ruby_prof/rp_allocation.c +0 -15
  36. data/ext/ruby_prof/rp_allocation.h +29 -33
  37. data/ext/ruby_prof/rp_call_tree.c +3 -0
  38. data/ext/ruby_prof/rp_call_tree.h +1 -4
  39. data/ext/ruby_prof/rp_call_trees.h +1 -4
  40. data/ext/ruby_prof/rp_measurement.c +0 -5
  41. data/ext/ruby_prof/rp_measurement.h +49 -53
  42. data/ext/ruby_prof/rp_method.c +3 -0
  43. data/ext/ruby_prof/rp_method.h +1 -4
  44. data/ext/ruby_prof/rp_profile.c +1 -1
  45. data/ext/ruby_prof/rp_profile.h +1 -5
  46. data/ext/ruby_prof/rp_stack.h +50 -53
  47. data/ext/ruby_prof/rp_thread.h +1 -4
  48. data/ext/ruby_prof/ruby_prof.h +1 -4
  49. data/ext/ruby_prof/vc/ruby_prof.vcxproj +7 -8
  50. data/lib/ruby-prof/assets/call_stack_printer.html.erb +746 -711
  51. data/lib/ruby-prof/assets/flame_graph_printer.html.erb +412 -0
  52. data/lib/ruby-prof/assets/graph_printer.html.erb +355 -355
  53. data/lib/ruby-prof/call_tree.rb +57 -57
  54. data/lib/ruby-prof/call_tree_visitor.rb +36 -36
  55. data/lib/ruby-prof/measurement.rb +17 -17
  56. data/lib/ruby-prof/printers/abstract_printer.rb +19 -33
  57. data/lib/ruby-prof/printers/call_info_printer.rb +53 -53
  58. data/lib/ruby-prof/printers/call_stack_printer.rb +168 -180
  59. data/lib/ruby-prof/printers/call_tree_printer.rb +132 -145
  60. data/lib/ruby-prof/printers/dot_printer.rb +177 -132
  61. data/lib/ruby-prof/printers/flame_graph_printer.rb +79 -0
  62. data/lib/ruby-prof/printers/flat_printer.rb +52 -52
  63. data/lib/ruby-prof/printers/graph_html_printer.rb +62 -63
  64. data/lib/ruby-prof/printers/graph_printer.rb +112 -113
  65. data/lib/ruby-prof/printers/multi_printer.rb +134 -127
  66. data/lib/ruby-prof/profile.rb +13 -0
  67. data/lib/ruby-prof/rack.rb +114 -105
  68. data/lib/ruby-prof/task.rb +147 -147
  69. data/lib/ruby-prof/thread.rb +20 -20
  70. data/lib/ruby-prof/version.rb +1 -1
  71. data/lib/ruby-prof.rb +50 -52
  72. data/lib/unprof.rb +10 -10
  73. data/ruby-prof.gemspec +66 -66
  74. data/test/abstract_printer_test.rb +25 -27
  75. data/test/alias_test.rb +203 -117
  76. data/test/call_tree_builder.rb +9 -9
  77. data/test/call_tree_visitor_test.rb +27 -27
  78. data/test/call_trees_test.rb +66 -66
  79. data/test/duplicate_names_test.rb +32 -32
  80. data/test/dynamic_method_test.rb +50 -50
  81. data/test/exceptions_test.rb +1 -1
  82. data/test/exclude_threads_test.rb +48 -48
  83. data/test/fiber_test.rb +72 -72
  84. data/test/inverse_call_tree_test.rb +174 -174
  85. data/test/line_number_test.rb +138 -1
  86. data/test/marshal_test.rb +2 -3
  87. data/test/measure_allocations.rb +26 -26
  88. data/test/measure_allocations_test.rb +340 -1
  89. data/test/measure_process_time_test.rb +3098 -3142
  90. data/test/measure_times.rb +56 -56
  91. data/test/measure_wall_time_test.rb +511 -372
  92. data/test/measurement_test.rb +82 -82
  93. data/test/merge_test.rb +48 -48
  94. data/test/multi_printer_test.rb +52 -66
  95. data/test/no_method_class_test.rb +15 -15
  96. data/test/pause_resume_test.rb +171 -171
  97. data/test/prime.rb +54 -54
  98. data/test/prime_script.rb +5 -5
  99. data/test/printer_call_stack_test.rb +4 -3
  100. data/test/printer_call_tree_test.rb +30 -30
  101. data/test/printer_flame_graph_test.rb +82 -0
  102. data/test/printer_flat_test.rb +3 -3
  103. data/test/printer_graph_html_test.rb +12 -9
  104. data/test/printer_graph_test.rb +5 -3
  105. data/test/printers_test.rb +162 -178
  106. data/test/printing_recursive_graph_test.rb +4 -4
  107. data/test/profile_test.rb +2 -2
  108. data/test/rack_test.rb +15 -5
  109. data/test/recursive_test.rb +139 -139
  110. data/test/scheduler.rb +367 -363
  111. data/test/singleton_test.rb +2 -1
  112. data/test/stack_printer_test.rb +61 -61
  113. data/test/start_stop_test.rb +106 -106
  114. data/test/test_helper.rb +4 -0
  115. data/test/thread_test.rb +229 -229
  116. data/test/unique_call_path_test.rb +123 -123
  117. data/test/yarv_test.rb +2 -2
  118. metadata +53 -11
  119. data/ext/ruby_prof/rp_measure_memory.c +0 -46
  120. data/lib/ruby-prof/compatibility.rb +0 -113
  121. data/test/compatibility_test.rb +0 -49
  122. data/test/measure_memory_test.rb +0 -1193
@@ -0,0 +1,64 @@
1
+ # Profiling Rails
2
+
3
+ To profile a Rails application it is vital to run it using production-like settings (cache classes, cache view lookups, etc.). Otherwise, Rails dependency loading code will overwhelm any time spent in the application itself (our tests show that Rails dependency loading causes a roughly 6x slowdown). The best way to do this is to create a new Rails environment, `profile`.
4
+
5
+ To profile Rails:
6
+
7
+ 1. Add ruby-prof to your Gemfile:
8
+
9
+ ```ruby
10
+ group :profile do
11
+ gem 'ruby-prof'
12
+ end
13
+ ```
14
+
15
+ Then install it:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ 2. Create `config/environments/profile.rb` with production-like settings and the ruby-prof middleware:
22
+
23
+ ```ruby
24
+ # config/environments/profile.rb
25
+ require_relative "production"
26
+
27
+ Rails.application.configure do
28
+ # Optional: reduce noise while profiling.
29
+ config.log_level = :warn
30
+
31
+ # Optional: disable controller/view caching if you want raw app execution timing.
32
+ config.action_controller.perform_caching = false
33
+
34
+ config.middleware.use Rack::RubyProf, path: Rails.root.join("tmp/profile")
35
+ end
36
+ ```
37
+
38
+ By default the rack adapter generates flat text, graph text, graph HTML, and call stack HTML reports.
39
+
40
+ 3. Start Rails in the profile environment:
41
+
42
+ ```bash
43
+ bin/rails server -e profile
44
+ ```
45
+
46
+ You can run a console in the same environment with:
47
+
48
+ ```bash
49
+ bin/rails console -e profile
50
+ ```
51
+
52
+ 4. Make a request to generate profile output:
53
+
54
+ ```bash
55
+ curl http://127.0.0.1:3000/
56
+ ```
57
+
58
+ 5. Inspect reports in `tmp/profile`:
59
+
60
+ ```bash
61
+ ls -1 tmp/profile
62
+ ```
63
+
64
+ Reports are generated per request path. Repeating the same request path overwrites the previous report files for that path.
@@ -0,0 +1,33 @@
1
+ # A small synthetic workload for demonstrating ruby-prof reports.
2
+ # word_freq.rb
3
+
4
+ def normalize(text)
5
+ text.downcase.gsub(/[^a-z\s]/, "")
6
+ end
7
+
8
+ def tokenize(text)
9
+ text.split(/\s+/)
10
+ end
11
+
12
+ def count_words(words)
13
+ counts = Hash.new(0)
14
+ words.each { |w| counts[w] += 1 }
15
+ counts
16
+ end
17
+
18
+ def top_words(counts, n = 10)
19
+ counts.sort_by { |_, v| -v }.take(n)
20
+ end
21
+
22
+ def run_example
23
+ text = <<~EOS * 200
24
+ Ruby is a dynamic, open source programming language with a focus on
25
+ simplicity and productivity. It has an elegant syntax that is natural
26
+ to read and easy to write.
27
+ EOS
28
+
29
+ normalized = normalize(text)
30
+ tokens = tokenize(normalized)
31
+ counts = count_words(tokens)
32
+ top = top_words(counts)
33
+ end
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Generates example reports for all ruby-prof printers.
5
+ # Usage: ruby docs/public/examples/generate_reports.rb
6
+
7
+ # To make testing/debugging easier test within this source tree versus an installed gem
8
+ require 'bundler/setup'
9
+
10
+ # Add ext directory to load path to make it easier to test locally built extensions
11
+ ext_path = File.expand_path(File.join(__dir__, '..', '..', '..', 'ext', 'ruby_prof'))
12
+ $LOAD_PATH.unshift(ext_path)
13
+
14
+ require 'fileutils'
15
+ require 'stringio'
16
+ require 'uri'
17
+ require 'ruby-prof'
18
+ require_relative 'example'
19
+
20
+ output_dir = File.join(File.dirname(__FILE__), "reports")
21
+ FileUtils.mkdir_p(output_dir)
22
+
23
+ def sanitize_local_file_links(path)
24
+ content = File.read(path)
25
+ content.gsub!(%r{href="file://[^"]*/docs/public/examples/([^"#]+)#\d+"}, 'href="../\1"')
26
+ content.gsub!(%r{title=".*?/docs/public/examples/([^":]+):\d+"}, 'title="\1"')
27
+ content.gsub!(%r{href="file://[^"]+"}, 'href="#"')
28
+ content.gsub!(%r{title="[^"]*(?:<internal:|&lt;internal:)[^"]+"}, 'title="internal"')
29
+ File.write(path, content)
30
+ end
31
+
32
+ result = RubyProf::Profile.profile(measure_mode: RubyProf::WALL_TIME) do
33
+ run_example
34
+ end
35
+
36
+ # Flame Graph
37
+ File.open(File.join(output_dir, "flame_graph.html"), "wb") do |f|
38
+ RubyProf::FlameGraphPrinter.new(result).print(f)
39
+ end
40
+
41
+ # Call Stack
42
+ File.open(File.join(output_dir, "call_stack.html"), "wb") do |f|
43
+ RubyProf::CallStackPrinter.new(result).print(f)
44
+ end
45
+ sanitize_local_file_links(File.join(output_dir, "call_stack.html"))
46
+
47
+ # Graph HTML
48
+ File.open(File.join(output_dir, "graph.html"), "wb") do |f|
49
+ RubyProf::GraphHtmlPrinter.new(result).print(f)
50
+ end
51
+ sanitize_local_file_links(File.join(output_dir, "graph.html"))
52
+
53
+ # Graph (text)
54
+ File.open(File.join(output_dir, "graph.txt"), "wb") do |f|
55
+ RubyProf::GraphPrinter.new(result).print(f)
56
+ end
57
+
58
+ # Flat
59
+ File.open(File.join(output_dir, "flat.txt"), "wb") do |f|
60
+ RubyProf::FlatPrinter.new(result).print(f)
61
+ end
62
+
63
+ # Call Info
64
+ File.open(File.join(output_dir, "call_info.txt"), "wb") do |f|
65
+ RubyProf::CallInfoPrinter.new(result).print(f)
66
+ end
67
+
68
+ # Dot
69
+ dot_io = StringIO.new
70
+ RubyProf::DotPrinter.new(result).print(dot_io)
71
+ dot_content = dot_io.string
72
+ File.open(File.join(output_dir, "graph.dot"), "wb") do |f|
73
+ f << dot_content
74
+ end
75
+
76
+ # Graphviz Online viewer with dot content embedded in URL
77
+ viewer_url = "https://dreampuf.github.io/GraphvizOnline/?engine=dot#" + URI.encode_uri_component(dot_content)
78
+ File.open(File.join(output_dir, "graphviz_viewer.html"), "wb") do |f|
79
+ f << %(<html><head><meta http-equiv="refresh" content="0;url=#{viewer_url}"></head></html>)
80
+ end
81
+
82
+ # Call Tree (calltree format)
83
+ RubyProf::CallTreePrinter.new(result).print(path: output_dir, profile: "profile")
84
+ # Rename PID-based filename to a stable name
85
+ Dir.glob(File.join(output_dir, "callgrind.out.*")).each do |f|
86
+ FileUtils.mv(f, File.join(output_dir, "callgrind.out"))
87
+ end
88
+
89
+ puts "Reports written to #{output_dir}/"
90
+ Dir.glob(File.join(output_dir, "*")).sort.each do |f|
91
+ puts " #{File.basename(f)}"
92
+ end
@@ -0,0 +1,27 @@
1
+ ----------------------------------------------------
2
+ Thread ID: 464
3
+ Fiber ID: 456
4
+ Total Time: 0.007774999990942888
5
+ Sort by:
6
+
7
+ [global]# (tt:0.01, st:0.00, wt:0.00, ct:0.01, call:1, )
8
+ Object#run_example (tt:0.01, st:0.00, wt:0.00, ct:0.01, call:1, )
9
+ String#* (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
10
+ Object#normalize (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
11
+ String#downcase (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
12
+ String#gsub (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
13
+ Object#tokenize (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
14
+ String#split (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
15
+ Object#count_words (tt:0.01, st:0.00, wt:0.00, ct:0.01, call:1, )
16
+ Hash#initialize (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
17
+ Array#each (tt:0.01, st:0.00, wt:0.00, ct:0.00, call:1, )
18
+ Hash#[] (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:5800, )
19
+ Integer#+ (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:5800, )
20
+ Hash#[]= (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:5800, )
21
+ Object#top_words (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
22
+ Enumerable#sort_by (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
23
+ Hash#each (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
24
+ Integer#-@ (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:25, )
25
+ Array#take (tt:0.00, st:0.00, wt:0.00, ct:0.00, call:1, )
26
+
27
+