Xcode / LLDB: как получить информацию об только что сгенерированном исключении?


84

Хорошо, представьте, что моя точка останова objc_exception_throwтолько что сработала. Я сижу в приглашении отладчика и хочу получить дополнительную информацию об объекте исключения. Где мне это найти?


2
Помните, что исключение только что возникло, его описание еще не напечатано на консоли.
Karoy Lorentey

Проверьте этот вопрос: stackoverflow.com/questions/711650
Николай Фетисов

Ответы:


163

Объект исключения передается в качестве первого аргумента objc_exception_throw. LLDB предоставляет $arg1.. $argnпеременные для ссылки на аргументы в правильном соглашении о вызовах, что упрощает распечатку деталей исключения:

(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]

Перед objc_exception_throwвыполнением этих команд обязательно выберите фрейм в стеке вызовов. См. «Расширенная отладка и средство очистки адресов» в видеороликах сеанса WWDC15, чтобы увидеть, как это выполняется на сцене.

Устаревшая информация

Если вы используете GDB, синтаксис для ссылки на первый аргумент зависит от соглашений о вызовах архитектуры, в которой вы работаете. Если вы выполняете отладку на реальном устройстве iOS, указатель на объект находится в регистре r0. Чтобы распечатать его или отправить ему сообщение, используйте следующий простой синтаксис:

(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]

В iPhone Simulator все аргументы функции передаются в стек, поэтому синтаксис значительно ужаснее. Самое короткое выражение, которое я смог построить, это *(id *)($ebp + 8). Чтобы сделать вещи менее болезненными, я предлагаю использовать вспомогательную переменную:

(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]

Вы также можете установить $exceptionавтоматически всякий раз, когда срабатывает точка останова, добавив список команд к objc_exception_throwточке останова.

(Обратите внимание , что во всех случаях , которые я тестировал, объект исключения также присутствовал в eaxи edxреестрах в момент точки останова попадании. Я не уверен , что будет всегда так, хотя.)

Добавлено из комментария ниже:

В lldb выберите кадр стека для objc_exception_throwи затем введите эту команду:

(lldb) po *(id *)($esp + 4)

6
Как это сделать в lldb? Я получаю сообщение об ошибке «ошибка: ссылка на id неоднозначна»
offex

2
вы можете указать источник этой информации? Я хотел бы узнать об этом больше
Жуан Нуньес

3
В настоящее время следующих работ для меня до пролога , когда breaing на objc_exception_throw в LLDB : po *(id *)($esp + 4).
wbyoung

7
Это сработало! Однако это не сработало, пока я не выбрал кадр стека 0 . ( objc_exception_throw).
funroll

7
po $eaxу меня работает в симуляторе как привязка к $r0устройству.
monkeydom

11

на новых симуляторах (iOS 8, 64bit) xcode 6 im, используя в кадре исключения: objc_exception_throw

po $rax

в 32-битной версии:

po $eax

Что такое rax?

Rax - это 64-битный регистр, который заменяет старый eax

Как найти все регистры?

register read

Источник википедия


Хм ... В Xcode 6.1 я получаю: (lldb) po $ rax error: Не удалось материализовать: не удалось прочитать значение регистра rax.
Ошибка

Симулятор или устройство @bradheintz? Я пробовал это с 6.0.1
Жуан Нуньес,

Не могли бы вы указать ссылку на свой источник для этого? Благодаря!
Крис Коновер,

Я просто написал в lldb: регистр читать. Затем с помощью этой информации мы знаем, что первый регистр в кадре исключения содержит сообщение об исключении.
Жуан Нуньес,

Хорошо, я нашел несколько документов: rax - это 64-битный регистр: в 64-битном длинном режиме вы можете использовать 64-битные регистры (например, rax вместо eax, rbx вместо ebx и т. Д.)
João Nunes

6

На момент написания этой статьи я стал самым популярным в Google по: lldb print exception . Таким образом, я добавляю этот ответ для учета lldb и x86_64.

Мои попытки найти исключение с помощью po $eaxне удались error: Couldn't materialize struct: Couldn't read eax (materialize). Другие попытки, описанные в связанных документах из более ранних ответов, также потерпели неудачу.

Ключевым моментом было то, что мне нужно было сначала щелкнуть objc_exception_throwфрейм в основном потоке. lldb не запускается в этом кадре.

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


1

Если у вас есть инструкция catch, поставьте там точку останова, и вы сможете проверить объект исключения в этой точке.

Если у вас нет оператора catch, продолжайте.

В терминале вы получите такое сообщение:

Завершение работы приложения из-за неперехваченного исключения «NSInvalidArgumentException», причина: « * - [__ NSPlaceholderDictionary initWithObjects: forKeys: count:]: попытка вставить нулевой объект из объектов [0]»

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

Похоже, что ответ Фнорда лучше всего, но мне не удалось заставить его работать в LLDB.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.