memory_profiler 0.9.13 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: