memory_profiler 0.9.14 → 1.0.0
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +21 -2
- data/bin/ruby-memory-profiler +8 -0
- data/lib/memory_profiler.rb +1 -0
- data/lib/memory_profiler/cli.rb +149 -0
- data/lib/memory_profiler/helpers.rb +29 -2
- data/lib/memory_profiler/polychrome.rb +2 -2
- data/lib/memory_profiler/reporter.rb +1 -5
- data/lib/memory_profiler/results.rb +20 -9
- data/lib/memory_profiler/top_n.rb +17 -12
- data/lib/memory_profiler/version.rb +1 -1
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dea36221189a4ea796d262870a9a09acf48fcfe3b6321814fc3e86cc1f4c37db
|
4
|
+
data.tar.gz: '059ab396fc3c0e928046dd3f5c0e6271a616a369f427ee8d134521bb934e8cd8'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69a830fb9ac126817342cc84e70f0796417d119ae337172a6b79a26d10e78a08146a4496915c384c7f993e8c514d43168e8e9d1d925acb856ca77092bdd1535e
|
7
|
+
data.tar.gz: 6de39e21847f551fac3b53107e5fa9d8cba5c6d00cfd39fb104432bc44f83008afefe9c46138ce4560e91880f245456b00f60718593198d735a379e4d93b8d71
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.0.0 - 02-12-2020
|
4
|
+
|
5
|
+
- Added new CLI `ruby-memory-profiler` which can be used to profile scripts @fatkodima
|
6
|
+
- Reduced memory usage when generating reports
|
7
|
+
- Some optimizations for Ruby 2.7
|
8
|
+
- Remove EOL Rubies: 2.3 and 2.4 are no longer supported (use an earlier version of the gem if needed)
|
9
|
+
|
3
10
|
## 0.9.14 - 28-06-2019
|
4
11
|
|
5
12
|
- Pass 'normalize_path: true' to pretty_print to have locations stripped
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](https://github.com/SamSaffron/memory_profiler/actions?query=workflow%3ACI)
|
2
2
|
[](https://rubygems.org/gems/memory_profiler)
|
3
3
|
|
4
4
|
# MemoryProfiler
|
@@ -7,7 +7,7 @@ A memory profiler for Ruby
|
|
7
7
|
|
8
8
|
## Requirements
|
9
9
|
|
10
|
-
Ruby(MRI) Version 2.
|
10
|
+
Ruby(MRI) Version 2.5.0 and above.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -25,6 +25,25 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
## Usage
|
27
27
|
|
28
|
+
There are two ways to use `memory_profiler`:
|
29
|
+
* command line
|
30
|
+
* convenience API
|
31
|
+
|
32
|
+
### Command Line
|
33
|
+
|
34
|
+
The easiest way to use memory_profiler is via the command line, which requires no modifications to your program. The basic usage is:
|
35
|
+
```
|
36
|
+
$ ruby-memory-profiler [options] <script.rb> [--] [script-options]
|
37
|
+
```
|
38
|
+
Where `script.rb` is the program you want to profile.
|
39
|
+
|
40
|
+
For a full list of options, execute the following command:
|
41
|
+
```
|
42
|
+
ruby-memory-profiler -h
|
43
|
+
```
|
44
|
+
|
45
|
+
### Convenience API
|
46
|
+
|
28
47
|
```ruby
|
29
48
|
require 'memory_profiler'
|
30
49
|
report = MemoryProfiler.report do
|
data/lib/memory_profiler.rb
CHANGED
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
module MemoryProfiler
|
6
|
+
class CLI
|
7
|
+
BIN_NAME = "ruby-memory-profiler"
|
8
|
+
VERSION_INFO = "#{BIN_NAME} #{MemoryProfiler::VERSION}"
|
9
|
+
|
10
|
+
STATUS_SUCCESS = 0
|
11
|
+
STATUS_ERROR = 1
|
12
|
+
|
13
|
+
DEFAULTS = {
|
14
|
+
ignore_files: "memory_profiler/lib"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
REPORTER_KEYS = [
|
18
|
+
:top, :trace, :ignore_files, :allow_files
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
RESULTS_KEYS = [
|
22
|
+
:to_file, :color_output, :retained_strings, :allocated_strings,
|
23
|
+
:detailed_report, :scale_bytes, :normalize_paths
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
private_constant :BIN_NAME, :VERSION_INFO,:STATUS_SUCCESS, :STATUS_ERROR,
|
27
|
+
:DEFAULTS, :REPORTER_KEYS, :RESULTS_KEYS
|
28
|
+
|
29
|
+
#
|
30
|
+
|
31
|
+
def run(argv)
|
32
|
+
options = {}
|
33
|
+
parser = option_parser(options)
|
34
|
+
parser.parse!(argv)
|
35
|
+
|
36
|
+
options = DEFAULTS.merge(options)
|
37
|
+
|
38
|
+
# Make sure the user specified at least one file
|
39
|
+
unless (script = argv.shift)
|
40
|
+
puts parser
|
41
|
+
puts ""
|
42
|
+
puts "#{VERSION_INFO} | ERROR: Must specify a script to run"
|
43
|
+
return STATUS_ERROR
|
44
|
+
end
|
45
|
+
|
46
|
+
MemoryProfiler.start(reporter_options(options))
|
47
|
+
load script
|
48
|
+
|
49
|
+
STATUS_SUCCESS
|
50
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => e
|
51
|
+
puts parser
|
52
|
+
puts e.message
|
53
|
+
STATUS_ERROR
|
54
|
+
ensure
|
55
|
+
report = MemoryProfiler.stop
|
56
|
+
report&.pretty_print(**results_options(options))
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def option_parser(options)
|
62
|
+
OptionParser.new do |opts|
|
63
|
+
opts.banner = <<~BANNER
|
64
|
+
|
65
|
+
#{VERSION_INFO}
|
66
|
+
A Memory Profiler for Ruby
|
67
|
+
|
68
|
+
Usage:
|
69
|
+
#{BIN_NAME} [options] <script.rb> [--] [script-options]
|
70
|
+
BANNER
|
71
|
+
|
72
|
+
opts.separator ""
|
73
|
+
opts.separator "Options:"
|
74
|
+
|
75
|
+
# Reporter options
|
76
|
+
opts.on("-m", "--max=NUM", Integer, "Max number of entries to output. (Defaults to 50)") do |arg|
|
77
|
+
options[:top] = arg
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("--classes=CLASSES", Array, "A class or list of classes you explicitly want to trace.") do |arg|
|
81
|
+
options[:trace] = arg.map { |klass| Object.const_get(klass) }
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on("--ignore-files=REGEXP", "A regular expression used to exclude certain files from tracing.") do |arg|
|
85
|
+
options[:ignore_files] = "#{arg}|memory_profiler/lib"
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("--allow-files=FILES", Array, "A string or list of strings to selectively include in tracing.") do |arg|
|
89
|
+
options[:allow_files] = arg
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.separator ""
|
93
|
+
|
94
|
+
# Results options
|
95
|
+
opts.on("-o", "--out=FILE", "Write output to a file instead of STDOUT.") do |arg|
|
96
|
+
options[:to_file] = arg
|
97
|
+
end
|
98
|
+
|
99
|
+
opts.on("--[no-]color", "Force color output on or off. (Enabled by default)") do |arg|
|
100
|
+
options[:color_output] = arg
|
101
|
+
end
|
102
|
+
|
103
|
+
opts.on("--retained-strings=NUM", Integer, "How many retained strings to print.") do |arg|
|
104
|
+
options[:retained_strings] = arg
|
105
|
+
end
|
106
|
+
|
107
|
+
opts.on("--allocated-strings=NUM", Integer, "How many allocated strings to print.") do |arg|
|
108
|
+
options[:allocated_strings] = arg
|
109
|
+
end
|
110
|
+
|
111
|
+
opts.on("--[no-]detailed", "Print detailed information. (Enabled by default)") do |arg|
|
112
|
+
options[:detailed_report] = arg
|
113
|
+
end
|
114
|
+
|
115
|
+
opts.on("--scale-bytes", "Calculates unit prefixes for the numbers of bytes.") do
|
116
|
+
options[:scale_bytes] = true
|
117
|
+
end
|
118
|
+
|
119
|
+
opts.on("--normalize-paths", "Print location paths relative to gem's source directory.") do
|
120
|
+
options[:normalize_paths] = true
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on("--pretty", "Easily enable options 'scale-bytes' and 'normalize-paths'") do
|
124
|
+
options[:scale_bytes] = options[:normalize_paths] = true
|
125
|
+
end
|
126
|
+
|
127
|
+
opts.separator ""
|
128
|
+
|
129
|
+
opts.on_tail("-h", "--help", "Show this help message.") do
|
130
|
+
puts opts
|
131
|
+
exit
|
132
|
+
end
|
133
|
+
|
134
|
+
opts.on_tail("-v", "--version", "Show program version.") do
|
135
|
+
puts VERSION_INFO
|
136
|
+
exit
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def reporter_options(options)
|
142
|
+
options.select { |k, _v| REPORTER_KEYS.include?(k) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def results_options(options)
|
146
|
+
options.select { |k, _v| RESULTS_KEYS.include?(k) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -29,8 +29,35 @@ module MemoryProfiler
|
|
29
29
|
@location_cache[file][line] ||= "#{file}:#{line}"
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
KERNEL_CLASS_METHOD = Kernel.instance_method(:class)
|
33
|
+
if UnboundMethod.method_defined?(:bind_call)
|
34
|
+
def object_class(obj)
|
35
|
+
klass = obj.class rescue nil
|
36
|
+
unless Class === klass
|
37
|
+
# attempt to determine the true Class when .class returns something other than a Class
|
38
|
+
klass = KERNEL_CLASS_METHOD.bind_call(obj)
|
39
|
+
end
|
40
|
+
klass
|
41
|
+
end
|
42
|
+
else
|
43
|
+
def object_class(obj)
|
44
|
+
klass = obj.class rescue nil
|
45
|
+
unless Class === klass
|
46
|
+
# attempt to determine the true Class when .class returns something other than a Class
|
47
|
+
klass = KERNEL_CLASS_METHOD.bind(obj).call
|
48
|
+
end
|
49
|
+
klass
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if Object.name.frozen? # Since Ruby 2.7 Module#name no longer allocate a new string
|
54
|
+
def lookup_class_name(klass)
|
55
|
+
((klass.is_a?(Class) && klass.name) || '<<Unknown>>').to_s
|
56
|
+
end
|
57
|
+
else
|
58
|
+
def lookup_class_name(klass)
|
59
|
+
@class_name_cache[klass] ||= ((klass.is_a?(Class) && klass.name) || '<<Unknown>>').to_s
|
60
|
+
end
|
34
61
|
end
|
35
62
|
|
36
63
|
def lookup_string(obj)
|
@@ -99,11 +99,7 @@ module MemoryProfiler
|
|
99
99
|
next if @ignore_files && @ignore_files =~ file
|
100
100
|
next if @allow_files && !(@allow_files =~ file)
|
101
101
|
|
102
|
-
klass = obj
|
103
|
-
unless Class === klass
|
104
|
-
# attempt to determine the true Class when .class returns something other than a Class
|
105
|
-
klass = Kernel.instance_method(:class).bind(obj).call
|
106
|
-
end
|
102
|
+
klass = helper.object_class(obj)
|
107
103
|
next if @trace && !trace.include?(klass)
|
108
104
|
|
109
105
|
begin
|
@@ -57,9 +57,9 @@ module MemoryProfiler
|
|
57
57
|
self.strings_retained = string_report(retained, top)
|
58
58
|
|
59
59
|
self.total_allocated = allocated.size
|
60
|
-
self.total_allocated_memsize = allocated
|
60
|
+
self.total_allocated_memsize = total_memsize(allocated)
|
61
61
|
self.total_retained = retained.size
|
62
|
-
self.total_retained_memsize = retained
|
62
|
+
self.total_retained_memsize = total_memsize(retained)
|
63
63
|
|
64
64
|
self
|
65
65
|
end
|
@@ -73,19 +73,22 @@ module MemoryProfiler
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def string_report(data, top)
|
76
|
-
grouped_strings =
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
grouped_strings = Hash.new { |hash, key| hash[key] = [] }
|
77
|
+
data.each_value do |stat|
|
78
|
+
if stat.string_value
|
79
|
+
grouped_strings[stat.string_value.object_id] << stat
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
grouped_strings = grouped_strings.values
|
80
84
|
|
81
85
|
if grouped_strings.size > top
|
82
|
-
|
83
|
-
grouped_strings
|
86
|
+
grouped_strings.sort_by!(&:size)
|
87
|
+
grouped_strings = grouped_strings.drop(grouped_strings.size - top)
|
84
88
|
end
|
85
89
|
|
86
90
|
grouped_strings
|
87
91
|
.sort! { |a, b| a.size == b.size ? a[0].string_value <=> b[0].string_value : b.size <=> a.size }
|
88
|
-
.first(top)
|
89
92
|
.map! do |list|
|
90
93
|
# Return array of [string, [[location, count], [location, count], ...]
|
91
94
|
[
|
@@ -171,6 +174,14 @@ module MemoryProfiler
|
|
171
174
|
|
172
175
|
private
|
173
176
|
|
177
|
+
def total_memsize(stat_hash)
|
178
|
+
sum = 0
|
179
|
+
stat_hash.each_value do |stat|
|
180
|
+
sum += stat.memsize
|
181
|
+
end
|
182
|
+
sum
|
183
|
+
end
|
184
|
+
|
174
185
|
def print_title(io, title)
|
175
186
|
io.puts
|
176
187
|
io.puts title
|
@@ -6,24 +6,29 @@ 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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
metric_memsize = Hash.new(0)
|
10
|
+
metric_objects_count = Hash.new(0)
|
11
|
+
|
12
|
+
each_value do |value|
|
13
|
+
metric = value.send(metric_method)
|
14
|
+
|
15
|
+
metric_memsize[metric] += value.memsize
|
16
|
+
metric_objects_count[metric] += 1
|
17
|
+
end
|
15
18
|
|
16
19
|
stats_by_memsize =
|
17
|
-
|
18
|
-
.
|
20
|
+
metric_memsize
|
21
|
+
.to_a
|
22
|
+
.sort_by! { |metric, memsize| [-memsize, metric] }
|
19
23
|
.take(max)
|
20
|
-
.map! { |metric, memsize
|
24
|
+
.map! { |metric, memsize| { data: metric, count: memsize } }
|
21
25
|
|
22
26
|
stats_by_count =
|
23
|
-
|
24
|
-
.
|
27
|
+
metric_objects_count
|
28
|
+
.to_a
|
29
|
+
.sort_by! { |metric, count| [-count, metric] }
|
25
30
|
.take(max)
|
26
|
-
.map! { |metric,
|
31
|
+
.map! { |metric, count| { data: metric, count: count } }
|
27
32
|
|
28
33
|
[stats_by_memsize, stats_by_count]
|
29
34
|
end
|
metadata
CHANGED
@@ -1,26 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memory_profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: Memory profiling routines for Ruby 2.
|
13
|
+
description: Memory profiling routines for Ruby 2.5+
|
14
14
|
email:
|
15
15
|
- sam.saffron@gmail.com
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- ruby-memory-profiler
|
17
18
|
extensions: []
|
18
19
|
extra_rdoc_files: []
|
19
20
|
files:
|
20
21
|
- CHANGELOG.md
|
21
22
|
- LICENSE.txt
|
22
23
|
- README.md
|
24
|
+
- bin/ruby-memory-profiler
|
23
25
|
- lib/memory_profiler.rb
|
26
|
+
- lib/memory_profiler/cli.rb
|
24
27
|
- lib/memory_profiler/helpers.rb
|
25
28
|
- lib/memory_profiler/monochrome.rb
|
26
29
|
- lib/memory_profiler/polychrome.rb
|
@@ -34,7 +37,7 @@ homepage: https://github.com/SamSaffron/memory_profiler
|
|
34
37
|
licenses:
|
35
38
|
- MIT
|
36
39
|
metadata: {}
|
37
|
-
post_install_message:
|
40
|
+
post_install_message:
|
38
41
|
rdoc_options: []
|
39
42
|
require_paths:
|
40
43
|
- lib
|
@@ -42,15 +45,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
45
|
requirements:
|
43
46
|
- - ">="
|
44
47
|
- !ruby/object:Gem::Version
|
45
|
-
version: 2.
|
48
|
+
version: 2.5.0
|
46
49
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
50
|
requirements:
|
48
51
|
- - ">="
|
49
52
|
- !ruby/object:Gem::Version
|
50
53
|
version: '0'
|
51
54
|
requirements: []
|
52
|
-
rubygems_version: 3.
|
53
|
-
signing_key:
|
55
|
+
rubygems_version: 3.1.4
|
56
|
+
signing_key:
|
54
57
|
specification_version: 4
|
55
|
-
summary: Memory profiling routines for Ruby 2.
|
58
|
+
summary: Memory profiling routines for Ruby 2.5+
|
56
59
|
test_files: []
|