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.
- checksums.yaml +4 -4
- data/{CHANGES → CHANGELOG.md} +115 -178
- data/README.md +5 -5
- data/bin/ruby-prof +1 -4
- data/bin/ruby-prof-check-trace +45 -45
- data/docs/advanced-usage.md +132 -0
- data/docs/alternatives.md +98 -0
- data/docs/architecture.md +122 -0
- data/docs/best-practices.md +27 -0
- data/docs/getting-started.md +130 -0
- data/docs/history.md +11 -0
- data/docs/index.md +45 -0
- data/docs/profiling-rails.md +64 -0
- data/docs/public/examples/example.rb +33 -0
- data/docs/public/examples/generate_reports.rb +92 -0
- data/docs/public/examples/reports/call_info.txt +27 -0
- data/docs/public/examples/reports/call_stack.html +835 -0
- data/docs/public/examples/reports/callgrind.out +150 -0
- data/docs/public/examples/reports/flame_graph.html +408 -0
- data/docs/public/examples/reports/flat.txt +45 -0
- data/docs/public/examples/reports/graph.dot +129 -0
- data/docs/public/examples/reports/graph.html +1319 -0
- data/docs/public/examples/reports/graph.txt +100 -0
- data/docs/public/examples/reports/graphviz_viewer.html +1 -0
- data/docs/public/images/call_stack.png +0 -0
- data/docs/public/images/class_diagram.png +0 -0
- data/docs/public/images/dot_printer.png +0 -0
- data/docs/public/images/flame_graph.png +0 -0
- data/docs/public/images/flat.png +0 -0
- data/docs/public/images/graph.png +0 -0
- data/docs/public/images/graph_html.png +0 -0
- data/docs/public/images/ruby-prof-logo.svg +1 -0
- data/docs/reports.md +150 -0
- data/docs/stylesheets/extra.css +80 -0
- data/ext/ruby_prof/rp_allocation.c +0 -15
- data/ext/ruby_prof/rp_allocation.h +29 -33
- data/ext/ruby_prof/rp_call_tree.c +3 -0
- data/ext/ruby_prof/rp_call_tree.h +1 -4
- data/ext/ruby_prof/rp_call_trees.h +1 -4
- data/ext/ruby_prof/rp_measurement.c +0 -5
- data/ext/ruby_prof/rp_measurement.h +49 -53
- data/ext/ruby_prof/rp_method.c +3 -0
- data/ext/ruby_prof/rp_method.h +1 -4
- data/ext/ruby_prof/rp_profile.c +1 -1
- data/ext/ruby_prof/rp_profile.h +1 -5
- data/ext/ruby_prof/rp_stack.h +50 -53
- data/ext/ruby_prof/rp_thread.h +1 -4
- data/ext/ruby_prof/ruby_prof.h +1 -4
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +7 -8
- data/lib/ruby-prof/assets/call_stack_printer.html.erb +746 -711
- data/lib/ruby-prof/assets/flame_graph_printer.html.erb +412 -0
- data/lib/ruby-prof/assets/graph_printer.html.erb +355 -355
- data/lib/ruby-prof/call_tree.rb +57 -57
- data/lib/ruby-prof/call_tree_visitor.rb +36 -36
- data/lib/ruby-prof/measurement.rb +17 -17
- data/lib/ruby-prof/printers/abstract_printer.rb +19 -33
- data/lib/ruby-prof/printers/call_info_printer.rb +53 -53
- data/lib/ruby-prof/printers/call_stack_printer.rb +168 -180
- data/lib/ruby-prof/printers/call_tree_printer.rb +132 -145
- data/lib/ruby-prof/printers/dot_printer.rb +177 -132
- data/lib/ruby-prof/printers/flame_graph_printer.rb +79 -0
- data/lib/ruby-prof/printers/flat_printer.rb +52 -52
- data/lib/ruby-prof/printers/graph_html_printer.rb +62 -63
- data/lib/ruby-prof/printers/graph_printer.rb +112 -113
- data/lib/ruby-prof/printers/multi_printer.rb +134 -127
- data/lib/ruby-prof/profile.rb +13 -0
- data/lib/ruby-prof/rack.rb +114 -105
- data/lib/ruby-prof/task.rb +147 -147
- data/lib/ruby-prof/thread.rb +20 -20
- data/lib/ruby-prof/version.rb +1 -1
- data/lib/ruby-prof.rb +50 -52
- data/lib/unprof.rb +10 -10
- data/ruby-prof.gemspec +66 -66
- data/test/abstract_printer_test.rb +25 -27
- data/test/alias_test.rb +203 -117
- data/test/call_tree_builder.rb +9 -9
- data/test/call_tree_visitor_test.rb +27 -27
- data/test/call_trees_test.rb +66 -66
- data/test/duplicate_names_test.rb +32 -32
- data/test/dynamic_method_test.rb +50 -50
- data/test/exceptions_test.rb +1 -1
- data/test/exclude_threads_test.rb +48 -48
- data/test/fiber_test.rb +72 -72
- data/test/inverse_call_tree_test.rb +174 -174
- data/test/line_number_test.rb +138 -1
- data/test/marshal_test.rb +2 -3
- data/test/measure_allocations.rb +26 -26
- data/test/measure_allocations_test.rb +340 -1
- data/test/measure_process_time_test.rb +3098 -3142
- data/test/measure_times.rb +56 -56
- data/test/measure_wall_time_test.rb +511 -372
- data/test/measurement_test.rb +82 -82
- data/test/merge_test.rb +48 -48
- data/test/multi_printer_test.rb +52 -66
- data/test/no_method_class_test.rb +15 -15
- data/test/pause_resume_test.rb +171 -171
- data/test/prime.rb +54 -54
- data/test/prime_script.rb +5 -5
- data/test/printer_call_stack_test.rb +4 -3
- data/test/printer_call_tree_test.rb +30 -30
- data/test/printer_flame_graph_test.rb +82 -0
- data/test/printer_flat_test.rb +3 -3
- data/test/printer_graph_html_test.rb +12 -9
- data/test/printer_graph_test.rb +5 -3
- data/test/printers_test.rb +162 -178
- data/test/printing_recursive_graph_test.rb +4 -4
- data/test/profile_test.rb +2 -2
- data/test/rack_test.rb +15 -5
- data/test/recursive_test.rb +139 -139
- data/test/scheduler.rb +367 -363
- data/test/singleton_test.rb +2 -1
- data/test/stack_printer_test.rb +61 -61
- data/test/start_stop_test.rb +106 -106
- data/test/test_helper.rb +4 -0
- data/test/thread_test.rb +229 -229
- data/test/unique_call_path_test.rb +123 -123
- data/test/yarv_test.rb +2 -2
- metadata +53 -11
- data/ext/ruby_prof/rp_measure_memory.c +0 -46
- data/lib/ruby-prof/compatibility.rb +0 -113
- data/test/compatibility_test.rb +0 -49
- 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:|<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
|
+
|