Jc/md/lp-01/06Debug1 Debugging Élémentaire. jc/md/lp-01/06Debug2 Objectif du chapitre Premier...

Preview:

Citation preview

jc/md/lp-01/06 Debug 1

Debugging

Élémentaire

jc/md/lp-01/06 Debug 2

Objectif du chapitre

• Premier contact avec les outils de mise au point• Génération en vue de debug• Usage des points d’arrêt, pas à pas• Visualisations diverses

– Code– Variables– Mémoire – Pile– …

jc/md/lp-01/06 Debug 3

Objectifs (1)

• La mise au point de programmes (debugging) nécessite l’emploi de techniques particulières

• Platform Builder offre– Un ensemble d’outils usuels

Exécution du programme en pas à pas Pose ou suppression de points d’arrêt Visualisation des variables, de la mémoire, des registres Suivi des enchaînements de fonctions, etc.

– Un outil « Kernel Tracker » pour visualiser la chronologie des exécutions

jc/md/lp-01/06 Debug 4

Objectifs (2)

• Pour utiliser les possibilités de mise au point, la cible doit être construite dans le mode particulier « Debug » par opposition au mode « Release » qui correspond à une version d’exploitation

• Étude du debugger en deux phases– Visualisations diverses, Breakpoint, etc.– Kernel Tracker ultérieurement

jc/md/lp-01/06 Debug 5

Point de départ

• Générer une plate-forme en mode Debug• Créer et générer une application avec un main

et des fonctions très simples • Exploration (partielle) de l’outil de debug

– Messages– Visualisation du code source– Breakpoint – Visualisation des variables, de la mémoire– Visualisation du code assembleur– Visualisation de la pile

jc/md/lp-01/06 Debug 6

Génération de la plate-forme « Debug »

• Utiliser l’assistant, comme précédemment, avec éventuellement un nouveau nom

• Au pas 6 de l’assistant, modifier les options• Cocher dans les options de la liste « Debug » :

« Enable Image Larger than 32 MB » • Sortir de l’assistant par « Finish »• Sélectionner la configuration active « Debug »• Générer la plate-forme (Build Platform)

jc/md/lp-01/06 Debug 7

Modification des options

jc/md/lp-01/06 Debug 8

Sélection de la taille des images

jc/md/lp-01/06 Debug 9

Sélection configuration : Debug

jc/md/lp-01/06 Debug 10

Génération de la plate-forme

jc/md/lp-01/06 Debug 11

Génération de l’image

jc/md/lp-01/06 Debug 12

Exécution d’une application

• Sans inclusion dans l’imageCréation du .exe

Téléchargement du noyau

Éxécution par la commande « Run Program »

• Avec inclusion dans l’imageCréation du .exe

Refaire une image avec choix de l’option d’insertion

Téléchargement du noyau avec l’application

Exécution à partir de la cible par la méthode Windows

jc/md/lp-01/06 Debug 13

Options de génération

• Pour voir les options de génération avec ou sans inclusion, nous prenons une application de type WCE Application

• Noms de nos applications écrits en majuscules pour les distinguer facilement des applications proposées par Windows CE

• Nom de l’application : BRKP• Nous ajouterons le code par la suite

jc/md/lp-01/06 Debug 14

Choix de l’option pour le projet

jc/md/lp-01/06 Debug 15

Inclusion application dans l’image

jc/md/lp-01/06 Debug 16

Exclusion application de l’image

jc/md/lp-01/06 Debug 17

Application BRKP

• Faire la somme et le produit de deux nombres entiers positifs avec

– Une fonction somme– Une fonction produit– Des variables statiques– Des variables dynamiques– Des passages d’arguments

• Introduire des messages de contrôle

jc/md/lp-01/06 Debug 18

Messages d’aide à la mise au point

• Édition habituelle de messages de contrôle dans l’application par des printf ou équivalents

• En plus, édition de messages par des macros dans la fenêtre de sortie de l’émulateur, et non dans le contexte de la cible

– RETAILMSG en version Retail– RETAILMSG et DEBUGMSG en version Debug

jc/md/lp-01/06 Debug 19

Macro RETAILMSG

• RETAILMSG(cond, printf_exp) • Impression si la condition est vraie de ce qu’on

mettrait dans un printf• Impression faite par une fonction équivalente à

printf mais qui écrit dans la fenêtre de sortie de l’émulateur et non dans la cible émulée

Exemple :RETAILMSG(1,(TEXT("Main: adresse de i= %x\n"),&i));

Équivalent dans la fenêtre de sortie à :printf("Main: adresse de i= %x\n",&i);

jc/md/lp-01/06 Debug 20

Application BRKP (1)

#include "stdafx.h"  

//prototypes des fonctionsDWORD dwSomme(DWORD dwA,DWORD dwB);DWORD dwProduit(DWORD dwA,DWORD dwB); DWORD dwValeur; //juste pour avoir une variable statique

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

jc/md/lp-01/06 Debug 21

Application BRKP (2)

{DWORD i,j;

//Visualisation des adresses dans la fenêtre de sortieRETAILMSG(1,(TEXT("Main: adresse de i= %x\n"),&i));RETAILMSG(1,(TEXT("Main: adresse de j= %x\n"),&j));RETAILMSG(1,(TEXT("Main: adresse de valeur=

%x\n"),&dwValeur));//Calcul de somme et produit des entiers positifs 5 et 3

i=5;j=3;

jc/md/lp-01/06 Debug 22

Application BRKP (3)

RETAILMSG(1,(TEXT("Main: i=%u j=%u\n"),i,j));

dwValeur=dwSomme(i,j);

RETAILMSG(1,(TEXT("Main: i+j=%u\n"),dwValeur));

printf(("Main: i=%u j=%u dwValeur =%u \n"),i,j,dwValeur);

dwValeur=dwProduit(i,j);

RETAILMSG(1,(TEXT("Main: i*j=%u\n"),dwValeur));

printf(("Main: i=%u j=%u dwValeur =%u \n"),i,j,dwValeur);

getchar();

return 0;

}

jc/md/lp-01/06 Debug 23

Application BRKP (4)

DWORD dwSomme(DWORD dwA,DWORD dwB)

{

//Addition de A et B par une méthode archaïque

DWORD dwC=0;

while(dwA--)

dwC++;

while(dwB--)

dwC++;

return dwC;

}

jc/md/lp-01/06 Debug 24

Application BRKP (5)

DWORD dwProduit(DWORD dwA,DWORD dwB){

// Multiplication de A et B par une méthode simpliste DWORD dwC;

for(dwC=0;dwB;dwB--)dwC=dwSomme(dwA,dwC);

return dwC;}

jc/md/lp-01/06 Debug 25

Option de génération en mode debug

jc/md/lp-01/06 Debug 26

Configure Remote Connection

jc/md/lp-01/06 Debug 27

Configure Emulator Download Service

jc/md/lp-01/06 Debug 28

Insertion et suppression de breakpoints

• Les breakpoints peuvent être placés ou enlevés avant ou après le lancement de l’application

• Avant lancement du noyau– Positionner le curseur souris sur une ligne du

programme– Faire un click droit pour ouvrir le menu– Faire Insert/Remove Breakpoint

• Après lancement du noyau, il faut que le debugger soit arrêté

jc/md/lp-01/06 Debug 29

Fenêtres en debug

• Dans la fenêtre principale de Platform Builder la fenêtre Workspace est différente et donne accès à des commandes après chargement de la cible

• D’autres fenêtres sont spéciales pour le debug et accessibles par le menu View, sous-menu Debug Windows, ou par une barre de contrôle

• Fenêtres libres ou ancrées en fenêtre principale• Mémorisation des caractéristiques des fenêtres• Fermeture par l’icône Windows

jc/md/lp-01/06 Debug 30

Exemple : fenêtre des variables

jc/md/lp-01/06 Debug 31

Écran avec la fenêtre des variables

Fenêtre nouvelle disponible pour des commandes lignes (shell)

Variables

Fenêtre principale

jc/md/lp-01/06 Debug 32

Groupe de fenêtres flottantes

jc/md/lp-01/06 Debug 33

Exemple de fenêtres ancrées

Registres du CPU

Fenêtre principale

ContexteWatch

Fenêtre nouvelle disponible pour des commandes lignes (shell)

Fenêtre de sortie

jc/md/lp-01/06 Debug 34

Barre de commande « Debug »

• Ouverte comme toutes les barres de commandes par un click droit dans une barre existante et coche de la case

• Une série d’icônes correspond à des commutateurs pour l’ouverture ou la fermeture des fenêtres de debug

• Une autre série correspond à l’exécution du code : Go simple (jusqu’à un point d’arrêt), pas à pas, avec saut d’une fonction, etc.

• Les bulles d’aide sont explicites

jc/md/lp-01/06 Debug 35

Essai de BRKP

• Génération de la plate-forme• Génération de l’application• Génération de l’image• Téléchargement du noyau avec ou sans

l’application suivant les choix• Exécution du programme• Fermeture de la cible• Déconnexion

jc/md/lp-01/06 Debug 36

Après téléchargement de la cible

jc/md/lp-01/06 Debug 37

Cible vierge

jc/md/lp-01/06 Debug 38

Exécution par la commande shell

Commande du shells BRKP

s pour start

jc/md/lp-01/06 Debug 39

Fenêtres du debugger après exécution

MessagesMessages

jc/md/lp-01/06 Debug 40

Cible après exécution

Attente du caractère Entrée

Taper Entrée pour finir BRKP

jc/md/lp-01/06 Debug 41

Pose d’un breakpoint

• Pose avant ou pendant la session de debug• Placer le curseur sur une ligne de code• Ouvrir le menu contextuel par un click droit• Insérer un point d’arrêt, marqué par un rond

rouge en marge (en fait c’est un commutateur qui insère ou supprime un point d’arrêt)

• Relancer le programme s’il ne l’est pas• Au début, on doit observer une flèche jaune

superposée au premier point d’arrêt

jc/md/lp-01/06 Debug 42

Arrêt du debugger

Icône d’arrêt

jc/md/lp-01/06 Debug 43

Après pose des points d’arrêt

jc/md/lp-01/06 Debug 44

Arrêt sur point d’arrêt

jc/md/lp-01/06 Debug 45

Exécution du programme en debug

• Chaque commande Go (menu ou icône) provoque l’exécution du programme jusqu’à :

– Rencontre d’un point d’arrêt– Rencontre d’une erreur d’exécution– Fin normale du programme

• La commande « Run Program » charge le programme dont on donne le nom puis déclenche une commande Go

• Exécution contrôlée en pas à pas par diverses commandes d’avance

jc/md/lp-01/06 Debug 46

Avance d’un pas

• Avance par– Le menu « Debug »– Les touches de fonction du clavier– Les icônes (dessins et bulles explicites)

• Plusieurs types d’avance– Un pas dans la fonction (Step Into)– Un pas en sautant la fonction (Step Over)– Un pas en ressortant de la fonction (Step Out)– Jusqu’au curseur (Run to Cursor)

jc/md/lp-01/06 Debug 47

Avance d’un pas simple

Avance un pas

jc/md/lp-01/06 Debug 48

Après avance d’un pas simple

Fenêtre des variables

i à jour, j ?Messages

jc/md/lp-01/06 Debug 49

Avance d’un pas avec saut

Avance avec saut de la fonction pour

ne pas exécuter toute la fonction en

pas à pas

Avance avec saut

j à jour

jc/md/lp-01/06 Debug 50

Avance avec sortie de la fonction

• Ce type de pas (Step Out) permet de cesser l’examen d’une fonction en pas à pas en retournant directement chez l’appelant

• Utile quand on est entré par inadvertance dans une fonction déjà au point qu’on ne désire pas dérouler en pas à pas

• Utile en particulier quand on est entré dans une fonction fournie par Platform Builder pour laquelle on ne dispose pas du code source

jc/md/lp-01/06 Debug 51

Avance avec sortie

Le pas simple va nous faire entrer dans la fonction printf pour laquelle nous n’avons

pas le code source

Un pas simple

jc/md/lp-01/06 Debug 52

Recherche du code source

Puisque le code source n’est pas sur la machine…

jc/md/lp-01/06 Debug 53

Fenêtre en mode désassemblage

Le pas à pas va nous conduire dans une nouvelle fonction

après une demande de localisation du

code source puisque ici la fonction est aussi inconnue.

jc/md/lp-01/06 Debug 54

Entrée dans la fonction imbriquée

On va sortir de cette fonction et retrouver la

demande de localisation pour le printf

jc/md/lp-01/06 Debug 55

Recherche du code source

Puisque le code source n’est toujours pas sur la machine…

jc/md/lp-01/06 Debug 56

Sortie de la fonction

Le contexte est redevenu printf

jc/md/lp-01/06 Debug 57

Suppression du désassemblage

jc/md/lp-01/06 Debug 58

Quelques pas jusqu’à la fonction

jc/md/lp-01/06 Debug 59

Entrée dans la fonction produit

Le contexte est notre fonction

dwProduit.

Pourquoi 5 ?On continue en

assembleur.

jc/md/lp-01/06 Debug 60

Code assembleur de dwProduit

Malgré la position de la flèche jaune sur le

code source, l’initialisation du for

n’est pas commencée.

jc/md/lp-01/06 Debug 61

Debug en assembleur

Après un pas, dwC est bien initialisée, on

repasse en mode normal

jc/md/lp-01/06 Debug 62

Après quelques pas, dans dwSomme

Le contexte est maintenant celui

de la fonction dwSomme. On

peut aussi voir les autres contextes.

jc/md/lp-01/06 Debug 63

Cible à ce stade

jc/md/lp-01/06 Debug 64

Visualisation des appels de fonctions

L’ouverture de la fenêtre « Call Stack » visualise la pile des appels de fonctions.

jc/md/lp-01/06 Debug 65

Fin de l’exécution

• Terminer le programme avec les commandes de déroulement « Go » ou d’avance par pas

• Quand on arrive sur le getchar() taper le caractère « Entrée » dans la cible.Mais à la suite de frappes au cours des manipulations, il se peut qu’un caractère soit déjà présent dans le tampon d’entrée et alimente le getchar() ; dans ce cas on ne verra pas l’attente du caractère ; il faut poursuivre les avances par pas.

jc/md/lp-01/06 Debug 66

Fin de l’exécution (2)

• Après exécution de la dernière instruction, le système doit encore exécuter des fonctions « épilogues » pour terminer complètement le programme. Il faut donc poursuivre l’avance par le debugger, normalement par des pas « Step Out » jusqu’à obtenir le déchargement de l’application.

• On pourra alors reprendre correctement une nouvelle session de debug pour examiner d’autres possibilités.

jc/md/lp-01/06 Debug 67

Cible à la fin de l’exécution

jc/md/lp-01/06 Debug 68

Fin complète de l’application

Messages indiquant le déchargement de l’application, du module console utilisé

indirectement par notre code et des symboles associés.

La fenêtre cible est revenue à son état initial.

Plus rien à exécuter

Commandes inactives

jc/md/lp-01/06 Debug 69

Nouvelle session

Nouvelle commande start

Commuter en debug source

jc/md/lp-01/06 Debug 70

Fenêtre « Variables »

• Barre « Toolbar » affichée avec le menu contextuel– Nom de la fonction– Arguments

• & symbolise une adresse, comme en C• Clicker un + présent devant un nom offre des

informations liées, adresse, valeur d’une variable, champs d’une structure, etc.

• Trois onglets– « Auto » pour les variables au fil de leur utilisation– « Locals » pour les variables locales– « This » qui correspond au pointeur « this »du C++

jc/md/lp-01/06 Debug 71

Fenêtre « Variables » (2)

• Affichage en hexadécimal ou en décimal• Modification des valeurs des variables en

sélectionnant la partie valeur à modifier et en saisissant la nouvelle valeur

• Modification visualisée par un affichage rouge répercutée dans les autres fenêtres

• Variables gérées automatiquement– Introduction de variable impossible– Suppression de variable impossible

jc/md/lp-01/06 Debug 72

Onglet « Auto » initial

lpCmdLine est un pointeur sur la ligne de commande ; sa

valeur est indiquée ainsi que la chaîne pointée, ici vide

Premier caractère de la chaîne UNICODE, donc ici fin de chaîne

jc/md/lp-01/06 Debug 73

Onglet « Auto » après avance

Variables gérées automatiquement

Modification possible après sélection de ce champ

jc/md/lp-01/06 Debug 74

Onglet « Locals »

Quand une variable est modifiée par une

instruction, elle apparaît en rouge

jc/md/lp-01/06 Debug 75

Onglet « Locals » (2)

Pas de confusion entre les

variables de même nom

Fenêtre du contexte de la fonction produit superposée par copier coller

Fenêtre du contexte de la fonction somme après de nombreux pas

jc/md/lp-01/06 Debug 76

Fenêtres « Watch »

• Fenêtres dans lesquelles on choisit les variables• Introduction de la variable par son nom• Suppression par sélection et « Delete »• Valeur modifiable et reportée dans les autres fenêtres• Modifications visualisées en rouge• Possibilité d’introduire une expression du style langage

C directement ou par le truchement d’une autre fenêtre : « QuickWatch »

• Gestion des variables suivant durée de vie et portée• Quatre onglets équivalents

jc/md/lp-01/06 Debug 77

Exemple « Watch1 » (1)

Un click droit ouvre un menu contextuel dont une entrée

indique le type de la variable.

jc/md/lp-01/06 Debug 78

Exemple « Watch1 » (2)

Capture juste après la

deuxième sortie de la fonction

dwSomme

Au pas suivant dwC prendra la valeur 10

jc/md/lp-01/06 Debug 79

Exemple « Watch1 » (3)

jc/md/lp-01/06 Debug 80

Exemple « QuickWatch »

jc/md/lp-01/06 Debug 81

Visualisation de la mémoire

• Deux fenêtres équivalentes « Memory 1 » et « Memory 2 »

• Affichage à partir de l’adresse fournie– numériquement en hexadécimal ou en décimal – par le nom d’une variable, d’une fonction

• Choix de l’affichage par le menu contextuel en octet, demi-mot, mot

• Modification directe de la mémoire, avec mise à jour dans les autres fenêtres et réciproquement

jc/md/lp-01/06 Debug 82

Exemple de visualisation mémoire

Adresse de i

Valeur de i

Adresse de dwSomme

Début du code de dwSomme

jc/md/lp-01/06 Debug 83

Représentation « Little Endian »

Adresse de i

Valeur de i en 32 bits

Modification arbitraire de i i : poids faibles

dans l’adresse la plus basse

i : poids forts dans l’adresse la

plus haute

jc/md/lp-01/06 Debug 84

Fenêtre « Registers »

• Visualisation des registres du processeur• Menu contextuel pour des options

– Registres flottants– Registres multimédia

• Modification possible en écrivant directement dans le registre

• Utilisables par leur nom symbolique pour fournir– Une adresse pour un pointeur– Une valeur pour un registre de données

jc/md/lp-01/06 Debug 85

Visualisation des registres

Nom symbolique

EAX Nom symbolique

EIP

jc/md/lp-01/06 Debug 86

« Debug » en mode assembleur

• Visualiser le code assembleur par le menu « View », l’icône, le menu contextuel ou encore la touche de fonction

• Toutes les commandes rencontrées dans la mise au point en langage source s’appliquent, en particulier les commandes d’avance par pas

• Les fenêtres de visualisation des registres et de la pile sont particulièrement utiles

jc/md/lp-01/06 Debug 87

Exemple : arrêt sur le breakpoint

Avancer par pas

À suivre

Activer le « Breakpoint »

jc/md/lp-01/06 Debug 88

Après de nombreux pas

Dans dwProduit avant le second appel de

dwSomme

jc/md/lp-01/06 Debug 89

Registres X86

• ESP:Stack Pointer Register– Pointeur de pile

• EBP:Stack-FrameBase Pointer– Pour accéder a des données passées par la pile

• EIP: Instruction Pointer

jc/md/lp-01/06 Debug 90

Exemple : dwValeur=dwSomme(i,j)

PILE

Nouveau ESP

Future valeur de retour

Nouveau EBP

Ancien EBP

Adresse retour

Paramètre 1

Paramètre 2

Ancien ESP

jc/md/lp-01/06 Debug 91

Visualisation de la pile (1)

Pile après appel de dwSomme

Pile avant appel de dwSomme

jc/md/lp-01/06 Debug 92

Visualisation de la pile (2)

Pile après sauvegarde de EBP puis

ECX

jc/md/lp-01/06 Debug 93

Visualisation de la pile (3)

• On peut assez facilement suivre l’évolution de la pile.

• Ici, à partir de l’adresse donnée par le pointeur de pile :

– ECX (A), EBP (1202FC00) sauvegardés par dwSomme

– La partie segment de l’adresse de retour placée par l’instruction call dans dwProduit : call dwSomme

– Les arguments dwA (5) et dwC (A) placés par dwProduit pour dwSomme

– La variable locale dwC de la fonction dwProduit– Puis, même chose pour l’appel de dwProduit par

main

jc/md/lp-01/06 Debug 94

Complément

• Ce ne sont que les principales fonctionnalités du debugger de base ; il reste beaucoup de possibilités à explorer.

• Nous explorerons ultérieurement toute une autre série de fonctionnalités dans le « Kernel Tracker »

• Un dernier groupe de commandes dont certaines sont très évoluées, est fourni par des commandes lignes à saisir dans la fenêtre « Shell » Windows CE accessible par l’icône CE

jc/md/lp-01/06 Debug 95

Conclusion

• Ce chapitre a permis de prendre en main les techniques les plus classiques de mise au point

Recommended