Class: ObjspaceHelpers

Inherits:
Object
  • Object
show all
Defined in:
lib/objspace_helpers/leaks.rb,
lib/objspace_helpers/helpers.rb,
lib/objspace_helpers/version.rb

Defined Under Namespace

Classes: TrackedObject

Constant Summary collapse

VERSION =
'0.0.3'

Class Method Summary collapse

Class Method Details

._addresses_to_info(ary) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'ext/objspace_helpers/objspace_helpers.c', line 24

static VALUE oh_addresses_to_info(VALUE self, VALUE ary)
{
  VALUE hash, obj_addr, info_hash;
  long i;

  hash = rb_hash_new();

  for (i = 0; i < RARRAY_LEN(ary); i += 1) {
    obj_addr = rb_ary_entry(ary, i);
    info_hash = rb_hash_new();
    objspace_info(oh_id2ref(self, obj_addr), info_hash);
    rb_hash_aset(hash, obj_addr, info_hash);
  }

  return hash;
}

._addresses_to_references(addresses) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'ext/objspace_helpers/objspace_helpers.c', line 47

static VALUE oh_addresses_to_references(VALUE self, VALUE addresses)
{
  VALUE hash, obj_addr, references;
  long i;

  hash = rb_hash_new();

  for (i = 0; i < RARRAY_LEN(addresses); i += 1) {
    obj_addr = rb_ary_entry(addresses, i);

    references = rb_ary_new();
    rb_objspace_reachable_objects_from(oh_id2ref(self, obj_addr), reachable_object_i, (void*)references);

    rb_hash_aset(hash, obj_addr, references);
  }

  return hash;
}

._dump_addressesObject



17
18
19
20
21
22
# File 'ext/objspace_helpers/objspace_helpers.c', line 17

static VALUE oh_dump_addresses(VALUE self)
{
  VALUE ary = rb_ary_new();
  rb_objspace_each_objects(collect_addresses, (void *)ary);
  return ary;
}

._id2ref(objid) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'ext/objspace_helpers/id2ref.c', line 18

VALUE
oh_id2ref(VALUE self, VALUE objid)
{
#if SIZEOF_LONG == SIZEOF_VOIDP
#define NUM2PTR(x) NUM2ULONG(x)
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
#define NUM2PTR(x) NUM2ULL(x)
#endif
  VALUE ptr;
  void *p0;

  ptr = NUM2PTR(objid);
  p0 = (void *)ptr;

  if (ptr == Qtrue) return Qtrue;
  if (ptr == Qfalse) return Qfalse;
  if (ptr == Qnil) return Qnil;
  if (FIXNUM_P(ptr)) return (VALUE)ptr;
  if (FLONUM_P(ptr)) return (VALUE)ptr;
  ptr = obj_id_to_ref(objid);

  if ((ptr % rb_intern("RVALUE_SIZE")) == (4 << 2)) {
    ID symid = ptr / rb_intern("RVALUE_SIZE");
    if (rb_id2name(symid) == 0) return Qundef;
    return ID2SYM(symid);
  }

  return (VALUE)ptr;
}

.diff(&block) ⇒ Object



14
15
16
17
18
19
# File 'lib/objspace_helpers/helpers.rb', line 14

def self.diff(&block)
  objs_before = dump_all_addresses
  block.call
  objs_after = dump_all_addresses
  objs_after - objs_before - [objs_after.object_id]
end

.dump_all_addressesObject



2
3
4
# File 'lib/objspace_helpers/helpers.rb', line 2

def self.dump_all_addresses
  _dump_addresses
end

.find_leak_sources(trace: false, &block) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/objspace_helpers/leaks.rb', line 36

def find_leak_sources(trace: false, &block)
  leaks = find_leaks(trace: trace, &block)
  leaks_by_source = {}
  top_level_leaks = []

  leaks.each do |leak|
    if leak.referenced_by.empty?
      top_level_leaks << leak
    else
      leak.referenced_by.each do |source|
        leaks_by_source[source] ||= []
        leaks_by_source[source] << leak
      end
    end
  end

  [top_level_leaks, leaks_by_source]
end

.find_leaks(trace: false, &block) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/objspace_helpers/leaks.rb', line 3

def find_leaks(trace: false, &block)
  ObjectSpace::trace_object_allocations_start if trace

  objs_before = _dump_addresses

  block.call

  # Run GC twice to try to get rid of as much false positives as possible
  ObjectSpace.garbage_collect
  ObjectSpace.garbage_collect

  objs_after = _dump_addresses

  ObjectSpace::trace_object_allocations_stop if trace

  leaked_addresses = objs_after - objs_before - [objs_after.object_id]

  GC.disable

  # Note (LukasFittl): 2x speedup if we move this to native code
  referenced_by = {}
  _addresses_to_references(objs_after).each do |addr, references|
    (references & leaked_addresses).each do |ref|
      referenced_by[ref] ||= []
      referenced_by[ref] << addr
    end
  end

  GC.enable

  TrackedObject.wrap(leaked_addresses, referenced_by)
end

.info_for_address(addresses) ⇒ Object



6
7
8
# File 'lib/objspace_helpers/helpers.rb', line 6

def self.info_for_address(addresses)
  _addresses_to_info(addresses)
end

.info_for_obj(obj) ⇒ Object



10
11
12
# File 'lib/objspace_helpers/helpers.rb', line 10

def self.info_for_obj(obj)
  _addresses_to_info([obj.object_id]).values.first
end