Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.
Dans ce didacticiel, vous allez rattrapper les erreurs levées par les instructions.
Le temps de lecture de ce didacticiel est estimé à 15 minutes si les fonctions ont été abordées.
Pour commencer, créez le canevas de l'application dans le fichier exécutable interruptions.svm en utilisant ce code :
#!/usr/bin/env svm
DESCRIPTION
Software interruptions example
END
LOG
DEBUG "Software interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmstr.so"
PROCESS "application"
CODE "main" INLINE
:memory INT/integer, STR/command
:label wait
:com.prompt "command" STR -> &command
:str.join "command_" @&command -> &command
:call @&command integer
:goto wait
:label command_quit
:shutdown
:label command_set
:com.prompt "integer" INT -> &integer
:return
:label command_inc
:shift &integer
:return
:label command_dec
:shift -1 &integer
:return
:label command_print
:com.message "integer: " @&integer
:return
END
END
Lancez l'application sans aucune option. Un prompt apparaît, et vous pouvez lancer des commandes :
./interruptions.svm
command: set
integer: 17
command: inc
command: inc
command: print
integer: 19
command: exit
### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(main, line 5) Interruption FAILURE not handled: Label command_exit is not defined in code main.
Core dump:
Kernel:
State: I, transmit_interruption, interrupted @(main, line 5) Interruption FAILURE not handled: Label command_exit is not defined in code main.
Processor:
State:
Next instruction: <main:1/4> (Current one: <main:1/3>)
Current memory : &0*0
Allocated memory: &0*2
Aliases : command integer
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Code at <main:1/3>:
:memory INT/integer , STR/command # <0> @(main, line 1)
:label wait
:com.prompt "command" STR -> &command # <1> @(main, line 3) WAITING
:str.join "command_" @&command -> &command # <2> @(main, line 4)
====== HERE ======
:call @&command integer # <3> @(main, line 5)
==================
:goto wait # <4> @(main, line 6)
:label command_quit
:shutdown # <5> @(main, line 8)
:label command_set
:com.prompt "integer" INT -> &integer # <6> @(main, line 10) WAITING
:return # <7> @(main, line 11)
:label command_inc
:shift &integer # <8> @(main, line 13)
:return # <9> @(main, line 14)
:label command_dec
:shift -1 &integer # <10> @(main, line 16)
:return # <11> @(main, line 17)
:label command_print
:com.message "integer: " @&integer # <12> @(main, line 19)
:return # <13> @(main, line 20)
Labels:
command_dec -> <10>
command_inc -> <8>
command_print -> <12>
command_quit -> <5>
command_set -> <6>
wait -> <1>
Symbols:
Memory:
&0: INT 19
&1: STR "command_exit"
Aliases:
command -> &1*1
integer -> &0*1
Free space:
### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
Process application interrupted: @(main, line 5) Interruption FAILURE not handled: Label command_exit is not defined in code main.
Et là, c'est un échec. A la moindre erreur de l'utilisateur de l'application, celle-ci s'arrête en crachant sur le terminal une erreur certes très utile pour le développeur, mais peu agréable pour l'utilisateur.
Cependant, avant d'agir sur cette condition d'erreur, lisez attentivement le texte généré. Il y apparaît une information importante : il s'agit d'une interruption de type FAILURE
.
Comme vous l'avez déjà constaté, l'application est exécutée par un processeur. En cas d'erreur, comme ici, une interruption est émise, et le processeur arrête d'exécuter l'application : on parle d'interruption non rattrapée.
Une condition d'erreur dans une instruction est matérialisée par une interruption du processeur.
Lorsque le processeur est interrompu, il arrête l'exécution du code.
Les types d'interruption fréquemment rencontrés sont :
FAILURE
pour les erreurs d'ordre général ou dûes à de mauvaises valeurs d'entrée,MEMORY
pour les erreurs liées à la mémoire,PROCESSOR
pour les erreurs qui pousserait le processeur à terminer dans un état invalide,NUMERIC
pour les erreurs numériques et de calcul,DEVICE
pour les erreurs d'entrée/sortie,TERMINATE
pour indiquer une demande de terminaison de l'application.Ce que l'on aimerait faire dans cette application est de pouvoir exécuter un code spécial en cas d'erreur, pour :
Modifiez le code de l'application pour obtenir :
#!/usr/bin/env svm
DESCRIPTION
Software interruptions example
END
LOG
DEBUG "Software interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmstr.so"
PROCESS "application"
CODE "main" INLINE
:memory INT/integer, STR/command
:label wait
:com.prompt "command" STR -> &command
:str.join "command_" @&command -> &command
:interruption FAILURE wrong_command
:debug BREAK
:call @&command integer
:interruption FAILURE
:goto wait
:label command_quit
:shutdown
:label command_set
:com.prompt "integer" INT -> &integer
:return
:label command_inc
:shift &integer
:return
:label command_dec
:shift -1 &integer
:return
:label command_print
:com.message "integer: " @&integer
:return
:label wrong_command
:com.message "Invalid command."
:return
END
END
Lancez maintenant l'application :
./interruptions.svm
command: set
integer: 17
command: inc
command: inc
command: dec
command: exit
Invalid command.
command: quit
L'erreur est maintenant issue d'une instruction de l'application, et l'application continue de s'exécuter.
Relancez l'application en mode débugueur, et regardez le processeur lorsqu'il exécute l'instruction :call
:
Dans la fenêtre du processeur, un lien existe entre l'interruption FAILURE
et l'adresse dans le code indiquée par l'étiquette "wrong_command" : il s'agit d'un gestionnaire d'interruption.
Quand ce gestionnaire est défini, au lieu d'arrêter toute l'exécution lorsqu'une interruption FAILURE
arrive, le processeur va réaliser un appel de fonction à l'adresse indiquée.
Exécutez pas à pas l'application, et observez ce qu'il se passe lorsqu'une commande inconnue est entrée dans le terminal : l'exécution normale du code est bien interrompue, et un appel implicite à "wrong_command" est effectué.
La gestion des interruptions est plutôt sensible dans une application. Pour couvrir un maximum de besoins, il existe trois niveaux de gestionnaires d'interruptions :
Lorsque plusieurs gestionnaires sont définis pour une interruption donnée, le premier dans la liste ci-dessus est utilisé.
Pour illustrer cet ordre de précédence, remplacez le code par :
#!/usr/bin/env svm
DESCRIPTION
Software interruptions example
END
LOG
DEBUG "Software interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmstr.so"
PLUGIN "svmint.so"
PROCESS "application"
CODE "main" INLINE
:memory INT/integer, STR/command
:interruption GLOBAL NUMERIC wrong_operation
:label wait
:com.prompt "command" STR -> &command
:str.join "command_" @&command -> &command
:interruption FAILURE wrong_command
:debug BREAK
:call @&command integer
:interruption FAILURE
:goto wait
:label command_quit
:shutdown
:label command_set
:interruption NUMERIC use_zero
:com.prompt "integer" INT -> &integer
:return
:label use_zero
:com.message "Use zero."
0 -> &integer
:return 2
:label command_inc
:shift &integer
:return
:label command_dec
:shift -1 &integer
:return
:label command_div
:int.div 100 @&integer -> &integer
:return
:label command_print
:com.message "integer: " @&integer
:return
:label wrong_command
:com.message "Invalid command."
:return
:label wrong_operation
:com.message "Invalid operation."
:return
END
END
Puis lancez cette application (potentiellement pas à pas dans le débugueur) :
./interruptions.svm
command: set
integer: 17
command: set
integer: one
Use zero.
command: div
Invalid operation.
command: quit
Dans cet exemple, vous pouvez constater plusieurs choses :
NUMERIC
(gestionnaire local plus prioritaire que le gestionnaire global),NUMERIC
(gestionnaire global),:return 2
dans un gestionnaire d'interruption pour couper l'exécution de la fonction où s'est produite l'interruption.Un gestionnaire d'interruption est une fonction qui sera appellée lorsqu'une interruption est levée.
:interruption interruption cible
.:interruption interruption
.CASCADE
après l'instruction :interruption
.GLOBAL
après l'instruction :interruption
.Pour chaque interruption, il peut y avoir jusqu'à trois gestionnaires d'interruption dans cet ordre de priorité :
Vous venez de voir comment gérer les erreurs émises par les instructions.
Une gestion des erreurs complète donne de la robustesse à une application. Il convient de bien anticiper où peuvent apparaître des erreurs et y apporter le traitement approprié.