memory_profiler 0.9.13 → 0.9.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df0b1ccbb40909e048bc0064c0f340997ab72629274a71704885c0d1988eb14c
4
- data.tar.gz: 73181a0702537823a3f040c1dbe64a8a432ef945fdc761d923b094c8194a57ce
3
+ metadata.gz: 187acbd9601ade1bb6ccdb60bfb4c305ada15097f02278529f8f6a6fc7d68666
4
+ data.tar.gz: 33d797db658d4a4507247a0ae9c4b92da568b86e9246f4571a94774b62285c6b
5
5
  SHA512:
6
- metadata.gz: db95ae6758da369bba200ccc7da4af2c01115f9c2d55d607a5e2e0bf135825a65acb0b44393870bdc0cc8fb4e1d0310533e40560721ab9a22856f175f661d58f
7
- data.tar.gz: e6e9ca6e80929277f9d937c516d6582d206360b086e38ccd15ce7fe2d4a9be14c684749f665a8151affd54bcc107068493a815bc0d4f60caa4660a473954717e
6
+ metadata.gz: 977f958e6b9f3292053ccef51bcef01828cda7d29760f131687d21b00222950fbf1ec4629a7021bfdc4665e137c3f75cfd729a29db60b879a5c30f08da0de097
7
+ data.tar.gz: 1cc221f3bf20520a3063031a4d9830e3303d9d9182cafb746aa77d0059888a4a640f2d6dd39a925e31398664018c98e9324565247046310c15ef823419bb6baf
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.14 - 28-06-2019
4
+
5
+ - Pass 'normalize_path: true' to pretty_print to have locations stripped
6
+ - Improve number formatting
7
+
3
8
  ## 0.9.13 - 22-03-2019
4
9
 
5
10
  - remove support explicitly for all EOL rubies, 2.1 and 2.2
data/README.md CHANGED
@@ -96,6 +96,9 @@ The `pretty_print` method can take a few options:
96
96
  * `allocated_strings`: how many allocated strings to print - can be given a String
97
97
  * `detailed_report`: should report include detailed information - can be given a Boolean
98
98
  * `scale_bytes`: flag to convert byte units (e.g. 183200000 is reported as 183.2 MB, rounds with a precision of 2 decimal digits) - can be given a Boolean
99
+ * `normalize_paths`: flag to remove a gem's directory path from printed locations - can be given a Boolean
100
+ *Note: normalized path of a "location" from Ruby's stdlib will be prefixed with `ruby/lib/`. e.g.: `ruby/lib/set.rb`, `ruby/lib/pathname.rb`, etc.*
101
+
99
102
 
100
103
  Check out `Results#pretty_print` for more details.
101
104
 
@@ -11,11 +11,11 @@ require "memory_profiler/results"
11
11
  require "memory_profiler/reporter"
12
12
 
13
13
  module MemoryProfiler
14
- def self.report(opts={},&block)
15
- Reporter.report(opts,&block)
14
+ def self.report(opts = {}, &block)
15
+ Reporter.report(opts, &block)
16
16
  end
17
17
 
18
- def self.start(opts={})
18
+ def self.start(opts = {})
19
19
  unless Reporter.current_reporter
20
20
  Reporter.current_reporter = Reporter.new(opts)
21
21
  Reporter.current_reporter.start
@@ -28,5 +28,3 @@ module MemoryProfiler
28
28
  Reporter.current_reporter = nil
29
29
  end
30
30
  end
31
-
32
-
@@ -5,7 +5,7 @@ module MemoryProfiler
5
5
 
6
6
  def initialize
7
7
  @gem_guess_cache = Hash.new
8
- @location_cache = Hash.new { |h,k| h[k] = Hash.new.compare_by_identity }
8
+ @location_cache = Hash.new { |h, k| h[k] = Hash.new.compare_by_identity }
9
9
  @class_name_cache = Hash.new.compare_by_identity
10
10
  @string_cache = Hash.new
11
11
  end
@@ -37,7 +37,7 @@ module MemoryProfiler
37
37
  # This string is shortened to 200 characters which is what the string report shows
38
38
  # The string report can still list unique strings longer than 200 characters
39
39
  # separately because the object_id of the shortened string will be different
40
- @string_cache[obj] ||= String.new << obj[0,200]
40
+ @string_cache[obj] ||= String.new << obj[0, 200]
41
41
  end
42
42
  end
43
43
  end
@@ -17,4 +17,4 @@ module MemoryProfiler
17
17
  end
18
18
  end
19
19
 
20
- end
20
+ end
@@ -29,7 +29,7 @@ module MemoryProfiler
29
29
  # @option opts :ignore_files a regular expression used to exclude certain files from tracing
30
30
  # @option opts :allow_files a string or array of strings to selectively include in tracing
31
31
  # @return [MemoryProfiler::Results]
32
- def self.report(opts={}, &block)
32
+ def self.report(opts = {}, &block)
33
33
  self.new(opts).run(&block)
34
34
  end
35
35
 
@@ -115,7 +115,7 @@ module MemoryProfiler
115
115
  # we do memsize first to avoid freezing as a side effect and shifting
116
116
  # storage to the new frozen string, this happens on @hash[s] in lookup_string
117
117
  memsize = ObjectSpace.memsize_of(obj)
118
- string = klass == String ? helper.lookup_string(obj) : nil
118
+ string = klass == String ? helper.lookup_string(obj) : nil
119
119
 
120
120
  # compensate for API bug
121
121
  memsize = rvalue_size if memsize > 100_000_000_000
@@ -14,12 +14,18 @@ module MemoryProfiler
14
14
  24 => 'YB'
15
15
  }.freeze
16
16
 
17
+ TYPES = ["allocated", "retained"].freeze
18
+ METRICS = ["memory", "objects"].freeze
19
+ NAMES = ["gem", "file", "location", "class"].freeze
20
+
17
21
  def self.register_type(name, stat_attribute)
18
22
  @@lookups ||= []
19
23
  @@lookups << [name, stat_attribute]
20
24
 
21
- ["allocated", "retained"].product(["objects", "memory"]).each do |type, metric|
22
- attr_accessor "#{type}_#{metric}_by_#{name}"
25
+ TYPES.each do |type|
26
+ METRICS.each do |metric|
27
+ attr_accessor "#{type}_#{metric}_by_#{name}"
28
+ end
23
29
  end
24
30
  end
25
31
 
@@ -47,7 +53,6 @@ module MemoryProfiler
47
53
  self.send("retained_objects_by_#{name}=", count_results)
48
54
  end
49
55
 
50
-
51
56
  self.strings_allocated = string_report(allocated, top)
52
57
  self.strings_retained = string_report(retained, top)
53
58
 
@@ -64,30 +69,33 @@ module MemoryProfiler
64
69
 
65
70
  scale = Math.log10(bytes).div(3) * 3
66
71
  scale = 24 if scale > 24
67
- "#{(bytes / 10.0**scale).round(2)} #{UNIT_PREFIXES[scale]}"
72
+ "%.2f #{UNIT_PREFIXES[scale]}" % (bytes / 10.0**scale)
68
73
  end
69
74
 
70
75
  def string_report(data, top)
71
- grouped_strings = data.values.
72
- keep_if { |stat| stat.string_value }.
73
- group_by { |stat| stat.string_value.object_id }.
74
- values
76
+ grouped_strings = data.values
77
+ .keep_if { |stat| stat.string_value }
78
+ .group_by { |stat| stat.string_value.object_id }
79
+ .values
75
80
 
76
81
  if grouped_strings.size > top
77
82
  cutoff = grouped_strings.sort_by!(&:size)[-top].size
78
83
  grouped_strings.keep_if { |list| list.size >= cutoff }
79
84
  end
80
85
 
81
- grouped_strings.
82
- sort! { |a, b| a.size == b.size ? a[0].string_value <=> b[0].string_value : b.size <=> a.size }.
83
- first(top).
84
- # Return array of [string, [[location, count], [location, count], ...]
85
- map! { |list| [list[0].string_value,
86
- list.group_by { |stat| stat.location }.
87
- map { |location, stat_list| [location, stat_list.size] }.
88
- sort_by!(&:last).reverse!
89
- ]
90
- }
86
+ grouped_strings
87
+ .sort! { |a, b| a.size == b.size ? a[0].string_value <=> b[0].string_value : b.size <=> a.size }
88
+ .first(top)
89
+ .map! do |list|
90
+ # Return array of [string, [[location, count], [location, count], ...]
91
+ [
92
+ list[0].string_value,
93
+ list.group_by { |stat| stat.location }
94
+ .map { |location, stat_list| [location, stat_list.size] }
95
+ .sort_by!(&:last)
96
+ .reverse!
97
+ ]
98
+ end
91
99
  end
92
100
 
93
101
  # Output the results of the report
@@ -98,6 +106,7 @@ module MemoryProfiler
98
106
  # @option opts [Integer] :allocated_strings how many allocated strings to print
99
107
  # @option opts [Boolean] :detailed_report should report include detailed information
100
108
  # @option opts [Boolean] :scale_bytes calculates unit prefixes for the numbers of bytes
109
+ # @option opts [Boolean] :normalize_paths print location paths relative to gem's source directory.
101
110
  def pretty_print(io = $stdout, **options)
102
111
  # Handle the special case that Ruby PrettyPrint expects `pretty_print`
103
112
  # to be a customized pretty printing function for a class
@@ -110,72 +119,114 @@ module MemoryProfiler
110
119
 
111
120
  if options[:scale_bytes]
112
121
  total_allocated_output = scale_bytes(total_allocated_memsize)
113
- total_retained_output = scale_bytes(total_retained_memsize)
122
+ total_retained_output = scale_bytes(total_retained_memsize)
114
123
  else
115
124
  total_allocated_output = "#{total_allocated_memsize} bytes"
116
- total_retained_output = "#{total_retained_memsize} bytes"
125
+ total_retained_output = "#{total_retained_memsize} bytes"
117
126
  end
118
127
 
119
128
  io.puts "Total allocated: #{total_allocated_output} (#{total_allocated} objects)"
120
129
  io.puts "Total retained: #{total_retained_output} (#{total_retained} objects)"
121
130
 
122
- if options[:detailed_report] != false
123
- io.puts
124
- ["allocated", "retained"]
125
- .product(["memory", "objects"])
126
- .product(["gem", "file", "location", "class"])
127
- .each do |(type, metric), name|
128
- scale_data = metric == "memory" && options[:scale_bytes]
129
- dump "#{type} #{metric} by #{name}", self.send("#{type}_#{metric}_by_#{name}"), io, scale_data
131
+ unless options[:detailed_report] == false
132
+ TYPES.each do |type|
133
+ METRICS.each do |metric|
134
+ NAMES.each do |name|
135
+ dump_data(io, type, metric, name, options)
130
136
  end
137
+ end
138
+ end
131
139
  end
132
140
 
133
141
  io.puts
134
- dump_strings(io, "Allocated", strings_allocated, limit: options[:allocated_strings])
135
- io.puts
136
- dump_strings(io, "Retained", strings_retained, limit: options[:retained_strings])
142
+ print_string_reports(io, options)
137
143
 
138
144
  io.close if io.is_a? File
139
145
  end
140
146
 
141
- private
142
-
143
- def dump_strings(io, title, strings, limit: nil)
144
- return unless strings
145
-
146
- if limit
147
- return if limit == 0
148
- strings = strings[0...limit]
147
+ def print_string_reports(io, options)
148
+ TYPES.each do |type|
149
+ dump_opts = {
150
+ normalize_paths: options[:normalize_paths],
151
+ limit: options["#{type}_strings".to_sym]
152
+ }
153
+ dump_strings(io, type, dump_opts)
149
154
  end
155
+ end
150
156
 
151
- io.puts "#{title} String Report"
152
- io.puts @colorize.line("-----------------------------------")
153
- strings.each do |string, stats|
154
- io.puts "#{stats.reduce(0) { |a, b| a + b[1] }.to_s.rjust(10)} #{@colorize.string((string.inspect))}"
155
- stats.sort_by { |x, y| [-y, x] }.each do |location, count|
156
- io.puts "#{@colorize.path(count.to_s.rjust(10))} #{location}"
157
+ def normalize_path(path)
158
+ @normalize_path ||= {}
159
+ @normalize_path[path] ||= begin
160
+ if %r!(/gems/.*)*/gems/(?<gemname>[^/]+)(?<rest>.*)! =~ path
161
+ "#{gemname}#{rest}"
162
+ elsif %r!ruby/2\.[^/]+/(?<stdlib>[^/.]+)(?<rest>.*)! =~ path
163
+ "ruby/lib/#{stdlib}#{rest}"
164
+ elsif %r!(?<app>[^/]+/(bin|app|lib))(?<rest>.*)! =~ path
165
+ "#{app}#{rest}"
166
+ else
167
+ path
157
168
  end
158
- io.puts
159
169
  end
160
- nil
161
170
  end
162
171
 
163
- def dump(description, data, io, scale_data)
164
- io.puts description
172
+ private
173
+
174
+ def print_title(io, title)
175
+ io.puts
176
+ io.puts title
165
177
  io.puts @colorize.line("-----------------------------------")
178
+ end
179
+
180
+ def print_output(io, topic, detail)
181
+ io.puts "#{@colorize.path(topic.to_s.rjust(10))} #{detail}"
182
+ end
183
+
184
+ def dump_data(io, type, metric, name, options)
185
+ print_title io, "#{type} #{metric} by #{name}"
186
+ data = self.send "#{type}_#{metric}_by_#{name}"
187
+
188
+ scale_data = metric == "memory" && options[:scale_bytes]
189
+ normalize_paths = options[:normalize_paths]
190
+
166
191
  if data && !data.empty?
167
192
  data.each do |item|
168
- data_string = scale_data ? scale_bytes(item[:count]) : item[:count].to_s
169
- io.puts "#{data_string.rjust(10)} #{item[:data]}"
193
+ count = scale_data ? scale_bytes(item[:count]) : item[:count]
194
+ value = normalize_paths ? normalize_path(item[:data]) : item[:data]
195
+ print_output io, count, value
170
196
  end
171
197
  else
172
198
  io.puts "NO DATA"
173
199
  end
174
- io.puts
200
+
201
+ nil
175
202
  end
176
203
 
177
- end
204
+ def dump_strings(io, type, options)
205
+ strings = self.send("strings_#{type}") || []
206
+ return if strings.empty?
178
207
 
179
- end
208
+ options = {} unless options.is_a?(Hash)
209
+
210
+ if (limit = options[:limit])
211
+ return if limit == 0
212
+ strings = strings[0...limit]
213
+ end
180
214
 
215
+ normalize_paths = options[:normalize_paths]
181
216
 
217
+ print_title(io, "#{type.capitalize} String Report")
218
+ strings.each do |string, stats|
219
+ print_output io, (stats.reduce(0) { |a, b| a + b[1] }), @colorize.string(string.inspect)
220
+ stats.sort_by { |x, y| [-y, x] }.each do |location, count|
221
+ location = normalize_path(location) if normalize_paths
222
+ print_output io, count, location
223
+ end
224
+ io.puts
225
+ end
226
+
227
+ nil
228
+ end
229
+
230
+ end
231
+
232
+ end
@@ -6,17 +6,26 @@ module MemoryProfiler
6
6
  # Returns results for both memory (memsize summed) and objects allocated (count) as a tuple.
7
7
  def top_n(max, metric_method)
8
8
 
9
- stat_totals = self.values.group_by(&metric_method).map do |metric, stats|
10
- [metric, stats.reduce(0) { |sum, stat| sum + stat.memsize }, stats.size]
11
- end
9
+ stat_totals =
10
+ self.values
11
+ .group_by(&metric_method)
12
+ .map do |metric, stats|
13
+ [metric, stats.reduce(0) { |sum, stat| sum + stat.memsize }, stats.size]
14
+ end
12
15
 
13
- stats_by_memsize = stat_totals.sort_by! { |metric, memsize, _count| [-memsize, metric] }.take(max).
14
- map! { |metric, memsize, _count| { data: metric, count: memsize } }
15
- stats_by_count = stat_totals.sort_by! { |metric, _memsize, count| [-count, metric] }.take(max).
16
- map! { |metric, _memsize, count| { data: metric, count: count } }
16
+ stats_by_memsize =
17
+ stat_totals
18
+ .sort_by! { |metric, memsize, _count| [-memsize, metric] }
19
+ .take(max)
20
+ .map! { |metric, memsize, _count| { data: metric, count: memsize } }
17
21
 
18
- [stats_by_memsize, stats_by_count]
22
+ stats_by_count =
23
+ stat_totals
24
+ .sort_by! { |metric, _memsize, count| [-count, metric] }
25
+ .take(max)
26
+ .map! { |metric, _memsize, count| { data: metric, count: count } }
19
27
 
28
+ [stats_by_memsize, stats_by_count]
20
29
  end
21
30
  end
22
31
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MemoryProfiler
4
- VERSION = "0.9.13"
4
+ VERSION = "0.9.14"
5
5
  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.13
4
+ version: 0.9.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-22 00:00:00.000000000 Z
11
+ date: 2019-06-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Memory profiling routines for Ruby 2.3+
14
14
  email: