Bricoler le Mode Dieu avec le Débogueur
Autres langues : English Español Deutsch 日本語 한국어 Português 中文
À l’époque, les jeux vidéo étaient différents. Non seulement leurs graphismes et leurs mécaniques ont évolué, mais ils avaient aussi une caractéristique qui ne semble pas très courante dans les jeux d’aujourd’hui : les codes de triche.
Les codes de triche étaient des séquences de touches qui vous donnaient quelque chose d’extraordinaire, comme des munitions infinies ou la capacité de traverser les murs. Le plus commun et puissant d’entre eux était le “god mode”, qui vous rendait invincible.

Voici à quoi ressemblait votre personnage dans Doom
lorsque vous entriez IDDQD
- la combinaison de touches pour le god mode. En fait, cette séquence de touches particulière était si populaire qu’elle est devenue un mème et a gagné en popularité au-delà du jeu lui-même.
Bien que le god mode ne soit plus aussi répandu dans les jeux qu’il l’était autrefois, et que l’ère du mème IDDQD semble s’estomper, on pourrait se demander s’il existe un équivalent contemporain. Personnellement, j’ai ma propre version moderne d’IDDQD - le débogueur. Bien qu’il ne soit pas nécessairement lié aux jeux, il évoque le même sentiment d’avoir des super-pouvoirs.
Space Invaders
Voici un petit scénario amusant pour illustrer mon propos. Même si vous n’êtes pas familier avec Doom, vous avez probablement vu ce jeu encore plus ancien appelé Space Invaders. Comme Doom, son intrigue est centrée sur le thème de combattre des envahisseurs dans l’espace.
Mon ami et collègue Eugene Nizienko a écrit un plugin IntelliJ IDEA qui vous permet de jouer à ce jeu directement dans l’éditeur
- un excellent moyen de passer du temps en attendant que l’indexation se termine.
Il n’y a pas de god mode dans ce jeu, mais si nous sommes très déterminés, pouvons-nous l’ajouter nous-mêmes ? Ressuscitons la tradition classique de pirater des programmes avec un débogueur pour le découvrir !
Soyez responsable ! J’ai obtenu le consentement d’Eugene avant de manipuler son programme. Si vous utilisez le débogueur sur du code qui ne vous appartient pas, assurez-vous de le faire de manière éthique. Sinon, ne le faites simplement pas.
Préparer les outils
Préparez-vous pour une expérience méta - nous allons déboguer IntelliJ IDEA en utilisant son propre débogueur.
Mais il y a un petit problème : lors du débogage d’IntelliJ IDEA, nous devons le suspendre, ce qui rendra l’IDE non réactif. Par conséquent, nous avons besoin d’une instance d’IDE supplémentaire qui restera fonctionnelle et servira d’outil de débogage.
Pour gérer plusieurs instances d’IDE, j’utiliserai JetBrains Toolbox App. C’est une application qui organise vos IDE JetBrains. Avec elle, vous pouvez installer plusieurs versions du même IDE ou créer des raccourcis pour les exécuter avec différents ensembles d’options VM.
Installons deux instances d’IntelliJ IDEA :
Si vous utilisez la même version de l’IDE pour les deux instances, assurez-vous de spécifier des répertoires système, config et logs différents dans Tool actions | Settings | Configuration. Sur cette page, vous pouvez également attribuer des noms aux instances de l’IDE pour plus de commodité. Je les ai nommées ‘Space Invaders’ et ‘Debug’.
Pour pouvoir déboguer l’instance Space Invaders, cliquez sur Tool actions à côté d’elle, puis allez dans Settings | Edit JVM options. Dans le fichier qui s’ouvre, collez la ligne suivante :
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
Cela fera que la JVM cible s’exécutera avec l’agent de débogage et écoutera les connexions de débogueur entrantes
sur le port 5005.
Lancer le jeu
Lancez l’instance ‘Space Invaders’, installez le jeu,
et lancez-le en exécutant l’action Space Invaders.
Pour trouver l’action, appuyez deux fois sur Shift et commencez à taper Space Invaders :
Jouons un moment et observons le comportement que nous voulons corriger : lorsque les missiles ennemis touchent le vaisseau spatial, nous perdons de la santé de la barre de vie dans le coin supérieur gauche.
Attacher et suspendre
Notre aventure de débogage commence par l’ouverture de l’instance ‘Debug’ de l’IDE et la création d’un nouveau projet Kotlin. Nous avons besoin de ce projet principalement parce qu’il ne serait pas possible de lancer le débogueur sans un.
De plus, IntelliJ IDEA inclut la bibliothèque standard Java/Kotlin dans le modèle de nouveau projet, que nous pourrions utiliser plus tard. J’expliquerai l’utilisation de la bibliothèque standard dans les chapitres suivants.
Après avoir créé le projet, allez dans le menu principal et sélectionnez Run | Attach to Process. Cela affichera la liste des JVM locales qui écoutent les demandes d’attachement de débogueur. Sélectionnez l’autre IDE en cours d’exécution dans la liste.
Nous devrions voir le message suivant dans la console confirmant que le débogueur s’est attaché avec succès à la VM cible.
Connected to the target VM, address: 'localhost:5005', transport: 'socket'
Maintenant, nous arrivons à la partie intéressante : comment suspendre l’application ?
Typiquement, nous définirions un point d’arrêt dans le code de l’application, mais dans ce cas, nous n’avons pas les sources pour IntelliJ IDEA ni pour le plugin Space Invaders. Non seulement cela nous empêche de définir un point d’arrêt, mais cela complique également notre compréhension du fonctionnement du programme. À première vue, il semble qu’il n’y ait rien à inspecter ou à parcourir pas à pas.
Heureusement pour nous, IntelliJ IDEA a une fonctionnalité appelée Pause Program. Elle vous permet de suspendre le programme à un moment arbitraire dans le temps, sans avoir besoin de spécifier la ligne de code correspondante. Vous pouvez la trouver dans la barre d’outils du débogueur ou dans le menu principal : Run | Debugging Actions | Pause Program.
L’application est suspendue, nous donnant un point de départ pour le débogage.
Pause Program est une technique très puissante, qui est particulièrement utile dans plusieurs scénarios avancés. Pour en savoir plus, consultez les articles connexes :
Trouver les objets pertinents
Si nous regardons notre objectif en termes de programmation, cela revient à empêcher la santé du vaisseau spatial de diminuer. Trouvons l’objet qui contient l’état correspondant.
Puisque nous ne savons rien du code du plugin, nous pouvons inspecter directement le tas en utilisant la vue Memory du débogueur d’IntelliJ IDEA :
Cette fonctionnalité vous donne des informations sur tous les objets qui sont actuellement vivants.
Tapons invaders et voyons si nous pouvons trouver quelque chose :
Apparemment, les classes du plugin sont dans le package com.github.nizienko.spaceinvaders .
Dans ce package, il y a une classe appelée GameState , qui a plusieurs instances vivantes.
À première vue, cela ressemble à ce dont nous avons besoin.
Double-cliquer sur GameState affiche toutes les instances de cette classe :
Il s’avère que c’est une enum - ce qui n’est pas exactement ce que nous cherchions.
En continuant notre recherche, nous tombons sur une seule instance de Game .
Développer le nœud nous permet d’inspecter les champs de l’instance :
La propriété health semble être celle qui nous intéresse ici.
Parmi ses champs, nous en trouvons un appelé _value .
Dans mon cas, la valeur est 100 , ce qui correspond au fait que
la barre de vie était pleine lorsque j’ai suspendu le jeu.
C’est donc probablement le bon champ à considérer, et sa valeur semble aller de 0 à 100 .
Mettons cette hypothèse à l’épreuve.
Faites un clic droit sur _value , puis sélectionnez Set Value.
Choisissez une valeur différente de votre valeur actuelle. Par exemple, je choisirai 50 .
À cette étape, nous rencontrons une erreur qui indique Cannot evaluate methods after Pause action :
Le problème survient parce que nous avons utilisé Pause Program au lieu de points d’arrêt, et cette fonctionnalité vient avec certaines limitations. Cependant, nous pouvons utiliser une petite astuce pour contourner cela.
Je l’ai décrite dans un de mes articles précédents couvrant l’action Pause Program. Au cas où vous l’auriez manqué, voici ce qu’il faut faire : une fois l’application en pause, effectuez une action de pas à pas, comme Step Into ou Step Over. Faire cela permettra l’utilisation de fonctionnalités avancées comme Set Value et Evaluate Expression.
Maintenant, nous devrions pouvoir définir la valeur pour health .
Essayez de modifier la valeur, puis reprenez l’application pour voir si la barre de vie affiche des changements.
Nous avons donc localisé l’objet qui contient l’état pertinent. Au minimum, nous pouvons recharger manuellement la barre de vie de temps en temps. Nous ne sommes peut-être pas encore complètement tirés d’affaire, mais nous y arrivons.
Étiquettes et expressions
Maintenant que nous avons identifié l’objet sur lequel nous concentrer, il serait pratique de le marquer. Au cas où vous ne seriez pas familier avec les étiquettes de débogage, voici à quoi ressemble un objet marqué :
Les étiquettes peuvent être bénéfiques de nombreuses façons. Dans le contexte de cet article, marquer l’objet pertinent garantit que nous pouvons l’utiliser directement dans des fonctionnalités comme Evaluate Expression sans dépendre du contexte d’exécution actuel.
Malheureusement, il n’est pas possible de marquer directement _value , mais nous pouvons marquer l’objet qui l’englobe.
Pour cela, faites un clic droit sur health , sélectionnez Mark Object, puis donnez-lui un nom.
Nous pouvons maintenant tester comment l’étiquette fonctionne ailleurs.
Ouvrez la boîte de dialogue Evaluate Expression
et entrez health_object_DebugLabel comme expression.
Comme vous pouvez le voir, l’objet est accessible de n’importe où dans le programme via la boîte de dialogue Evaluate :
Qu’en est-il de changer la santé du vaisseau spatial depuis Evaluate ?
health_object_DebugLabel._value = 100 ne fonctionne pas.
En même temps, _value semble être un champ de support d’une propriété Kotlin.
Si c’est vrai, Kotlin a dû générer un getter correspondant :
health_object_DebugLabel.getValue()
La boîte de dialogue Evaluate ne pense pas que c’est du code valide, mais essayons quand même :
L’expression retourne la santé actuelle du vaisseau spatial, donc cette approche fonctionne ! Comme vous vous y attendez, le setter fonctionne aussi :
health_object_DebugLabel.setValue(100)
Après avoir évalué le setter, reprenons l’application et vérifions que les changements ont pris effet. Oui - je vois une barre de vie pleine !
Accrocher l’expression
La seule étape restante pour atteindre notre objectif est d’automatiser la modification de l’état pour que le rechargement de la santé se fasse en arrière-plan, nous laissant profiter du gameplay sans interruptions.
Cela peut être fait en utilisant des points d’arrêt non suspensifs. Ce type de point d’arrêt est couramment utilisé pour la journalisation ; cependant, l’expression de journalisation n’a pas nécessairement besoin d’être pure. Par conséquent, nous pouvons introduire l’effet de bord souhaité dans l’expression de journalisation. Mais sans le code source, il semble que nous n’ayons nulle part où définir ce point d’arrêt.
Vous vous souvenez quand j’ai dit que nous pourrions utiliser les sources de la bibliothèque standard Java/Kotlin ? Voici l’idée : IntelliJ IDEA et ses plugins sont écrits en Java/Kotlin, et ils utilisent Swing comme framework d’interface utilisateur. Par conséquent, Space Invaders appelle certainement du code de ces dépendances. Cela signifie que nous pouvons utiliser leurs sources pour définir des points d’arrêt.
Par souci de simplicité, nous n’avons pas spécifié de version du JDK. Au lieu de cela, nous avons initialisé le projet avec la version suggérée par IntelliJ IDEA. Cependant, pour de meilleurs résultats, nous recommandons d’utiliser des sources qui correspondent étroitement à la version utilisée pour exécuter votre programme.
Il existe de nombreux emplacements appropriés pour définir un point d’arrêt.
J’ai décidé de définir un point d’arrêt de méthode dans java.awt.event.KeyListener::keyPressed .
Cela déclenchera l’effet de bord chaque fois que nous appuyons sur une touche :
Définir un point d’arrêt avec une expression dans du code critique pourrait considérablement ralentir l’application cible.
Retournons à Space Invaders et voyons si notre IDDQD maison fonctionne. Ça marche !
Conclusion
Dans cet article, nous avons utilisé le débogueur pour découvrir comment une application fonctionne sous le capot. Ensuite, nous avons pu naviguer dans sa mémoire et modifier sa fonctionnalité, tout cela sans toucher aux sources de l’application ! J’espère que ma comparaison du débogueur avec IDDQD n’était pas trop audacieuse, et que vous avez appris des techniques qui vous donneront un avantage dans vos défis de débogage.
J’aimerais adresser mes félicitations à Eugene Nizienko pour avoir créé le plugin Space Invaders et Egor Ushakov pour être une source constante d’inspiration en débogage et programmation. Les ordinateurs sont deux fois plus amusants avec des gens comme eux autour.
Si vous avez des défis de débogage en tête que vous voulez que je traite dans les prochains articles, faites-le moi savoir !
Bon piratage !