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.
Pour commencer, créez le canevas de l'extension dans le fichier debug.svm_plugin en utilisant ce code :
PLUGIN debug
DEFINE
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.
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.
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.
svm_debug_break
crée un micro point d'arrêt dans le corps d'une instruction.svm_debug_notify
crée une notification dans le débugueur sans arrêter l'exécution du code.Les extensions peuvent également manipuler les points d'arrêt portant sur le code de la machine virtuelle.
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.
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.
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.
:debug
depuis le code des extensions.svm_debug_breakpoint_add_break
et svm_debug_breakpoint_remove_break
gèrent les points d'arrêt de code via un symbole.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.svm_debug_breakpoint_add_interruption
et svm_debug_breakpoint_remove_interruption
gèrent les points d'arrêt d'interruption.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.
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é.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.
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.
svm_debug_form_new
permet de créer un formulaire.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.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.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.