memory_profiler 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -10
- data/lib/memory_profiler/helpers.rb +22 -27
- data/lib/memory_profiler/reporter.rb +46 -49
- data/lib/memory_profiler/results.rb +43 -63
- data/lib/memory_profiler/stat.rb +16 -1
- data/lib/memory_profiler/top_n.rb +11 -44
- data/lib/memory_profiler/version.rb +1 -1
- metadata +8 -26
- data/.gitignore +0 -17
- data/.travis.yml +0 -4
- data/Gemfile +0 -4
- data/Guardfile +0 -7
- data/Rakefile +0 -8
- data/memory_profiler.gemspec +0 -26
- data/test/test_helper.rb +0 -3
- data/test/test_helpers.rb +0 -32
- data/test/test_reporter.rb +0 -101
- data/test/test_results.rb +0 -9
- data/test/test_stat_hash.rb +0 -6
- data/test/test_top_n.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7e1c880efe5b674c1d8281a7f8659ca224abafe
|
4
|
+
data.tar.gz: 37e0a418e082d1fd1af188a0966efdc9e97c92d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e900a6abc28f73ceffc12dae74ba7cb416c53930b5eb9703d00b25a075afda2f9137f6a4f52f6958b91b9afa057c9fc4845227517c147226a1ab99de7dc384c3
|
7
|
+
data.tar.gz: 915bd319992cbfa7ba1f38a64ae8a5e83358a1bb390f67ac283dfe70cab03463e1698e869a73641f563c4a9aaa29d5b0732049e29341d46c4de1d61f042b2089
|
data/README.md
CHANGED
@@ -35,7 +35,14 @@ report.pretty_print
|
|
35
35
|
|
36
36
|
## Options
|
37
37
|
|
38
|
-
|
38
|
+
The report method can take a few options:
|
39
|
+
|
40
|
+
* `top`: maximum number of entries to display in a report (default is 50)
|
41
|
+
* `allow_files`: include only certain files from tracing - can be given as a String, Regexp, or array of Strings
|
42
|
+
* `ignore_files`: exclude certain files from tracing - can be given as a String or Regexp
|
43
|
+
* `trace`: an array of classes for which you explicitly want to trace object allocations
|
44
|
+
|
45
|
+
Check out `Reporter#new` for more details.
|
39
46
|
|
40
47
|
```
|
41
48
|
pry> require 'memory_profiler'
|
@@ -77,14 +84,6 @@ rubygems x 305879
|
|
77
84
|
. . .
|
78
85
|
```
|
79
86
|
|
80
|
-
Other options include:
|
81
|
-
|
82
|
-
* `top`: maximum number of entries to display in a report
|
83
|
-
* `trace`: an array of classes for which you explicitly want to trace object allocations
|
84
|
-
* `ignore_files`: a regular expression used to exclude certain files from tracing (opposite of `allow_files`)
|
85
|
-
|
86
|
-
Check out `Reporter#new` for more details.
|
87
|
-
|
88
87
|
## Example Session
|
89
88
|
|
90
89
|
You can easily use memory_profiler to profile require impact of a gem, for example:
|
@@ -366,6 +365,9 @@ Memory profiler also performs some String analysis to help you find strings that
|
|
366
365
|
|
367
366
|
## Changelog
|
368
367
|
|
368
|
+
### 0.9.5
|
369
|
+
- Improved stability and performance @dgynn
|
370
|
+
|
369
371
|
### 0.9.4
|
370
372
|
- FIX: remove incorrect RVALUE offset on 2.2 @dgynn
|
371
373
|
- FEATURE: add total memory usage @dgynn
|
@@ -380,7 +382,6 @@ Memory profiler also performs some String analysis to help you find strings that
|
|
380
382
|
- This is quite stable, upping version to reflect
|
381
383
|
- Fixed bug where it would crash when location was nil for some reason
|
382
384
|
|
383
|
-
|
384
385
|
### 0.0.4
|
385
386
|
- Added compatibility with released version of Ruby 2.1.0
|
386
387
|
- Cleanup to use latest APIs available in 2.1.0
|
@@ -1,37 +1,32 @@
|
|
1
1
|
module MemoryProfiler
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
elsif /(?<app>[^\/]+\/(bin|app|lib))/ =~ path
|
9
|
-
app
|
10
|
-
else
|
11
|
-
"other"
|
12
|
-
end
|
2
|
+
class Helpers
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@gem_guess_cache = Hash.new
|
6
|
+
@location_cache = Hash.new { |h,k| h[k] = Hash.new.compare_by_identity }
|
7
|
+
@class_name_cache = Hash.new.compare_by_identity
|
13
8
|
end
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
def guess_gem(path)
|
11
|
+
@gem_guess_cache[path] ||=
|
12
|
+
if /(\/gems\/.*)*\/gems\/(?<gemname>[^\/]+)/ =~ path
|
13
|
+
gemname
|
14
|
+
elsif /\/rubygems[\.\/]/ =~ path
|
15
|
+
"rubygems".freeze
|
16
|
+
elsif /(?<app>[^\/]+\/(bin|app|lib))/ =~ path
|
17
|
+
app
|
18
|
+
else
|
19
|
+
"other".freeze
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
22
|
-
|
23
|
-
if !old || count < old
|
24
|
-
count
|
25
|
-
else
|
26
|
-
nil
|
27
|
-
end
|
23
|
+
def lookup_location(file, line)
|
24
|
+
@location_cache[file][line] ||= "#{file}:#{line}"
|
28
25
|
end
|
29
26
|
|
30
|
-
def
|
31
|
-
|
32
|
-
ObjectSpace.each_object do |obj|
|
33
|
-
i += 1
|
34
|
-
end
|
27
|
+
def lookup_class_name(klass)
|
28
|
+
@class_name_cache[klass] ||= klass.name || '<<Unknown>>'
|
35
29
|
end
|
30
|
+
|
36
31
|
end
|
37
32
|
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'objspace'
|
2
2
|
module MemoryProfiler
|
3
|
-
# Reporter is the top level
|
3
|
+
# Reporter is the top level API used for generating memory reports.
|
4
4
|
#
|
5
5
|
# @example Measure object allocation in a block
|
6
|
-
#
|
7
6
|
# report = Reporter.report(top: 50) do
|
8
7
|
# 5.times { "foo" }
|
9
8
|
# end
|
@@ -12,84 +11,71 @@ module MemoryProfiler
|
|
12
11
|
|
13
12
|
def initialize(opts = {})
|
14
13
|
@top = opts[:top] || 50
|
15
|
-
@trace = opts[:trace]
|
16
|
-
@ignore_files = opts[:ignore_files]
|
17
|
-
@allow_files
|
14
|
+
@trace = opts[:trace] && Array(opts[:trace])
|
15
|
+
@ignore_files = opts[:ignore_files] && Regexp.new(opts[:ignore_files])
|
16
|
+
@allow_files = opts[:allow_files] && /#{Array(opts[:allow_files]).join('|')}/
|
18
17
|
end
|
19
18
|
|
20
|
-
# Helper for generating new reporter and running against block
|
19
|
+
# Helper for generating new reporter and running against block.
|
20
|
+
# @param [Hash] opts the options to create a report with
|
21
|
+
# @option opts :top max number of entries to output
|
22
|
+
# @option opts :trace a class or an array of classes you explicitly want to trace
|
23
|
+
# @option opts :ignore_files a regular expression used to exclude certain files from tracing
|
24
|
+
# @option opts :allow_files a string or array of strings to selectively include in tracing
|
25
|
+
# @return [MemoryProfiler::Results]
|
21
26
|
def self.report(opts={}, &block)
|
22
27
|
self.new(opts).run(&block)
|
23
28
|
end
|
24
29
|
|
25
30
|
# Collects object allocation and memory of ruby code inside of passed block.
|
26
|
-
#
|
27
|
-
# @param [Hash] opts the options to create a message with.
|
28
|
-
# @option opts [Fixnum] :top max number of entries to output in report
|
29
|
-
# @option opts [Array <Class>] :trace an array of classes you explicitly want to trace
|
30
|
-
# @return [MemoryProfiler::Results]
|
31
31
|
def run(&block)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
|
36
|
-
Helpers.full_gc
|
33
|
+
GC.start
|
37
34
|
GC.disable
|
38
35
|
|
36
|
+
generation = GC.count
|
39
37
|
ObjectSpace.trace_object_allocations do
|
40
|
-
generation = GC.count
|
41
38
|
block.call
|
42
|
-
allocated = object_list(generation, rvalue_size)
|
43
39
|
end
|
44
|
-
|
45
|
-
|
46
|
-
results.strings_allocated = results.string_report(allocated, top)
|
40
|
+
allocated = object_list(generation)
|
41
|
+
retained = StatHash.new.compare_by_identity
|
47
42
|
|
48
43
|
GC.enable
|
44
|
+
GC.start
|
49
45
|
|
50
|
-
|
46
|
+
# Caution: Do not allocate any new Objects between the call to GC.start and the completion of the retained
|
47
|
+
# lookups. It is likely that a new Object would reuse an object_id from a GC'd object.
|
51
48
|
|
52
|
-
retained = StatHash.new
|
53
49
|
ObjectSpace.each_object do |obj|
|
50
|
+
next unless ObjectSpace.allocation_generation(obj) == generation
|
54
51
|
begin
|
55
52
|
found = allocated[obj.__id__]
|
56
53
|
retained[obj.__id__] = found if found
|
57
54
|
rescue
|
58
55
|
# __id__ is not defined on BasicObject, skip it
|
59
|
-
# we can probably
|
56
|
+
# we can probably transplant the object_id at this point,
|
60
57
|
# but it is quite rare
|
61
58
|
end
|
62
59
|
end
|
60
|
+
ObjectSpace.trace_object_allocations_clear
|
63
61
|
|
62
|
+
results = Results.new
|
64
63
|
results.register_results(allocated, retained, top)
|
65
64
|
results
|
66
65
|
end
|
67
66
|
|
68
|
-
|
69
|
-
!trace
|
70
|
-
end
|
71
|
-
|
72
|
-
def ignore_file?(file)
|
73
|
-
return true if file == __FILE__
|
74
|
-
@ignore_files && @ignore_files =~ file
|
75
|
-
end
|
76
|
-
|
77
|
-
def allow_file?(file)
|
78
|
-
return true if @allow_files.empty?
|
79
|
-
!/#{@allow_files.join('|')}/.match(file).to_s.empty?
|
80
|
-
end
|
67
|
+
private
|
81
68
|
|
82
69
|
# Iterates through objects in memory of a given generation.
|
83
70
|
# Stores results along with meta data of objects collected.
|
84
|
-
def object_list(generation
|
71
|
+
def object_list(generation)
|
85
72
|
|
86
|
-
results = StatHash.new
|
87
73
|
objs = []
|
88
74
|
|
89
75
|
ObjectSpace.each_object do |obj|
|
90
|
-
next unless
|
76
|
+
next unless ObjectSpace.allocation_generation(obj) == generation
|
91
77
|
begin
|
92
|
-
if
|
78
|
+
if !trace || trace.include?(obj.class)
|
93
79
|
objs << obj
|
94
80
|
end
|
95
81
|
rescue
|
@@ -97,29 +83,40 @@ module MemoryProfiler
|
|
97
83
|
end
|
98
84
|
end
|
99
85
|
|
86
|
+
rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
|
87
|
+
rvalue_size_adjustment = RUBY_VERSION < '2.2' ? rvalue_size : 0
|
88
|
+
helper = Helpers.new
|
89
|
+
|
90
|
+
result = StatHash.new.compare_by_identity
|
91
|
+
|
100
92
|
objs.each do |obj|
|
101
|
-
file = ObjectSpace.allocation_sourcefile(obj)
|
102
|
-
next if
|
93
|
+
file = ObjectSpace.allocation_sourcefile(obj) || "(no name)".freeze
|
94
|
+
next if @ignore_files && @ignore_files =~ file
|
95
|
+
next if @allow_files && !(@allow_files =~ file)
|
103
96
|
|
104
97
|
line = ObjectSpace.allocation_sourceline(obj)
|
105
|
-
|
106
|
-
|
98
|
+
location = helper.lookup_location(file, line)
|
99
|
+
klass = obj.class
|
100
|
+
class_name = helper.lookup_class_name(klass)
|
101
|
+
gem = helper.guess_gem(file)
|
102
|
+
string = '' << obj if klass == String
|
107
103
|
|
108
|
-
class_name = obj.class.name rescue "BasicObject"
|
109
104
|
begin
|
110
105
|
object_id = obj.__id__
|
111
106
|
|
112
|
-
memsize = ObjectSpace.memsize_of(obj)
|
113
|
-
memsize += rvalue_size if RUBY_VERSION < '2.2'.freeze
|
107
|
+
memsize = ObjectSpace.memsize_of(obj) + rvalue_size_adjustment
|
114
108
|
# compensate for API bug
|
115
109
|
memsize = rvalue_size if memsize > 100_000_000_000
|
116
|
-
|
110
|
+
result[object_id] = MemoryProfiler::Stat.new(class_name, gem, file, location, memsize, string)
|
117
111
|
rescue
|
118
112
|
# __id__ is not defined, give up
|
119
113
|
end
|
120
114
|
end
|
121
115
|
|
122
|
-
|
116
|
+
# Although `objs` will go out of scope, clear the array so objects can definitely be GCd
|
117
|
+
objs.clear
|
118
|
+
|
119
|
+
result
|
123
120
|
end
|
124
121
|
end
|
125
122
|
end
|
@@ -1,89 +1,69 @@
|
|
1
1
|
module MemoryProfiler
|
2
2
|
class Results
|
3
3
|
|
4
|
-
def self.register_type(name,
|
5
|
-
|
6
|
-
|
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]
|
4
|
+
def self.register_type(name, stat_attribute)
|
5
|
+
@@lookups ||= []
|
6
|
+
@@lookups << [name, stat_attribute]
|
19
7
|
|
8
|
+
["allocated", "retained"].product(["objects", "memory"]).each do |type, metric|
|
9
|
+
attr_accessor "#{type}_#{metric}_by_#{name}"
|
20
10
|
end
|
21
11
|
end
|
22
12
|
|
23
|
-
register_type
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
register_type :file, lambda { |stat|
|
28
|
-
stat.file || "(no name)"
|
29
|
-
}
|
30
|
-
|
31
|
-
register_type :location, lambda { |stat|
|
32
|
-
"#{stat.file}:#{stat.line}"
|
33
|
-
}
|
34
|
-
|
35
|
-
register_type :class, lambda { |stat|
|
36
|
-
"#{stat.class_name}"
|
37
|
-
}
|
13
|
+
register_type 'gem', :gem
|
14
|
+
register_type 'file', :file
|
15
|
+
register_type 'location', :location
|
16
|
+
register_type 'class', :class_name
|
38
17
|
|
39
18
|
attr_accessor :strings_retained, :strings_allocated
|
40
19
|
attr_accessor :total_retained, :total_allocated
|
41
20
|
attr_accessor :total_retained_memsize, :total_allocated_memsize
|
42
21
|
|
43
|
-
def self.from_raw(allocated, retained, top)
|
44
|
-
self.new.register_results(allocated, retained, top)
|
45
|
-
end
|
46
|
-
|
47
22
|
def register_results(allocated, retained, top)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
self.send "#{name}=", result
|
23
|
+
|
24
|
+
@@lookups.each do |name, stat_attribute|
|
25
|
+
|
26
|
+
memsize_results, count_results = allocated.top_n(top, stat_attribute)
|
27
|
+
|
28
|
+
self.send("allocated_memory_by_#{name}=", memsize_results)
|
29
|
+
self.send("allocated_objects_by_#{name}=", count_results)
|
30
|
+
|
31
|
+
memsize_results, count_results = retained.top_n(top, stat_attribute)
|
32
|
+
|
33
|
+
self.send("retained_memory_by_#{name}=", memsize_results)
|
34
|
+
self.send("retained_objects_by_#{name}=", count_results)
|
61
35
|
end
|
62
36
|
|
37
|
+
|
38
|
+
self.strings_allocated = string_report(allocated, top)
|
63
39
|
self.strings_retained = string_report(retained, top)
|
64
40
|
|
65
|
-
self.total_allocated = allocated.
|
66
|
-
self.total_allocated_memsize = allocated.values.map(&:memsize).inject(:+)
|
67
|
-
self.total_retained = retained.
|
68
|
-
self.total_retained_memsize = retained.values.map(&:memsize).inject(:+)
|
41
|
+
self.total_allocated = allocated.size
|
42
|
+
self.total_allocated_memsize = allocated.values.map!(&:memsize).inject(0, :+)
|
43
|
+
self.total_retained = retained.size
|
44
|
+
self.total_retained_memsize = retained.values.map!(&:memsize).inject(0, :+)
|
69
45
|
|
70
46
|
self
|
71
47
|
end
|
72
48
|
|
73
|
-
StringStat = Struct.new(:string, :count, :location)
|
74
|
-
|
75
49
|
def string_report(data, top)
|
76
|
-
data
|
77
|
-
.
|
78
|
-
.map { |
|
79
|
-
.group_by { |string,
|
80
|
-
.sort_by {
|
50
|
+
data.values
|
51
|
+
.keep_if { |stat| stat.string_value }
|
52
|
+
.map! { |stat| [stat.string_value, stat.location] }
|
53
|
+
.group_by { |string, _location| string }
|
54
|
+
.sort_by {|string, list| [-list.size, string] }
|
81
55
|
.first(top)
|
82
|
-
.map { |string, list| [string, list.group_by { |
|
83
|
-
|
56
|
+
.map { |string, list| [string, list.group_by { |_string, location| location }
|
57
|
+
.map { |location, locations| [location, locations.size] }
|
58
|
+
]
|
59
|
+
}
|
84
60
|
end
|
85
61
|
|
86
|
-
|
62
|
+
# Output the results of the report
|
63
|
+
# @param [Hash] options the options for output
|
64
|
+
# @option opts [String] :to_file a path to your log file
|
65
|
+
# @option opts [Boolean] :color_output a flag for whether to colorize output
|
66
|
+
def pretty_print(io = STDOUT, options = {})
|
87
67
|
io = File.open(options[:to_file], "w") if options[:to_file]
|
88
68
|
|
89
69
|
color_output = options.fetch(:color_output) { io.respond_to?(:isatty) && io.isatty }
|
@@ -116,7 +96,7 @@ module MemoryProfiler
|
|
116
96
|
io.puts @colorize.line("-----------------------------------")
|
117
97
|
strings.each do |string, stats|
|
118
98
|
io.puts "#{stats.reduce(0) { |a, b| a + b[1] }.to_s.rjust(10)} #{@colorize.string((string[0..200].inspect))}"
|
119
|
-
stats.sort_by { |x, y| -y }.each do |location, count|
|
99
|
+
stats.sort_by { |x, y| [-y, x] }.each do |location, count|
|
120
100
|
io.puts "#{@colorize.path(count.to_s.rjust(10))} #{location}"
|
121
101
|
end
|
122
102
|
io.puts
|
@@ -127,7 +107,7 @@ module MemoryProfiler
|
|
127
107
|
def dump(description, data, io)
|
128
108
|
io.puts description
|
129
109
|
io.puts @colorize.line("-----------------------------------")
|
130
|
-
if data
|
110
|
+
if data && !data.empty?
|
131
111
|
data.each do |item|
|
132
112
|
io.puts "#{item[:count].to_s.rjust(10)} #{item[:data]}"
|
133
113
|
end
|
data/lib/memory_profiler/stat.rb
CHANGED
@@ -1,3 +1,18 @@
|
|
1
1
|
module MemoryProfiler
|
2
|
-
Stat
|
2
|
+
class Stat
|
3
|
+
|
4
|
+
attr_reader :class_name, :gem, :file, :location, :memsize, :string_value
|
5
|
+
|
6
|
+
def initialize(class_name, gem, file, location, memsize, string_value)
|
7
|
+
@class_name = class_name
|
8
|
+
@gem = gem
|
9
|
+
|
10
|
+
@file = file
|
11
|
+
@location = location
|
12
|
+
|
13
|
+
@memsize = memsize
|
14
|
+
@string_value = string_value
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
3
18
|
end
|
@@ -1,54 +1,21 @@
|
|
1
1
|
module MemoryProfiler
|
2
2
|
module TopN
|
3
|
-
#
|
4
|
-
#
|
5
|
-
def top_n(max
|
3
|
+
# Fast approach for determining the top_n entries in a Hash of Stat objects.
|
4
|
+
# Returns results for both memory (memsize summed) and objects allocated (count) as a tuple.
|
5
|
+
def top_n(max, metric)
|
6
6
|
|
7
|
-
|
8
|
-
if block_given?
|
9
|
-
self.map { |row|
|
10
|
-
yield(row)
|
11
|
-
}
|
12
|
-
else
|
13
|
-
self.dup
|
14
|
-
end
|
7
|
+
stats_by_metric = self.values.map! { |stat| [stat.send(metric), stat.memsize] }
|
15
8
|
|
16
|
-
|
17
|
-
|
9
|
+
stat_totals = stats_by_metric.group_by { |metric_value, _memsize| metric_value }.
|
10
|
+
map { |key, values| [key, values.reduce(0) { |sum, item| _key, memsize = item ; sum + memsize }, values.size] }
|
18
11
|
|
19
|
-
|
12
|
+
stats_by_memsize = stat_totals.sort_by! { |metric, memsize, _count| [-memsize, metric] }.first(max).
|
13
|
+
map! { |metric, memsize, _count| { data: metric, count: memsize } }
|
14
|
+
stats_by_count = stat_totals.sort_by! { |metric, _memsize, count| [-count, metric] }.first(max).
|
15
|
+
map! { |metric, _memsize, count| { data: metric, count: count } }
|
20
16
|
|
21
|
-
|
22
|
-
count = 0
|
23
|
-
lowest_count = 0
|
17
|
+
[stats_by_memsize, stats_by_count]
|
24
18
|
|
25
|
-
sorted << nil
|
26
|
-
|
27
|
-
sorted.each do |row|
|
28
|
-
|
29
|
-
current_item, current_count = row
|
30
|
-
|
31
|
-
unless current_item == last
|
32
|
-
if count > lowest_count
|
33
|
-
found << {data: last, count: count}
|
34
|
-
end
|
35
|
-
|
36
|
-
if found.length > max
|
37
|
-
found.sort!{|x,y| x[:count] <=> y[:count] }
|
38
|
-
found.delete_at(0)
|
39
|
-
lowest_count = found[0][:count]
|
40
|
-
end
|
41
|
-
|
42
|
-
count = 0
|
43
|
-
last = current_item
|
44
|
-
end
|
45
|
-
|
46
|
-
count += (current_count || 1) unless row.nil?
|
47
|
-
end
|
48
|
-
|
49
|
-
found
|
50
|
-
.sort!{|x,y| x[:count] <=> y[:count] }
|
51
|
-
.reverse
|
52
19
|
end
|
53
20
|
end
|
54
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memory_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,20 +80,15 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description: Memory profiling routines for Ruby
|
83
|
+
description: Memory profiling routines for Ruby 2.1+
|
84
84
|
email:
|
85
85
|
- sam.saffron@gmail.com
|
86
86
|
executables: []
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
-
- ".gitignore"
|
91
|
-
- ".travis.yml"
|
92
|
-
- Gemfile
|
93
|
-
- Guardfile
|
94
90
|
- LICENSE.txt
|
95
91
|
- README.md
|
96
|
-
- Rakefile
|
97
92
|
- lib/memory_profiler.rb
|
98
93
|
- lib/memory_profiler/helpers.rb
|
99
94
|
- lib/memory_profiler/monochrome.rb
|
@@ -104,14 +99,7 @@ files:
|
|
104
99
|
- lib/memory_profiler/stat_hash.rb
|
105
100
|
- lib/memory_profiler/top_n.rb
|
106
101
|
- lib/memory_profiler/version.rb
|
107
|
-
|
108
|
-
- test/test_helper.rb
|
109
|
-
- test/test_helpers.rb
|
110
|
-
- test/test_reporter.rb
|
111
|
-
- test/test_results.rb
|
112
|
-
- test/test_stat_hash.rb
|
113
|
-
- test/test_top_n.rb
|
114
|
-
homepage: ''
|
102
|
+
homepage: https://github.com/SamSaffron/memory_profiler
|
115
103
|
licenses:
|
116
104
|
- MIT
|
117
105
|
metadata: {}
|
@@ -123,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
111
|
requirements:
|
124
112
|
- - ">="
|
125
113
|
- !ruby/object:Gem::Version
|
126
|
-
version:
|
114
|
+
version: 2.1.0
|
127
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
116
|
requirements:
|
129
117
|
- - ">="
|
@@ -131,14 +119,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
119
|
version: '0'
|
132
120
|
requirements: []
|
133
121
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.4.5
|
122
|
+
rubygems_version: 2.4.5.1
|
135
123
|
signing_key:
|
136
124
|
specification_version: 4
|
137
|
-
summary: Memory profiling routines for Ruby
|
138
|
-
test_files:
|
139
|
-
- test/test_helper.rb
|
140
|
-
- test/test_helpers.rb
|
141
|
-
- test/test_reporter.rb
|
142
|
-
- test/test_results.rb
|
143
|
-
- test/test_stat_hash.rb
|
144
|
-
- test/test_top_n.rb
|
125
|
+
summary: Memory profiling routines for Ruby 2.1+
|
126
|
+
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Guardfile
DELETED
data/Rakefile
DELETED
data/memory_profiler.gemspec
DELETED
@@ -1,26 +0,0 @@
|
|
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
|
data/test/test_helper.rb
DELETED
data/test/test_helpers.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require_relative '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
|
data/test/test_reporter.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class TestReporter < Minitest::Test
|
4
|
-
|
5
|
-
class Foo; end
|
6
|
-
|
7
|
-
def allocate_strings(n)
|
8
|
-
n.times do
|
9
|
-
""
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_counts
|
14
|
-
a = nil
|
15
|
-
result = MemoryProfiler::Reporter.report do
|
16
|
-
allocate_strings(10)
|
17
|
-
a = "hello"
|
18
|
-
end
|
19
|
-
assert_equal(11, result.total_allocated)
|
20
|
-
assert_equal(1, result.total_retained)
|
21
|
-
assert_equal(1, result.retained_objects_by_location.length)
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_class_tracing
|
25
|
-
result = MemoryProfiler::Reporter.report(:trace => [Foo]) do
|
26
|
-
"hello"
|
27
|
-
"hello"
|
28
|
-
Foo.new
|
29
|
-
end
|
30
|
-
assert_equal(1, result.total_allocated)
|
31
|
-
assert_equal(0, result.total_retained)
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_ignore_file
|
35
|
-
result = MemoryProfiler::Reporter.report(:ignore_files => /test_reporter\.rb/) do
|
36
|
-
"hello"
|
37
|
-
"hello"
|
38
|
-
Foo.new
|
39
|
-
end
|
40
|
-
|
41
|
-
assert_equal(0, result.total_allocated)
|
42
|
-
assert_equal(0, result.total_retained)
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_no_color_output
|
46
|
-
report = MemoryProfiler::Reporter.report do
|
47
|
-
allocate_strings(10)
|
48
|
-
end
|
49
|
-
io = StringIO.new
|
50
|
-
report.pretty_print io, color_output: false
|
51
|
-
assert(!io.string.include?("\033"), 'excludes color information')
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_color_output
|
55
|
-
report = MemoryProfiler::Reporter.report do
|
56
|
-
allocate_strings(10)
|
57
|
-
end
|
58
|
-
io = StringIO.new
|
59
|
-
report.pretty_print io, color_output: true
|
60
|
-
assert(io.string.include?("\033"), 'includes color information')
|
61
|
-
end
|
62
|
-
|
63
|
-
class StdoutMock < StringIO
|
64
|
-
def isatty
|
65
|
-
true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def test_color_output_defaults_to_true_when_run_from_tty
|
70
|
-
report = MemoryProfiler::Reporter.report do
|
71
|
-
allocate_strings(10)
|
72
|
-
end
|
73
|
-
io = StdoutMock.new
|
74
|
-
report.pretty_print io
|
75
|
-
assert(io.string.include?("\033"), 'includes color information')
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_mono_output_defaults_to_true_when_not_run_from_tty
|
79
|
-
report = MemoryProfiler::Reporter.report do
|
80
|
-
allocate_strings(10)
|
81
|
-
end
|
82
|
-
io = StringIO.new
|
83
|
-
report.pretty_print io
|
84
|
-
assert(!io.string.include?("\033"), 'excludes color information')
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_reports_can_be_reused_with_different_color_options
|
88
|
-
report = MemoryProfiler::Reporter.report do
|
89
|
-
allocate_strings(10)
|
90
|
-
end
|
91
|
-
|
92
|
-
io = StringIO.new
|
93
|
-
report.pretty_print io, color_output: true
|
94
|
-
assert(io.string.include?("\033"), 'includes color information')
|
95
|
-
|
96
|
-
io = StringIO.new
|
97
|
-
report.pretty_print io, color_output: false
|
98
|
-
assert(!io.string.include?("\033"), 'excludes color information')
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
data/test/test_results.rb
DELETED
data/test/test_stat_hash.rb
DELETED
data/test/test_top_n.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require_relative '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
|