Волшебство Отладки iOS
iOS содержит много 'секретных' средств отладки, включая переменные окружения, предпочтения, подпрограммы, вызываемые от GDB, и т.д. Этот technote описывает эти средства. Если Вы разрабатываете для iOS, необходимо просмотреть этот список, чтобы видеть, пропускаете ли Вы что-то, что сделает Вашу жизнь проще.
Введение
Все системы Apple включают средства отладки, добавленные командами инженеров Apple, чтобы помочь разработать и отладить определенные подсистемы. Многие из этих средств остаются в выпущенном системном программном обеспечении, и можно использовать их для отладки кода. Этот technote описывает некоторые более широко полезные. В случаях, где средство отладки документируется в другое место, существует краткий обзор средства и ссылки к существующей документации.
Многие подробные данные, покрытые этим technote, варьируются от платформы до платформы и выпуска к выпуску. Также, можно встретиться с незначительными изменениями между платформами, и в более старых или более новых системах. Известные значительные изменения вызываются в тексте.
Этот technote был записан в отношении iOS 4.1.
В дополнение к этой технической проблеме совместимости на уровне двоичных кодов имейте в виду, что приложения для iOS должны соответствовать различным юридическим соглашениям и Инструкциям по Анализу App Store.
Этот technote покрытия усовершенствовал отладку методов. Если Вы просто начинаете, необходимо консультироваться со следующим материалом:
GDB является основным средством отладки системы. Для полного описания GDB посмотрите Отладку с GDB.
XCode является интегрированной средой разработки (IDE) Apple. Это включает сложный графический отладчик, реализованный как обертка вокруг GDB. Для получения дополнительной информации о XCode, посмотрите раздел «Tools & Languages» Ссылочной Библиотеки разработчика Apple.
Этот technote не покрывает отладку производительности. При попытке отладить проблему производительности, лучшее место для запуска является Началом работы с документом Производительности.
Основы
Более поздние разделы этого technote описывают средства отладки подробно. Многие из этих средств используют подобные методы, чтобы включить и отключить средство и видеть его вывод. В этом разделе описываются эти общие методы.
Включение средств отладки
Некоторые средства отладки включены по умолчанию. Однако большинство средств должно быть включено с помощью одного из методов, описанных в следующих разделах.
Переменные окружения
Во многих случаях можно включить средство отладки путем установки определенной переменной окружения. Можно сделать это использование исполнимого инспектора в XCode. Рисунок 1 показывает пример этого.
Предпочтения
Некоторые средства отладки включены путем установки специального предпочтения. Можно установить такое предпочтение отладки путем конфигурирования параметра командной строки в XCode. Рисунок 2 показывает, как это сделано.
Вызываемые подпрограммы
Много системных платформ включают подпрограммы та отладочная информация печати в 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, когда Вы остановились в первой инструкции функции.
Что | Синтаксис 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.
Что | Синтаксис GDB |
---|---|
обратный адрес | * (интервал *) $esp |
первый параметр | * (интервал*)($esp+4) |
второй параметр | * (интервал*)($esp+8) |
... и т.д. |
|
После вводной части подпрограммы можно получить доступ к параметрам относительно указателя кадра (зарегистрируйте EBP). Таблица 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.
Размер | 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 перечисляет некоторые более полезные.
Переменная | Сводка |
---|---|
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 перечисляет некоторые более полезные переменные.
Переменная | Сводка |
---|---|
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] | Статистика производительности запуска печати |
Примечания:
На стороне 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.
Бит | Действие |
---|---|
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, если Вы остановились в первой инструкции метода. Для получения дополнительной информации об этом, посмотрите Некоторый Требуемый блок.
Архитектура | сам | _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, особенно полезны.
Функция | Сводка |
---|---|
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 выделяет самые интересные.
Имя | Значение по умолчанию | Действие |
---|---|---|
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. |