bleak_house 4.6 → 5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,63 +1,10 @@
1
1
 
2
- v4.6. Allow snapshotting without running the GC first.
3
-
4
- v4.5. Enable SET_CURRENT_SOURCE() for better reporting (tmm1, joedamato).
5
-
6
- v4.4. Switch to Ruby 1.8.7p174.
7
-
8
- v4.3. Build against OpenSSL 0.9.8j or later (kennethkalmer). Bugfixes (Pete Hodgson, joshuabates).
9
-
10
- v4.2.1. Harden patch/build process. Fix bug.
11
-
12
- v4.1.1. Update bundled Ruby to patchlevel 230.
13
-
14
- v4.1. Support calculating deltas in analyze utility.
15
-
16
- v4.0. Track spawn points live instead of sampling.
17
-
18
- v3.7.1. Fix some nil issues with corrupted dumps (cody caughlan).
19
-
20
- v3.7. Sample object contents. Restore Rails 1.2.x compatibility.
21
-
22
- v3.6. Rails 2.0.2 compatibility.
23
-
24
- v3.5.1. Update bundled Ruby to patchlevel 110.
25
-
26
- v3.5. Explain when you need more frames. Use a cross-platform version of 'which'. Slight accuracy improvement.
27
-
28
- v3.4. Clearer output descriptions; work around a Marshal bug on x64; fix for missing immortal leaks.
29
-
30
- v3.3. Build Ruby in gem install step; bundle Ruby 1.8.6 source; fixes for truncated final frames.
31
-
32
- v3.2. Use Ccsv for faster parsing.
33
-
34
- v3.0.3. Caching; build task; impact factor.
35
-
36
- v3.0.1. Rewrite. Remove Gruffs and graphs. Become a standard compiled extension. Walk the symbol table. Track object histories. Use custom CSV format instead of YAML.
37
-
38
- v2.7. Version renumbering; apologies if this messes anyone up.
39
-
40
- v2.6. RDoc documentation; use new Echoe setup; fix for namespaced controllers.
41
-
42
- v2.5. Detect if core keys are present or not; fixes for analyzing non-Rails apps.
43
-
44
- v2.4. Log heap usage; remove pure-Ruby profiler.
45
-
46
- v2.3. Subtract Rails core counts from action counts and join paired frames.
47
-
48
- v2.2. Change smoothing algorithm to be more intuitive and so mem usage is correct.
49
-
50
- v2.1. Close file properly.
51
-
52
- v2. C instrumentation with YAML output.
53
-
54
- v1.3. Log memory usage on *nix.
55
-
56
- v1.2. Fix nil directory traversal error on Windows.
57
-
58
- v1.1. Gem version
59
-
60
- v1. Dumped Rublique; replaced with a lightweight non-delta marshalling version.
61
-
62
- v0. First version
2
+ BleakHouse Changelog
3
+
4
+ 5. totally awesome C instrumentation
5
+ 4. log memory usage on *nix
6
+ 3.1. fix nil directory traversal error on windows
7
+ 3. gem version
8
+ 2. dumped rublique once I understood what it did; replaced with a lightweight non-delta marshalling version
9
+ 1. first version
63
10
 
File without changes
data/Manifest CHANGED
@@ -1,21 +1,22 @@
1
- CHANGELOG
2
- LICENSE
3
- LICENSE_BSD
4
- Manifest
5
- README
6
- Rakefile
7
- TODO
8
- bin/bleak
9
- ext/build_ruby.rb
10
- ext/build_snapshot.rb
11
- ext/extconf.rb
12
- ext/snapshot.c
13
- ext/snapshot.h
14
- lib/bleak_house.rb
15
- lib/bleak_house/analyzer.rb
16
- lib/bleak_house/hook.rb
17
- ruby/ruby-1.8.7-p174.tar.bz2
18
- ruby/ruby-1.8.7.patch
19
- test/benchmark/bench.rb
20
- test/test_helper.rb
21
- test/unit/test_bleak_house.rb
1
+ ./CHANGELOG
2
+ ./LICENSE_AFL
3
+ ./Manifest
4
+ ./README
5
+ ./Rakefile
6
+ ./init.rb
7
+ ./install.rb
8
+ ./lib/bleak_house/LICENSE_BSD
9
+ ./lib/bleak_house/action_controller.rb
10
+ ./lib/bleak_house/analyze.rb
11
+ ./lib/bleak_house/bleak_house.rb
12
+ ./lib/bleak_house/c.rb
13
+ ./lib/bleak_house/dispatcher.rb
14
+ ./lib/bleak_house/gruff_hacks.rb
15
+ ./lib/bleak_house/mem_logger.rb
16
+ ./lib/bleak_house/rake_task_redefine_task.rb
17
+ ./lib/bleak_house/ruby.rb
18
+ ./lib/bleak_house/support_methods.rb
19
+ ./lib/bleak_house.rb
20
+ ./patches/gc.c.patch
21
+ ./tasks/bleak_house_tasks.rake
22
+ ./test/unit/test_bleak_house.rb
data/README CHANGED
@@ -1,125 +1,41 @@
1
1
 
2
2
  BleakHouse
3
3
 
4
- A library for finding memory leaks. Patched for Ruby 1.8.7p72.
4
+ REQUIREMENTS:
5
5
 
6
- An experiment.
7
- Run git clone and then rake gem;
8
- gem install bleak_house
6
+ A *nix operating system. Windows is not supported.
9
7
 
10
- == License
8
+ Gems: 'gruff', 'rmagick', 'active_support', 'RubyInline'
11
9
 
12
- Copyright 2007, 2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. Portions copyright 2006 Eric Hodel and used with permission. See the included LICENSE_BSD file.
10
+ It is highly recommended that you compile the bleak_house patched
11
+ Ruby build. In the plugin directory:
12
+ rake ruby:build
13
13
 
14
- The public certificate for this gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
14
+ USAGE:
15
15
 
16
- If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
16
+ To profile your application:
17
+ RAILS_ENV=production BLEAK_HOUSE=true ruby-bleak-house ./script/server
18
+
19
+ Browse around manually, thrash your entire app with a script,
20
+ target troublesome controllers/actions, etc.
17
21
 
18
- == Features
22
+ Then, to analyze:
23
+ RAILS_ENV=production SMOOTHNESS=2 rake bleak_house:analyze
19
24
 
20
- * leak-proof C instrumentation
21
- * minimal impact on runtime performance
22
- * fast analysis step
23
- * tracks all objects allocated on the heap, including internal types like <tt>T_NODE</tt>
24
- * easy integration into any program, not just Rails
25
+ And then look in log/bleak_house/.
25
26
 
26
- == Requirements
27
+ TROUBLESHOOTING
27
28
 
28
- * A unix-like operating system
29
- * Ruby 1.8.7
29
+ If you see the error "Symbol not found: _rb_gc_heaps_used",
30
+ it means you installed the patched binary, but tried to run the
31
+ server with the regular binary.
30
32
 
31
- = Usage
33
+ COPYRIGHT AND LICENSING
32
34
 
33
- == Installation
35
+ Copyright (c) 2007 Cloudburst, LLC. See the included LICENSE_AFL
36
+ file.
34
37
 
35
- Install the gem:
36
- sudo gem install bleak_house
37
-
38
- The installation takes a long time because it compiles a patched Ruby 1.8.7 binary for you. It is installed as <tt>ruby-bleak-house</tt> alongside your regular <tt>ruby</tt> binary.
39
-
40
- Please see the forum ( http://rubyforge.org/forum/forum.php?forum_id=13983 ) if you have installation problems.
41
-
42
- == Usage
43
-
44
- We will profile a Rails app as an example. Note that BleakHouse works equally well in any Ruby program.
45
-
46
- First, to setup the app for profiling, add the following at the bottom of <tt>config/environment.rb</tt>:
47
- require 'bleak_house' if ENV['BLEAK_HOUSE']
48
-
49
- Then, to engage the logger (possibly in a live deployment situation), start a server instance as so:
50
- RAILS_ENV=production BLEAK_HOUSE=1 ruby-bleak-house ./script/server
51
-
52
- Look for the message:
53
- ** Bleakhouse: installed
54
-
55
- Exercise your app. After a couple hundred requests, hit CTRL-C. The server will stop and BleakHouse will produce a dumpfile in <tt>/tmp</tt>:
56
-
57
- ** BleakHouse: working...
58
- ** BleakHouse: complete
59
- ** Bleakhouse: run 'bleak /tmp/bleak.5979.000.dump' to analyze.
60
-
61
- To analyze it, just run the listed command. The top 20 leakiest lines will be listed:
62
-
63
- 191691 total objects
64
- Final heap size 191691 filled, 220961 free
65
- Displaying top 20 most common line/class pairs
66
- 89513 __null__:__null__:__node__
67
- 41438 __null__:__null__:String
68
- 2348 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:Array
69
- 1508 /opt/local//lib/ruby/gems/1.8/specifications/gettext-1.90.0.gemspec:14:String
70
- 1021 /opt/local//lib/ruby/gems/1.8/specifications/heel-0.2.0.gemspec:14:String
71
- 951 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:111:String
72
- 935 /opt/local//lib/ruby/site_ruby/1.8/rubygems/specification.rb:557:String
73
- 834 /opt/local//lib/ruby/site_ruby/1.8/rubygems/version.rb:146:Array
74
- ...
75
-
76
- You can pass an integer as the second parameter to <tt>bleak</tt> if you want to see more lines than the default.
77
-
78
- The underscored types are special Ruby internal structs, but can be real leaks just as easily as fullblown classes.
79
-
80
- = Extras
81
-
82
- == Injecting a signal
83
-
84
- You can send <tt>SIGUSR2</tt> to a BleakHouse-instrumented program to snag a dump at any time. Once the dump completes, the program will continue to run. Dumps are named based on the host process id, and sequential dumps are numbered in ascending order.
85
-
86
- == Tips
87
-
88
- Do not try to detect Rails leaks in <tt>development</tt> mode. Make a separate <tt>benchmark</tt> environment if you need to, and make sure all your production caching is turned on.
89
-
90
- It is normal to see lots of <tt>null:null</tt> references, especially for nodes. Using <tt>eval()</tt> too much can be a cause of node leaks. You can sometimes track <tt>eval()</tt> by using sourceline macros in your code:
91
-
92
- eval("CODE", nil, __FILE__, __LINE__)
93
-
94
- You may get library require errors if you install <tt>ruby-bleak-house</tt> 1.8.7 alongside a different verson of Ruby. You could try to patch your local version of Ruby instead, or you can get <tt>ruby-bleak-house</tt> to lie about its version. Just make sure that the <tt>bleak-house</tt> library is the first thing required (even before Rubygems):
95
-
96
- ruby-bleak-house -I `ruby -e 'puts \`gem which bleak_house\`.split("\n")[1][0..-16]'` -rbleak_house
97
-
98
- It is not recommended that you use <tt>ruby-bleak-house</tt> as your production Ruby binary, since it will be slightly slower and use slightly more memory. It is unlikely, however, to affect stability.
99
-
100
- If BleakHouse doesn't report any heap growth but you still have memory growth, you might have a broken C extension, or have encounted a {real leak in the interpreter}[http://groups.google.com/group/god-rb/browse_thread/thread/01cca2b7c4a581c2]. Try using Valgrind[http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/] instead.
101
-
102
- == Methods
103
-
104
- The easiest way to fix a leak is to make it repeatable.
105
-
106
- First, write a script that exercises your app in a deterministic way. Run it for a small number of loops; then run <tt>bleak</tt>. Then run it for a larger number of loops, and run <tt>bleak</tt> again. The lines that grow significantly between runs are your leaks for that codepath.
107
-
108
- Now, look at those lines in the source and try to figure out what references them. Where do the return values go? Add some breakpoints or output backtraces to <tt>STDERR</tt> as you go. Eventually you should find a point where it is relatively clear that a reference is getting maintained.
109
-
110
- Try to remove that reference, run your script again, and see if the object counts have dropped.
111
-
112
- == Reporting problems
113
-
114
- The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=13983].
115
-
116
- Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
117
-
118
- == Further resources
119
-
120
- * http://blog.evanweaver.com/articles/2008/04/06/bleakhouse-4/
121
- * http://blog.evanweaver.com/articles/2008/02/05/valgrind-and-ruby/
122
- * http://blog.evanweaver.com/articles/2007/05/12/let-me-hit-you-with-some-knowledge
123
- * http://blog.evanweaver.com/articles/2007/05/06/leak-proof-direct-heap-instrumentation-for-bleak_house
124
- * http://blog.evanweaver.com/articles/2007/04/28/bleak_house
38
+ Portions of lib/bleak_house/c.rb copyright (c) 2006 Eric Hodel and
39
+ used under license (see included the lib/bleak_house/LICENSE_BSD
40
+ file).
125
41
 
data/Rakefile CHANGED
@@ -1,12 +1,104 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'lib/bleak_house/rake_task_redefine_task'
1
4
 
2
- require 'echoe'
5
+ DIR = File.dirname(__FILE__)
3
6
 
4
- Echoe.new("bleak_house") do |p|
5
- p.author = "Evan Weaver"
6
- p.project = "fauna"
7
- p.summary = "A library for finding memory leaks."
8
- p.url = "http://blog.evanweaver.com/files/doc/fauna/bleak_house/"
9
- p.docs_host = 'blog.evanweaver.com:~/www/bax/public/files/doc/'
10
- p.require_signed = true
11
- p.rdoc_pattern = /^ext.*\.c|lib.*|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
7
+ begin
8
+ require 'rake/clean'
9
+ gem 'echoe', '>= 1.2'
10
+ require 'echoe'
11
+ require 'fileutils'
12
+ include FileUtils
13
+
14
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
15
+ VERS = `cat CHANGELOG`[/^([\d\.]+)\. /, 1]
16
+
17
+ taskmsg = File.open(DIR + "/tasks/bleak_house_tasks.rake").readlines
18
+ taskmsg = taskmsg[0..3] + [taskmsg[7][2..-1]] + taskmsg[9..-1]
19
+
20
+ echoe = Echoe.new("bleak_house", VERS) do |p|
21
+ p.author = "Evan Weaver"
22
+ p.rubyforge_name = "fauna"
23
+ p.name = "bleak_house"
24
+ p.description = "BleakHouse is a Rails plugin for finding memory leaks. It tracks ObjectSpace for your entire app, and produces charts of references by controller, by action, and by object class."
25
+ p.changes = `cat CHANGELOG`[/^([\d\.]+\. .*)/, 1]
26
+ p.email = "evan at cloudbur dot st"
27
+ p.summary = p.description
28
+ p.url = "http://blog.evanweaver.com"
29
+ p.need_tar = false
30
+ p.need_tar_gz = true
31
+ p.test_globs = ["*_test.rb"]
32
+ p.extra_deps = ["RubyInline"]
33
+ p.clean_globs = CLEAN
34
+ p.spec_extras = {:post_install_message =>
35
+ "
36
+ Thanks for installing Bleak House #{VERS}.
37
+
38
+ For each Rails app you want to profile, you will need to add the following
39
+ rake task in RAILS_ROOT/lib/tasks/bleak_house_tasks.rake to be able to run
40
+ the analyzer:
41
+ " + taskmsg.join(" ") + "\n"}
42
+ end
43
+
44
+ rescue LoadError => boom
45
+ puts "You are missing a dependency required for meta-operations on this gem."
46
+ puts "#{boom.to_s.capitalize}."
47
+
48
+ desc 'Run tests.'
49
+ task :default => :test
50
+ end
51
+
52
+ desc 'Run tests.'
53
+ Rake::Task.redefine_task("test") do
54
+ system "ruby-bleak-house -Ibin:lib:test test/unit/test_bleak_house.rb #{ENV['METHOD'] ? "--name=#{ENV['METHOD']}" : ""}"
12
55
  end
56
+
57
+ desc 'Build a patched binary.'
58
+ namespace :ruby do
59
+ task :build do
60
+ if RUBY_PLATFORM =~ /win32|windows/
61
+ puts "ERROR: windows not supported."
62
+ exit
63
+ end
64
+ require 'fileutils'
65
+ puts "building patched Ruby binary"
66
+ tmp = "/tmp/"
67
+ Dir.chdir(tmp) do
68
+ build_dir = "bleak_house"
69
+ binary_dir = File.dirname(`which ruby`)
70
+ FileUtils.rm_rf(build_dir) rescue nil
71
+ Dir.mkdir(build_dir)
72
+ begin
73
+ Dir.chdir(build_dir) do
74
+ puts " downloading Ruby source"
75
+ bz2 = "ruby-1.8.6.tar.bz2"
76
+ system("wget 'http://rubyforge.org/frs/download.php/20425/ruby-1.8.6.tar.bz2' &> wget.log")
77
+ puts " extracting"
78
+ system("tar xjf #{bz2} &> tar.log")
79
+ File.delete bz2
80
+ Dir.chdir("ruby-1.8.6") do
81
+ puts " patching" or system("patch -p0 < \'#{DIR}/patches/gc.c.patch\' &> ../patch.log")
82
+ puts " configuring" or system("./configure --prefix=#{binary_dir[0..-5]} &> ../configure.log") # --with-static-linked-ext
83
+ puts " making" or system("make &> ../make.log")
84
+
85
+ binary = "#{binary_dir}/ruby-bleak-house"
86
+ puts " installing as #{binary}"
87
+ FileUtils.cp("./ruby", binary)
88
+ FileUtils.chmod(0755, binary)
89
+ puts " done"
90
+ end
91
+ end
92
+ rescue Object => e
93
+ puts "ERROR: please see the last modified log file in #{tmp}#{build_dir}, perhaps\nit will contain a clue."
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ #def check_configure_size
100
+ # if (size = File.size("configure")) != 476155
101
+ # raise "Configure size is wrong (got #{size})"
102
+ # end
103
+ #end
104
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+
2
+ require 'bleak_house'
@@ -0,0 +1,7 @@
1
+ puts "
2
+
3
+ I was such a shy little thing that I seldom dared to open my lips,
4
+ and never dared to open my heart, to anybody else.
5
+ - Dickens, Bleak House
6
+
7
+ "
@@ -1,22 +1,13 @@
1
1
 
2
- unless RUBY_PATCHLEVEL >= 905
3
- raise "This build of Ruby has not been successfully patched for BleakHouse."
4
- end
5
-
6
- RUBY_VERSION = `ruby -v`.split(" ")[1]
7
- require 'snapshot'
8
- require 'bleak_house/hook'
2
+ if ENV['BLEAK_HOUSE']
9
3
 
10
- class << BleakHouse
11
- private :ext_snapshot
12
- end
4
+ require 'dispatcher' # rails
13
5
 
14
- module BleakHouse
6
+ require 'bleak_house/bleak_house'
7
+ require 'bleak_house/mem_logger'
8
+ require 'bleak_house/dispatcher'
9
+ require 'bleak_house/action_controller'
15
10
 
16
- # Walk the live, instrumented objects on the heap and write them to
17
- # <tt>logfile</tt>. Accepts an optional number of GC runs to perform
18
- # before dumping the heap.
19
- def self.snapshot(logfile, gc_runs = 3)
20
- ext_snapshot(logfile, gc_runs)
21
- end
11
+ BleakHouse.warn "enabled (log/bleak_house_#{RAILS_ENV}.dump)"
12
+
22
13
  end
@@ -0,0 +1,16 @@
1
+
2
+ class ActionController::Base
3
+ class << self
4
+ def process_with_bleak_house(request, *args)
5
+ BleakHouse.set_request_name request
6
+ process_without_bleak_house(request, *args)
7
+ end
8
+ alias_method_chain :process, :bleak_house
9
+
10
+ def process_with_exception_with_bleak_house(request, *args)
11
+ BleakHouse.set_request_name request, "/error"
12
+ process_with_exception_without_bleak_house(request, *args)
13
+ end
14
+ alias_method_chain :process_with_exception, :bleak_house
15
+ end
16
+ end
@@ -0,0 +1,135 @@
1
+
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require 'active_support'
6
+
7
+ gem 'gruff', '= 0.2.8'
8
+ require 'gruff'
9
+
10
+ # require, but make rdoc not whine
11
+ load "#{File.dirname(__FILE__)}/gruff_hacks.rb"
12
+ load "#{File.dirname(__FILE__)}/support_methods.rb"
13
+
14
+ Gruff::Base::LEFT_MARGIN = 200
15
+ Gruff::Base::NEGATIVE_TOP_MARGIN = 30
16
+ Gruff::Base::MAX_LEGENDS = 28
17
+
18
+ class BleakHouse
19
+ class Analyze
20
+
21
+ SMOOTHNESS = ((i = ENV['SMOOTHNESS'].to_i) < 2 ? 2 : i)/2 * 2
22
+ MEM_KEY = "memory usage"
23
+ DIR = "#{RAILS_ROOT}/log/bleak_house/"
24
+
25
+ def initialize(data, increments, name)
26
+ @data = data
27
+ @increments = increments
28
+ @name = name
29
+ end
30
+
31
+ def draw
32
+ g = Gruff::Line.new("1024x768")
33
+ g.title = @name
34
+ g.x_axis_label = "time"
35
+ g.legend_font_size = g.legend_box_size = 14
36
+ g.title_font_size = 24
37
+ g.marker_font_size = 14
38
+
39
+ @data.map do |key, values|
40
+ ["#{(key.to_s.empty? ? '[Unknown]' : key).gsub(/.*::/, '')} (#{key == MEM_KEY ? "relative" : values.to_i.max})", values] # hax
41
+ end.sort_by do |key, values|
42
+ 0 - key[/.*?([\d]+)\)$/, 1].to_i
43
+ end.each do |key, values|
44
+ g.data(key, values.to_i)
45
+ end
46
+
47
+ labels = {}
48
+ mod = (@increments.size / 4.0).ceil
49
+ @increments.each_with_index do |increment, index|
50
+ labels[index] = increment if (index % mod).zero?
51
+ end
52
+ g.labels = labels
53
+
54
+ g.minimum_value = 0
55
+ # g.maximum_value = @maximum
56
+
57
+ g.write(@name.to_filename + ".png")
58
+ end
59
+
60
+ def self.aggregate(data, selector, namer)
61
+ aggregate_data = {}
62
+ increments = []
63
+ data.each_with_index do |frameset, index|
64
+ increments << frameset.time
65
+ frameset.data.keys.select do |key|
66
+ key =~ selector #or key =~ Regexp.new(specials.keys.join('|'))
67
+ end.each do |key|
68
+ aggregate_data[key.to_s[namer, 1]] ||= []
69
+ aggregate_data[key.to_s[namer, 1]][index] += frameset.data[key].to_i
70
+ end
71
+ end
72
+ [aggregate_data, increments]
73
+ end
74
+
75
+ def self.build_all(filename)
76
+ unless File.exists? filename
77
+ puts "No data file found: #{filename}"
78
+ exit
79
+ end
80
+ FileUtils.rm_r(DIR) rescue nil
81
+ Dir.mkdir(DIR)
82
+
83
+ Dir.chdir(DIR) do
84
+
85
+ puts "parsing data"
86
+ data = YAML.load_file(filename)
87
+ data = data[0..(-1 - data.size % SMOOTHNESS)]
88
+ data = data.in_groups_of(SMOOTHNESS).map do |frames|
89
+ timestamp = frames.map(&:time).sum / SMOOTHNESS
90
+ values = frames.map(&:data).inject(Hash.new(0)) do |total, this_frame|
91
+ this_frame.each do |key, value|
92
+ total[key] += value
93
+ end
94
+ total
95
+ end
96
+ [Time.at(timestamp).strftime("%H:%M:%S"), values]
97
+ end
98
+ puts "#{data.size} frames after smoothing"
99
+
100
+ puts "entire app"
101
+ controller_data, increments = aggregate(data, //, /^(.*?)($|\/|::::)/)
102
+ if controller_data.has_key? MEM_KEY
103
+ controller_data_without_memory = controller_data.dup
104
+ controller_data_without_memory.delete(MEM_KEY)
105
+ scale_factor = controller_data_without_memory.values.flatten.to_i.max / controller_data[MEM_KEY].max.to_f * 0.8 rescue 1
106
+ controller_data[MEM_KEY] = controller_data[MEM_KEY].map{|x| (x * scale_factor).to_i }
107
+ end
108
+ Analyze.new(controller_data, increments, "objects by controller").draw
109
+
110
+ # in each controller, by action
111
+ controller_data.keys.each do |controller|
112
+ @mem = (controller == MEM_KEY)
113
+ puts(@mem ? " #{controller}" : " action for #{controller} controller")
114
+ Dir.descend(controller) do
115
+ action_data, increments = aggregate(data, /^#{controller}($|\/|::::)/, /\/(.*?)($|\/|::::)/)
116
+ Analyze.new(action_data, increments, @mem ? "#{controller} in kilobytes" : "objects by action in /#{controller}_controller").draw
117
+
118
+ # in each action, by object class
119
+ action_data.keys.each do |action|
120
+ action = "unknown" if action.to_s == ""
121
+ puts " class for #{action} action"
122
+ Dir.descend(action) do
123
+ class_data, increments = aggregate(data, /^#{controller}#{"\/#{action}" unless action == "unknown"}($|\/|::::)/,
124
+ /::::(.*)/)
125
+ Analyze.new(class_data, increments, "objects by class in /#{controller}/#{action}").draw
126
+ end
127
+ end unless @mem
128
+
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ end
135
+ end