3

我希望能够在每次分配、保留、释放或解除分配特定CFType对象(对于我当前的目的, a )时记录消息(最好中断调试器)。CGPDFDocument

因为没有需要a 的Create...()方法,所以我正在尝试像这样临时更改默认分配器:CGPDFDocumentCFAllocatorRef

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

_CFRuntimeCreateInstance()at #2 调用CFRetain()上面详述的问题之前。

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

(我想我可能能够弄清楚如何使用 DTrace 来探测CFRetain()和,并由forCFRelease()过滤,但我不知道要探测什么来解除分配(分配并不那么重要,因为我知道它是在 内部完成的)。此外,我更希望能够在保留/释放/解除分配时中断调试器,我认为使用 DTrace 是不可能的。)CFTypeIDCGPDFDocumentCGPDFDocumentCreateWithProvider()

更新:现在阅读了源代码后,CFRelease我意识到我误解了context.retainand的目的context.release——它们是为了保留和释放context.info. 所以上面描述的整个方法是行不通的。但是,也许 DTrace/Instruments 向导仍然可以发挥一些魔力?!

4

1 回答 1

0

这是一个非常有趣的问题。由于您已经深入研究了过滤 DTrace,并且您正在深入研究CFRelease源代码,因此您可以查看使用 gdb 断点条件来选择何时中断。要确定是否会发生解除分配,只需使用CFGetRetainCount().

就是说,我猜你是在追查一些过度释放的崩溃,对吧?要看的东西可能比逆向工程更有用CFRelease()

  • CF僵尸
  • Instruments 的 Allocations 工具提供了对象何时被保留和释放、分配和销毁的完整堆栈。打开选项“记录引用计数”。
于 2011-02-24T14:25:23.320 回答