Как мне регистрировать сообщения / прерывать выполнение для выделения, сохранения, освобождения и освобождения объектов CFType? - PullRequest
3 голосов
/ 24 февраля 2011

Я хочу иметь возможность регистрировать сообщения (и предпочтительно прерывать отладчик) каждый раз, когда конкретный объект 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) имеет NULL для context.retain и context.release, поэтому у меня нетлюбые оригинальные реализации для вызова.Возможно, поэтому, когда я пытаюсь выполнить приведенный выше код, я получаю следующую трассировку стека:

#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() в # 2 вызовет проблемный CFRetain(), подробно описанный выше.

Может кто-нибудь помочь мне понять, что здесь происходит (особенно, как обработчик по умолчанию обрабатывает сохранение и освобождение, и почему я получаю SIGTRAP);как это исправить;и есть ли лучший способ сделать то, что я пытаюсь сделать?

(я подумал, что смогу разобраться, как использовать DTrace для проверки CFRetain() и CFRelease(), отфильтрованных с помощью CFTypeID для CGPDFDocument, но я не знаю, что проверять на освобождение (распределение не так важно отслеживать, поскольку я знаю, что оно выполняется в CGPDFDocumentCreateWithProvider()). Также я предпочел бы иметь возможность перейти котладчик при сохранении / выпуске / освобождении, что я не считаю возможным с помощью DTrace.)

ОБНОВЛЕНИЕ: Прочитав исходный код для CFReleaseЯ понимаю, что неправильно понял цель context.retain и context.release - они предназначены для сохранения и освобождения context.info.Таким образом, весь описанный выше подход не является началом.Однако, возможно, мастер DTrace / Instruments все еще может творить магию?!

1 Ответ

0 голосов
/ 24 февраля 2011

Это очень интересная проблема.Поскольку вы углубились в изучение фильтрации DTrace и погрузились в источник CFRelease, вы можете взглянуть на использование условий точки останова gdb, чтобы выбрать, когда прерывать.Чтобы определить, произойдет ли освобождение, просто используйте CFGetRetainCount().

Тем не менее, я предполагаю, что вы дергаете себя за волосы, выслеживая какую-то переблокировку, верно?Вещи, на которые стоит обратить внимание, вероятно, более полезны, чем обратное проектирование CFRelease():

  • CFZombie
  • Инструмент Распределения инструментов обеспечивает полные стеки, когда объекты былисохраняется и освобождается, выделяется и уничтожается.Включите опцию «Записать счетчик ссылок».
...