title: How do I log messages / break execution for alloc, retain, release and dealloc of CFType objects?
body: <p>I want to be able to log messages (and preferably break to the debugger) each time a specific <code>CFType</code> object (for my current purposes, a <code>CGPDFDocument</code>) is allocated, retained, released or deallocated.</p>
  
  <p>Because there isn't a <code>Create...()</code> method for <code>CGPDFDocument</code> which takes a <code>CFAllocatorRef</code>, I'm trying to change the default allocator temporarily like this:</p>
  
  <pre><code>void MyPDFDocumentCreate()
  {
      // ...
  
      CFAllocatorRef defaultAllocator = CFAllocatorGetDefault();
      CFAllocatorSetDefault(MyLogAllocator());
  
      CGPDFDocumentRef documentRef = CGPDFDocumentCreateWithProvider(provider);
  
      CFAllocatorSetDefault(defaultAllocator);
  
      // ...
  }
  </code></pre>
  
  <p>where <code>MyLogAllocator()</code> is defined as follows:</p>
  
  <pre><code>static void *(*DefaultAllocate)(CFIndex size, CFOptionFlags hint, void *info);
  static const void *(*DefaultRetain)(const void *info);
  static void (*DefaultRelease)(const void *info);
  
  void *LogAllocate(CFIndex size, CFOptionFlags hint, void *info)
  {
      fprintf(stderr, "LogAllocate %p", info);
      if (DefaultAllocate)
          return DefaultAllocate(size, hint, info);
      else
          return NULL;
  }
  
  const void *LogRetain(const void *info)
  {
      fprintf(stderr, "LogRetain");
      if (DefaultRetain)
          return DefaultRetain(info);
      else
          return info;
  }
  
  void LogRelease(const void *info)
  {
      fprintf(stderr, "LogRelease");
      if (DefaultRelease)
          DefaultRelease(info);
  }
  
  static CFAllocatorRef MyLogAllocator()
  {
      static CFAllocatorRef theLogAllocator = NULL;
  
      if (!theLogAllocator)
      {
          CFAllocatorContext context;
          CFAllocatorRef defaultAllocator = CFAllocatorGetDefault();
          CFAllocatorGetContext(defaultAllocator, &amp;context);
  
          DefaultAllocate = context.allocate;
          DefaultRetain = context.retain;
          DefaultRelease = context.release;
  
          context.allocate = LogAllocate;
          context.retain = LogRetain;
          context.release = LogRelease;
  
          theLogAllocator = CFAllocatorCreate(kCFAllocatorUseContext, &amp;context);
      }
  
      return theLogAllocator;
  }
  </code></pre>
  
  <p>However, it seems that the default allocator (<code>kCFAllocatorSystemDefault</code> as far as I can tell) has NULL for context.retain and context.release, so I don't have any original implementations to call. That may be why, when I try the code above, I get the following stack trace:</p>
  
  <pre><code>#0  0x357ded12 in CFRetain ()
  #1  0x357dcb68 in _CFRuntimeCreateInstance ()
  #2  0x303fe35e in CGTypeCreateInstanceWithAllocator ()
  #3  0x303fe34c in CGTypeCreateInstance ()
  #4  0x304b32f4 in CGPDFDocumentCreateWithProvider ()
  #5  0x000293f4 in MyPDFDocumentCreate ([...]) at [...]
  </code></pre>
  
  <p>XCode doesn't actually tell me why it's halting, but if I try to continue I get:</p>
  
  <pre><code>(gdb) continue
  Continuing.
  
  Program received signal SIGTRAP, Trace/breakpoint trap.
  0x357ded12 in CFRetain ()
  (gdb) continue
  Continuing.
  
  Program received signal SIGTRAP, Trace/breakpoint trap.
  0x357ded12 in CFRetain ()
  (gdb) 
  </code></pre>
  
  <p>However many times I continue, I get the same SIGTRAP. I don't know how to interpret it; the only breakpoint I have set up is a symbolic one on <code>objc_exception_throw</code>.</p>
  
  <p>One thing to note is that <code>LogRetain()</code> and <code>LogAllocate()</code> are each called successfully once (in that order) from <code>CFAllocatorCreate()</code>:</p>
  
  <pre><code>#0  LogRetain (info=0x1a8000) at [...]
  #1  0x358086f2 in CFAllocatorCreate ()
  #2  0x00028d58 in MyLogAllocator () at [...] 
  #3  0x000293e0 in MyPDFDocumentCreate ([...]) at [...]
  
  #0  LogAllocate (size=104, hint=0, info=0x1a8000) at [...] 
  #1  0x3580882e in CFAllocatorCreate ()
  #2  0x00028d58 in MyLogAllocator () at [...]
  #3  0x000293e0 in MyPDFDocumentCreate ([...]) at [...]
  </code></pre>
  
  <p>And then <code>LogAllocate()</code> is again successful from <code>CFAllocatorAllocate()</code>:</p>
  
  <pre><code>#0  LogAllocate (size=64, hint=1024, info=0x1a8000) at [...] 
  #1  0x357dcc06 in CFAllocatorAllocate ()
  #2  0x357dcb04 in _CFRuntimeCreateInstance ()
  #3  0x303fe35e in CGTypeCreateInstanceWithAllocator ()
  #4  0x303fe34c in CGTypeCreateInstance ()
  #5  0x304b32f4 in CGPDFDocumentCreateWithProvider ()
  #6  0x000293f4 in MyPDFDocumentCreate ([...]) at [...]
  </code></pre>
  
  <p>before the <code>_CFRuntimeCreateInstance()</code> at #2 calls the problematic <code>CFRetain()</code> detailed above.</p>
  
  <p>Could somebody please help me to understand what's going on here (especially how the default allocator handles retain and release, and why I'm getting the SIGTRAP); how to fix it; and whether there's a better way to do what I'm trying to do?</p>
  
  <p>(I figured I might be able to work out how to use DTrace to probe <code>CFRetain()</code> and <code>CFRelease()</code>, filtered by the <code>CFTypeID</code> for <code>CGPDFDocument</code>, but I wouldn't know what to probe for deallocation (allocation is not so important to track as I know it's done within <code>CGPDFDocumentCreateWithProvider()</code>). Also, I'd prefer to be able to break to the debugger on retain / release / deallocate, which I don't think is possible using DTrace.)</p>
  
  <p><strong>UPDATE:</strong> Having now read the <a href="http://www.opensource.apple.com/source/CF/CF-550/CFRuntime.c" rel="nofollow">source code</a> for <code>CFRelease</code> I realise I misunderstood the purpose of <code>context.retain</code> and <code>context.release</code> -- they are for retaining and releasing <code>context.info</code>. So the entire approach described above is a non-starter. However, perhaps a DTrace/Instruments wizard may still be able to work some magic?!</p>