memory-profiler 1.0.2 → 1.0.3
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 +7 -0
- data/Gemfile +11 -0
- data/LICENSE +12 -12
- data/README +71 -71
- data/Rakefile +7 -0
- data/lib/memory-profiler.rb +338 -332
- data/memory-profiler.gemspec +27 -0
- metadata +10 -10
- data/Gemfile.lock +0 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9dc143b51ee61fd99bfbe646490321b4dc106981
|
4
|
+
data.tar.gz: 679e188284bee7611d02e3bdf79b19a2ba2902e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ee3cde0472d1c96deb9d30ed7f180ceae46046000bb54f676f19179f5c185a1cc34ab0ce01cf497b3465c2603b03f5541632706d10cacd994a6212de3a943ba
|
7
|
+
data.tar.gz: 9002ee4f5c8d1444c833ddf25930918c9645af00f82859b839cd93a1301bc46d489b648bfd501649944d9d7494d88126c023f26e2b47e2dfc4b9f26d33b350fa
|
data/Gemfile
ADDED
data/LICENSE
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
Copyright 2011 Matthew Kerwin
|
2
|
-
|
3
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
you may not use this file except in compliance with the License.
|
5
|
-
You may obtain a copy of at
|
6
|
-
|
7
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
|
9
|
-
Unless required by applicable law or agreed to in writing, software
|
10
|
-
distributed under the License is distributed on an "AS IS" BASES,
|
11
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
See the License for the specific language governing permissions and
|
1
|
+
Copyright 2011 Matthew Kerwin
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASES,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
13
|
limitations under the License.
|
data/README
CHANGED
@@ -1,71 +1,71 @@
|
|
1
|
-
A rudimentary Ruby memory profiler that uses pure in-VM techniques to
|
2
|
-
analyse the object space and attempt to determine memory usage trends.
|
3
|
-
|
4
|
-
* Takes instantaneous snapshots, and can be used to determine trends
|
5
|
-
between subsequent instances.
|
6
|
-
* Can also be used to detect memory leaks (obscure object references,
|
7
|
-
etc.) in particular blocks or sections of code.
|
8
|
-
|
9
|
-
Note that this uses pure Ruby code and techniques, without patches to
|
10
|
-
the VM. As such it is trivial to install and use, but it doesn't have
|
11
|
-
access to raw memory management/garbage collection data, so is forced
|
12
|
-
to estimate, and it will affect performance noticeably.
|
13
|
-
|
14
|
-
It has been tested with the following Ruby versions (ruby -v):
|
15
|
-
* ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux]
|
16
|
-
* ruby 1.9.1p243 (2009-07-16 revision 24175) [i486-linux]
|
17
|
-
|
18
|
-
This project was inspired by the similar Ruby memory profiler at
|
19
|
-
http://code.google.com/p/ruby-memory-profiler/ , which was apparently
|
20
|
-
released under a BSD (or BSD-style) license; but since no attribution
|
21
|
-
details were included, I haven't copied them here anywhere.
|
22
|
-
|
23
|
-
-------------------------------------------------------------------------
|
24
|
-
|
25
|
-
The simplest way to use this utility is to copy the file:
|
26
|
-
lib/memory-profiler.rb
|
27
|
-
to your project somewhere, and include it directly.
|
28
|
-
|
29
|
-
However the correct way is to build the gem, so that you can use it in
|
30
|
-
all your projects.
|
31
|
-
|
32
|
-
-------------------------------------------------------------------------
|
33
|
-
|
34
|
-
To build the gem
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Then in your code:
|
40
|
-
|
41
|
-
require 'rubygems'
|
42
|
-
require 'memory-profiler'
|
43
|
-
|
44
|
-
-------------------------------------------------------------------------
|
45
|
-
|
46
|
-
Refer to RDoc documentation for more detail, but here's an example for
|
47
|
-
using the utility in your Ruby program:
|
48
|
-
|
49
|
-
# start the daemon, and let us know the file to which it reports
|
50
|
-
puts MemoryProfiler.start_daemon( :limit=>5, :delay=>10, :marshall_size=>true, :sort_by=>:absdelta )
|
51
|
-
|
52
|
-
5.times do
|
53
|
-
blah = Hash.new([])
|
54
|
-
|
55
|
-
# compare memory space before and after executing a block of code
|
56
|
-
rpt = MemoryProfiler.start( :limit=>10 ) do
|
57
|
-
# some activities likely to create object references
|
58
|
-
100.times{ blah[1] << 'aaaaa' }
|
59
|
-
1000.times{ blah[2] << 'bbbbb' }
|
60
|
-
end
|
61
|
-
|
62
|
-
# display the report in a (slightly) readable form
|
63
|
-
puts MemoryProfiler.format(rpt)
|
64
|
-
|
65
|
-
sleep 7
|
66
|
-
end
|
67
|
-
|
68
|
-
# terminate the daemon
|
69
|
-
MemoryProfiler.stop_daemon
|
70
|
-
|
71
|
-
-------------------------------------------------------------------------
|
1
|
+
A rudimentary Ruby memory profiler that uses pure in-VM techniques to
|
2
|
+
analyse the object space and attempt to determine memory usage trends.
|
3
|
+
|
4
|
+
* Takes instantaneous snapshots, and can be used to determine trends
|
5
|
+
between subsequent instances.
|
6
|
+
* Can also be used to detect memory leaks (obscure object references,
|
7
|
+
etc.) in particular blocks or sections of code.
|
8
|
+
|
9
|
+
Note that this uses pure Ruby code and techniques, without patches to
|
10
|
+
the VM. As such it is trivial to install and use, but it doesn't have
|
11
|
+
access to raw memory management/garbage collection data, so is forced
|
12
|
+
to estimate, and it will affect performance noticeably.
|
13
|
+
|
14
|
+
It has been tested with the following Ruby versions (ruby -v):
|
15
|
+
* ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-linux]
|
16
|
+
* ruby 1.9.1p243 (2009-07-16 revision 24175) [i486-linux]
|
17
|
+
|
18
|
+
This project was inspired by the similar Ruby memory profiler at
|
19
|
+
http://code.google.com/p/ruby-memory-profiler/ , which was apparently
|
20
|
+
released under a BSD (or BSD-style) license; but since no attribution
|
21
|
+
details were included, I haven't copied them here anywhere.
|
22
|
+
|
23
|
+
-------------------------------------------------------------------------
|
24
|
+
|
25
|
+
The simplest way to use this utility is to copy the file:
|
26
|
+
lib/memory-profiler.rb
|
27
|
+
to your project somewhere, and include it directly.
|
28
|
+
|
29
|
+
However the correct way is to build the gem, so that you can use it in
|
30
|
+
all your projects.
|
31
|
+
|
32
|
+
-------------------------------------------------------------------------
|
33
|
+
|
34
|
+
To build the gem:
|
35
|
+
|
36
|
+
gem build memory-profiler.gemspec
|
37
|
+
gem install memory-profiler-1.0.2.gem
|
38
|
+
|
39
|
+
Then in your code:
|
40
|
+
|
41
|
+
require 'rubygems' # Ruby 1.8 only
|
42
|
+
require 'memory-profiler'
|
43
|
+
|
44
|
+
-------------------------------------------------------------------------
|
45
|
+
|
46
|
+
Refer to RDoc documentation for more detail, but here's an example for
|
47
|
+
using the utility in your Ruby program:
|
48
|
+
|
49
|
+
# start the daemon, and let us know the file to which it reports
|
50
|
+
puts MemoryProfiler.start_daemon( :limit=>5, :delay=>10, :marshall_size=>true, :sort_by=>:absdelta )
|
51
|
+
|
52
|
+
5.times do
|
53
|
+
blah = Hash.new([])
|
54
|
+
|
55
|
+
# compare memory space before and after executing a block of code
|
56
|
+
rpt = MemoryProfiler.start( :limit=>10 ) do
|
57
|
+
# some activities likely to create object references
|
58
|
+
100.times{ blah[1] << 'aaaaa' }
|
59
|
+
1000.times{ blah[2] << 'bbbbb' }
|
60
|
+
end
|
61
|
+
|
62
|
+
# display the report in a (slightly) readable form
|
63
|
+
puts MemoryProfiler.format(rpt)
|
64
|
+
|
65
|
+
sleep 7
|
66
|
+
end
|
67
|
+
|
68
|
+
# terminate the daemon
|
69
|
+
MemoryProfiler.stop_daemon
|
70
|
+
|
71
|
+
-------------------------------------------------------------------------
|
data/Rakefile
ADDED
data/lib/memory-profiler.rb
CHANGED
@@ -1,332 +1,338 @@
|
|
1
|
-
#!/usr/bin/ruby --
|
2
|
-
# vim: tabstop=2:softtabstop=2:shiftwidth=2:noexpandtab
|
3
|
-
=begin
|
4
|
-
|
5
|
-
Author: Matthew Kerwin <matthew@kerwin.net.au>
|
6
|
-
Version: 1.0.
|
7
|
-
Date:
|
8
|
-
|
9
|
-
|
10
|
-
Copyright 2011 Matthew Kerwin.
|
11
|
-
|
12
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
13
|
-
you may not use this file except in compliance with the License.
|
14
|
-
You may obtain a copy of the License at
|
15
|
-
|
16
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
17
|
-
|
18
|
-
Unless required by applicable law or agreed to in writing, software
|
19
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
20
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
21
|
-
See the License for the specific language governing permissions and
|
22
|
-
limitations under the License.
|
23
|
-
|
24
|
-
=end
|
25
|
-
|
26
|
-
require 'sync'
|
27
|
-
|
28
|
-
module MemoryProfiler
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
1
|
+
#!/usr/bin/ruby --
|
2
|
+
# vim: tabstop=2:softtabstop=2:shiftwidth=2:noexpandtab
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Author: Matthew Kerwin <matthew@kerwin.net.au>
|
6
|
+
Version: 1.0.3
|
7
|
+
Date: 2014-04-19
|
8
|
+
|
9
|
+
|
10
|
+
Copyright 2011,2014 Matthew Kerwin.
|
11
|
+
|
12
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
13
|
+
you may not use this file except in compliance with the License.
|
14
|
+
You may obtain a copy of the License at
|
15
|
+
|
16
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
|
18
|
+
Unless required by applicable law or agreed to in writing, software
|
19
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
20
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
21
|
+
See the License for the specific language governing permissions and
|
22
|
+
limitations under the License.
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
require 'sync'
|
27
|
+
|
28
|
+
module MemoryProfiler
|
29
|
+
DEFAULTS = {
|
30
|
+
# general options
|
31
|
+
:sort_by => :current,
|
32
|
+
:only => [],
|
33
|
+
:ignore => [],
|
34
|
+
:limit => 20,
|
35
|
+
:force_gc => false,
|
36
|
+
# daemon options
|
37
|
+
:delay => 60,
|
38
|
+
:filename => nil,
|
39
|
+
# ObjectSpaceAnalyser options
|
40
|
+
:string_debug => false,
|
41
|
+
:marshal_size => false,
|
42
|
+
}
|
43
|
+
@@daemon_thread = nil #:nodoc:
|
44
|
+
@@daemon_sync = Sync.new #:nodoc:
|
45
|
+
|
46
|
+
@@start_data = nil #:nodoc:
|
47
|
+
@@start_sync = Sync.new #:nodoc:
|
48
|
+
|
49
|
+
#
|
50
|
+
# Begins an analysis thread that runs periodically, reporting to a text
|
51
|
+
# file at: /tmp/memory_profiler-<pid>.log
|
52
|
+
#
|
53
|
+
# Returns the filename used.
|
54
|
+
#
|
55
|
+
# Options:
|
56
|
+
# :delay => 60 # number of seconds between summaries
|
57
|
+
# :filename => nil # override the generated default
|
58
|
+
# See: #start for other options
|
59
|
+
#
|
60
|
+
def self.start_daemon(opt = {})
|
61
|
+
opt = DEFAULTS.merge(opt)
|
62
|
+
filename = opt[:filename] || "/tmp/memory_profiler-#{Process.pid}.log"
|
63
|
+
@@daemon_sync.synchronize(:EX) do
|
64
|
+
raise 'daemon process already running' if @@daemon_thread
|
65
|
+
@@daemon_thread = Thread.new do
|
66
|
+
prev = Hash.new(0)
|
67
|
+
file = File.open(filename, 'w')
|
68
|
+
loop do
|
69
|
+
begin
|
70
|
+
GC.start if opt[:force_gc]
|
71
|
+
curr = ObjectSpaceAnalyser.analyse(opt)
|
72
|
+
data = self._delta(curr, prev, opt)
|
73
|
+
|
74
|
+
file.puts '-'*80
|
75
|
+
file.puts Time.now.to_s
|
76
|
+
data.each {|k,c,d| file.printf( "%5d %+5d %s\n", c, d, k.name ) }
|
77
|
+
|
78
|
+
prev = curr
|
79
|
+
GC.start if opt[:force_gc]
|
80
|
+
rescue ::Exception => err
|
81
|
+
$stderr.puts "** MemoryProfiler daemon error: #{err}", err.backtrace.map{|b| "\t#{b}" }
|
82
|
+
end
|
83
|
+
sleep opt[:delay]
|
84
|
+
end #loop
|
85
|
+
end #Thread.new
|
86
|
+
end
|
87
|
+
filename
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Terminates the analysis thread started by #start_daemon
|
92
|
+
#
|
93
|
+
def self.stop_daemon
|
94
|
+
@@daemon_sync.synchronize(:EX) do
|
95
|
+
raise 'no daemon process running' unless @@daemon_thread
|
96
|
+
@@daemon_thread.kill
|
97
|
+
@@daemon_thread.join
|
98
|
+
@@daemon_thread = nil
|
99
|
+
end
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Generates an instantaneous report on the current Ruby ObjectSpace, saved to
|
105
|
+
# a text file at: /tmp/memory_profiler-<pid>-<time>.log
|
106
|
+
#
|
107
|
+
# Returns the filename used.
|
108
|
+
#
|
109
|
+
# See: #start for valid/default options, except that :sort_by may
|
110
|
+
# only have the value :current or :none when using #report
|
111
|
+
#
|
112
|
+
def self.report(opt = {})
|
113
|
+
opt = DEFAULTS.merge(opt)
|
114
|
+
GC.start if opt[:force_gc]
|
115
|
+
|
116
|
+
data = ObjectSpaceAnalyser.analyse(opt)
|
117
|
+
|
118
|
+
if opt[:sort_by] == :current
|
119
|
+
data = data.to_a.sort_by{|k,v| -v }
|
120
|
+
data = data[0,opt[:limit]] if opt[:limit] > 0 and opt[:limit] < data.length
|
121
|
+
elsif opt[:sort_by] != :none
|
122
|
+
warn "MemoryProfiler: invalid option :sort_by => #{opt[:sort_by].inspect}; using :none"
|
123
|
+
end
|
124
|
+
|
125
|
+
filename = opt[:filename] || "/tmp/memory_profiler-#{Process.pid}-#{Time.now.to_i}.log"
|
126
|
+
File.open(filename, 'w') do |f|
|
127
|
+
data.each {|k,c| f.printf( "%5d %s\n", c, k.name ) }
|
128
|
+
end
|
129
|
+
|
130
|
+
GC.start if opt[:force_gc]
|
131
|
+
filename
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# If a block is given, executes it and returns a summary. Otherwise,
|
136
|
+
# starts the analyser, and waits for a call to #restart or #stop.
|
137
|
+
#
|
138
|
+
# Returned data is an array of:
|
139
|
+
# [ [Class, current_usage, usage_delta], ... ]
|
140
|
+
#
|
141
|
+
# Options:
|
142
|
+
# :sort_by => :current # how to order classes; :current | :delta | :absdelta | :none
|
143
|
+
#
|
144
|
+
# :only => [] # list of only classes to scan; if empty, scans all classes
|
145
|
+
# :ignore => [] # list of classes to exclude from reports (including sub-classes and modules, but not namespaces)
|
146
|
+
# :limit => 20 # how many of the top classes to report (less than 1 means 'all'); only matters if :sort_by is not :none
|
147
|
+
#
|
148
|
+
# :force_gc => true # if true, forces a garbage collection before and after generating report
|
149
|
+
#
|
150
|
+
# :string_debug => false # see ObjectSpaceAnalyser#analyse
|
151
|
+
# :marshal_size => false # see ObjectSpaceAnalyser#analyse
|
152
|
+
#
|
153
|
+
def self.start(opt = {}, &block)
|
154
|
+
opt = DEFAULTS.merge(opt)
|
155
|
+
if block_given?
|
156
|
+
# get pre-block analysis of ObjectSpace
|
157
|
+
GC.start if opt[:force_gc]
|
158
|
+
prev = ObjectSpaceAnalyser.analyse(opt)
|
159
|
+
GC.start if opt[:force_gc]
|
160
|
+
|
161
|
+
yield
|
162
|
+
|
163
|
+
# get post-block analysis of ObjectSpace
|
164
|
+
GC.start if opt[:force_gc]
|
165
|
+
curr = ObjectSpaceAnalyser.analyse(opt)
|
166
|
+
|
167
|
+
# calculate the differences before and after execution
|
168
|
+
data = self._delta(curr, prev, opt)
|
169
|
+
|
170
|
+
# return it
|
171
|
+
GC.start if opt[:force_gc]
|
172
|
+
data
|
173
|
+
else
|
174
|
+
@@start_sync.synchronize(:EX) do
|
175
|
+
raise 'already started' if @@start_data
|
176
|
+
|
177
|
+
GC.start if opt[:force_gc]
|
178
|
+
@@start_data = [ObjectSpaceAnalyser.analyse(opt), opt]
|
179
|
+
GC.start if opt[:force_gc]
|
180
|
+
end
|
181
|
+
self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Stops the current analysis and emits the results.
|
187
|
+
#
|
188
|
+
# See: #start
|
189
|
+
#
|
190
|
+
def self.stop
|
191
|
+
prev = nil
|
192
|
+
opt = nil
|
193
|
+
@@start_sync.synchronize(:EX) do
|
194
|
+
raise 'not started' unless @@start_data
|
195
|
+
prev, opt = @@start_data
|
196
|
+
@@start_data = nil
|
197
|
+
end
|
198
|
+
|
199
|
+
# get the current state of affairs
|
200
|
+
GC.start if opt[:force_gc]
|
201
|
+
curr = ObjectSpaceAnalyser.analyse(opt)
|
202
|
+
|
203
|
+
# calculate the differences before and after execution
|
204
|
+
data = self._delta(curr, prev, opt)
|
205
|
+
|
206
|
+
# return it
|
207
|
+
GC.start if opt[:force_gc]
|
208
|
+
data
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Stops the current analysis, emits the results, and immediately starts
|
213
|
+
# a new analysis.
|
214
|
+
#
|
215
|
+
# See: #stop, #start
|
216
|
+
#
|
217
|
+
def self.restart(opt = {})
|
218
|
+
res = self.stop
|
219
|
+
self.start(opt)
|
220
|
+
res
|
221
|
+
end
|
222
|
+
|
223
|
+
# @private
|
224
|
+
# => [ [Class, current, delta], ... ]
|
225
|
+
def self._delta(curr, prev, opt={}) #:nodoc:
|
226
|
+
opt = DEFAULTS.merge(opt)
|
227
|
+
|
228
|
+
# determine the difference between current and previous
|
229
|
+
delta = Hash.new(0)
|
230
|
+
(curr.keys + prev.keys).each do |k|
|
231
|
+
delta[k] = curr[k] - prev[k]
|
232
|
+
end
|
233
|
+
data = delta.map{|k,d| [k, curr[k].to_i, d]}
|
234
|
+
|
235
|
+
# organise data according to given options
|
236
|
+
case opt[:sort_by]
|
237
|
+
when :none
|
238
|
+
opt[:limit] = -1
|
239
|
+
when :current
|
240
|
+
data = data.sort_by{|k,c,d| -( c ) }
|
241
|
+
when :delta
|
242
|
+
data = data.sort_by{|k,c,d| -( d ) }
|
243
|
+
when :absdelta
|
244
|
+
data = data.sort_by{|k,c,d| -( d.abs ) }
|
245
|
+
else
|
246
|
+
warn "MemoryProfiler: invalid option :sort_by => #{opt[:sort_by].inspect}; using :none"
|
247
|
+
opt[:limit] = -1
|
248
|
+
end
|
249
|
+
data = data[0,opt[:limit]] if opt[:limit] > 0 and opt[:limit] < data.length
|
250
|
+
|
251
|
+
# return it
|
252
|
+
data
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# Formats data, such as that returned by #start , into a printable,
|
257
|
+
# readable string.
|
258
|
+
#
|
259
|
+
def self.format(data)
|
260
|
+
" Curr. Delta Class\n" +
|
261
|
+
" ----- ----- -----\n" +
|
262
|
+
data.map{|k,c,d| sprintf(" %5d %+5d %s\n", c, d, k.name) }.join
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
module ObjectSpaceAnalyser
|
267
|
+
#
|
268
|
+
# Returns a hash mapping each Class to its usage.
|
269
|
+
#
|
270
|
+
# If opt[:marshal_size] is true, the usage is estimated using Marshal.dump() for each instance;
|
271
|
+
# otherwise it is a simple instance count.
|
272
|
+
#
|
273
|
+
# If opt[:string_debug] is true, the analyser writes a text file containing every string
|
274
|
+
# in the Ruby ObjectSpace, at: /tmp/memory_profiler-<pid>-strings-<time>.log
|
275
|
+
#
|
276
|
+
# Uses opt[:only] and opt[:ignore] , as per MemoryProfiler#start
|
277
|
+
#
|
278
|
+
def self.analyse(opt = {})
|
279
|
+
opt = MemoryProfiler::DEFAULTS.merge(opt)
|
280
|
+
marshal_size = !!(opt[:marshal_size] || opt[:marshall_size])
|
281
|
+
string_debug = !!opt[:string_debug]
|
282
|
+
ign = opt[:ignore]
|
283
|
+
only = opt[:only]
|
284
|
+
|
285
|
+
res = Hash.new(0)
|
286
|
+
str = [] if string_debug
|
287
|
+
ObjectSpace.each_object do |o|
|
288
|
+
if res[o.class] or ((only.empty? or only.any?{|y| o.is_a? y }) and ign.none?{|x| o.is_a? x })
|
289
|
+
res[o.class] += (marshal_size ? self.__sizeof(o) : 1)
|
290
|
+
end
|
291
|
+
str.push o.inspect if string_debug and o.class == String
|
292
|
+
end
|
293
|
+
if string_debug
|
294
|
+
self.__save str
|
295
|
+
str = nil
|
296
|
+
end
|
297
|
+
res
|
298
|
+
end
|
299
|
+
|
300
|
+
# Estimates the size of an object using Marshal.dump()
|
301
|
+
# Defaults to 1 if anything goes wrong.
|
302
|
+
def self.__sizeof(o) #:nodoc:
|
303
|
+
if o.respond_to? :dump
|
304
|
+
Marshal.dump(o).size
|
305
|
+
else
|
306
|
+
1
|
307
|
+
end
|
308
|
+
rescue ::Exception
|
309
|
+
1
|
310
|
+
end
|
311
|
+
|
312
|
+
# a single place where the magic filename is defined
|
313
|
+
def self.__save(str) #:nodoc:
|
314
|
+
File.open("/tmp/memory_profiler-#{Process.pid}-strings-#{Time.now.to_i}.log", 'w') do |f|
|
315
|
+
str.sort.each{|s| f.puts s }
|
316
|
+
end
|
317
|
+
str = nil
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
if $0 == __FILE__
|
324
|
+
puts MemoryProfiler.start_daemon( :limit=>5, :delay=>10, :marshal_size=>true, :sort_by=>:absdelta )
|
325
|
+
|
326
|
+
5.times do
|
327
|
+
blah = Hash.new([])
|
328
|
+
rpt = MemoryProfiler.start( :limit=>10 ) do
|
329
|
+
100.times{ blah[1] << 'aaaaa' }
|
330
|
+
1000.times{ blah[2] << 'bbbbb' }
|
331
|
+
end
|
332
|
+
puts MemoryProfiler.format(rpt)
|
333
|
+
sleep 7
|
334
|
+
end
|
335
|
+
|
336
|
+
MemoryProfiler.stop_daemon
|
337
|
+
end
|
338
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/user_interaction' if RUBY_VERSION >= '1.9.1' and Gem::VERSION >= '1.4'
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'memory-profiler'
|
7
|
+
s.version = '1.0.3'
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Matthew Kerwin']
|
10
|
+
s.email = ['matthew@kerwin.net.au']
|
11
|
+
s.homepage = 'https://github.com/phluid61/memory-profiler-gem'
|
12
|
+
s.summary = 'A Ruby Memory Profiler'
|
13
|
+
s.description = 'A rudimentary memory profiler that uses pure in-VM techniques to analyse the object space and attempt to determine memory usage trends.'
|
14
|
+
s.license = 'Apache License 2.0'
|
15
|
+
s.rubyforge_project = 'mem-prof-ruby'
|
16
|
+
|
17
|
+
s.required_rubygems_version = '>= 1.3.6'
|
18
|
+
|
19
|
+
s.files = Rake::FileList['lib/**/*.rb', '[A-Z]*'].to_a
|
20
|
+
s.require_path = 'lib'
|
21
|
+
|
22
|
+
s.has_rdoc = true
|
23
|
+
s.rdoc_options << '--title' << 'Memory Profiler' <<
|
24
|
+
'--main' << 'MemoryProfiler' <<
|
25
|
+
'--line-numbers' <<
|
26
|
+
'--tab-width' << '2'
|
27
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memory-profiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Matthew Kerwin
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-04-18 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: A rudimentary memory profiler that uses pure in-VM techniques to analyse
|
15
14
|
the object space and attempt to determine memory usage trends.
|
@@ -20,12 +19,15 @@ extensions: []
|
|
20
19
|
extra_rdoc_files: []
|
21
20
|
files:
|
22
21
|
- lib/memory-profiler.rb
|
22
|
+
- Gemfile
|
23
23
|
- LICENSE
|
24
24
|
- README
|
25
|
-
-
|
25
|
+
- Rakefile
|
26
|
+
- memory-profiler.gemspec
|
26
27
|
homepage: https://github.com/phluid61/memory-profiler-gem
|
27
28
|
licenses:
|
28
29
|
- Apache License 2.0
|
30
|
+
metadata: {}
|
29
31
|
post_install_message:
|
30
32
|
rdoc_options:
|
31
33
|
- --title
|
@@ -38,21 +40,19 @@ rdoc_options:
|
|
38
40
|
require_paths:
|
39
41
|
- lib
|
40
42
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
43
|
requirements:
|
43
|
-
- -
|
44
|
+
- - '>='
|
44
45
|
- !ruby/object:Gem::Version
|
45
46
|
version: '0'
|
46
47
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
48
|
requirements:
|
49
|
-
- -
|
49
|
+
- - '>='
|
50
50
|
- !ruby/object:Gem::Version
|
51
51
|
version: 1.3.6
|
52
52
|
requirements: []
|
53
53
|
rubyforge_project: mem-prof-ruby
|
54
|
-
rubygems_version:
|
54
|
+
rubygems_version: 2.0.3
|
55
55
|
signing_key:
|
56
|
-
specification_version:
|
56
|
+
specification_version: 4
|
57
57
|
summary: A Ruby Memory Profiler
|
58
58
|
test_files: []
|