Техническое примечание TN2239

Волшебство Отладки iOS

iOS содержит много 'секретных' средств отладки, включая переменные окружения, предпочтения, подпрограммы, вызываемые от GDB, и т.д. Этот technote описывает эти средства. Если Вы разрабатываете для iOS, необходимо просмотреть этот список, чтобы видеть, пропускаете ли Вы что-то, что сделает Вашу жизнь проще.

Введение
Основы
Некоторый требуемый блок
Инструменты
CrashReporter
BSD
Core Services
Прикладные службы
Какао и касание какао
Сети
Питание
Продвиньте уведомления
Средство моделирования iPhone
История версии документа

Введение

Все системы Apple включают средства отладки, добавленные командами инженеров Apple, чтобы помочь разработать и отладить определенные подсистемы. Многие из этих средств остаются в выпущенном системном программном обеспечении, и можно использовать их для отладки кода. Этот technote описывает некоторые более широко полезные. В случаях, где средство отладки документируется в другое место, существует краткий обзор средства и ссылки к существующей документации.

Многие подробные данные, покрытые этим technote, варьируются от платформы до платформы и выпуска к выпуску. Также, можно встретиться с незначительными изменениями между платформами, и в более старых или более новых системах. Известные значительные изменения вызываются в тексте.

Этот technote был записан в отношении iOS 4.1.

В дополнение к этой технической проблеме совместимости на уровне двоичных кодов имейте в виду, что приложения для iOS должны соответствовать различным юридическим соглашениям и Инструкциям по Анализу App Store.

Этот technote покрытия усовершенствовал отладку методов. Если Вы просто начинаете, необходимо консультироваться со следующим материалом:

Этот technote не покрывает отладку производительности. При попытке отладить проблему производительности, лучшее место для запуска является Началом работы с документом Производительности.

Основы

Более поздние разделы этого technote описывают средства отладки подробно. Многие из этих средств используют подобные методы, чтобы включить и отключить средство и видеть его вывод. В этом разделе описываются эти общие методы.

Включение средств отладки

Некоторые средства отладки включены по умолчанию. Однако большинство средств должно быть включено с помощью одного из методов, описанных в следующих разделах.

Переменные окружения

Во многих случаях можно включить средство отладки путем установки определенной переменной окружения. Можно сделать это использование исполнимого инспектора в XCode. Рисунок 1 показывает пример этого.

Рисунок 1  , Устанавливающий переменные окружения в XCode

Предпочтения

Некоторые средства отладки включены путем установки специального предпочтения. Можно установить такое предпочтение отладки путем конфигурирования параметра командной строки в XCode. Рисунок 2 показывает, как это сделано.

Рисунок 2  , Устанавливающий параметры командной строки в XCode

Вызываемые подпрограммы

Много системных платформ включают подпрограммы та отладочная информация печати в stderr. Эти подпрограммы могут быть специально предназначены, чтобы быть вызываемыми из GDB, или они могут просто быть существующими подпрограммами API, которые полезны при отладке. Перечисление 1 показывает пример того, как вызвать отладочную программу от GDB; в частности это вызывает CFBundleGetAllBundles получить список всех пакетов, загруженных в приложении, и затем, распечатывает тот список путем вызова CFShow подпрограмма.

Перечисление 1  Вызывая отладочную программу от GDB

(gdb) call (void)CFShow((void *)CFBundleGetAllBundles())
<CFArray 0x10025c010 [0x7fff701faf20]>{type = mutable-small, count = 59, values = (
    0 : CFBundle 0x100234d00 </System/Library/Frameworks/CoreData.framework> …
    […]
    12 : CFBundle 0x100237790 </System/Library/Frameworks/Security.framework> …
    […]
    23 : CFBundle 0x100194eb0 </System/Library/Frameworks/CoreFoundation.framework> …
    […]
)}

Точно так же при вызове подпрограммы с нестандартными параметрами Вы, возможно, должны бросить свои параметры, чтобы гарантировать, что GDB передает их правильно.

Если Вы не видите вывод от подпрограммы, Вы, возможно, должны смотреть на консольный журнал, как описано в разделе Seeing Debug Output.

На практике это только влияет на Intel 32-разрядный код, как используется Средством моделирования iPhone.

Профили конфигурации

Некоторые подсистемы iOS имеют средства отладки, которые могут быть включены путем установки специального профиля конфигурации. Для примера этого см. Уведомления Нажатия. Вы устанавливаете такой профиль конфигурации обычным способом:

  • поместите его на веб-сервер, и затем загрузите его с помощью Safari на устройстве

  • присоедините его к электронной почте, отправьте его по почте в учетную запись, доступную от устройства, и затем выполните Почту и откройте присоединение профиля конфигурации

Можно узнать больше о профилях конфигурации путем чтения различных документов, доступных из iPhone в Бизнес-сайте.

Наблюдение вывода отладки

Программы, генерирующие вывод отладки обычно, делают настолько использующий из следующих механизмов:

  • NSLog

  • печать к stderr

  • системный журнал

NSLog высокоуровневый API для журналирования, использующегося экстенсивно кодом Objective C. Точное поведение NSLog удивительно сложно, и изменялся значительно в течение долгого времени, делая его вне объема этого документа. Однако достаточно знать это NSLog печать к stderr, или журналы к системному журналу или обоим. Так, если Вы понимаете те два механизма, Вы видите что-либо зарегистрированное через NSLog.

Печать к stderr один из обычно используемых выходных механизмов. Учитывая важность этой темы, это покрыто подробно в следующем разделе.

Самый простой способ просмотреть системный журнал находится на вкладке Console в окне Organizer XCode. Если Вы работаете с пользователями, не хотящими устанавливать XCode, они могут просмотреть системный журнал с помощью Утилиты Конфигурации iPhone.

Консольный вывод

Много программ, и действительно много системных платформ, распечатывают сообщения отладки к stderr. Местом назначения для этого вывода в конечном счете управляет программа: это может перенаправить stderr любому месту назначения это выбирает. Однако в большинстве случаев программа не перенаправляет stderr, таким образом, вывод переходит к месту назначения по умолчанию, наследованному программой от ее среды запуска. Это обычно - одно из следующего:

  • При запуске приложения GUI как оно было бы запущено обычным пользователем, система перенаправляет любые сообщения, распечатанные на stderr к системному журналу. Можно просмотреть эти сообщения с помощью методов, описанных ранее.

  • При выполнении программы из XCode Вы видите stderr вывод в окне консоли отладки XCode (выбирают пункт меню Console из меню Run для наблюдения этого окна).

Присоединение к рабочей программе (использующий Присоединение XCode для меню Process, или attach команда в GDB), автоматически не соединяет программу stderr к Вашему окну GDB. Можно сделать это из GDB использование приема, описанного в разделе «Seeing stdout and stderr After Attaching» Технического примечания TN2030, 'GDB для Ветеранов MacsBug'.

Некоторый требуемый блок

В то время как очень необычно записать существенное количество кода в ассемблере теперь, все еще полезно иметь основное понимание того темного искусства. Это - особенно истина, когда Вы отлаживаете, особенно при отладке катастрофических отказов, происходящих в библиотеках или платформах, для которых у Вас нет исходного кода. Этот раздел покрывает некоторые самые основные методы, необходимые для отладки программ на уровне ассемблера. В частности это описывает, как установить точки останова, параметры доступа, и получить доступ к обратному адресу на всей поддерживаемой архитектуре.

Где различие является значительным, примеры уровня ассемблера в этом technote от armv7 двоичный файл, работающий на iPhone 3GS. Однако в большинстве случаев различие не является значительным, и пример мог бы быть от множества различной архитектуры, возможно даже от Mac. Очень просто адаптировать такие примеры к другой архитектуре. Старшие значащие различия:

И это точно элементы, покрытые следующими архитектурно-зависимыми разделами.

В этом контексте стандартные параметры являются целыми числами (которые помещаются в единственный регистр), перечисления и указатели (включая указатели на массивы и указатели на функции). Нестандартные параметры являются числами с плавающей точкой, векторами, структурами, целые числа, больше, чем регистр и любой параметр после последнего фиксированного параметра подпрограммы, берущей переменное число параметров.

Для подробного описания соглашений о вызовах для всех устройств на iOS посмотрите iOS Руководство по Вызову функции ABI. Intel 32-разрядная архитектура, используемая Средством моделирования iPhone, описан в Mac OS X Руководство по Вызову функции ABI.

Перед чтением следующих разделов критически важно, что Вы понимаете одну тонкость GDB. Поскольку GDB является, в глубине души, отладчиком исходного уровня при установке точки останова на подпрограмме GDB не устанавливает точку останова на первой инструкции подпрограммы; скорее это устанавливает точку останова в первой инструкции после вводной части подпрограммы. С точки зрения отладки на уровне исходного кода это имеет совершенный смысл. В отладчике исходного уровня Вы никогда не хотите продвинуться через вводную часть подпрограммы. Однако при выполнении отладки на уровне ассемблера, проще получить доступ к параметрам перед выполнениями вводной части. Поэтому расположение параметров в первой инструкции подпрограммы определяется вызовом функции ABI, но вводной части позволяют переставить вещи вокруг по ее усмотрению. Кроме того, каждая вводная часть может сделать это немного отличающимся способом. Так единственный способ получить доступ к параметрам после того, как вводная часть выполнилась, должен демонтировать вводную часть и разработать, куда все пошло. Это обычно, но не всегда, довольно просто, но это - все еще дополнительная работа.

Лучший способ сказать GDB устанавливать точку останова в первой инструкции подпрограммы состоит в том, чтобы снабдить префиксом стандартное имя «*». Перечисление 2 показывает пример этого.

Перечисление 2  Прежде и после вводной части

(gdb) b CFStringCreateWithFormat
Breakpoint 1 at 0x34427ff8
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x34427ff8 <CFStringCreateWithFormat+8>
-- The breakpoint is not at the first instruction.
-- Disassembling the routine shows that GDB has skipped the prologue.
(gdb) x/5i CFStringCreateWithFormat
0x34427ff0 <CFStringCreateWithFormat>: push    {r2, r3}
0x34427ff2 <CFStringCreateWithFormat+2>: push    {r7, lr}
0x34427ff4 <CFStringCreateWithFormat+4>: add r7, sp, #0
0x34427ff6 <CFStringCreateWithFormat+6>: sub sp, #4
0x34427ff8 <CFStringCreateWithFormat+8>: add r3, sp, #12
-- So we use a "*" prefix to disable GDB's 'smarts'.
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x34427ff0
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep n   0x34427ff8 <CFStringCreateWithFormat+8>
2   breakpoint     keep n   0x34427ff0 <CFStringCreateWithFormat>

Напротив, некоторые другие списки в этом документе создавались на Mac OS X, и таким образом используют традиционные комментарии GDB.

Наконец при поиске информации о конкретных инструкциях, знать, что Меню справки у Акулы (включенный в инструменты разработчика XCode) имеет ссылку системы команд для ARM, Intel и архитектуры PowerPC.

ARM

В программах ARM первые четыре параметра передаются в регистрах. Обратный адрес находится в регистре LR. Таблица 1 показывает, как получить доступ к этим значениям от GDB, когда Вы остановились в первой инструкции функции.

Табличные 1  параметры Доступа на ARM

Что

Синтаксис GDB

обратный адрес

$lr

первый параметр

$r0

второй параметр

$r1

третий параметр

$r2

четвертый параметр

$r3

По возврату из функции результат находится в регистре R0 ($r0).

Поскольку параметры передаются в регистрах, нет никакого прямого способа получить доступ к параметрам после вводной части.

Перечисление 3 показывает пример того, как использовать эту информацию для доступа к параметрам в GDB.

  Параметры перечисления 3 на ARM

-- We have to start the program from Xcode. Before we do that, we go to 
-- Xcode's Breakpoints window and set a symbolic breakpoint on 
-- CFStringCreateWithFormat.
GNU gdb 6.3.50-20050815 […]
-- We've stopped after the prologue.
(gdb) p/a $pc
$1 = 0x34427ff8 <CFStringCreateWithFormat+8>
-- Let's see what the prologue has done to the registers 
-- holding our parameters.
(gdb) x/5i $pc-8
0x34427ff0 <CFStringCreateWithFormat>: push    {r2, r3}
0x34427ff2 <CFStringCreateWithFormat+2>: push    {r7, lr}
0x34427ff4 <CFStringCreateWithFormat+4>: add r7, sp, #0
0x34427ff6 <CFStringCreateWithFormat+6>: sub sp, #4
0x34427ff8 <CFStringCreateWithFormat+8>: add r3, sp, #12
-- Hey, the prologue hasn't modified R0..R3, so we're OK.
--
-- first parameter is "alloc"
(gdb) p/a $r0
$2 = 0x3e801d60 <__kCFAllocatorSystemDefault>
-- second parameter is "formatOptions"
(gdb) p/a $r1
$3 = 0x0
-- third parameter is "format"
(gdb) call (void)CFShow($r2)
managed/%@/%@
-- It turns out the prologue has left LR intact as well.
-- So we can get our return address.
--
-- IMPORTANT: Bit zero of the return address indicates that it lies 
-- in a Thumb routine. So when using a return address in LR or on 
-- the stack, always mask off bit zero.
(gdb) p/a $lr & 0xfffffffe
$4 = 0x34427e18 <__CFXPreferencesGetManagedSourceForBundleIDAndUser+48>
-- Now clear the breakpoint and set a new one before the prologue.
(gdb) del 1
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x34427ff0
(gdb) c
Continuing.

Breakpoint 2, 0x34427ff0 in CFStringCreateWithFormat ()
-- We're at the first instruction. The parameters are guaranteed 
-- to be in the right registers.
(gdb) p/a $pc
$5 = 0x34427ff0 <CFStringCreateWithFormat>
-- first parameter is "alloc"
(gdb) p/a $r0
$6 = 0x3e801d60 <__kCFAllocatorSystemDefault>
-- second parameter is "formatOptions"
(gdb) p/a $r1
$7 = 0x0
-- third parameter is "format"
(gdb) call (void)CFShow($r2)
%@/%@.plist
-- return address is in LR; again, we mask off bit zero
(gdb) p/a $lr & 0xfffffffe
$8 = 0x34427e7c <__CFXPreferencesGetManagedSourceForBundleIDAndUser+148>
(gdb) b *0x34427e7c
Breakpoint 3 at 0x34427e7c
-- Delete the other breakpoint to avoid thread-based confusion.
(gdb) del 2
-- Continue to the return address.
(gdb) c
Continuing.

Breakpoint 3, 0x34427e7c in __CFXPreferencesGetManagedSourceForBundleIDAndUser ()
-- function result
(gdb) p/a $r0
$9 = 0x1062d0
(gdb) call (void)CFShow($r0)
mobile/.GlobalPreferences.plist

32-разрядный Intel

Intel 32-разрядная архитектура используется в Средстве моделирования iPhone.

В 32-разрядных программах Intel параметры передаются штабелю. В первой инструкции подпрограммы главное слово штабеля содержит обратный адрес, следующее слово содержит первый (крайний левый) параметр, следующее слово содержит второй параметр и т.д. Таблица 2 показывает, как получить доступ к этим значениям от GDB.

Табличные 2  параметры Доступа на 32-разрядном Intel

Что

Синтаксис GDB

обратный адрес

* (интервал *) $esp

первый параметр

* (интервал*)($esp+4)

второй параметр

* (интервал*)($esp+8)

... и т.д.

После вводной части подпрограммы можно получить доступ к параметрам относительно указателя кадра (зарегистрируйте EBP). Таблица 3 показывает синтаксис.

Табличные 3  параметры Доступа после вводной части

Что

Синтаксис GDB

предыдущий кадр

* (интервал *) $ebp

обратный адрес

* (интервал*)($ebp+4)

первый параметр

* (интервал*)($ebp+8)

второй параметр

* (интервал*)($ebp+12)

... и т.д.

По возврату из функции результат находится в регистре EAX ($eax).

Перечисление 4 показывает пример того, как использовать эту информацию для доступа к параметрам в GDB.

  Параметры перечисления 4 на 32-разрядном Intel

$ # Use the -arch i386 argument to GDB to get it to run the 
$ # 32-bit Intel binary.
$ gdb -arch i386 /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) fb CFStringCreateWithFormat
Breakpoint 1 at 0x31ec6d6
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
Reading symbols for shared libraries […]
Breakpoint 1, 0x940e36d6 in CFStringCreateWithFormat ()
(gdb) # We've stopped after the prologue.
(gdb) p/a $pc
$1 = 0x940e36d6 <CFStringCreateWithFormat+6>
(gdb) # However, for 32-bit Intel we don't need to inspect 
(gdb) # the prologue because the parameters are on the stack.
(gdb) # We can access them relative to EBP.
(gdb) #
(gdb) # first parameter is "alloc"
(gdb) p/a *(int*)($ebp+8)
$2 = 0xa0473ee0 <__kCFAllocatorSystemDefault>
(gdb) # second parameter is "formatOptions"
(gdb) p/a *(int*)($ebp+12)
$3 = 0x0
(gdb) # third parameter is "format"
(gdb) call (void)CFShow(*(int*)($ebp+16))
%@
(gdb) # return address is at EBP+4
(gdb) p/a *(int*)($ebp+4)
$4 = 0x940f59fb <__CFXPreferencesGetNamedVolatileSourceForBundleID+59>
(gdb) # Now clear the breakpoint and set a new one before the prologue.
(gdb) del 1
(gdb) b *CFStringCreateWithFormat
Breakpoint 2 at 0x940e36d0
(gdb) c
Continuing.

Breakpoint 2, 0x940e36d0 in CFStringCreateWithFormat ()
(gdb) # We're at the first instruction. We must access 
(gdb) # the parameters relative to ESP.
(gdb) p/a $pc
$6 = 0x940e36d0 <CFStringCreateWithFormat>
(gdb) # first parameter is "alloc"
(gdb) p/a *(int*)($esp+4)
$7 = 0xa0473ee0 <__kCFAllocatorSystemDefault>
(gdb) # second parameter is "formatOptions"
(gdb) p/a *(int*)($esp+8)
$8 = 0x0
(gdb) # third parameter is "format"
(gdb) call (void)CFShow(*(int*)($esp+12))
managed/%@/%@
(gdb) # return address is on the top of the stack
(gdb) p/a *(int*)$esp
$9 = 0x940f52cc <__CFXPreferencesGetManagedSourceForBundleIDAndUser+76>
(gdb) # Set a breakpoint on the return address.
(gdb) b *0x940f52cc
Breakpoint 3 at 0x940f52cc
(gdb) c
Continuing.

Breakpoint 3, 0x940f52cc in __CFXPreferencesGetManagedSourceForBundleIDAndUser ()
(gdb) # function result
(gdb) p/a $eax
$10 = 0x1079d0
(gdb) call (void)CFShow($eax)
managed/com.apple.TextEdit/kCFPreferencesCurrentUser

Глюки архитектуры

Следующие разделы описывают несколько глюков, с которыми Вы могли бы встретиться при отладке на уровне ассемблера.

Дополнительные параметры

При рассмотрении параметров на уровне ассемблера имейте в виду следующее:

  • Если подпрограмма является функцией членства C++, существует неявный первый параметр для this.

  • Если подпрограмма является методом Objective C, существует два неявных первых параметра (см. Objective C для подробных данных об этом).

  • Если компилятор может найти все вызывающие стороны функции (это обычно происходит, когда функция объявляется как static), это может принять решение передать параметры той функции нестандартным способом. Это очень редко на архитектуре, имеющей эффективный основанный на регистре ABI, но это довольно характерно для Intel 32-разрядные программы. Так, если Вы устанавливаете точку останова на статической функции в Intel 32-разрядная программа, не упускаете это очень запутывающее поведение.

Порядок байтов и размеры модуля

При исследовании памяти в GDB дела идут более гладкие при использовании корректного размера модуля. Таблица 4 является сводкой размеров модуля, поддерживаемых GDB.

Таблица 4  размеры модуля GDB

Размер

C тип

Модуль GDB

Мнемосхема

1 байт

символ

b

байт

2 байта

короткий

h

полуслово

4 байта

интервал

w

слово

8 байтов

долго или долго долго

g

гигант

Это особенно важно при отладке в системах с прямым порядком байтов (все устройства на iOS и средство моделирования), потому что Вы получаете запутывающие результаты при указании неправильного размера модуля. Перечисление 5 показывает пример этого (взятый от Mac, но результатами было бы то же на устройстве на iOS). Вторые и третьи параметры CFStringCreateWithCharacters укажите массив символов Unicode. Каждый элемент является a UniChar, который является 16-разрядным числом в формате собственного порядка байтов. Поскольку мы работаем на системе с прямым порядком байтов, необходимо вывести этот массив с помощью корректного размера модуля, иначе все выглядит испорченным.

Перечисление 5  Используя правильный размер модуля

$ gdb
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) attach Finder
Attaching to process 4732.
Reading symbols for shared libraries . done
Reading symbols for shared libraries […]
0x00007fff81963e3a in mach_msg_trap ()
(gdb) b *CFStringCreateWithCharacters
Breakpoint 1 at 0x7fff86070520
(gdb) c
Continuing.

Breakpoint 1, 0x00007fff86070520 in CFStringCreateWithCharacters ()
(gdb) # The third parameter is the number of UniChars 
(gdb) # in the buffer pointed to by the first parameter.
(gdb) p (int)$rdx
$1 = 18
(gdb) # Dump the buffer as shorts. Everything makes sense.
(gdb) # This is the string "Auto-Save Recovery".
(gdb) x/18xh $rsi
0x10b7df292: 0x0041  0x0075  0x0074  0x006f  0x002d  0x0053  0x0061  0x0076
0x10b7df2a2: 0x0065  0x0020  0x0052  0x0065  0x0063  0x006f  0x0076  0x0065
0x10b7df2b2: 0x0072  0x0079
(gdb) # Now dump the buffer as words. Most confusing.
(gdb) # It looks like "uAotS-va eeRocevyr"!
(gdb) x/9xw $rsi
0x10b7df292: 0x00750041      0x006f0074      0x0053002d      0x00760061
0x10b7df2a2: 0x00200065      0x00650052      0x006f0063      0x00650076
0x10b7df2b2: 0x00790072
(gdb) # Now dump the buffer as bytes. This is a little less 
(gdb) # confusing, but you still have to remember that it's big 
(gdb) # endian data.
(gdb) x/36xb $rsi
0x10b7df292: 0x41    0x00    0x75    0x00    0x74    0x00    0x6f    0x00
0x10b7df29a: 0x2d    0x00    0x53    0x00    0x61    0x00    0x76    0x00
0x10b7df2a2: 0x65    0x00    0x20    0x00    0x52    0x00    0x65    0x00
0x10b7df2aa: 0x63    0x00    0x6f    0x00    0x76    0x00    0x65    0x00
0x10b7df2b2: 0x72    0x00    0x79    0x00

Управляемый катастрофический отказ

В некоторых случаях полезно разрушить Вашу программу с сохранением контроля над ситуацией. Один распространенный способ сделать это должно вызвать аварийное прекращение работы. Другая опция состоит в том, чтобы использовать __builtin_trap встроенная функция, генерирующая специфичную для машины инструкцию прерывания. Перечисление 6 показывает, как это сделано.

Перечисление 6  , Отказывающее через __ builtin_trap

int main(int argc, char **argv) {     __builtin_trap();     return 1; }

При выполнении программы в отладчике Вы сразу остановитесь в строке перед __builtin_trap вызвать. Иначе программа разрушит и сгенерирует отчет катастрофического отказа.

Будьте внимательны, что жизненный цикл приложения для iOS является объектом контроля за работой пользователей, означая, что приложения для iOS не должны просто выходить. Ваша сборка конечных версий должна только вызвать abort при обстоятельствах, где это отказало бы так или иначе, и abort вызов предотвращает ущерб пользовательских данных или позволяет Вам более легко диагностировать проблему.

Инструменты

Инструменты являются приложением для того, чтобы динамично проследить и профилировать код. Это работает на Mac OS X и позволяет Вам предназначаться для программ, работающих на Mac OS X, устройствах на iOS и Средстве моделирования iPhone.

В то время как Инструменты прежде всего фокусируются на отладке производительности, можно также использовать ее для отладки ошибок. Например, инструмент ObjectAlloc может помочь Вам разыскать и сверх - и ошибки под выпуском.

Одна особенно хорошая функция Инструментов - то, что это предоставляет Вам легкий доступ к зомби. См. Инструментальное Руководство пользователя для подробных данных об этом и других функциях Instruments.

CrashReporter

CrashReporter является неоценимым средством отладки, регистрирующим информацию обо всех программах тот катастрофический отказ. Все время это включено; все, что необходимо сделать, смотреть на его вывод.

CrashReporter описан подробно в Техническом примечании TN2151, 'Поняв и Анализируя Доклады Сбоя приложения iPhone OS'.

BSD

Процесс реализаций подсистемы BSD, память, файл и сетевая инфраструктура, и таким образом критически важны по отношению ко всем программам в системе. BSD реализует много аккуратных средств отладки, которые можно использовать в своих интересах.

Средство выделения памяти

Средство выделения памяти по умолчанию включает много средств отладки, которые можно включить через переменные окружения. Они полностью документируются в страницу руководства. Таблица 5 перечисляет некоторые более полезные.

Таблица 5  Некоторые полезные переменные окружения средства выделения памяти

Переменная

Сводка

MallocScribble

Заполните выделенную память 0xAA и набросайте освобожденную память с 0x55

MallocGuardEdges

Добавьте защитные страницы прежде и после больших выделений

MallocStackLogging

Рекордные следы для каждого блока памяти для помощи средствам отладки памяти; если блок выделен и затем сразу освобожден, обе записи удалены из журнала, помогающего сократить размер журнала

MallocStackLoggingNoCompact

То же как MallocStackLogging, но сохраняет все записи в журнале

Средство выделения памяти по умолчанию также регистрирует сообщения, если оно обнаруживает определенные общие проблемы программирования. Например, при освобождении блока памяти дважды или свободной памяти, которую Вы никогда не выделяли, free может распечатать сообщение, показанное в Перечислении 7. Число в круглых скобках является процессом ID.

Перечисление 7  общее сообщение, распечатанное свободным

DummyPhone(1839) malloc: *** error for object 0x826600: double free *** set a breakpoint in malloc_error_break to debug

Можно отладить этот вид проблемы путем выполнения программы в GDB и надевания точки останова malloc_error_break. Как только Вы поражаете точку останова, можно использовать GDB's backtrace команда для определения непосредственной вызывающей стороны.

Наконец, можно программно проверить непротиворечивость «кучи» с помощью malloc_zone_check подпрограмма (от <malloc.h>).

Стандартная библиотека C++

Стандартная библиотека C++ поддерживает много функций отладки:

  • Установите _GLIBCXX_DEBUG переменная времени компиляции для включения режима отладки в стандартной библиотеке C++.

    Этот элемент не поддерживается в GCC 4.2 или позже, и Вы не должны использовать его в том контексте.

  • В версиях до GCC 4.0, набор GLIBCPP_FORCE_NEW переменная окружения к 1 для отключения памяти, кэширующейся в стандартной библиотеке C++. Это позволяет Вам отлаживать свои выделения C++ с другими средствами отладки памяти.

    В GCC 4.0 и позже это - поведение по умолчанию.

Динамический компоновщик (dyld)

Динамический компоновщик (dyld) поддерживает много средств отладки, которые можно включить через переменные окружения. Они полностью документируются в страницу руководства. Таблица 6 перечисляет некоторые более полезные переменные.

Таблица 6  Динамические переменные окружения компоновщика

Переменная

Сводка

DYLD_IMAGE_SUFFIX

Поиск библиотек с этим суффиксом сначала

DYLD_PRINT_LIBRARIES

Загрузки библиотеки журнала

DYLD_PRINT_LIBRARIES_POST_LAUNCH

Как выше, но только после того, как основное работало

DYLD_PRINT_OPTS [1]

Распечатайте разовые запуском параметры командной строки

DYLD_PRINT_ENV [1]

Распечатайте разовые запуском переменные окружения

DYLD_PRINT_APIS [1]

Зарегистрируйте dyld вызовы API (например, dlopen)

DYLD_PRINT_BINDINGS [1]

Привязка символа журнала

DYLD_PRINT_INITIALIZERS [1]

Вызовы инициализации изображения журнала

DYLD_PRINT_SEGMENTS [1]

Отображение сегмента журнала

DYLD_PRINT_STATISTICS [1]

Статистика производительности запуска печати

Примечания:

  1. На стороне Mac OS X они только доступны на Mac OS X 10.4 и позже. Они, однако, доступны на всех версиях iOS.

В то время как эти переменные окружения реализованы на iOS, многие из них ограничили утилиту из-за ограниченной среды в той системе.

Core Services

Базовая основа

Платформа Core Foundation (CF) экспортирует CFShow подпрограмма, распечатывающая описание любого объекта CF к stderr. Можно выполнить этот вызов от собственного кода, однако, это особенно полезно, когда вызвано от GDB. Перечисление 8 показывает пример этого.

Перечисление 8  вызывая CFShow от GDB

$ gdb /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) fb CFRunLoopAddSource
Breakpoint 1 at 0x624dd2f195cfa8
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
Reading symbols for shared libraries […]
Breakpoint 1, 0x00007fff8609bfa8 in CFRunLoopAddSource ()
(gdb) # Check that the prologue hasn't changed $rdi.
(gdb) p/a 0x00007fff8609bfa8
$1 = 0x7fff8609bfa8 <CFRunLoopAddSource+24>
(gdb) p/a $pc
$2 = 0x7fff8609bfa8 <CFRunLoopAddSource+24>
(gdb) x/8i $pc-24
0x7fff8609bf90 <CFRunLoopAddSource>: push   %rbp
0x7fff8609bf91 <CFRunLoopAddSource+1>: mov    %rsp,%rbp
0x7fff8609bf94 <CFRunLoopAddSource+4>: mov    %rbx,-0x20(%rbp)
0x7fff8609bf98 <CFRunLoopAddSource+8>: mov    %r12,-0x18(%rbp)
0x7fff8609bf9c <CFRunLoopAddSource+12>: mov    %r13,-0x10(%rbp)
0x7fff8609bfa0 <CFRunLoopAddSource+16>: mov    %r14,-0x8(%rbp)
0x7fff8609bfa4 <CFRunLoopAddSource+20>: sub    $0x40,%rsp
0x7fff8609bfa8 <CFRunLoopAddSource+24>: mov    %rdi,%r12
(gdb) # Nope. Go ahead and CFShow it.
(gdb) call (void)CFShow($rdi)
<CFRunLoop 0x100115540 [0x7fff70b8bf20]>{
    locked = false, 
    wakeup port = 0x1e07, 
    stopped = false,
    current mode = (none),
    common modes = <CFBasicHash 0x1001155a0 [0x7fff70b8bf20]>{
        type = mutable set, 
        count = 1,
        entries =>
            2 : <CFString 0x7fff70b693d0 [0x7fff70b8bf20]>{
                contents = "kCFRunLoopDefaultMode"
            }
    },
    common mode items = (null),
    modes = <CFBasicHash 0x1001155d0 [0x7fff70b8bf20]>{
        type = mutable set, 
        count = 1,
        entries =>
            0 : <CFRunLoopMode 0x100115670 [0x7fff70b8bf20]>{
                name = kCFRunLoopDefaultMode, 
                locked = false, 
                port set = 0x1f03,
                sources = (null),
                observers = (null),
                timers = (null)
        },
    }
}

Существует много других подпрограмм CF, которые Вы могли бы счесть полезным для вызова от GDB, включая CFGetRetainCount, CFBundleGetMainBundle, и CFRunLoopGetCurrent.

Зомби!

Базовая Основа поддерживает вызванную переменную окружения CFZombieLevel. Это интерпретирует эту переменную как целое число, содержащее ряд флаговых битов. Таблица 7 описывает в настоящее время определяющиеся биты. Они могут помочь Вам разыскать различные проблемы управления памятью CF.

Табличные  определения 7 битов для переменной окружения CFZombieLevel

Бит

Действие

0

набросайте освободил память CF

1

когда набросок освободил память CF, не набрасывайте объектный заголовок (CFRuntimeBase)

4

никогда свободная память раньше не содержала объекты CF

7

если установлено, освобождение каракулей с помощью битов 8.. 15, иначе используйте 0xFC

8.. 15

если бит 7 установлен, освобождение каракулей с помощью этого значения

16

набросайте выделил память CF

23

если установлено, выделения каракулей с помощью битов 24.. 31, иначе используйте 0xCF

24.. 31

если бит 23 установлен, выделения каракулей с помощью этого значения

Прикладные службы

Базовая анимация

Базовый инструмент Анимации позволяет Вам измерить частоту кадров своего приложения и видеть различные типы получения. См. Инструментальное Руководство пользователя для подробных данных.

Какао и касание какао

Все объекты Какао (все произошло из NSObject) поддержка a description метод, возвращающийся NSString описание объекта. Наиболее удобный способ получить доступ к этому описанию через Описание Печати XCode к команде меню Console. Также, если Вы - наркоман командной строки, можно использовать GDB's print-object (или po если коротко) команда, как проиллюстрировано Перечислением 9.

Перечисление 9  Используя почтовую команду GDB

$ gdb /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) fb -[NSCFDictionary copyWithZone:]
Breakpoint 1 at 0x83126e97675259
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
Reading symbols for shared libraries […]
Breakpoint 1, 0x00007fff837aa259 in -[NSCFDictionary copyWithZone:] ()
(gdb) po $rdi
{
    AddExtensionToNewPlainTextFiles = 1;
    AutosaveDelay = 30;
    CheckGrammarWithSpelling = 0;
    CheckSpellingWhileTyping = 1;
    […]
}

Objective C

Для повреждения на исключении Objective C, независимо от того, как это брошено, устанавливает символьную точку останова на objc_exception_throw. Самый простой способ установить такую точку останова с Остановкой XCode на команде меню Objective-C Exceptions.

Отладка Objective C уровня ассемблера

При отладке кода Какао на уровне ассемблера имейте в виду следующие функции времени выполнения Objective C:

  • Компилятор Objective C добавляет два неявных параметра к каждому методу, первым из которых является указатель на вызываемый объект (self).

  • Второй неявный параметр является селектором метода (_cmd). В Objective C это имеет тип SEL; в GDB можно распечатать это как струну до.

  • Время выполнения Objective C диспетчеризирует методы через семью функции C. Обычно замеченный objc_msgSend, но некоторое использование архитектуры objc_msgSend_stret для методов, который возвращает структуры и некоторое использование архитектуры objc_msgSend_fpret для методов тот возврат значения с плавающей точкой. Существуют также эквивалентные функции для вызова super (objc_msgSendSuper и т.д.).

  • Первое слово любого объекта Objective C ( isa поле), указатель на класс объекта.

Таблица 8 является сводкой того, как получить доступ self и _cmd от GDB, если Вы остановились в первой инструкции метода. Для получения дополнительной информации об этом, посмотрите Некоторый Требуемый блок.

Табличный 8  Доступ сам и _cmd

Архитектура

сам

_cmd

ARM

$r0

$r1

32-разрядный Intel

* (интервал*)($esp+4)

* (интервал*)($esp+8)

Перечисление 10 показывает пример того, как использовать эту информацию от GDB.

  Время выполнения Objective C перечисления 10 'секреты'

$ gdb /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) # Give the runtime a chance to start up.
(gdb) fb NSApplicationMain
Breakpoint 1 at 0x9374bc69df7307
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
Reading symbols for shared libraries […]
Breakpoint 1, 0x00007fff841a0307 in NSApplicationMain ()
(gdb) # Set a breakpoint on -retain.
(gdb) b *'-[NSObject(NSObject) retain]'
Breakpoint 2 at 0x7fff8608a860
(gdb) c
Continuing.

Breakpoint 2, 0x00007fff8608a860 in -[NSObject(NSObject) retain] ()
(gdb) # Hit the breakpoint; dump the first 4 words of the object
(gdb) x/4xg $rdi
0x1001138f0: 0x00007fff7055e6d8  0x0041002f01a00000
0x100113900: 0x0069006c00700070  0x0069007400610063
(gdb) # Now print the selector
(gdb) x/s $rsi
0x7fff848d73d8: "retain"
(gdb) # Want to 'po' object; must disable the breakpoint first
(gdb) dis
(gdb) po $rdi
/Applications/TextEdit.app
(gdb) # Print the 'isa' pointer, which is a Class object.
(gdb) po 0xa02d9740
NSPathStore2

При отладке без символов можно использовать функции со времени выполнения Objective C для помощи усилиям по отладке. Подпрограммы, показанные в Таблице 9, особенно полезны.

Таблица 9  Полезные функции времени выполнения Objective C

Функция

Сводка

id objc_getClass (символ константы *имя);

Получите Объект класса Objective C для данного имени класса

SEL sel_getUid (символ константы *str);

Получите Objective C SEL для данного имени метода

IMP class_getMethodImplementation (Класс cls, имя SEL);

Получите указатель на код, реализующий данный метод в данном классе

Перечисление 11 показывает пример отладки -[DocumentController openUntitledDocumentAndDisplay:error:] метод TextEdit, даже при том, что TextEdit не поставляет с символами.

Перечисление 11  Используя время выполнения Objective C для отладки без символов

$ gdb -arch x86_64 /Applications/TextEdit.app
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) […]
(gdb) r
Starting program: /Applications/TextEdit.app/Contents/MacOS/TextEdit 
Reading symbols for shared libraries […]
^C
(gdb) # Try to find the 
(gdb) # -[DocumentController openUntitledDocumentAndDisplay:error:] 
(gdb) # symbol.
(gdb) info func openUntitledDocumentAndDisplay
All functions matching regular expression "openUntitledDocumentAndDisplay":

Non-debugging symbols:
0x00007fff843ac083 -[NSDocumentController openUntitledDocumentAndDisplay:error:]
(gdb) # These are not the droids we're looking for. It turns out that 
(gdb) # TextEdit ships with its symbols stripped, so we'll have to do 
(gdb) # this the hard way.
(gdb) #
(gdb) # Get the Class object for the DocumentController class.
(gdb) set $class=(void *)objc_getClass("DocumentController")
(gdb) # Get the SEL object for the "openUntitledDocumentAndDisplay:error:" method.
(gdb) set $sel=(void *)sel_getUid("openUntitledDocumentAndDisplay:error:")
(gdb) # Get a pointer to the method implementation.
(gdb) call (void*)class_getMethodImplementation($class, $sel)
$1 = (void *) 0x100001966
(gdb) # Confirm that this is sensible. Looks like a method prologue to me.
(gdb) x/4i 0x00009aa5
0x100001966: push   %rbp
0x100001967: mov    %rsp,%rbp
0x10000196a: push   %r12
0x10000196c: push   %rbx
(gdb) # Set a breakpoint on the method.
(gdb) b *0x100001966
Breakpoint 1 at 0x100001966
(gdb) # Resume execution, and then create a new, untitled document.
(gdb) c
Continuing.
[…]
Breakpoint 1, 0x0000000100001966 in ?? ()
(gdb) # We've hit our breakpoint; print the parameters, starting with 
(gdb) # the implicit "self" and "SEL" parameters that are common to all 
(gdb) # methods, followed by the method-specific "display" and 
(gdb) # "error" parameters.
(gdb) po $rdi
<DocumentController: 0x100227a50>
(gdb) x/s $rsi
0x7fff848e4e04: "openUntitledDocumentAndDisplay:error:"
(gdb) p (int)$rdx
$2 = 1
(gdb) x/xg $rcx
0x7fff5fbff108: 0x00000001001238f0

Можно узнать больше о функциях времени выполнения Objective C и структурах данных путем просмотра заголовков в /usr/include/objc/.

Основа

Основа имеет много средств отладки, включенных переменными окружения. Таблица 10 выделяет самые интересные.

Табличные 10  переменные окружения Основы

Имя

Значение по умолчанию

Действие

NSZombieEnabled

НЕТ

Если установлено в ДА, освобожденные объекты 'зомбированы'; это позволяет Вам быстро отлаживать проблемы, куда Вы отправляете сообщение в уже освобожденный объект; посмотрите Больше Зомби! для подробных данных

NSDeallocateZombies

НЕТ

Если установлено в ДА, память для 'зомбированных' объектов фактически освобождена

NSUnbufferedIO

НЕТ

Если установлено в ДА, Основа будет использовать, освободил буфер I/O для stdout (stderr, освобожден буфер по умолчанию),

Сохраните количества

Можно использовать -retainCount для получения тока сохраняют количество объекта. В то время как это может иногда быть полезным средством отладки, быть очень тщательным при интерпретации результатов. Перечисление 12 показывает один потенциальный источник беспорядка.

  Запутывающее перечисление 12 сохраняет количество

(gdb) set $s=(void *)[NSClassFromString(@"NSString") string]
(gdb) p (int)[$s retainCount]
$4 = 2147483647
(gdb) p/x 2147483647
$5 = 0x7fffffff
(gdb) # The system maintains a set of singleton strings for commonly 
(gdb) # used values, like the empty string. The retain count for these 
(gdb) # strings is a special value indicating that the object can't be 
(gdb) # released.

Другой общий источник беспорядка является механизмом автовыпуска. Если объект был автовыпущен, сохранять количество выше, чем Вы могли бы иначе думать, факт, который это компенсируется пулом автовыпуска, выпускающим его в некоторый момент в будущем.

Можно определить, какие объекты находятся в том, что автовыпуск объединяет путем вызова _CFAutoreleasePoolPrintPools распечатать содержание всего автовыпуска объединяет на штабеле пула автовыпуска.

Перечисление 13  , Распечатывающее автовыпуск, объединяет штабель

(gdb) call (void)_CFAutoreleasePoolPrintPools()
- -- ---- -------- Autorelease Pools -------- ---- -- -
==== top of stack ================
  0x327890 (NSCFDictionary)
  0x32cf30 (NSCFNumber)
  […]
==== top of pool, 10 objects ================
  0x306160 (__NSArray0)
  0x127020 (NSEvent)
  0x127f60 (NSEvent)
==== top of pool, 3 objects ================
- -- ---- -------- ----------------- -------- ---- -- -

В более ранних системах можно использовать NSAutoreleasePoolCountForObject функция для определения, сколько раз объект был добавлен к пулу автовыпуска. Для примера см. Перечисление 14.

  Вызов перечисления 14 NSAutoreleasePoolCountForObject

(gdb) # d is an NSDictionary created with -[NSDictionary dictionaryWithObjectsAndKeys:].
(gdb) p d
$1 = (NSDictionary *) 0x12d620
(gdb) po d
{
    test = 12345;
}
(gdb) p (int)[d retainCount]
$2 = 1
(gdb) p (int)NSAutoreleasePoolCountForObject(d)
$3 = 1

Больше зомби!

Общий тип ошибки, когда программирование с Какао сверхвыпускает объект. Это обычно заставляет Ваше приложение отказывать, но катастрофический отказ происходит после того, как последний подсчет ссылок выпущен (когда Вы пытаетесь передать освобожденный объект), который обычно вполне удаляется из исходной ошибки. NSZombieEnabled Ваш лучший выбор для отладки этого вида проблем; это раскроет любую попытку взаимодействовать с освобожденным объектом.

Самый простой способ включить зомби через Инструменты. Однако можно также включить зомби через переменную окружения. Перечисление 15 показывает тип сообщения, которое Вы будете видеть в этом случае.

Перечисление 15  эффект NSZombie

$ NSZombieEnabled=YES build/Debug/DummyMac.app/Contents/MacOS/DummyMac  […] -[AppDelegate testAction:] […] *** -[CFNumber release]: message sent to deallocated instance 0x3737c0 Trace/BPT trap

Как показано в Перечислении 15, система выполнит команду контрольного останова, если это обнаружит зомби. Если Вы будете работать под GDB, то программа остановится, и можно смотреть на след для наблюдения цепочки вызовов, инициировавших детектор зомби.

NSZombieEnabled также объекты Основы Ядра влияния, а также объекты Objective C. Однако Вы будете только уведомлены при доступе к объекту Основы Ядра зомби от Objective C, не при доступе к нему через CF API.

Другое волшебство основы

Если Вы используете Наблюдение Значения ключа, и Вы хотите знать, кто наблюдает то, что на определенном объекте, можно получить информацию наблюдения для того объекта и распечатать его использование с помощью объектной команды печати GDB. Перечисление 16 показывает пример этого.

  Наблюдатели значения ключа Дисплея перечисления 16

(gdb) # self is some Objective-C object.
(gdb) po self
<ZoneInfoManager: 0x48340d0>
(gdb) # Let's see who's observing what key paths.
(gdb) po [self observationInfo]
<NSKeyValueObservationInfo 0x48702d0> (
<NSKeyValueObservance 0x4825490: Observer: 0x48436e0, \
Key path: zones, Options: <New: NO, Old: NO, Prior: NO> \
Context: 0x0, Property: 0x483a820>
)

Можно установить NSShowNonLocalizedStrings предпочтение к строкам поиска, которые должны были быть локализованы, но не были. После того, как включенный, если Вы запрашиваете, локализованная строка и строка не найдены в строковом файле, система возвратит капитализируемую строку и зарегистрирует сообщение к консоли. Это - отличный способ раскрыть проблемы с устаревшими локализациями.

Эта установка влияние NSLocalizedString, все ее варианты и вся базовая инфраструктура включая CFCopyLocalizedString.

UIKit

UIView реализует полезное description метод. Кроме того, это реализует a recursiveDescription метод, который можно вызвать для получения сводки всей иерархии представления.

  Описание UIView's перечисления 17 и recursiveDescription методы

-- The following assumes that you're stopped in some view controller method.
(gdb) po [self view]
<UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
Current language: auto; currently objective-c
(gdb) po [[self view] recursiveDescription]
<UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
   | <UIRoundedRectButton: 0x6a103e0; frame = (124 196; 72 37); opaque = NO; […]
   |    | <UIButtonLabel: 0x6a117b0; frame = (19 8; 34 21); text = 'Test'; […]

Сети

Самый критический инструмент для отладки сетевого кода является трассировкой пакетов. Технический QA1176 Вопросов и ответов, 'Получение Трассировки пакетов' обсуждает, как получить трассировку пакетов на Mac OS X.

В то время как iOS не имеет никаких встроенных пакетных средств трассировки, можно часто делать полезные успехи путем взятия трассировки пакетов на соседнем Mac.

Питание

Энергетический инструмент Диагностики является отличным способом понять, как Ваше приложение влияет на время работы от батареи. См. Инструментальное Руководство пользователя для подробных данных.

Продвиньте уведомления

Техническое примечание TN2265, 'Диагностируя Уведомления Нажатия' является подробным руководством по отладке проблем уведомления нажатия. Это также включает профиль конфигурации (APNsLogging.mobileconfig) то, что можно установить для включения дополнительного журналирования отладки.

Средство моделирования iPhone

Средство моделирования iPhone позволяет Вашей сборке и выполняет Ваш код iOS Mac OS X. Поскольку Средство моделирования iPhone является приложением Mac OS X, можно предназначаться для него со множеством Mac OS X определенные средства отладки. Например, если у Вас есть проблема, которая может только быть решена DTrace (который не доступен на iOS), можно запустить приложение в средстве моделирования и указать на DTrace на это.

Для получения дополнительной информации о средствах отладки Mac OS X, посмотрите Техническое примечание TN2124, 'Волшебство Отладки Mac OS X'.



История версии документа


ДатаПримечания
22.01.2011

Новый документ, описывающий большое количество полезных советов отладки iOS.