Track and Log Disposable Object Instances - Yortw/Yort.Trashy GitHub Wiki

You can have Trashy track participating disposable objects, then periodically log the results. Trashy will report objects as either 'dead' or 'alive'. If alive then the instance currently has references, is not eligible for garbage collection and does not yet indicate a problem with improper disposal. 'Dead' objects have no references and have been or will be garbage collected, but have not been explicitly disposed (disposal via finalizer doesn't count). Dead objects represent improper behaviour by application code. On many platforms, Trashy can provide the stacktrace where the object was created, making it easier to track down the application code responsible for failing to dispose the instance.

Objects implementing dispose via Trashy base classes automatically participate in tracking. Those implementing dispose manually will need to call DisposableTracker.RegisterInstance and DisposableTracker.UnregisterInstance.

Tracking objects does add some overhead in terms of memory and cpu resources, therefore tracking is disabled by default. You can enable tracking or restrict tracking to specific types.

Manually Participating in Tracking

RegisterInstance

Call DisposableTracker.RegisterInstance from your object's constructors to notify the tracking system of your object instance.

public MyDisposable()
{
    DisposableTracker.RegisterInstance(this);
}

UnregisterInstance

Call DisposableTracker.UnregisterInstance from your Dispose() or Dispose(bool) method, only when the object is being deliberately disposed. This tells the tracker the object was correctly disposed and should no longer be tracked.

public void Dispose(bool disposing)
{
    if (disposing)
    {
      DisposableTracker.UnregisterInstance(this);

      //Your dispose logic
    }

    //Your dispose logic
}

Configuring & Controlling Tracking

Enabled Property

This boolean property controls whether tracking is actually enabled or not. It default to false (not enabled), no tracking is performed even when register/unregister methods are called. Set the property to true to enable tracking of objects created subsequently. Setting this property to false after objects have been tracked will clear all tracking information as well as stop subsequently created instances from being tracked.

RegisterTrackedType

To track only specific types call RegisterTrackedType passing the System.Type of the instances to track. If one or more types are registered, only those types are tracked. If no types are explicitly registered, then all types are tracked. For this reason, types should be registered prior to setting Enabled to true.

UnregisterTrackedType

To stop tracking a specific type of object call UnregisterTrackedType passing the System.Type of the instances to stop tracking. This will not only stop tracking that type but also remove any current tracking information for instances of that type. If the only type registered is unregistered, the system will return to trackin all types. For this reason, set Enabled to false then unregister types if you want to turn off tracking altogether.

Captuing Stack Traces

You can tell the tracker to capture the stack trace at which the object was registered. This will add additional performance and memory overhead but significantly reduces the difficulty of finding and fixing improperly disposed objects. To enable stack trace capture, set DisposeTracker.CaptureStackTraceAtCreation to true.

Logging

Log When Objects are Registered

You can optionally log when objects are registered with the tracker (usually this is when the object is created). To do this, provide a delegate or lambda function for the DisposableTracker.DisposableRegisteredLogger property. The value shoud be an Action, where the argument contains details of the registered instance. Setting DisposableTracker.DisposableRegisteredLogger to null will stop logging registration. The logging method can also set the TrackedDisposable.State property to a string containing more information about the specific object instance (such as a name or id). This information will then be available in other logging calls for helping track specific instances in the log.

  DisposableTracker.DisposableRegisteredLogger = LogObjectRegistered;

  private void LogObjectRegistered(TrackedDisposable tracked)
  {
    System.Diagnostics.Trace.WriteLine("New instance of " + tracked.InstaceType.FullName + " created at " + tracked.CreationStackTrace);
    tracked.State = ((MyDisposableWithId)tracked.instance).Id;
  }

Log When Objects are Unregistered

You can optionally log when objects are unregistered with the tracker (usually this is when the object is properly disposed). To do this, provide a delegate or lambda function for the DisposableTracker.DisposableUnregisteredLogger property. The value shoud be an Action, where the argument contains details of the registered instance. Setting DisposableTracker.DisposableRegisteredLogger to null will stop logging registration.

  DisposableTracker.DisposableUnregisteredLogger = LogObjectUnregistered;

  private void LogObjectUnregistered(TrackedDisposable tracked)
  {
    System.Diagnostics.Trace.WriteLine("Disposed instance of " + tracked.InstaceType.FullName + " created at " + tracked.CreationStackTrace);
  }

Logging Tracked Instances (Alive or Dead)

You can log the status of currently tracked objects by calling DisposableTracker.EnumerateTrackedInstances with an Action instance. This will provide all the details of the tracked instance.

This sample logs only objects that have been improperly disposed (if any at this time), by checking the TrackedDisposable.IsAlive property.

  DisposableTracker.EnumerateTrackedInstances(LogImproperlyDisposedObjects)

  private void LogImproperlyDisposedObjects(TrackedDisposable tracked)
  {
      if (!tacked.IsAlive)
        System.Diagnostics.Trace.WriteLine("Improperly disposed instance of " + tracked.InstaceType.FullName + " created at " + tracked.CreationStackTrace + " - state: " + tracked.State);
  }

A few notes on the TrackedDisposable passed to the delegate. If IsAlive is true the object currently has references and has not yet been garbage collected. If false, the item was garbage collected without being disposed, and this represents a problem in application code.

If the item has been garbage collected then the TrackedDisposable.Instance will return null, so do not attept to use it. Ensure you check IsAlive and perform null checking if using the Instance property. Instead of using the Instance property directly, use the InstanceType property to determine the type of object, or the State property to determine identifying details (see Log When Objects are Registered) for details on how to set the content of the State property at registration time).