OK, alors imaginez que mon point d'arrêt dans objc_exception_throw
vient de se déclencher. Je suis assis à l'invite du débogueur et je veux obtenir plus d'informations sur l'objet d'exception. Où le trouve-t-on?
L'objet d'exception est transmis comme premier argument à objc_exception_throw
. LLDB fournit des variables $arg1
.. $argn
Pour faire référence aux arguments dans la convention d'appel correcte, ce qui simplifie l'impression des détails de l'exception:
(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]
Assurez-vous de sélectionner le cadre objc_exception_throw
Dans la pile d'appels avant d'exécuter ces commandes. Voir le "Débogage avancé et l'assainisseur d'adresses" dans les vidéos de la session WWDC15 pour voir cela effectué sur scène.
Informations obsolètes
Si vous êtes sur GDB, la syntaxe pour faire référence au premier argument dépend des conventions d'appel de l'architecture sur laquelle vous travaillez. Si vous déboguez sur un appareil iOS réel, le pointeur vers l'objet est dans le registre r0
. Pour l'imprimer ou lui envoyer des messages, utilisez la syntaxe simple suivante:
(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]
Sur iPhone Simulator, tous les arguments de fonction sont passés sur la pile, la syntaxe est donc beaucoup plus horrible. L'expression la plus courte que j'ai pu construire qui y parvienne est *(id *)($ebp + 8)
. Pour rendre les choses moins douloureuses, je suggère d'utiliser une variable de commodité:
(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]
Vous pouvez également définir $exception
Automatiquement chaque fois que le point d'arrêt est déclenché en ajoutant une liste de commandes au point d'arrêt objc_exception_throw
.
(Notez que dans tous les cas que j'ai testés, l'objet exception était également présent dans les registres eax
et edx
au moment où le point d'arrêt a frappé. Je ne suis pas sûr que ce sera toujours le cas , bien que.)
Ajouté à partir du commentaire ci-dessous:
Dans lldb, sélectionnez le cadre de pile pour objc_exception_throw
, Puis entrez cette commande:
(lldb) po *(id *)($esp + 4)
sur les nouveaux simulateurs (iOS 8, 64 bits) xcode 6 im utilisant dans le cadre d'exception: objc_exception_throw
po $rax
en 32 bits:
po $eax
Qu'est-ce que le rax?
Rax est un registre 64 bits qui remplace l'ancien eax
Comment trouver tous les registres?
register read
Au moment d'écrire ces lignes, ce message est mon meilleur hit Google pour: lldb print exception. Ainsi, j'ajoute cette réponse pour tenir compte de lldb et x86_64.
Mes tentatives pour trouver l'exception à l'aide de po $eax
Ont échoué avec error: Couldn't materialize struct: Couldn't read eax (materialize)
. D'autres tentatives décrites dans les documents liés des réponses précédentes ont également échoué.
La clé était que je devais d'abord cliquer sur le cadre objc_exception_throw
Dans mon fil principal. lldb ne démarre pas dans ce cadre.
Dans tous mes exemples de recherche et suivants, cette entrée de blog a été le premier à expliquer les choses d'une manière qui a fonctionné pour moi. Il est plus moderne, publié en août 2012.
Si vous avez une instruction catch, placez-y un point d'arrêt et vous pouvez inspecter l'objet d'exception à ce stade.
Si vous n'avez pas de déclaration catch, continuez.
Vous recevrez un message dans votre terminal comme ceci:
Arrêt de l'application en raison d'une exception non interceptée 'NSInvalidArgumentException', raison: ' * - [__ NSPlaceholderDictionary initWithObjects: forKeys: count:]: tentative d'insertion d'un objet nul à partir d'objets [0] "
Cependant, vous cherchez probablement un moyen de l'inspecter sans continuer car vous perdrez votre trace de pile Nice lorsque l'application sera fermée.
Pour cela, il semble que la réponse de Fnord soit la meilleure, mais je n'ai pas pu le faire fonctionner en LLDB.