【问题标题】:How do I log messages / break execution for alloc, retain, release and dealloc of CFType objects?如何为 CFType 对象的分配、保留、释放和解除分配记录消息/中断执行?
【发布时间】:2011-07-03 04:58:54
【问题描述】:

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

因为CGPDFDocument 没有采用CFAllocatorRefCreate...() 方法,所以我正在尝试像这样临时更改默认分配器:

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

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

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

(我想我可能会想出如何使用 DTrace 来探测 CFRetain()CFRelease(),通过 CFTypeID 过滤为 CGPDFDocument,但我不知道要探测什么来解除分配(跟踪分配并不那么重要,因为我知道它是在CGPDFDocumentCreateWithProvider() 内完成的。此外,我更希望能够在保留/释放/解除分配时中断调试器,我认为使用 DTrace 是不可能的。 )

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

【问题讨论】:

  • 嗨,我有点困惑为 CFAllocatorRef 分配内存。我知道使用 default 和 NULL 创建内存的两种方法,但该内存对于我的应用程序来说是不够的。你能帮我为我的应用程序创建一些手动内存..!!感谢提前

标签: xcode macos ios core-foundation dtrace


【解决方案1】:

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

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

  • CFZombie
  • Instruments 的 Allocations 工具提供了对象被保留和释放、分配和销毁时间的完整堆栈。打开“记录引用计数”选项。

【讨论】:

  • 这是泄漏而不是过度发布,我最终编写了一个最小的非原子 ObjC 包装器并更改了代码,但下次我将使用 GDB 断点或仪器。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
相关资源
最近更新 更多