rack-allocation_stats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/demo_rack_apps/Rails_3.2.15/app/controllers/application_controller.rb +3 -0
  3. data/demo_rack_apps/Rails_3.2.15/app/controllers/projects_controller.rb +83 -0
  4. data/demo_rack_apps/Rails_3.2.15/app/controllers/tasks_controller.rb +83 -0
  5. data/demo_rack_apps/Rails_3.2.15/app/helpers/application_helper.rb +2 -0
  6. data/demo_rack_apps/Rails_3.2.15/app/helpers/projects_helper.rb +2 -0
  7. data/demo_rack_apps/Rails_3.2.15/app/helpers/tasks_helper.rb +2 -0
  8. data/demo_rack_apps/Rails_3.2.15/app/models/project.rb +4 -0
  9. data/demo_rack_apps/Rails_3.2.15/app/models/task.rb +4 -0
  10. data/demo_rack_apps/Rails_3.2.15/config/application.rb +63 -0
  11. data/demo_rack_apps/Rails_3.2.15/config/boot.rb +6 -0
  12. data/demo_rack_apps/Rails_3.2.15/config/environment.rb +5 -0
  13. data/demo_rack_apps/Rails_3.2.15/config/environments/development.rb +37 -0
  14. data/demo_rack_apps/Rails_3.2.15/config/environments/production.rb +67 -0
  15. data/demo_rack_apps/Rails_3.2.15/config/environments/test.rb +37 -0
  16. data/demo_rack_apps/Rails_3.2.15/config/initializers/backtrace_silencers.rb +7 -0
  17. data/demo_rack_apps/Rails_3.2.15/config/initializers/inflections.rb +15 -0
  18. data/demo_rack_apps/Rails_3.2.15/config/initializers/mime_types.rb +5 -0
  19. data/demo_rack_apps/Rails_3.2.15/config/initializers/secret_token.rb +7 -0
  20. data/demo_rack_apps/Rails_3.2.15/config/initializers/session_store.rb +8 -0
  21. data/demo_rack_apps/Rails_3.2.15/config/initializers/wrap_parameters.rb +14 -0
  22. data/demo_rack_apps/Rails_3.2.15/config/routes.rb +64 -0
  23. data/demo_rack_apps/Rails_3.2.15/db/migrate/20131104031828_create_projects.rb +11 -0
  24. data/demo_rack_apps/Rails_3.2.15/db/migrate/20131104031829_create_tasks.rb +11 -0
  25. data/demo_rack_apps/Rails_3.2.15/db/schema.rb +32 -0
  26. data/demo_rack_apps/Rails_3.2.15/db/seeds.rb +15 -0
  27. data/demo_rack_apps/Rails_3.2.15/test/functional/projects_controller_test.rb +49 -0
  28. data/demo_rack_apps/Rails_3.2.15/test/functional/tasks_controller_test.rb +49 -0
  29. data/demo_rack_apps/Rails_3.2.15/test/performance/browsing_test.rb +12 -0
  30. data/demo_rack_apps/Rails_3.2.15/test/test_helper.rb +13 -0
  31. data/demo_rack_apps/Rails_3.2.15/test/unit/helpers/projects_helper_test.rb +4 -0
  32. data/demo_rack_apps/Rails_3.2.15/test/unit/helpers/tasks_helper_test.rb +4 -0
  33. data/demo_rack_apps/Rails_3.2.15/test/unit/project_test.rb +7 -0
  34. data/demo_rack_apps/Rails_3.2.15/test/unit/task_test.rb +7 -0
  35. data/demo_rack_apps/Rails_4.0.1/app/controllers/application_controller.rb +5 -0
  36. data/demo_rack_apps/Rails_4.0.1/app/controllers/projects_controller.rb +74 -0
  37. data/demo_rack_apps/Rails_4.0.1/app/controllers/tasks_controller.rb +74 -0
  38. data/demo_rack_apps/Rails_4.0.1/app/helpers/application_helper.rb +2 -0
  39. data/demo_rack_apps/Rails_4.0.1/app/helpers/projects_helper.rb +2 -0
  40. data/demo_rack_apps/Rails_4.0.1/app/helpers/tasks_helper.rb +2 -0
  41. data/demo_rack_apps/Rails_4.0.1/app/models/project.rb +3 -0
  42. data/demo_rack_apps/Rails_4.0.1/app/models/task.rb +3 -0
  43. data/demo_rack_apps/Rails_4.0.1/config/application.rb +24 -0
  44. data/demo_rack_apps/Rails_4.0.1/config/boot.rb +4 -0
  45. data/demo_rack_apps/Rails_4.0.1/config/environment.rb +5 -0
  46. data/demo_rack_apps/Rails_4.0.1/config/environments/development.rb +29 -0
  47. data/demo_rack_apps/Rails_4.0.1/config/environments/production.rb +80 -0
  48. data/demo_rack_apps/Rails_4.0.1/config/environments/test.rb +36 -0
  49. data/demo_rack_apps/Rails_4.0.1/config/initializers/backtrace_silencers.rb +7 -0
  50. data/demo_rack_apps/Rails_4.0.1/config/initializers/filter_parameter_logging.rb +4 -0
  51. data/demo_rack_apps/Rails_4.0.1/config/initializers/inflections.rb +16 -0
  52. data/demo_rack_apps/Rails_4.0.1/config/initializers/mime_types.rb +5 -0
  53. data/demo_rack_apps/Rails_4.0.1/config/initializers/secret_token.rb +12 -0
  54. data/demo_rack_apps/Rails_4.0.1/config/initializers/session_store.rb +3 -0
  55. data/demo_rack_apps/Rails_4.0.1/config/initializers/wrap_parameters.rb +14 -0
  56. data/demo_rack_apps/Rails_4.0.1/config/routes.rb +60 -0
  57. data/demo_rack_apps/Rails_4.0.1/db/migrate/20131102201320_create_projects.rb +11 -0
  58. data/demo_rack_apps/Rails_4.0.1/db/migrate/20131102201321_create_tasks.rb +11 -0
  59. data/demo_rack_apps/Rails_4.0.1/db/schema.rb +32 -0
  60. data/demo_rack_apps/Rails_4.0.1/db/seeds.rb +15 -0
  61. data/demo_rack_apps/Rails_4.0.1/test/controllers/projects_controller_test.rb +49 -0
  62. data/demo_rack_apps/Rails_4.0.1/test/controllers/tasks_controller_test.rb +49 -0
  63. data/demo_rack_apps/Rails_4.0.1/test/helpers/projects_helper_test.rb +4 -0
  64. data/demo_rack_apps/Rails_4.0.1/test/helpers/tasks_helper_test.rb +4 -0
  65. data/demo_rack_apps/Rails_4.0.1/test/models/project_test.rb +7 -0
  66. data/demo_rack_apps/Rails_4.0.1/test/models/task_test.rb +7 -0
  67. data/demo_rack_apps/Rails_4.0.1/test/test_helper.rb +15 -0
  68. data/lib/rack/allocation_stats.rb +22 -0
  69. data/lib/rack/allocation_stats/action.rb +14 -0
  70. data/lib/rack/allocation_stats/call_app_directly.rb +14 -0
  71. data/lib/rack/allocation_stats/formatters/base.rb +11 -0
  72. data/lib/rack/allocation_stats/formatters/html.rb +23 -0
  73. data/lib/rack/allocation_stats/formatters/json.rb +12 -0
  74. data/lib/rack/allocation_stats/formatters/text.rb +33 -0
  75. data/lib/rack/allocation_stats/middleware.rb +62 -0
  76. data/lib/rack/allocation_stats/tracer.rb +80 -0
  77. data/spec/factories.rb +53 -0
  78. data/spec/hello_world_app.rb +21 -0
  79. data/spec/rack/allocation_stats_spec.rb +216 -0
  80. data/spec/sinatra_app.rb +10 -0
  81. data/spec/sinatra_templates_app.rb +85 -0
  82. data/spec/spec_helper.rb +55 -0
  83. data/spec/yajl_app.rb +20 -0
  84. data/spec/yaml_app.rb +20 -0
  85. metadata +169 -0
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class TasksControllerTest < ActionController::TestCase
4
+ setup do
5
+ @task = tasks(:one)
6
+ end
7
+
8
+ test "should get index" do
9
+ get :index
10
+ assert_response :success
11
+ assert_not_nil assigns(:tasks)
12
+ end
13
+
14
+ test "should get new" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+
19
+ test "should create task" do
20
+ assert_difference('Task.count') do
21
+ post :create, task: { description: @task.description, name: @task.name, project_id: @task.project_id }
22
+ end
23
+
24
+ assert_redirected_to task_path(assigns(:task))
25
+ end
26
+
27
+ test "should show task" do
28
+ get :show, id: @task
29
+ assert_response :success
30
+ end
31
+
32
+ test "should get edit" do
33
+ get :edit, id: @task
34
+ assert_response :success
35
+ end
36
+
37
+ test "should update task" do
38
+ patch :update, id: @task, task: { description: @task.description, name: @task.name, project_id: @task.project_id }
39
+ assert_redirected_to task_path(assigns(:task))
40
+ end
41
+
42
+ test "should destroy task" do
43
+ assert_difference('Task.count', -1) do
44
+ delete :destroy, id: @task
45
+ end
46
+
47
+ assert_redirected_to tasks_path
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class ProjectsHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class TasksHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ProjectTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class TaskTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,15 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ ActiveRecord::Migration.check_pending!
7
+
8
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
9
+ #
10
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
11
+ # -- they do not yet inherit this setting
12
+ fixtures :all
13
+
14
+ # Add more helper methods to be used by all tests here...
15
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require "bundler/setup"
5
+ require "rack"
6
+ require "allocation_stats"
7
+
8
+ require_relative "allocation_stats/action"
9
+ require_relative "allocation_stats/call_app_directly"
10
+ require_relative "allocation_stats/middleware"
11
+ require_relative "allocation_stats/tracer"
12
+
13
+ require_relative "allocation_stats/formatters/base"
14
+ require_relative "allocation_stats/formatters/html"
15
+ require_relative "allocation_stats/formatters/json"
16
+ require_relative "allocation_stats/formatters/text"
17
+
18
+ module Rack::AllocationStats
19
+ def self.new(app, options = {})
20
+ Middleware.new(app, options)
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class Action
6
+ def initialize(env, middleware)
7
+ @env = env
8
+ @request = Rack::Request.new(env)
9
+ @get_params = @request.GET.clone
10
+ @middleware = middleware
11
+ @stats = nil
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class CallAppDirectly < Action
6
+ def act
7
+ @result = @middleware.call_app(@env)
8
+ end
9
+
10
+ def response
11
+ @result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats::Formatters
5
+ class Base
6
+ def initialize(tracer, allocations)
7
+ @tracer = tracer
8
+ @allocations = allocations
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::HTML < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ interactive_dir = File.join(__dir__, "..", "interactive")
7
+
8
+ index_html_erb = ERB.new(File.read(File.join(interactive_dir, "index.html.erb")))
9
+ jquery_min_js = File.read(File.join(interactive_dir, "jquery-2.0.3.min.js"))
10
+ jquery_ui_min_js = File.read(File.join(interactive_dir, "jquery-ui-1.10.3.custom.min.js"))
11
+ underscore_min_js = File.read(File.join(interactive_dir, "underscore-min.js"))
12
+ allocations_js_erb = ERB.new(File.read(File.join(interactive_dir, "allocations.js.erb")))
13
+ allocations_js = allocations_js_erb.result(binding)
14
+ interactive_js = File.read(File.join(interactive_dir, "interactive.js"))
15
+ jquery_ui_min_css = File.read(File.join(interactive_dir, "jquery-ui-1.10.3.custom.min.css"))
16
+ style_css = File.read(File.join(interactive_dir, "style.css"))
17
+
18
+ allocating_gems = ["no gems allocated any objects"]
19
+
20
+ html_body = index_html_erb.result(binding)
21
+ return [html_body]
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::JSON < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ allocations = @allocations.to_a.map do |group_key, allocations|
7
+ { group_key: group_key, allocations: allocations }
8
+ end
9
+
10
+ return [Yajl::Encoder.encode(allocations)]
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ class Rack::AllocationStats::Formatters::Text < Rack::AllocationStats::Formatters::Base
5
+ def format
6
+ file_length = @allocations.inject(0) { |length, allocation| [allocation[0][0].length , length].max }
7
+
8
+ total_count = @tracer.stats.allocations.all.size
9
+ bytes = @tracer.stats.allocations.bytes.all.inject { |sum, e| sum + e }
10
+ sums = "Total: #{total_count} allocations; #{bytes} bytes\n\n"
11
+
12
+ body = []
13
+
14
+ if @tracer.gc_report
15
+ gc_report = @tracer.stats.gc_profiler_report + "\n\n"
16
+ body += [gc_report]
17
+ end
18
+
19
+ running_total = 0
20
+ body + @allocations.map do |key, group|
21
+ sourcefile = key[0]
22
+ sourceline = key[1]
23
+ klass = key[2]
24
+ my_pct = (group.size * 1.0 / total_count) * 100
25
+ running_total += group.size
26
+ running_pct = (running_total * 1.0 / total_count) * 100
27
+
28
+ # TODO add a parameter to show/hide pct
29
+ #"%-#{file_length+4}s allocated %4d `%s` (%4.2f %4.2f)\n" % ["#{sourcefile}:#{sourceline}", group.size, klass, my_pct, running_pct]
30
+ "%-#{file_length+4}s allocated %4d `%s`\n" % ["#{sourcefile}:#{sourceline}", group.size, klass]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,62 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ module Rack::AllocationStats
5
+ class Middleware
6
+ include Rack::Utils
7
+
8
+ def initialize(app, options = {})
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @env = env
14
+ action = choose_action
15
+ action.act
16
+ action.response
17
+ end
18
+
19
+ def call_app(env)
20
+ @app.call(env)
21
+ end
22
+
23
+ def choose_action
24
+ request = Rack::Request.new(@env)
25
+ if request.GET["ras"] && request.GET["ras"].has_key?("help")
26
+ @content_type = "text/plain"
27
+ help_text
28
+ elsif request.GET["ras"] && request.GET["ras"]["trace"]
29
+ @content_type = content_type(request.GET["ras"]["output"])
30
+ Tracer.new(@env, self)
31
+ else
32
+ CallAppDirectly.new(@env, self)
33
+ end
34
+ end
35
+
36
+ def content_type(output_type)
37
+ case output_type
38
+ when "interactive" then "text/html"
39
+ when "json" then "application/json"
40
+ else "text/plain"
41
+ end
42
+ end
43
+
44
+ def allocation_stats_response(body)
45
+ [200, headers(body), Array(body)]
46
+ end
47
+
48
+ def headers(body)
49
+ {
50
+ "Content-Type" => @content_type,
51
+ "Content-Length" => body.inject(0) { |len, part| len + bytesize(part) }.to_s
52
+ }
53
+ end
54
+
55
+ def help_text
56
+ app = OpenStruct.new
57
+ app.body = [File.read(File.join(__dir__, "help.txt"))]
58
+ app.response = allocation_stats_response(app.body)
59
+ app
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,80 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require 'yajl'
5
+
6
+ module Rack::AllocationStats
7
+ class Tracer < Action
8
+ include Rack::Utils
9
+
10
+ attr_reader :gc_report, :stats
11
+
12
+ def initialize(*args)
13
+ super
14
+ request = Rack::Request.new(@env)
15
+
16
+ # Once we're here, GET["ras"] better exist
17
+ @scope = request.GET["ras"]["scope"]
18
+ @times = (request.GET["ras"]["times"] || 1).to_i
19
+ @gc_report = request.GET["ras"]["gc_report"]
20
+ @output = (request.GET["ras"]["output"] || :columnar).to_sym
21
+ @alias_paths = request.GET["ras"]["alias_paths"] || false
22
+ @new_env = delete_custom_params(@env)
23
+ end
24
+
25
+ def act
26
+ @stats = AllocationStats.trace do
27
+ @times.times do
28
+ @middleware.call_app(@new_env)
29
+ end
30
+ end
31
+ end
32
+
33
+ def response
34
+ allocations = scoped_allocations
35
+
36
+ if @output == :interactive
37
+ allocations = allocations.all
38
+ @middleware.allocation_stats_response(Formatters::HTML.new(self, allocations).format)
39
+ elsif @output == :json
40
+ allocations = default_groups_and_sort(allocations)
41
+ @middleware.allocation_stats_response(Formatters::JSON.new(self, allocations).format)
42
+ else
43
+ allocations = default_groups_and_sort(allocations)
44
+ @middleware.allocation_stats_response(Formatters::Text.new(self, allocations).format)
45
+ end
46
+ end
47
+
48
+ def scoped_allocations
49
+ allocations = @stats.allocations(alias_paths: @alias_paths)
50
+
51
+ return allocations if @scope.nil?
52
+
53
+ if @scope == "."
54
+ return allocations.from_pwd
55
+ else
56
+ return allocations.from(@scope)
57
+ end
58
+ end
59
+
60
+ def default_groups_and_sort(allocations)
61
+ allocations.
62
+ group_by(:sourcefile, :sourceline, :class_plus).
63
+ sort_by_size.
64
+ all
65
+ end
66
+
67
+ def delete_custom_params(env)
68
+ new_env = env
69
+
70
+ get_params = Rack::Request.new(new_env).GET
71
+ get_params.delete("ras")
72
+
73
+ new_env.delete("rack.request.query_string")
74
+ new_env.delete("rack.request.query_hash")
75
+
76
+ new_env["QUERY_STRING"] = build_query(get_params)
77
+ new_env
78
+ end
79
+ end
80
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,53 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ FactoryGirl.define do
5
+ factory :allocation, class: AllocationStats::Allocation do
6
+
7
+ ignore do
8
+ object "A String"
9
+ sourcefile "foo/bar.rb"
10
+ sourceline 7
11
+ end
12
+
13
+ initialize_with { new(object) }
14
+
15
+ after(:build) do |allocation, evaluator|
16
+ allocation.instance_variable_set(:@sourcefile, evaluator.sourcefile)
17
+ allocation.instance_variable_set(:@sourceline, evaluator.sourceline)
18
+ end
19
+ end
20
+
21
+ # This might be overly complicated for now... but it was terribly fun to learn
22
+ # FactoryGirl's API, and I'll leave it for future use...
23
+ factory :stats, class: AllocationStats do
24
+ ignore do
25
+ size 5
26
+ files ["foo/bar.rb", "baz/quux.rb"]
27
+ sourceline 7
28
+ end
29
+
30
+ after(:build) do |stats, evaluator|
31
+ allocations = []
32
+ size = evaluator.size
33
+ files_count = evaluator.files.size
34
+
35
+ # 3 from 1st file
36
+ if size > 0
37
+ file = evaluator.files[0]
38
+ count = [size, 3].min
39
+ count.times { allocations << FactoryGirl.build(:allocation, sourcefile: file, sourceline: evaluator.sourceline) }
40
+ size -= count
41
+ end
42
+
43
+ # more from 2nd file
44
+ if size > 0
45
+ file = evaluator.files[1 % files_count]
46
+ size.times { allocations << FactoryGirl.build(:allocation, sourcefile: file, sourceline: evaluator.sourceline) }
47
+ size -= count
48
+ end
49
+
50
+ stats.instance_variable_set(:@new_allocations, allocations)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ require "rack"
5
+ require "yaml"
6
+
7
+ class HelloWorldApp
8
+ FULL_PATH = __FILE__
9
+
10
+ attr_accessor :allocating_lines
11
+
12
+ def initialize
13
+ @allocating_lines = []
14
+ end
15
+
16
+ def call(env)
17
+ a = "Ahhhhhhhhhhhhhhhhhhhhh"; @allocating_lines << __LINE__
18
+ #y = YAML.dump(["one string", "two string"]) # lots OF objects not from here
19
+ @allocating_lines << __LINE__; [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]
20
+ end
21
+ end