objspace_helpers 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/objspace_helpers/id2ref.c +46 -0
- data/ext/objspace_helpers/id2ref.h +9 -0
- data/lib/objspace_helpers/leaks.rb +55 -0
- data/lib/objspace_helpers/tracked_object.rb +31 -0
- data/lib/objspace_helpers/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbb176043e7fcf26f81faec74c26725353c914aa
|
4
|
+
data.tar.gz: 3dd9ba529a9b4fbc949cecf0f085c542858d46c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14b5bb3c4aed3a80f68c7873cf681cb6e0df30452ccdc2bf4908649aed509338ef06b7be47aef212798e4e4874e478114ea2b7af01f461d601409313c5f38312
|
7
|
+
data.tar.gz: 60219c051f2596767b4aa28b9a5178a98255a6ec91e9e7862e87014926658b724e07332853a0ab0c3038e5a78c29c7237ffdfa86000501b0a2248607e44dc15e
|
@@ -0,0 +1,46 @@
|
|
1
|
+
/* Note (LukasFittl): Copy of ObjectSpace._id2ref from Ruby 2.1 source,
|
2
|
+
* directly callable without the VM overhead, and more foregiving for internal objects.
|
3
|
+
*/
|
4
|
+
|
5
|
+
#include <id2ref.h>
|
6
|
+
|
7
|
+
#if SIZEOF_LONG == SIZEOF_VOIDP
|
8
|
+
# define nonspecial_obj_id(obj) (VALUE)((SIGNED_VALUE)(obj)|FIXNUM_FLAG)
|
9
|
+
# define obj_id_to_ref(objid) ((objid) ^ FIXNUM_FLAG) /* unset FIXNUM_FLAG */
|
10
|
+
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
|
11
|
+
# define nonspecial_obj_id(obj) LL2NUM((SIGNED_VALUE)(obj) / 2)
|
12
|
+
# define obj_id_to_ref(objid) (FIXNUM_P(objid) ? \
|
13
|
+
((objid) ^ FIXNUM_FLAG) : (NUM2PTR(objid) << 1))
|
14
|
+
#else
|
15
|
+
# error not supported
|
16
|
+
#endif
|
17
|
+
|
18
|
+
VALUE
|
19
|
+
oh_id2ref(VALUE self, VALUE objid)
|
20
|
+
{
|
21
|
+
#if SIZEOF_LONG == SIZEOF_VOIDP
|
22
|
+
#define NUM2PTR(x) NUM2ULONG(x)
|
23
|
+
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
|
24
|
+
#define NUM2PTR(x) NUM2ULL(x)
|
25
|
+
#endif
|
26
|
+
VALUE ptr;
|
27
|
+
void *p0;
|
28
|
+
|
29
|
+
ptr = NUM2PTR(objid);
|
30
|
+
p0 = (void *)ptr;
|
31
|
+
|
32
|
+
if (ptr == Qtrue) return Qtrue;
|
33
|
+
if (ptr == Qfalse) return Qfalse;
|
34
|
+
if (ptr == Qnil) return Qnil;
|
35
|
+
if (FIXNUM_P(ptr)) return (VALUE)ptr;
|
36
|
+
if (FLONUM_P(ptr)) return (VALUE)ptr;
|
37
|
+
ptr = obj_id_to_ref(objid);
|
38
|
+
|
39
|
+
if ((ptr % rb_intern("RVALUE_SIZE")) == (4 << 2)) {
|
40
|
+
ID symid = ptr / rb_intern("RVALUE_SIZE");
|
41
|
+
if (rb_id2name(symid) == 0) return Qundef;
|
42
|
+
return ID2SYM(symid);
|
43
|
+
}
|
44
|
+
|
45
|
+
return (VALUE)ptr;
|
46
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class ObjspaceHelpers
|
2
|
+
class << self
|
3
|
+
def find_leaks(trace: false, &block)
|
4
|
+
ObjectSpace::trace_object_allocations_start if trace
|
5
|
+
|
6
|
+
objs_before = _dump_addresses
|
7
|
+
|
8
|
+
block.call
|
9
|
+
|
10
|
+
# Run GC twice to try to get rid of as much false positives as possible
|
11
|
+
ObjectSpace.garbage_collect
|
12
|
+
ObjectSpace.garbage_collect
|
13
|
+
|
14
|
+
objs_after = _dump_addresses
|
15
|
+
|
16
|
+
ObjectSpace::trace_object_allocations_stop if trace
|
17
|
+
|
18
|
+
leaked_addresses = objs_after - objs_before - [objs_after.object_id]
|
19
|
+
|
20
|
+
GC.disable
|
21
|
+
|
22
|
+
# Note (LukasFittl): 2x speedup if we move this to native code
|
23
|
+
referenced_by = {}
|
24
|
+
_addresses_to_references(objs_after).each do |addr, references|
|
25
|
+
(references & leaked_addresses).each do |ref|
|
26
|
+
referenced_by[ref] ||= []
|
27
|
+
referenced_by[ref] << addr
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
GC.enable
|
32
|
+
|
33
|
+
TrackedObject.wrap(leaked_addresses, referenced_by)
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_leak_sources(trace: false, &block)
|
37
|
+
leaks = find_leaks(trace: trace, &block)
|
38
|
+
leaks_by_source = {}
|
39
|
+
top_level_leaks = []
|
40
|
+
|
41
|
+
leaks.each do |leak|
|
42
|
+
if leak.referenced_by.empty?
|
43
|
+
top_level_leaks << leak
|
44
|
+
else
|
45
|
+
leak.referenced_by.each do |source|
|
46
|
+
leaks_by_source[source] ||= []
|
47
|
+
leaks_by_source[source] << leak
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
[top_level_leaks, leaks_by_source]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ObjspaceHelpers::TrackedObject
|
2
|
+
def self.wrap(addresses, referenced_by = [])
|
3
|
+
Array.new(addresses || []).map do |address|
|
4
|
+
new(address, wrap(referenced_by[address]))
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.for_object(obj)
|
9
|
+
new ObjspaceHelpers._address_of_obj(obj)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :address
|
13
|
+
attr_reader :referenced_by
|
14
|
+
|
15
|
+
def initialize(address, referenced_by = [])
|
16
|
+
@address = address
|
17
|
+
@referenced_by = referenced_by
|
18
|
+
end
|
19
|
+
|
20
|
+
def info
|
21
|
+
ObjspaceHelpers._addresses_to_info([@address]).values.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def klass
|
25
|
+
self.class.new(info['class'])
|
26
|
+
end
|
27
|
+
|
28
|
+
def dereference
|
29
|
+
ObjspaceHelpers._id2ref(@address)
|
30
|
+
end
|
31
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: objspace_helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukas Fittl
|
@@ -62,6 +62,8 @@ files:
|
|
62
62
|
- LICENSE
|
63
63
|
- Rakefile
|
64
64
|
- ext/objspace_helpers/extconf.rb
|
65
|
+
- ext/objspace_helpers/id2ref.c
|
66
|
+
- ext/objspace_helpers/id2ref.h
|
65
67
|
- ext/objspace_helpers/objspace_helpers.c
|
66
68
|
- ext/objspace_helpers/objspace_helpers.h
|
67
69
|
- ext/objspace_helpers/objspace_info.c
|
@@ -71,6 +73,8 @@ files:
|
|
71
73
|
- ext/objspace_helpers/ruby_private/objspace.h
|
72
74
|
- lib/objspace_helpers.rb
|
73
75
|
- lib/objspace_helpers/helpers.rb
|
76
|
+
- lib/objspace_helpers/leaks.rb
|
77
|
+
- lib/objspace_helpers/tracked_object.rb
|
74
78
|
- lib/objspace_helpers/version.rb
|
75
79
|
homepage: http://github.com/lfittl/objspace_helpers
|
76
80
|
licenses:
|