memory_profiler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4e06600592ba32b7ac0b1184619e2c5f5f121af8
4
+ data.tar.gz: 15408fc5c3da8f1d71c5e156f41d84d34e269fb2
5
+ SHA512:
6
+ metadata.gz: cb7fd523c0abf056c31fdfaae902a35ebf04122fd422fb21d339afe3a7513fb34f712e8ed7c4422ffa00313aeb037656adde9f62885cfb8574367ea949ad2cc8
7
+ data.tar.gz: c592a9668fbd024dd176fcebab3938b031efdd09ee42c6840ce61f1a0a5fd0dd0e0f60c3cdaeff4f9119121594ad95daa5eb821f4eb0a7eeeeba09d7e0b51ee6
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in memory_profiler.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard :minitest do
2
+ # with Minitest::Unit
3
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
4
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/test_#{m[2]}.rb" }
5
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
6
+
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # MemoryProfiler
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'memory_profiler'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install memory_profiler
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/*_test.rb"
6
+ end
7
+
8
+
@@ -0,0 +1,37 @@
1
+ module MemoryProfiler
2
+ module Helpers
3
+ def self.guess_gem(path)
4
+ if /(\/gems\/.*)*\/gems\/(?<gem>[^\/]+)/ =~ path
5
+ gem
6
+ elsif /\/rubygems\// =~ path
7
+ "rubygems"
8
+ elsif /(?<app>[^\/]+\/(bin|app|lib))/ =~ path
9
+ app
10
+ else
11
+ "other"
12
+ end
13
+ end
14
+
15
+ # helper to work around GC.start not freeing everything
16
+ def self.full_gc
17
+ # attempt to work around lazy sweep, need a cleaner way
18
+ GC.start while new_count = decreased_count(new_count)
19
+ end
20
+
21
+ def self.decreased_count(old)
22
+ count = count_objects
23
+ if !old || count < old
24
+ count
25
+ else
26
+ nil
27
+ end
28
+ end
29
+
30
+ def self.count_objects
31
+ i = 0
32
+ ObjectSpace.each_object do |obj|
33
+ i += 1
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,82 @@
1
+ require 'objspace'
2
+ module MemoryProfiler
3
+ class Reporter
4
+
5
+ def self.report(top=50, &block)
6
+ report = self.new
7
+ report.run(top,&block)
8
+ end
9
+
10
+ def run(top=50,&block)
11
+ allocated, rvalue_size = nil
12
+
13
+ # calcualte RVALUE
14
+ GC::Profiler.enable
15
+ Helpers.full_gc
16
+ begin
17
+ data = GC::Profiler.raw_data[0]
18
+ # so hacky, but no other way
19
+ rvalue_size = data[:HEAP_TOTAL_SIZE] / data[:HEAP_TOTAL_OBJECTS]
20
+ GC::Profiler.disable
21
+ end
22
+ GC.disable
23
+
24
+ ObjectSpace.trace_object_allocations do
25
+ generation = GC.count
26
+ block.call
27
+ allocated = object_list(generation, rvalue_size)
28
+ end
29
+
30
+ GC.enable
31
+
32
+ Helpers.full_gc
33
+
34
+ retained = StatHash.new
35
+ ObjectSpace.each_object do |obj|
36
+ begin
37
+ found = allocated[obj.__id__]
38
+ retained[obj.__id__] = found if found
39
+ rescue
40
+ # __id__ is not defined, skip it
41
+ end
42
+ end
43
+
44
+ Results.from_raw(allocated,retained,top)
45
+
46
+ end
47
+
48
+ def object_list(generation, rvalue_size)
49
+ results = StatHash.new
50
+ objs = []
51
+
52
+ ObjectSpace.each_object do |obj|
53
+ objs << obj
54
+ end
55
+
56
+ objs.each do |obj|
57
+ if generation == ObjectSpace.allocation_generation(obj)
58
+ file = ObjectSpace.allocation_sourcefile(obj)
59
+ unless file == __FILE__
60
+ line = ObjectSpace.allocation_sourceline(obj)
61
+ class_path = ObjectSpace.allocation_class_path(obj)
62
+ method_id = ObjectSpace.allocation_method_id(obj)
63
+
64
+ class_name = obj.class.name rescue "BasicObject"
65
+ begin
66
+ object_id = obj.__id__
67
+
68
+ memsize = ObjectSpace.memsize_of(obj) + rvalue_size
69
+ # compensate for API bug
70
+ memsize = rvalue_size if memsize > 100_000_000_000
71
+ results[object_id] = Stat.new(class_name, file, line, class_path, method_id, memsize)
72
+ rescue
73
+ # __id__ is not defined, give up
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ results
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,88 @@
1
+ module MemoryProfiler
2
+ class Results
3
+
4
+ def self.register_type(name, lookup)
5
+ ["allocated","retained"].product(["objects","memory"]).each do |type, metric|
6
+ full_name = "#{type}_#{metric}_by_#{name}"
7
+ attr_accessor full_name
8
+
9
+ @@lookups ||= []
10
+ mapped = lookup
11
+
12
+ if metric == "memory"
13
+ mapped = lambda{|stat|
14
+ [lookup.call(stat), stat.memsize]
15
+ }
16
+ end
17
+
18
+ @@lookups << [full_name, mapped]
19
+
20
+ end
21
+ end
22
+
23
+ register_type :gem, lambda{|stat|
24
+ Helpers.guess_gem("#{stat.file}")
25
+ }
26
+
27
+ register_type :file, lambda{|stat|
28
+ stat.file
29
+ }
30
+
31
+ register_type :location, lambda{|stat|
32
+ "#{stat.file}:#{stat.line}"
33
+ }
34
+
35
+ attr_accessor :total_allocated
36
+ attr_accessor :total_retained
37
+
38
+ def self.from_raw(allocated, retained, top)
39
+ self.new.register_results(allocated,retained,top)
40
+ end
41
+
42
+ def register_results(allocated, retained, top)
43
+ @@lookups.each do |name, lookup|
44
+ mapped = lambda{|tuple|
45
+ lookup.call(tuple[1])
46
+ }
47
+
48
+ result =
49
+ if name =~ /^allocated/
50
+ allocated.top_n(top, &mapped)
51
+ else
52
+ retained.top_n(top, &mapped)
53
+ end
54
+
55
+ self.send "#{name}=", result
56
+ self.total_allocated = allocated.count
57
+ self.total_retained = retained.count
58
+ end
59
+
60
+ self
61
+ end
62
+
63
+ def pretty_print
64
+ puts "Total allocated #{total_allocated}"
65
+ puts "Total retained #{total_retained}"
66
+ puts
67
+ ["allocated","retained"]
68
+ .product(["memory", "objects"])
69
+ .product(["gem", "file", "location"])
70
+ .each do |(type, metric), name|
71
+ dump "#{type} #{metric} by #{name}", self.send("#{type}_#{metric}_by_#{name}")
72
+ end
73
+ end
74
+
75
+ def dump(description, data)
76
+ puts description
77
+ puts "-----------------------------------"
78
+ if data
79
+ data.each do |item|
80
+ puts "#{item[:data]} x #{item[:count]}"
81
+ end
82
+ else
83
+ puts "NO DATA"
84
+ end
85
+ puts
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ module MemoryProfiler
2
+ Stat = Struct.new(:class_name, :file, :line, :class_path, :method_id, :memsize)
3
+ end
@@ -0,0 +1,5 @@
1
+ module MemoryProfiler
2
+ class StatHash < Hash
3
+ include TopN
4
+ end
5
+ end
@@ -0,0 +1,51 @@
1
+ module MemoryProfiler
2
+ module TopN
3
+ # Efficient mechanism for finding top_n entries in a list
4
+ # optional block can specify custom element and weight
5
+ def top_n(max = 10)
6
+
7
+ sorted =
8
+ if block_given?
9
+ self.map { |row|
10
+ yield(row)
11
+ }
12
+ else
13
+ self.dup
14
+ end
15
+
16
+ sorted.sort!
17
+
18
+ found = []
19
+
20
+ last = sorted[0]
21
+ count = 0
22
+ lowest_count = 0
23
+
24
+ sorted << nil
25
+
26
+ sorted.each do |row|
27
+
28
+ current_item, current_count = row
29
+
30
+ unless current_item == last
31
+ if count > lowest_count
32
+ found << {data: last, count: count}
33
+ end
34
+
35
+ if found.length > max
36
+ found.sort!{|x,y| x[:count] <=> y[:count] }
37
+ found.delete_at(0)
38
+ lowest_count = found[0][:count]
39
+ end
40
+
41
+ count = 0
42
+ last = current_item
43
+ end
44
+
45
+ count += (current_count || 1) unless row.nil?
46
+ end
47
+
48
+ found.reverse
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module MemoryProfiler
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,15 @@
1
+ require "memory_profiler/version"
2
+ require "memory_profiler/helpers"
3
+ require "memory_profiler/top_n"
4
+ require "memory_profiler/stat"
5
+ require "memory_profiler/stat_hash"
6
+ require "memory_profiler/results"
7
+ require "memory_profiler/reporter"
8
+
9
+ module MemoryProfiler
10
+ def self.report(top=50,&block)
11
+ Reporter.report(top,&block)
12
+ end
13
+ end
14
+
15
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'memory_profiler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "memory_profiler"
8
+ spec.version = MemoryProfiler::VERSION
9
+ spec.authors = ["Sam Saffron"]
10
+ spec.email = ["sam.saffron@gmail.com"]
11
+ spec.description = %q{Memory profiling routines for Ruby Head}
12
+ spec.summary = %q{Memory profiling routines for Ruby Head}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "guard"
25
+ spec.add_development_dependency "guard-minitest"
26
+ end
@@ -0,0 +1,2 @@
1
+ require 'memory_profiler'
2
+ require 'minitest/pride'
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module MemoryProfiler
4
+
5
+ class TestHelpers < Minitest::Test
6
+ def assert_gem_parse(expected, path)
7
+ assert_equal(expected, Helpers.guess_gem(path))
8
+ end
9
+
10
+ def test_rubygems_parse
11
+ assert_gem_parse( "rubygems",
12
+ "/home/sam/.rbenv/versions/ruby-head/lib/ruby/2.1.0/rubygems/version.rb")
13
+ end
14
+
15
+ def test_standard_parse
16
+ assert_gem_parse( "rails_multisite",
17
+ "/home/sam/Source/discourse/vendor/gems/rails_multisite/lib")
18
+ end
19
+
20
+ def test_another_standard_parse
21
+ assert_gem_parse( "activesupport-3.2.12",
22
+ "/home/sam/.rbenv/versions/ruby-head/lib/ruby/gems/2.1.0/gems/activesupport-3.2.12/lib/active_support/dependencies.rb")
23
+ end
24
+
25
+ def test_app_path_parse
26
+ assert_gem_parse( "discourse/app",
27
+ "/home/sam/Source/discourse/app/assets")
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class TestReporter < Minitest::Test
4
+ def allocate_strings(n)
5
+ n.times do
6
+ ""
7
+ end
8
+ end
9
+
10
+ def test_counts
11
+ a = nil
12
+ result = MemoryProfiler::Reporter.report do
13
+ allocate_strings(10)
14
+ a = "hello"
15
+ end
16
+ assert_equal(11, result.total_allocated)
17
+ assert_equal(1, result.total_retained)
18
+ assert_equal(1, result.retained_objects_by_location.length)
19
+ end
20
+
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class TestResults < Minitest::Test
4
+ def test_pretty_print_works
5
+ MemoryProfiler::Results.new.pretty_print
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ require 'test_helper'
2
+
3
+ class TestStatHash < Minitest::Test
4
+
5
+
6
+ end
@@ -0,0 +1,39 @@
1
+ require 'test_helper'
2
+
3
+ class ArrayWithTopN < Array
4
+ include MemoryProfiler::TopN
5
+ end
6
+
7
+ class TestTopN < Minitest::Test
8
+
9
+ def tn(*vals)
10
+ ArrayWithTopN.new.concat(vals)
11
+ end
12
+
13
+ def test_top_n
14
+ data = tn( 7,1,2,2,3,3,99,3 )
15
+ results = data.top_n(2)
16
+
17
+ assert_equal([{data: 3, count: 3}, {data: 2, count: 2}], results)
18
+ end
19
+
20
+ def test_top_n_with_block
21
+ data = tn( 0,3,6,1,4,2 )
22
+
23
+ results = data.top_n(2) do |r|
24
+ r%3
25
+ end
26
+
27
+ assert_equal([{data: 0, count: 3}, {data: 1, count: 2}], results)
28
+ end
29
+ def test_top_n_with_block_and_size
30
+ data = tn( [1,100], [1,10], [2,1], [2,1], [2,1],[3,100] )
31
+
32
+ results = data.top_n(2) do |r|
33
+ r
34
+ end
35
+
36
+ assert_equal([{data: 1, count: 110}, {data: 3, count: 100}], results)
37
+ end
38
+
39
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memory_profiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sam Saffron
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
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: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Memory profiling routines for Ruby Head
84
+ email:
85
+ - sam.saffron@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - Guardfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/memory_profiler.rb
97
+ - lib/memory_profiler/helpers.rb
98
+ - lib/memory_profiler/reporter.rb
99
+ - lib/memory_profiler/results.rb
100
+ - lib/memory_profiler/stat.rb
101
+ - lib/memory_profiler/stat_hash.rb
102
+ - lib/memory_profiler/top_n.rb
103
+ - lib/memory_profiler/version.rb
104
+ - memory_profiler.gemspec
105
+ - test/test_helper.rb
106
+ - test/test_helpers.rb
107
+ - test/test_reporter.rb
108
+ - test/test_results.rb
109
+ - test/test_stat_hash.rb
110
+ - test/test_top_n.rb
111
+ homepage: ''
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.1.0.rc.2
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Memory profiling routines for Ruby Head
135
+ test_files:
136
+ - test/test_helper.rb
137
+ - test/test_helpers.rb
138
+ - test/test_reporter.rb
139
+ - test/test_results.rb
140
+ - test/test_stat_hash.rb
141
+ - test/test_top_n.rb