# Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez apprendre à manipuler le débugueur depuis les extensions.

Le temps de lecture de ce didacticiel est estimé à 25 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier debug.svm_plugin en utilisant ce code :

    PLUGIN debug

    DEFINE

# Investigation d'extension

La première interaction entre les extensions et le débugueur permet d'investiguer des problèmes au sein du code des extensions, ou de rendre visibles dans le débugueur des étapes intermédiaires du traitement des extensions.

## Micro-points d'arrêt

La première manière consiste en un point d'arrêt placé dans le code de l'extension.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.seq INT -> PTR
    %{
    	auto s = ARGV_VALUE(0,integer);
    	if(s<0)
    	{
    		ERROR_INTERNAL(FAILURE,"Negative size");
    	}
    	::svm_debug_break__raw(svm,CURRENT(kernel),"Before allocation");
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,INTEGER,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_debug_break__raw(svm,CURRENT(kernel),"Before write");
    	SVM_Address pa = ::svm_value_pointer_get_address(svm,p);
    	SVM_Size ps = ::svm_value_pointer_get_size(svm,p);
    	for(SVM_Size i=0 ; i<ps ; ++i)
    	{
    		SVM_Value_Integer v = ::svm_value_integer_new(svm,i);
    		::svm_value_state_set_movable(svm,v);
    		::svm_memory_write_address(svm,CURRENT(kernel),(pa+i),v);
    		::svm_debug_break__raw(svm,CURRENT(kernel),"Write");
    	}
    	::svm_debug_break__raw(svm,CURRENT(kernel),"Before return");
    	return p;
    %}

Générez et compilez l'extension, puis écrivez une application qui appelle cette instruction. Puis, lancez l'application dans le débugueur : chaque passage du code de l'instruction dans la fonction d'interface programmatique `svm_debug_break` arrête l'exécution de l'instruction jusqu'à la reprise de l'exécution depuis le débugueur.

## Notifications

Parfois, provoquer un arrêt à chaque étape de l'exécution est plutôt lourd. Pour pallier à cela, l'interface programmatique permet aussi d'envoyer des notifications. Ces notifications envoient un texte comme un micro point d'arrêt, mais n'arrête pas l'exécution du code.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.seq INT -> PTR
    %{
    	::svm_debug_break__raw(svm,CURRENT(kernel),"Start");
    	auto s = ARGV_VALUE(0,integer);
    	if(s<0)
    	{
    		ERROR_INTERNAL(FAILURE,"Negative size");
    	}
    	::svm_debug_notify__raw(svm,CURRENT(kernel),"Before allocation");
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,INTEGER,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_debug_notify__raw(svm,CURRENT(kernel),"Before write");
    	SVM_Address pa = ::svm_value_pointer_get_address(svm,p);
    	SVM_Size ps = ::svm_value_pointer_get_size(svm,p);
    	for(SVM_Size i=0 ; i<ps ; ++i)
    	{
    		SVM_Value_Integer v = ::svm_value_integer_new(svm,i);
    		::svm_value_state_set_movable(svm,v);
    		::svm_memory_write_address(svm,CURRENT(kernel),(pa+i),v);
    		::svm_debug_notify__raw(svm,CURRENT(kernel),"Write");
    	}
    	::svm_debug_notify__raw(svm,CURRENT(kernel),"Before return");
    	return p;
    %}

Regénérez et compilez l'extension. Puis, lancez la même application dans le débugueur : cette fois, l'instruction s'arrête une seule fois au début. Ensuite, les notifications apparaissent dans le débugueur sans que la machine n'arrête l'exécution de l'instruction.

- L'interface programmatique permet d'investiguer des problèmes dans le code des extensions.
- La fonction `svm_debug_break` crée un micro point d'arrêt dans le corps d'une instruction.
- La fonction `svm_debug_notify` crée une notification dans le débugueur sans arrêter l'exécution du code.

# Points d'arrêts

Les extensions peuvent également manipuler les points d'arrêt portant sur le code de la machine virtuelle.

## Code

La première possibilité est un ajout de point d'arrêt à un endroit du code machine.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.add SYM
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_debug_breakpoint_add_break(svm,CURRENT(kernel),s);
    %}

    INSTRUCTION debug.remove SYM
    %{
    	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_debug_breakpoint_remove_break(svm,CURRENT(kernel),s);
    %}

Regénérez et compilez l'extension, puis utilisez ce code pour créer une petite application de test :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmplugindebug/libsvmdebug.so"
    PLUGIN "svmrun.so"
    PROCESS "test"
    	CODE "main" INLINE
    		:debug BREAK
    		:run.trace 1
    		:debug.add $"s"
    		:run.trace 2
    	:symbol s
    		:run.trace 3
    		:debug.remove $"s"
    		:run.trace 4
    	END
    END

Lancez cette application dans le débugueur, et exécutez la pas à pas en regardant le code. Lorsque l'instruction `:debug.add` est exécutée, un point d'arrêt apparaît au niveau du symbole comme si un clic de souris l'avait placé. Il disparaît ensuite lorsque l'instruction `:debug.remove` est invoquée.

## Mémoire

La seconde possibilité concerne les points d'arrêt mémoire.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.add_read INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_add_memoryread(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.remove_read INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_remove_memoryread(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.add_write INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_add_memorywrite(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.remove_write INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_remove_memorywrite(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.add_access INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_add_memoryaccess(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.remove_access INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_remove_memoryaccess(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.add_free INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_add_memoryfree(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION debug.remove_free INT
    %{
    	SVM_Address a = ARGV_VALUE(0,integer);
    	::svm_debug_breakpoint_remove_memoryfree(svm,CURRENT(kernel),a);
    %}

Regénérez et compilez l'extension. Créez une petite application de test qui emploie ces instructions pour placer des points d'arrêt mémoire, et lancez la en mode débugueur.

## Interruptions

La dernière possibilité concerne les interruptions.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.add IRQ
    %{
    	SVM_Value_Interruption i = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_debug_breakpoint_add_interruption(svm,CURRENT(kernel),i);
    %}

    INSTRUCTION debug.remove IRQ
    %{
    	SVM_Value_Interruption i = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_debug_breakpoint_remove_interruption(svm,CURRENT(kernel),i);
    %}

Regénérez et compilez l'extension. Créez une petite application de test qui emploie ces instructions pour placer des points d'arrêt interruption et qui utilise l'instruction `:run.interrupt` pour lancer des interruptions. Lancez cette application en mode débugueur.

- L'interface programmatique permet d'émuler complètement l'instruction `:debug` depuis le code des extensions.
- Les fonctions `svm_debug_breakpoint_add_break` et `svm_debug_breakpoint_remove_break` gèrent les points d'arrêt de code via un symbole.
- Les fonctions suivantes gèrent les points d'arrêt mémoire :
  - `svm_debug_breakpoint_add_memoryread` et `svm_debug_breakpoint_remove_memoryread` pour la lecture,
  - `svm_debug_breakpoint_add_memorywrite` et `svm_debug_breakpoint_remove_memorywrite` pour l'écriture,
  - `svm_debug_breakpoint_add_memoryaccess` et `svm_debug_breakpoint_remove_memoryaccess` pour les accès mémoire autres que lecture et écriture,
  - `svm_debug_breakpoint_add_memoryfree` et `svm_debug_breakpoint_remove_memoryfree` pour la libération mémoire.
- Les fonctions `svm_debug_breakpoint_add_interruption` et `svm_debug_breakpoint_remove_interruption` gèrent les points d'arrêt d'interruption.

# Formulaires

Un dernier type d'arrêt est disponible, et celui-ci est un peu particulier : au lieu de fournir des informations à celui qui investigue dans le débugueur, il permet d'injecter des données dans l'application depuis le débugueur. Ces point d'arrêt sont appelés formulaires, et sont affichés dans la boite de dialogue des points d'arrêt.

## Création

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.form -> PTR ?
    %{
    	SVM_Debug_Form f = ::svm_debug_form_new(svm,"Form example");
    	::svm_debug_form_append_checkbox__raw(svm,f,"boolean",FALSE);
    	SVM_Value *t = ::svm_value_array_new(svm,3);
    	t[0] = ::svm_value_string_new__raw(svm,"a");
    	t[1] = ::svm_value_string_new__raw(svm,"b");
    	t[2] = ::svm_value_integer_new(svm,42);
    	::svm_debug_form_append_selection(svm,f,"restricted",3,t);
    	::svm_debug_form_append_integer__raw(svm,f,"integer",3,1,10);
    	::svm_debug_form_append_string__raw(svm,f,"string","a string",3,8);
    	::svm_debug_form_append_text(svm,f,"text",20,10);
    %}

Pour le moment, ce n'est pas la peine de générer l'extension, cette instruction est encore incomplète.

Cependant, elle contient déjà la création d'un formulaire, ainsi que la définition des champs de ce formulaire :

- `svm_debug_form_append_checkbox` ajoute une case à cocher, dont le résultat sera un booléen,
- `svm_debug_form_append_selection` ajoute une liste déroulante, dont le résultat sera l'object sélectionné transformé en chaine de caractères,
- `svm_debug_form_append_integer` ajoute un champ entier, avec possibilité de tester si l'entier donné est entre deux valeurs fixées,
- `svm_debug_form_append_string` ajoute un champ texte, avec possibilité de tester si la chaine donnée a une taille comprise entre deux valeurs fixées,
- `svm_debug_form_append_text` ajoute aussi un champ texte, mais invite à entrer plusieurs lignes, et dont le format lignes versus colonnes est fixé.

## Soumission

Une fois créé, le formulaire peut être soumis à l'utilisateur.

Modifiez le code de l'extension :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.form -> PTR ?
    %{
    	SVM_Debug_Form f = ::svm_debug_form_new(svm,"Form example");
    	::svm_debug_form_append_checkbox__raw(svm,f,"boolean",FALSE);
    	SVM_Value *t = ::svm_value_array_new(svm,3);
    	t[0] = ::svm_value_string_new__raw(svm,"a");
    	t[1] = ::svm_value_string_new__raw(svm,"b");
    	t[2] = ::svm_value_integer_new(svm,42);
    	::svm_debug_form_append_selection(svm,f,"restricted",3,t);
    	::svm_debug_form_append_integer__raw(svm,f,"integer",3,1,10);
    	::svm_debug_form_append_string__raw(svm,f,"string","a string",3,8);
    	::svm_debug_form_append_text(svm,f,"text",20,10);
    	SVM_Value *r = ::svm_debug_form_request(svm,f);
    	if(not r)
    	{
    		return NEW_NULL_VALUE(pointer);
    	}
    	SVM_Size s = 0;
    	for(SVM_Value *it=r ; *it ; ++it)
    	{
    		::svm_value_state_set_movable(svm,*it);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),p,r);
    	return p;
    %}

Regénérez et compilez l'extension. Créez une petite application de test qui emploie cette instruction et lancez la en mode débugueur. Dans la fenêtre mémoire du noyau contenant le formulaire, observez les valeurs renvoyées par le formulaire.

## Utilisations possibles

Les formulaires peuvent être utilisés pour configurer les extensions en mode débugueur, notamment pour influencer leur niveau de verbosité dans le log système ou celui du débugueur.

Ils peuvent aussi être utilisés pour introduire des valeurs inattendues dans l'exécution du code de l'application pour observer son comportement.

Une autre utilisation possible est tout simplement l'injection de code depuis le débugueur :

    PLUGIN debug

    DEFINE

    INSTRUCTION debug.code
    %{
    	SVM_Debug_Form f = ::svm_debug_form_new(svm,"Code injection");
    	::svm_debug_form_append_string__raw(svm,f,"Name","debug",3,8);
    	::svm_debug_form_append_text(svm,f,"Code",30,10);
    	::svm_debug_form_append_checkbox__raw(svm,f,"Debug mode",TRUE);
    	SVM_Value *r = ::svm_debug_form_request(svm,f);
    	if(not r)
    	{
    		RETURN;
    	}
    	SVM_String sc = ::svm_value_string_get(svm,r[1]);
    	std::string rsc = RAW_STRING(sc)+"\n:return\n";
    	SVM_Variable c = ::svm_code_compile(svm,r[0],::svm_value_string_new__raw(svm,rsc.c_str()));
    	if(::svm_variable_type_is_code(svm,c))
    	{
    		SVM_Value_Symbol s = ::svm_value_symbol_new(svm,c,0);
    		if(::svm_value_boolean_get(svm,r[2]))
    		{
    			::svm_debug_breakpoint_add_break(svm,CURRENT(kernel),s);
    		}
    		::svm_processor_call_global(svm,CURRENT(kernel),s,::svm_processor_get_currentpointer(svm,CURRENT(kernel)));
    	}
    	else
    	{
    		::svm_debug_notify(svm,CURRENT(kernel),c);
    	}
    %}

L'utilisation de cette instruction dans une application permet de faire exécuter du code en plus du code de l'application, dans une fonction dédiée.

- L'interface programmatique permet à une extension de récupérer des données depuis l'interface du débugueur au travers d'un formulaire.
- La fonction `svm_debug_form_new` permet de créer un formulaire.
- Les fonctions `svm_debug_form_append_checkbox`, `svm_debug_form_append_selection`, `svm_debug_form_append_integer`, `svm_debug_form_append_string` et `svm_debug_form_append_text` permettent d'ajouter des champs au formulaire.
- La fonction `svm_debug_form_request` permet d'envoyer le formulaire au débugueur, et d'attendre la réponse de l'utilisateur. La fonction retourne un tableau de valeurs qui correspondent aux valeurs des champs dans le même ordre.
- Quand l'application n'est pas lancée en mode débugueur, la création du formulaire est ignorée et la soumission renvoie un pointeur C nul.

# Conclusion

Vous venez de voir comment manipuler le débugueur depuis une extension.

Les fonctions présentées ici permettent simplement d'augmenter la capacité du débugueur à investiguer à l'intérieur des extensions.

Elles permettent aussi d'automatiser des tâches d'investigation comme la gestion des points d'arrêt.

Enfin, les formulaires ouvrent des possiblités d'interférence avec l'application en mode débugueur, en lui apportant des données au travers de l'interface du débugueur.