memory_tracker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module MemoryTracker
4
+ describe MemoryTracker do
5
+ before :each do
6
+ @env = double('env')
7
+ allow(@env).to receive(:controller) { 'Boat' }
8
+ allow(@env).to receive(:action) { 'sail' }
9
+
10
+ @memory_store = double('memory_store')
11
+ @logfile_store = double('logfile_store')
12
+ @tracker = MemoryTracker.instance
13
+ @tracker.stores.clear
14
+ end
15
+
16
+ it 'should be a singleton' do
17
+ lambda { MemoryTracker.new }.should raise_error(NoMethodError)
18
+ end
19
+
20
+ it 'should push requests to all stores' do
21
+ allow(@memory_store).to receive(:name) { :memory }
22
+ allow(@logfile_store).to receive(:name) { :gcstat_logfile }
23
+ expect(@memory_store).to receive(:push)
24
+ expect(@logfile_store).to receive(:push)
25
+
26
+ @tracker.add_store(@memory_store)
27
+ @tracker.add_store(@logfile_store)
28
+
29
+ @tracker.start_request(@env)
30
+ @tracker.end_request
31
+ end
32
+
33
+ it 'should populate livestore' do
34
+ @tracker.add_store(Stores::InMemoryStore::Manager.new)
35
+
36
+ Request.stub(:rss) { 100 }
37
+ @tracker.start_request(@env)
38
+ Request.stub(:rss) { 108 }
39
+ @tracker.end_request
40
+
41
+ stats = @tracker.stats(:memory)
42
+ stats.count('Boat', 'sail').should == 1
43
+ stats.count('Boat', 'moor').should == 0
44
+ stats.fetch('Boat', 'sail', :rss).should == 8
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module MemoryTracker
4
+ describe Request do
5
+ before :each do
6
+ @env = double('env')
7
+ @request = Request.new(@env)
8
+ end
9
+
10
+ it 'should initalize start_gcstat' do
11
+ @request.start_gcstat.should be_a(GcStat)
12
+ @request.start_gcstat.stats.keys.should include :rss
13
+ @request.start_gcstat.stats.keys.should include :vsize
14
+ end
15
+
16
+ it 'should have a controller' do
17
+ allow(@env).to receive(:controller) { 'Foo' }
18
+ allow(@env).to receive(:action) { :bar }
19
+ @request.controller.should == 'Foo'
20
+ @request.action.should == :bar
21
+ end
22
+
23
+ context :close do
24
+ it 'should initialize end_gcstat' do
25
+ @request.close
26
+ @request.end_gcstat.should be_a(GcStat)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ module MemoryTracker
2
+ module Stores
3
+ describe GcstatLogfileStore do
4
+ before :each do
5
+ logger_class = double("logger_class")
6
+ allow(logger_class).to receive(:new)
7
+ @logstore = GcstatLogfileStore.new logger_class, "foo/log"
8
+ end
9
+
10
+ it 'implements the store role' do
11
+ @logstore.should respond_to(:name)
12
+ @logstore.should respond_to(:push)
13
+ @logstore.should respond_to(:stats)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ def sample_stats(factor=1)
4
+ base = {
5
+ :rss => 1,
6
+ :vsize => 2,
7
+ :count => 4,
8
+ :heap_used => 86,
9
+ :heap_length => 138,
10
+ :heap_increment =>52,
11
+ :heap_live_num =>34768,
12
+ :heap_free_num =>22099,
13
+ :heap_final_num =>0,
14
+ :total_allocated_object =>68540,
15
+ :total_freed_object =>33772
16
+ }
17
+ sample = {}
18
+ base.each { |k,v| sample[k] = v * factor }
19
+ sample
20
+ end
21
+
22
+ def stub_gcstat_delta(controller, action, factor=1)
23
+ allow(@env).to receive(:controller) { controller }
24
+ allow(@env).to receive(:action) { action }
25
+ MemoryTracker::GcStatDelta.any_instance.stub(:stats) { sample_stats(factor) }
26
+ end
27
+
28
+ module MemoryTracker
29
+ module Stores
30
+ module InMemoryStore
31
+ describe Manager do
32
+ def start_time
33
+ Time.new(2013,01,01,0,0,0)
34
+ end
35
+
36
+ before :each do
37
+ @env = double('env')
38
+ end
39
+
40
+ it 'should respond to name' do
41
+ manager = Manager.new(60)
42
+ manager.should respond_to(:name)
43
+ end
44
+
45
+ it 'should return stats from older window' do
46
+ Time.stub(:now).and_return(start_time)
47
+ manager = Manager.new(60)
48
+ request = Request.new(@env)
49
+ stub_gcstat_delta('Boat', 'sail')
50
+ manager.push(request.close)
51
+ Time.stub(:now).and_return(start_time + 10)
52
+ request = Request.new(@env)
53
+ stub_gcstat_delta('Car', 'drive', 2)
54
+ manager.push(request.close)
55
+ Time.stub(:now).and_return(start_time + 40)
56
+ request = Request.new(@env)
57
+ stub_gcstat_delta('Boat', 'sail', 3)
58
+ manager.push(request.close)
59
+
60
+ stats = manager.stats
61
+ stats.fetch('Boat', 'sail', :rss).should == 4
62
+ stats.fetch('Car', 'drive', :rss).should == 2
63
+ end
64
+
65
+ it 'should rotate windows' do
66
+ Time.stub(:now).and_return(start_time)
67
+ manager = Manager.new(60)
68
+ request = Request.new(@env)
69
+ stub_gcstat_delta('Boat', 'sail')
70
+ manager.push(request.close)
71
+ Time.stub(:now).and_return(start_time + 10)
72
+ request = Request.new(@env)
73
+ stub_gcstat_delta('Car', 'drive', 2)
74
+ manager.push(request.close)
75
+ Time.stub(:now).and_return(start_time + 40)
76
+ request = Request.new(@env)
77
+ stub_gcstat_delta('Boat', 'sail', 3)
78
+ manager.push(request.close)
79
+ Time.stub(:now).and_return(start_time + 70)
80
+
81
+ stats = manager.stats
82
+ stats.fetch('Boat', 'sail', :rss).should == 3
83
+ stats.fetch('Car', 'drive', :rss).should be_nil
84
+ end
85
+ end
86
+
87
+ describe StatInterval do
88
+
89
+ before :each do
90
+ @interval = InMemoryStore::StatInterval.new(Time.now, 5*60)
91
+ @env = double('env')
92
+ end
93
+
94
+ it 'should accept requests' do
95
+ env = double('env')
96
+ request = Request.new(env)
97
+ allow(env).to receive(:controller) { :Foo }
98
+ allow(env).to receive(:action) { :bar }
99
+ request.close
100
+ request.controller.should == :Foo
101
+ request.action.should == :bar
102
+ @interval.push(request)
103
+ end
104
+
105
+ it 'should accumulate one request' do
106
+ req1 = Request.new(@env)
107
+ req1.close
108
+ stub_gcstat_delta('Boat', 'sail')
109
+ @interval.push(req1)
110
+ @interval.stats.should be_a(Stats)
111
+ @interval.stats.fetch('Boat', 'sail', :rss).should == 1
112
+ end
113
+
114
+ it 'should accumulate several requests' do
115
+ req1 = Request.new(@env)
116
+ req1.close
117
+ stub_gcstat_delta('Boat', 'sail')
118
+ @interval.push(req1)
119
+ req2 = Request.new(@env)
120
+ req2.close
121
+ stub_gcstat_delta('Boat', 'sail', 2)
122
+ @interval.push(req2)
123
+ req3 = Request.new(@env)
124
+ req3.close
125
+ stub_gcstat_delta('Boat', 'moor', 5)
126
+ @interval.push(req3)
127
+ req4 = Request.new(@env)
128
+ req4.close
129
+ stub_gcstat_delta('Car', 'drive', 1)
130
+ @interval.push(req4)
131
+
132
+ stats = @interval.stats
133
+ stats.count('Boat', 'sail').should == 2
134
+ stats.fetch('Boat', 'sail', :rss).should == 3
135
+ stats.fetch('Boat', 'sail', :count).should == 12
136
+ stats.count('Boat', 'moor').should == 1
137
+ stats.fetch('Boat', 'moor', :rss).should == 5
138
+ stats.fetch('Boat', 'moor', :count).should == 20
139
+ stats.count('Car', 'drive').should == 1
140
+ stats.fetch('Car', 'drive', :rss).should == 1
141
+ stats.fetch('Car', 'drive', :count).should == 4
142
+ end
143
+
144
+ it 'should be enumerable' do
145
+ req1 = Request.new(@env)
146
+ req1.close
147
+ stub_gcstat_delta('Boat', 'sail')
148
+ @interval.push(req1)
149
+ req2 = Request.new(@env)
150
+ req2.close
151
+ stub_gcstat_delta('Boat', 'moor')
152
+ @interval.push(req2)
153
+
154
+ @interval.size.should == 2
155
+ @interval.to_a.should include(['Boat', 'sail', { :num => 1, :gcstat => sample_stats} ])
156
+ @interval.to_a.should include(['Boat', 'moor', { :num => 1, :gcstat => sample_stats} ])
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,17 @@
1
+ module MemoryTracker
2
+ module Stores
3
+ describe UrlLogfileStore do
4
+ before :each do
5
+ logger_class = double("logger_class")
6
+ allow(logger_class).to receive(:new)
7
+ @logstore = UrlLogfileStore.new logger_class, "foo/log"
8
+ end
9
+
10
+ it 'implements the store role' do
11
+ @logstore.should respond_to(:name)
12
+ @logstore.should respond_to(:push)
13
+ @logstore.should respond_to(:stats)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'singleton'
5
+
6
+ require 'rspec'
7
+ require 'sys/proctable'
8
+ require 'memory_tracker'
9
+ require 'memory_tracker/gc_stat'
10
+ require 'memory_tracker/request'
11
+ require 'memory_tracker/memory_tracker'
12
+ require 'memory_tracker/stores/in_memory_store'
13
+ require 'memory_tracker/stores/gcstat_logfile_store'
14
+ require 'memory_tracker/stores/url_logfile_store'
15
+
16
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
17
+ ENV["RAILS_ENV"] ||= 'test'
18
+ # require 'rspec/rails'
19
+ # require 'rspec/mocks/standalone'
20
+
21
+ # Requires supporting ruby files with custom matchers and macros, etc,
22
+ # in spec/support/ and its subdirectories.
23
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
24
+
25
+
26
+ RSpec.configure do |config|
27
+ # == Mock Framework
28
+ #
29
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
30
+ #
31
+ # config.mock_with :mocha
32
+ # config.mock_with :flexmock
33
+ # config.mock_with :rr
34
+ # config.mock_with :rspec
35
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memory_tracker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Philippe Le Rohellec
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sys-proctable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: debugger
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.14.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.14.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rdoc
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.12'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jeweler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.8.7
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 1.8.7
97
+ - !ruby/object:Gem::Dependency
98
+ name: sys-proctable
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Collect and analyze memory usage data for each individual Rails action
112
+ controller.
113
+ email: philippe@lerohellec.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files:
117
+ - LICENSE.txt
118
+ - README.rdoc
119
+ files:
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - LICENSE.txt
123
+ - README.rdoc
124
+ - Rakefile
125
+ - VERSION
126
+ - app/controllers/memory_tracker/dashboards_controller.rb
127
+ - app/helpers/memory_tracker/dashboards_helper.rb
128
+ - app/views/layouts/memory_tracker.html.erb
129
+ - app/views/memory_tracker/dashboards/index.html.erb
130
+ - config/routes.rb
131
+ - docs/design.rb
132
+ - lib/memory_tracker.rb
133
+ - lib/memory_tracker/engine.rb
134
+ - lib/memory_tracker/env.rb
135
+ - lib/memory_tracker/gc_stat.rb
136
+ - lib/memory_tracker/memory_tracker.rb
137
+ - lib/memory_tracker/middleware.rb
138
+ - lib/memory_tracker/request.rb
139
+ - lib/memory_tracker/stores/gcstat_logfile_store.rb
140
+ - lib/memory_tracker/stores/in_memory_store.rb
141
+ - lib/memory_tracker/stores/url_logfile_store.rb
142
+ - memory_tracker.gemspec
143
+ - spec/lib/memory_tracker_spec.rb
144
+ - spec/lib/request_spec.rb
145
+ - spec/lib/stores/gcstat_logfile_store_spec.rb
146
+ - spec/lib/stores/in_memory_store_spec.rb
147
+ - spec/lib/stores/url_logfile_store_spec.rb
148
+ - spec/spec_helper.rb
149
+ homepage: http://github.com/plerohellec/memory_tracker
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.0.3
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Rails memory allocations tracker
173
+ test_files: []