# Introduction

Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.

Dans ce didacticiel, vous allez manipuler le débugueur depuis le code de manière plus étendue.

Le temps de lecture de ce didacticiel est estimé à 15 minutes si l'utilisation basique du débugueur et les instructions extensions ont été abordées.

# Mise en place

Pour commencer, créez le canevas de l'application dans le fichier exécutable points_arret_etendus.svm en utilisant ce code :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		0 -> &i
    		11 -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

# Points d'arrêt interruption

Lancez l'application :

    ./points_arret_etendus.svm
    Error

Le résultat indique qu'une interruption mémoire a été levée lors de l'exécution de l'application.

Quand l'application est un peu conséquente, avoir un point d'arrêt sur une interruption peut se révéler efficace pour diagnostiquer dans le débugueur la cause de l'erreur.

Modifiez le code pour ajouter un point d'arrêt sur interruption :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:debug ADD MEMORY
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		0 -> &i
    		11 -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

Puis lancez l'application en mode débugueur. L'application est directement sur un point d'arrêt là où l'interruption a été levée :

Advanced debug
Breakpoint list

    Interruption:
    At <main:1/6>
    With: MEMORY

Code main - K main - P application

    :debug ADD MEMORY
    :interruption CASCADE MEMORY failure
    :call function P
    :shutdown
    :label function
    :memory INT/i , STR/s
    0 -> &i
    11 -> &s
    :shift &i
    :str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    :com.message @&i " " @&s
    :return
    :label failure
    :com.message "Error"

Auto-scroll to
Current
with
above
Display
Processor - K main - P application
State:
Next instruction: <main:1/7>
Current instruction: <main:1/6>
Code
Current memory: &0*0
Allocated memory: &0*2
Defined aliases: i s
Local interruptions:
Cascaded local interruptions: MEMORY => <main:1/11>
Code
Flags:
Cascaded flags:
Return stack:
State:
Next instruction: <main:1/3>
Current instruction: <main:1/2>
Code
Current memory: &0*0
Allocated memory:
Defined aliases:
Local interruptions:
Cascaded local interruptions: MEMORY => <main:1/11>
Code
Flags:
Cascaded flags:
Global interruptions:
Waiting interruptions: MEMORY

En bas de la fenêtre du processeur, vous pouvez noter que l'interruption mémoire est en attente de traitement.

- L'instruction `:debug ADD interruption` permet d'ajouter un point d'arrêt sur une interruption.
- Le point d'arrêt n'est pas déclenché lorsqu'il est mis en place, mais lorsqu'une interruption doit être traitée par le processeur. Cela peut se produire sur n'importe quelle instruction, ou ne pas se produire du tout.
- L'instruction `:debug REMOVE interruption` permet de retirer un point d'arrêt sur une interruption.

# Points d'arrêt mémoire

Tout comme pour les interruptions, il peut être très pratique de pouvoir arrêter l'application sur une opération mémoire.

## Lecture

Corrigez la source de l'interruption et modifiez le code :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:debug ADD READ &0
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		0 -> &i
    		"11" -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

Lancez de nouveau l'application en mode débugueur. Lorsque le second point d'arrêt est atteint, vous devez obtenir :

Advanced debug
Breakpoint list

    Memory:
    At <main:1/7>
    Read at &0:
      Type: INT
      Value: 0
    Memory:
    At <main:1/9>
    Read at &0:
      Type: INT
      Value: 1

Memory - K main - P application

Address | Type | Value
--- | --- | ---
&0 | INT | 1
&1 | STR | "x1"

Alias | Pointer
--- | ---
i | &0*1
s | &1*1

Address:
Display
Aliases
P
Focus
Back
Clear
Code main - K main - P application

    :debug ADD READ &0
    :interruption CASCADE MEMORY failure
    :call function P
    :shutdown
    :label function
    :memory INT/i , STR/s
    0 -> &i
    "11" -> &s
    :shift &i
    :str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    :com.message @&i " " @&s
    :return
    :label failure
    :com.message "Error"

Auto-scroll to
Current
with
above
Display

L'application a été arrêtée dans le débugueur lors des deux lectures à l'adresse &0. Notez que l'adresse passée à l'instruction `:debug ADD READ` peut être calculée comme n'importe quelle adresse.

De plus, le débugueur montre dans la liste des points d'arrêt le type et la valeur pour chacune des lectures.

## Ecriture

Modifiez à nouveau le code :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		:debug ADD WRITE &s
    		0 -> &i
    		"11" -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

Puis lancez une nouvelle fois l'application avec le débugueur :

Advanced debug
Breakpoint list

    Memory:
    At <main:1/6>
    Write at &1:
      Type: STR
      Value:
    Replaced by:
      Type: STR
      Value: "11"

Memory - K main - P application

Address | Type | Value
--- | --- | ---
&0 | INT | 0
&1 | STR |

Alias | Pointer
--- | ---
i | &0*1
s | &1*1

Address:
Display
Aliases
P
Focus
Back
Clear
Code main - K main - P application

    :interruption CASCADE MEMORY failure
    :call function P
    :shutdown
    :label function
    :memory INT/i , STR/s
    :debug ADD WRITE &s
    0 -> &i
    "11" -> &s
    :shift &i
    :str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    :com.message @&i " " @&s
    :return
    :label failure
    :com.message "Error"

Auto-scroll to
Current
with
above
Display

Cette fois, l'application est arrêtée sur l'écriture en mémoire à l'adresse &1. Le point d'arrêt indique l'ancien type, l'ancienne valeur et les nouveaux type et valeur.

## Accès

Modifiez encore le code :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		:debug ADD ACCESS &s
    		0 -> &i
    		"11" -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

Lancez dans le débugueur pour obtenir :

Advanced debug
Breakpoint list

    Memory:
    At <main:1/8>
    Access at &1:
      Type: STR
      Value: "x1"

Code main - K main - P application

    :interruption CASCADE MEMORY failure
    :call function P
    :shutdown
    :label function
    :memory INT/i , STR/s
    :debug ADD ACCESS &s
    0 -> &i
    "11" -> &s
    :shift &i
    :str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    :com.message @&i " " @&s
    :return
    :label failure
    :com.message "Error"

Auto-scroll to
Current
with
above
Display
Memory - K main - P application

Address | Type | Value
--- | --- | ---
&0 | INT | 1
&1 | STR | "11"

Alias | Pointer
--- | ---
i | &0*1
s | &1*1

Address:
Display
Aliases
P
Focus
Back
Clear

L'instruction `:str.replace` n'arrête pas l'application sur une lecture ou une écriture en mémoire, mais sur un accès à la mémoire.

Un accès en mémoire est une opération qui modifie une valeur directement stockée en mémoire sans écriture explicite. Seuls les nouveaux type et valeur sont indiqués dans le point d'arrêt.

Une seule exception existe : l'instruction `:shift` réalise un accès mémoire, mais simule une lecture et une écriture sur les points d'arrêt pour des raisons pratiques.

## Libération

Modifiez une dernière fois le code :

    #!/usr/bin/env svm
    DESCRIPTION
    Advanced debug example
    END
    LOG
    DEBUG "Advanced debug" STYLE "default"
    PLUGIN "svmcom.so"
    PLUGIN "svmstr.so"
    PROCESS "application"
    	CODE "main" INLINE
    		:interruption CASCADE MEMORY failure
    		:call function P
    		:shutdown
    	:label function
    		:memory INT/i, STR/s
    		:debug ADD FREE &s
    		0 -> &i
    		"11" -> &s
    		:shift &i
    		:str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    		:com.message @&i " " @&s
    		:return
    	:label failure
    		:com.message "Error"
    	END
    END

Et lancez l'application avec le débugueur :

Advanced debug
Breakpoint list

    Memory:
    At <main:1/10>
    Delete at &1:
      Type: STR
      Value: "x1"

Memory - K main - P application

Address | Type | Value
--- | --- | ---
&0 | INT | 1
&1 | STR | "x1"

Alias | Pointer
--- | ---
i | &0*1
s | &1*1

Address:
Display
Aliases
P
Focus
Back
Clear
Code main - K main - P application

    :interruption CASCADE MEMORY failure
    :call function P
    :shutdown
    :label function
    :memory INT/i , STR/s
    :debug ADD FREE &s
    0 -> &i
    "11" -> &s
    :shift &i
    :str.replace @&s 1 CONST str.pattern "[0-9]" => "x"
    :com.message @&i " " @&s
    :return
    :label failure
    :com.message "Error"

Auto-scroll to
Current
with
above
Display

Dans ce cas, l'application s'arrête sur l'instruction qui libère la mémoire à l'adresse &1. Le type et la valeur avant suppression sont indiqués dans le point d'arrêt.

- L'instruction `:debug ADD READ adresse_memoire` permet de placer un point d'arrêt sur une lecture mémoire.
- L'instruction `:debug ADD WRITE adresse_memoire` permet de placer un point d'arrêt sur une écriture mémoire.
- L'instruction `:debug ADD ACCESS adresse_memoire` permet de placer un point d'arrêt sur un accès mémoire. Un accès mémoire est une modification directe d'une valeur en mémoire sans écriture explicite.
- L'instruction `:debug ADD FREE adresse_memoire` permet de placer un point d'arrêt sur une libération mémoire.
- Le mot-clef `ADD` peut être remplacé par le mot-clef `REMOVE` pour retirer le point d'arrêt dans les quatre instructions précédentes.
- L'adresse mémoire peut être calculée, et n'est pas nécessairement une adresse constante.

# Points d'arrêt extension

Une dernière catégorie de points d'arrêt peut être rencontrée. Créer de tels points d'arrêt est accessible uniquement à des utilisateurs avancés, mais peuvent être rencontrés par des utilisateurs débutants. Ainsi, nous nous contenterons ici de montrer dans le débugueur à quoi ils ressemblent.

## Points d'arrêt

Les extensions peuvent contenir des points d'arrêt au beau milieu de leur exécution pour expliciter comment elle se déroule et son impact sur la machine virtuelle. L'exécution de l'application s'arrête sur un tel point d'arrêt :

Advanced debug
Breakpoint list

    Plugin:
    At <main:1/0>
    PROCESS application : INSTRUCTION debug.break:
    custom

## Notifications

Les extensions peuvent aussi vouloir afficher des informations complémentaires dans le débugueur sans toutefois arrêter l'application.

Ce sont des notifications :

Advanced debug
Breakpoint list

    Notification:
    At <main:1/0>
    PROCESS application : INSTRUCTION debug.notif:
    custom

## Formulaires

Les extensions peuvent également demander des informations complémentaires permettant de faciliter les investigations, ou destinées à tester le code dans des conditions particulières.

Pour cela, une extension peut soumettre un formulaire directement dans la liste des points d'arrêt :

Advanced debug
Breakpoint list

    Form:
    At <main:1/0>
    PROCESS application : INSTRUCTION debug.form:
form: checkbox selection integer string text Submit

Tant que le formulaire n'est pas validé, l'application reste arrêtée (relancer le processeur n'a aucun effet). Une fois les valeurs remplies et validées en cliquant sur le bouton "Submit", le formulaire se transforme en la liste des valeurs entrées :

Advanced debug
Breakpoint list

    Form:
    At <main:1/0>
    PROCESS application : INSTRUCTION debug.form:
form:
    no
checkbox selection
    plugin.entry
integer
    23
string
    value bis
text
    this is a nice text
    on several lines

# Conclusion

Vous venez de voir comment utiliser les points d'arrêt étendus du débugueur.

Ces nouveaux points d'arrêt permettent de cibler efficacement les investigations en se focalisant sur les événements importants de l'application indépendamment de leur localisation dans le code.

Les extensions peuvent également interagir avec le débugueur pour apporter des compléments d'information utiles sur leur fonctionnement.