如何为CFType对象的alloc,retain,release和dealloc记录消息/中断执行?

时间:2011-02-24 00:13:24

标签: xcode macos ios core-foundation dtrace

我希望每次分配,保留,释放或取消分配特定CFType对象(当前用途,CGPDFDocument)时,都能记录消息(最好是中断到调试器)

因为Create...()没有CGPDFDocument方法需要CFAllocatorRef,所以我试图像这样暂时更改默认分配器:

void MyPDFDocumentCreate()
{
    // ...

    CFAllocatorRef defaultAllocator = CFAllocatorGetDefault();
    CFAllocatorSetDefault(MyLogAllocator());

    CGPDFDocumentRef documentRef = CGPDFDocumentCreateWithProvider(provider);

    CFAllocatorSetDefault(defaultAllocator);

    // ...
}

其中MyLogAllocator()的定义如下:

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, &context);

        DefaultAllocate = context.allocate;
        DefaultRetain = context.retain;
        DefaultRelease = context.release;

        context.allocate = LogAllocate;
        context.retain = LogRetain;
        context.release = LogRelease;

        theLogAllocator = CFAllocatorCreate(kCFAllocatorUseContext, &context);
    }

    return theLogAllocator;
}

但是,似乎默认分配器(kCFAllocatorSystemDefault据我所知)对于context.retain和context.release都有NULL,所以我没有任何原始实现来调用。这可能是为什么,当我尝试上面的代码时,我得到以下堆栈跟踪:

#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 [...]

XCode实际上并没有告诉我为什么会停止,但如果我继续尝试,我会得到:

(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) 

然而,我继续多次,我得到相同的SIGTRAP。我不知道如何解释它;我设置的唯一断点是objc_exception_throw上的象征性断点。

有一点需要注意的是,LogRetain()LogAllocate()每次都被CFAllocatorCreate()成功调用一次(按此顺序):

#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 [...]

然后LogAllocate()再次成功CFAllocatorAllocate()

#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 [...]

在#{1}}#2之前调用上面详述的有问题的_CFRuntimeCreateInstance()

有人可以帮我理解这里发生了什么(尤其是默认分配器如何处理保留和释放,以及为什么我要获得SIGTRAP);如何解决它;以及是否有更好的方法来做我想做的事情?

(我想我可能会弄清楚如何使用DTrace来探测CFRetain()CFRetain()CFRelease()CFTypeID过滤,但我不会知道如何调用deallocation(分配不是那么重要,因为我知道它在CGPDFDocument内完成)。而且,我更愿意能够在retain / release / deallocate上打破调试器,我认为使用DTrace是不可能的。)

更新:现在阅读CGPDFDocumentCreateWithProvider()的{​​{3}}我意识到我误解了CFReleasecontext.retain的目的 - 他们是保留和释放context.release。所以上面描述的整个方法都是非首发的。但是,也许DTrace / Instruments向导可能仍然可以运用一些魔法?!

1 个答案:

答案 0 :(得分:0)

这是一个非常有趣的问题。由于您已经深入研究过滤DTrace,并且您正在深入了解CFRelease源,那么您可以查看使用gdb断点条件来选择何时中断。要确定是否要进行释放,只需使用CFGetRetainCount()

那就是说,我猜你是在拉着你的头发追踪一些过度释放的崩溃,对吧?要查看的内容可能比逆向工程CFRelease()更有用:

  • CFZombie
  • 仪器的分配工具提供了保留和释放,分配和销毁对象的完整堆栈。打开“记录参考计数”选项。