alloc_track 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +18 -0
- data/LICENSE.md +21 -0
- data/README.md +52 -0
- data/Rakefile +36 -0
- data/alloc_track.gemspec +15 -0
- data/ext/alloc_track/alloc_track.c +270 -0
- data/ext/alloc_track/extconf.rb +15 -0
- data/test/benchmark_alloc_track.rb +28 -0
- data/test/test_alloc_track.rb +76 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2bcef216fe18bd1578ecc1b47f26e10cf6669ad
|
4
|
+
data.tar.gz: 2f003bcc8dc4f0f30a7123518192fb7c9a3c22da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50d0ecf47e7a19ac2f9b59e484ab84e8379243d25c7a31487be87924d6b4f486bdecb50613874fcdc07b98a90b1c60946d264257df4597cab590233ebbd6b142
|
7
|
+
data.tar.gz: 86ee7fc454d489abb83df54d01d6fb226da1f0d66f56e5dacc764bf02fbc173494b3ef0bfe1abf2af7f159a978899ca05c4e3c8030c15d62d3bbb743c009a7e7
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Scott Francis <scott.francis@shopify.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
## alloc_track
|
2
|
+
|
3
|
+
Tracks the number of outstanding allocations on a Ruby thread using the internal tracepoint APIs.
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
- C extension for webscale
|
8
|
+
- Allocations are counted per thread
|
9
|
+
- Can raise an exception when allocations exceed a certain threshold
|
10
|
+
|
11
|
+
### Usage
|
12
|
+
|
13
|
+
It can be used to track the number of allocated objects over a period of time:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'alloc_track/alloc_track'
|
17
|
+
|
18
|
+
AllocTrack.start
|
19
|
+
100.times { Object.new }
|
20
|
+
puts AllocTrack.delta # >= 100
|
21
|
+
GC.start
|
22
|
+
puts AllocTrack.alloc # >= 100
|
23
|
+
puts AllocTrack.delta # <= 100
|
24
|
+
puts AllocTrack.free # >= 100
|
25
|
+
AllocTrack.stop
|
26
|
+
```
|
27
|
+
|
28
|
+
Perhaps more useful is the ability to raise when the number of allocations crosses a certain threshold:
|
29
|
+
```ruby
|
30
|
+
require 'alloc_track/alloc_track'
|
31
|
+
|
32
|
+
AllocTrack.limit 100 do
|
33
|
+
200.times { Object.new } # raises AllocTrack::LimitExceeded
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
### Performance
|
38
|
+
|
39
|
+
In a contrived benchmark that simply allocates 10,000,000 new objects, alloc_track adds ~30% overhead:
|
40
|
+
|
41
|
+
```
|
42
|
+
[vagrant] ~/src/alloc_track (master *%) $ ruby -Ilib ./test/benchmark_alloc_track.rb
|
43
|
+
user system total real
|
44
|
+
none: 1.540000 0.000000 1.540000 ( 1.545192)
|
45
|
+
tracking: 2.020000 0.000000 2.020000 ( 2.034162)
|
46
|
+
```
|
47
|
+
|
48
|
+
Expect real-world (operations other than just memory allocation) performance overhead to be much less severe.
|
49
|
+
|
50
|
+
### Limitations
|
51
|
+
|
52
|
+
- Allocation tracker can only be run on one thread per process
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
# ==========================================================
|
4
|
+
# Packaging
|
5
|
+
# ==========================================================
|
6
|
+
|
7
|
+
GEMSPEC = eval(File.read('alloc_track.gemspec'))
|
8
|
+
|
9
|
+
require 'rubygems/package_task'
|
10
|
+
Gem::PackageTask.new(GEMSPEC) do |pkg|
|
11
|
+
end
|
12
|
+
|
13
|
+
# ==========================================================
|
14
|
+
# Ruby Extension
|
15
|
+
# ==========================================================
|
16
|
+
|
17
|
+
require 'rake/extensiontask'
|
18
|
+
Rake::ExtensionTask.new('alloc_track', GEMSPEC) do |ext|
|
19
|
+
ext.ext_dir = 'ext/alloc_track'
|
20
|
+
ext.lib_dir = 'lib/alloc_track'
|
21
|
+
end
|
22
|
+
task :build => :compile
|
23
|
+
|
24
|
+
# ==========================================================
|
25
|
+
# Testing
|
26
|
+
# ==========================================================
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new 'test' do |t|
|
30
|
+
t.test_files = FileList['test/test_*.rb']
|
31
|
+
end
|
32
|
+
task :test => :build
|
33
|
+
|
34
|
+
task :benchmark => :build do
|
35
|
+
ruby "-Ilib ./test/benchmark_alloc_track.rb"
|
36
|
+
end
|
data/alloc_track.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'alloc_track'
|
3
|
+
s.version = '0.0.2'
|
4
|
+
s.summary = 'allocation tracker for ruby 2.1+'
|
5
|
+
s.description = 'tracks memory allocations with rgengc in ruby 2.1'
|
6
|
+
|
7
|
+
s.homepage = 'https://github.com/csfrancis/alloc_track'
|
8
|
+
s.authors = 'Scott Francis'
|
9
|
+
s.email = 'scott.francis@shopify.com'
|
10
|
+
s.license = 'MIT'
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.extensions = ['ext/alloc_track/extconf.rb']
|
14
|
+
s.add_development_dependency 'rake-compiler', '~> 0.9'
|
15
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
#include "ruby/ruby.h"
|
2
|
+
#include "ruby/intern.h"
|
3
|
+
#include "ruby/debug.h"
|
4
|
+
|
5
|
+
#define ALLOCTRACK_OBJ_BIT FL_USER18
|
6
|
+
|
7
|
+
typedef struct stat_collector {
|
8
|
+
struct stat_collector *next; /* not currently used */
|
9
|
+
VALUE thread;
|
10
|
+
int current_alloc;
|
11
|
+
int current_free;
|
12
|
+
int current_limit;
|
13
|
+
int limit_signal;
|
14
|
+
} stat_collector_t;
|
15
|
+
|
16
|
+
static VALUE mAllocTrack;
|
17
|
+
static VALUE tpval, tpval_exception;
|
18
|
+
static VALUE eAllocTrackError, eAllocTrackLimitExceeded;
|
19
|
+
static stat_collector_t *root_collector, *current_collector;
|
20
|
+
|
21
|
+
#define LOG(s) fprintf(stderr, s); fflush(stderr);
|
22
|
+
|
23
|
+
static stat_collector_t *
|
24
|
+
add_collector(VALUE thread)
|
25
|
+
{
|
26
|
+
stat_collector_t *c = (stat_collector_t *) calloc(1, sizeof(*c));
|
27
|
+
c->thread = thread;
|
28
|
+
if (root_collector) {
|
29
|
+
c->next = root_collector;
|
30
|
+
root_collector = c;
|
31
|
+
} else {
|
32
|
+
root_collector = c;
|
33
|
+
rb_tracepoint_enable(tpval);
|
34
|
+
}
|
35
|
+
return c;
|
36
|
+
}
|
37
|
+
|
38
|
+
static void
|
39
|
+
remove_collector(VALUE thread)
|
40
|
+
{
|
41
|
+
stat_collector_t *c, *prev = NULL;
|
42
|
+
|
43
|
+
for (c = root_collector; c != NULL; prev = c, c = c->next) {
|
44
|
+
if (c->thread == thread) {
|
45
|
+
if (!prev) {
|
46
|
+
root_collector = c->next;
|
47
|
+
} else {
|
48
|
+
prev->next = c->next;
|
49
|
+
}
|
50
|
+
current_collector = NULL;
|
51
|
+
free(c);
|
52
|
+
break;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
if (!root_collector) {
|
57
|
+
rb_tracepoint_disable(tpval);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
static stat_collector_t *
|
62
|
+
get_collector(VALUE thread)
|
63
|
+
{
|
64
|
+
stat_collector_t *c;
|
65
|
+
|
66
|
+
if (current_collector && current_collector->thread == thread) {
|
67
|
+
return current_collector;
|
68
|
+
}
|
69
|
+
|
70
|
+
for (c = root_collector; c != NULL; c = c->next) {
|
71
|
+
if (c->thread == thread) {
|
72
|
+
/* cache the collector so we don't have to scan the list every time */
|
73
|
+
current_collector = c;
|
74
|
+
return c;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return NULL;
|
79
|
+
}
|
80
|
+
|
81
|
+
static VALUE
|
82
|
+
started()
|
83
|
+
{
|
84
|
+
return get_collector(rb_thread_current()) ? Qtrue : Qfalse;
|
85
|
+
}
|
86
|
+
|
87
|
+
static void
|
88
|
+
validate_started()
|
89
|
+
{
|
90
|
+
if (!started()) {
|
91
|
+
rb_raise(eAllocTrackError, "allocation tracker has not been started");
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
static void
|
96
|
+
validate_stopped()
|
97
|
+
{
|
98
|
+
if (started()) {
|
99
|
+
rb_raise(eAllocTrackError, "allocation tracker already started");
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
static VALUE
|
104
|
+
start()
|
105
|
+
{
|
106
|
+
validate_stopped();
|
107
|
+
/* TODO: support multiple running trackers */
|
108
|
+
if (root_collector) {
|
109
|
+
rb_raise(eAllocTrackError, "allocation tracker already running on another thread");
|
110
|
+
}
|
111
|
+
add_collector(rb_thread_current());
|
112
|
+
return Qnil;
|
113
|
+
}
|
114
|
+
|
115
|
+
static VALUE
|
116
|
+
stop()
|
117
|
+
{
|
118
|
+
validate_started();
|
119
|
+
remove_collector(rb_thread_current());
|
120
|
+
return Qnil;
|
121
|
+
}
|
122
|
+
|
123
|
+
static VALUE
|
124
|
+
alloc()
|
125
|
+
{
|
126
|
+
validate_started();
|
127
|
+
return INT2FIX(get_collector(rb_thread_current())->current_alloc);
|
128
|
+
}
|
129
|
+
|
130
|
+
static VALUE
|
131
|
+
_free()
|
132
|
+
{
|
133
|
+
validate_started();
|
134
|
+
return INT2FIX(get_collector(rb_thread_current())->current_free);
|
135
|
+
}
|
136
|
+
|
137
|
+
static VALUE
|
138
|
+
delta()
|
139
|
+
{
|
140
|
+
stat_collector_t *c;
|
141
|
+
validate_started();
|
142
|
+
c = get_collector(rb_thread_current());
|
143
|
+
return INT2FIX(c->current_alloc - c->current_free);
|
144
|
+
}
|
145
|
+
|
146
|
+
static VALUE
|
147
|
+
do_limit(VALUE arg)
|
148
|
+
{
|
149
|
+
start();
|
150
|
+
get_collector(rb_thread_current())->current_limit = FIX2INT(arg);
|
151
|
+
return rb_yield(Qnil);
|
152
|
+
}
|
153
|
+
|
154
|
+
static VALUE
|
155
|
+
ensure_stopped(VALUE arg)
|
156
|
+
{
|
157
|
+
if (started()) {
|
158
|
+
stop();
|
159
|
+
}
|
160
|
+
return Qnil;
|
161
|
+
}
|
162
|
+
|
163
|
+
static VALUE
|
164
|
+
limit(VALUE self, VALUE num_allocs)
|
165
|
+
{
|
166
|
+
if (!rb_block_given_p()) {
|
167
|
+
rb_raise(rb_eArgError, "block required");
|
168
|
+
}
|
169
|
+
if (!RB_TYPE_P(num_allocs, T_FIXNUM)) {
|
170
|
+
rb_raise(rb_eArgError, "limit() must be passed a number");
|
171
|
+
}
|
172
|
+
validate_stopped();
|
173
|
+
return rb_ensure(do_limit, num_allocs, ensure_stopped, Qnil);
|
174
|
+
}
|
175
|
+
|
176
|
+
static int
|
177
|
+
is_collector_enabled(stat_collector_t *c)
|
178
|
+
{
|
179
|
+
return c->limit_signal == 0 ? 1 : 0;
|
180
|
+
}
|
181
|
+
|
182
|
+
static int
|
183
|
+
is_collector_limit_exceeded(stat_collector_t *c)
|
184
|
+
{
|
185
|
+
return c->limit_signal;
|
186
|
+
}
|
187
|
+
|
188
|
+
static void
|
189
|
+
tracepoint_hook(VALUE tpval, void *data)
|
190
|
+
{
|
191
|
+
stat_collector_t *c;
|
192
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
193
|
+
rb_event_flag_t flag = rb_tracearg_event_flag(tparg);
|
194
|
+
VALUE obj = rb_tracearg_object(tparg);
|
195
|
+
switch(flag) {
|
196
|
+
case RUBY_INTERNAL_EVENT_NEWOBJ:
|
197
|
+
if ((c = get_collector(rb_thread_current())) != NULL && is_collector_enabled(c)) {
|
198
|
+
RBASIC(obj)->flags |= ALLOCTRACK_OBJ_BIT;
|
199
|
+
c->current_alloc++;
|
200
|
+
|
201
|
+
if (c->current_limit && (c->current_alloc - c->current_free) > c->current_limit) {
|
202
|
+
c->limit_signal = 1;
|
203
|
+
if (!rb_tracepoint_enabled_p(tpval_exception)) {
|
204
|
+
/*
|
205
|
+
it's not safe to raise an exception from an internal event handler.
|
206
|
+
in order to get around this, we enable a normal tracepoint on all
|
207
|
+
events and raise from there.
|
208
|
+
*/
|
209
|
+
rb_tracepoint_enable(tpval_exception);
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
break;
|
214
|
+
case RUBY_INTERNAL_EVENT_FREEOBJ:
|
215
|
+
if ((c = get_collector(rb_thread_current())) != NULL && is_collector_enabled(c) &&
|
216
|
+
(RBASIC(obj)->flags & ALLOCTRACK_OBJ_BIT)) {
|
217
|
+
c->current_free++;
|
218
|
+
}
|
219
|
+
break;
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
static int
|
224
|
+
any_collectors_with_exceeded_limits()
|
225
|
+
{
|
226
|
+
stat_collector_t *c;
|
227
|
+
for (c = root_collector; c != NULL; c = c->next) {
|
228
|
+
if (c->limit_signal) {
|
229
|
+
return 1;
|
230
|
+
}
|
231
|
+
}
|
232
|
+
return 0;
|
233
|
+
}
|
234
|
+
|
235
|
+
static void
|
236
|
+
exception_tracepoint_hook(VALUE tpval, void *data)
|
237
|
+
{
|
238
|
+
VALUE th = rb_thread_current();
|
239
|
+
stat_collector_t *c;
|
240
|
+
if ((c = get_collector(th)) != NULL && is_collector_limit_exceeded(c)) {
|
241
|
+
remove_collector(th);
|
242
|
+
if (!any_collectors_with_exceeded_limits()) {
|
243
|
+
rb_tracepoint_disable(tpval_exception);
|
244
|
+
}
|
245
|
+
rb_raise(eAllocTrackLimitExceeded, "allocation limit exceeded");
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
void
|
250
|
+
Init_alloc_track()
|
251
|
+
{
|
252
|
+
mAllocTrack = rb_define_module("AllocTrack");
|
253
|
+
|
254
|
+
rb_define_singleton_method(mAllocTrack, "start", start, 0);
|
255
|
+
rb_define_singleton_method(mAllocTrack, "started?", started, 0);
|
256
|
+
rb_define_singleton_method(mAllocTrack, "stop", stop, 0);
|
257
|
+
rb_define_singleton_method(mAllocTrack, "alloc", alloc, 0);
|
258
|
+
rb_define_singleton_method(mAllocTrack, "free", _free, 0);
|
259
|
+
rb_define_singleton_method(mAllocTrack, "delta", delta, 0);
|
260
|
+
rb_define_singleton_method(mAllocTrack, "limit", limit, 1);
|
261
|
+
|
262
|
+
eAllocTrackError = rb_define_class_under(mAllocTrack, "Error", rb_eStandardError);
|
263
|
+
eAllocTrackLimitExceeded = rb_define_class_under(mAllocTrack, "LimitExceeded", rb_eStandardError);
|
264
|
+
|
265
|
+
tpval = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ|RUBY_INTERNAL_EVENT_FREEOBJ, tracepoint_hook, NULL);
|
266
|
+
tpval_exception = rb_tracepoint_new(0, RUBY_EVENT_TRACEPOINT_ALL, exception_tracepoint_hook, NULL);
|
267
|
+
|
268
|
+
rb_gc_register_mark_object(tpval);
|
269
|
+
rb_gc_register_mark_object(tpval_exception);
|
270
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
$CFLAGS = "-O3"
|
4
|
+
|
5
|
+
have_func('rb_tracepoint_enable')
|
6
|
+
|
7
|
+
gc_event = have_const('RUBY_INTERNAL_EVENT_NEWOBJ')
|
8
|
+
|
9
|
+
if gc_event
|
10
|
+
create_makefile('alloc_track/alloc_track')
|
11
|
+
else
|
12
|
+
File.open('Makefile', 'w') do |f|
|
13
|
+
f.puts "install:\n\t\n"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'alloc_track/alloc_track'
|
3
|
+
|
4
|
+
def alloc_obj
|
5
|
+
Object.new
|
6
|
+
end
|
7
|
+
|
8
|
+
n = 100
|
9
|
+
i = 100000
|
10
|
+
|
11
|
+
Benchmark.bm(8) do |x|
|
12
|
+
x.report("none:") do
|
13
|
+
GC.start
|
14
|
+
n.times do
|
15
|
+
i.times { alloc_obj }
|
16
|
+
GC.start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
x.report("tracking:") do
|
20
|
+
GC.start
|
21
|
+
AllocTrack.start
|
22
|
+
n.times do
|
23
|
+
i.times { alloc_obj }
|
24
|
+
GC.start
|
25
|
+
end
|
26
|
+
AllocTrack.stop
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'alloc_track/alloc_track'
|
3
|
+
|
4
|
+
class TestAllocTrack < Test::Unit::TestCase
|
5
|
+
def test_allocate
|
6
|
+
AllocTrack.start
|
7
|
+
100.times { Object.new }
|
8
|
+
assert_operator AllocTrack.delta, :>=, 100
|
9
|
+
AllocTrack.stop
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_allocate_with_gc
|
13
|
+
AllocTrack.start
|
14
|
+
100.times { Object.new }
|
15
|
+
GC.start
|
16
|
+
assert_operator AllocTrack.alloc, :>=, 100
|
17
|
+
assert_operator AllocTrack.delta, :<, 100
|
18
|
+
assert_operator AllocTrack.free, :>=, 100
|
19
|
+
AllocTrack.stop
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_thread_not_included
|
23
|
+
AllocTrack.start
|
24
|
+
t = Thread.new do
|
25
|
+
100.times { Object.new }
|
26
|
+
end
|
27
|
+
t.join
|
28
|
+
assert_operator AllocTrack.delta, :<, 100
|
29
|
+
AllocTrack.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_delta_raises_when_not_started
|
33
|
+
assert_raise AllocTrack::Error do
|
34
|
+
AllocTrack.delta
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_limit_with_no_block
|
39
|
+
assert_raise ArgumentError do
|
40
|
+
AllocTrack.limit 100
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_limit_with_non_number
|
45
|
+
assert_raise ArgumentError do
|
46
|
+
AllocTrack.limit "foo" do
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_limit_raises
|
52
|
+
assert_raise AllocTrack::LimitExceeded do
|
53
|
+
AllocTrack.limit 10 do
|
54
|
+
200.times { Object.new }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_within_limit
|
60
|
+
assert_nothing_raised do
|
61
|
+
AllocTrack.limit 100 do
|
62
|
+
50.times { Object.new }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_limit_exception_stops
|
68
|
+
assert_raise RuntimeError do
|
69
|
+
AllocTrack.limit 100 do
|
70
|
+
raise RuntimeError
|
71
|
+
end
|
72
|
+
end
|
73
|
+
refute AllocTrack.started?
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: alloc_track
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Scott Francis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake-compiler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
description: tracks memory allocations with rgengc in ruby 2.1
|
28
|
+
email: scott.francis@shopify.com
|
29
|
+
executables: []
|
30
|
+
extensions:
|
31
|
+
- ext/alloc_track/extconf.rb
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- LICENSE.md
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- alloc_track.gemspec
|
41
|
+
- ext/alloc_track/alloc_track.c
|
42
|
+
- ext/alloc_track/extconf.rb
|
43
|
+
- test/benchmark_alloc_track.rb
|
44
|
+
- test/test_alloc_track.rb
|
45
|
+
homepage: https://github.com/csfrancis/alloc_track
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.2.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: allocation tracker for ruby 2.1+
|
69
|
+
test_files: []
|