0.01
Repository is archived
No commit activity in last 3 years
No release in over 3 years
LivingDead traces objects to see if they are retained or freed by MRI
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

 Project Readme

This never worked like it should. Archiving

LivingDead

Build Status

This module allows you to see if an object is retained "still alive" or if it is freed "dead".

Dancing Zombies

Problems

There be dragons, see LivingDead.gc_start to see some of the hacks we have to do, for who knows why, to get this to work.

Install

In your Gemfile add:

gem 'living_dead'

Then run

$ bundle install

How it works

Before you use this you should understand how it works. This gem is a c-extension. It hooks into the Ruby tracepoint API and registers a hook for the RUBY_INTERNAL_EVENT_FREEOBJ event. This event gets called when an object is freed (i.e. it is not retained and garbage collection has been called).

When you call LivingDead.trace(obj) we store the object_id of the thing you are "tracing" in a hash. Then inside of our c-extension hook we listen for when an object is freed. When this happens we check to see if that object's object_id matches one in our hash. If it does we mark it down in a separate "freed" hash.

We don't retain the objects you are tracing but we do keep a copy of the object_id, we can then use this same number to check the freed hash to see if it was recorded as being freed.

WARNING: Seriously, see LivingDead.gc_start. This library isn't bullet proof.

Quick Start

Require the library and use LivingDead.trace to "trace" an object. Later use LivingDead.traced_objects to iterate through "tracers" of each object.

Here is an example of tracing an object that is not retained:

require 'living_dead'

def run
  obj = Object.new
  LivingDead.trace(obj)

  return nil
end

run

puts LivingDead.traced_objects.select {|tracer| tracer.retained? }.count
# => 0

Note: Calling LivingDead.traced_objects auto calls GC.start, you don't need to do it manually. However you should look at the implementation of LivingDead.gc_start to understand the depth of hacks you're playing with.

Here is an example of tracing an object that IS retained:

require 'living_dead'

def run
  obj = Object.new
  LivingDead.trace(obj)

  return obj
end

@retained_here = run

puts LivingDead.traced_objects.select {|tracer| tracer.retained? }.count
# => 1

You can get more ways of interacting with a tracer by looking at LivingDead::ObjectTrace.

Development

Compile the code:

$ rake compile

Run the tests:

$ rake spec

or "why not both":

$ rake compile spec

License

MIT

Copyright Richard Schneeman 2016