MOGWAI NANO 0.8 : un débogueur pas à pas directement sur le microcontrôleur

La version 0.8 de MOGWAI NANO est en ligne. Sa grande nouveauté, c’est un débogueur pas à pas intégré : vous lancez un programme en mode debug depuis MOGWAI NANO Studio, et vous le suivez instruction par instruction, pendant qu’il s’exécute sur votre ESP32. L’instruction sur le point d’être exécutée est surlignée dans votre code source, vous pouvez mettre en pause, avancer d’un pas, reprendre, regarder la pile, modifier une variable… comme dans n’importe quel environnement de développement digne de ce nom.

La 0.8 apporte aussi le support du BLE en mode périphérique sur ESP32. Mais commençons par le morceau de choix.


Pourquoi déboguer sur un microcontrôleur, c’est compliqué

Si vous suivez MOGWAI NANO, vous savez comment ça marche : vous écrivez votre programme dans le Studio, avec toute la syntaxe confortable de MOGWAI (if...then...else, while...do, for...do…). Au moment de l’exécution, ce code est désucré, c’est-à-dire transformé en sa forme canonique, une suite de mots RPN bien plus simple, puis envoyé au device qui le parse et l’exécute.

Ce que vous écrivez :

{
if (a 10 >) then { "grand" ? } else { "petit" ? }
}

nano.run

Ce que le device reçoit :

{ a 10 > } eval { "grand" ? } { "petit" ? } IFELSE

C’est parfait pour exécuter, beaucoup moins pour déboguer : le code qui s’exécute n’a plus rien à voir avec celui que vous avez tapé. Impossible de dire à l’utilisateur « votre programme en est là » : les deux formes sont trop différentes pour qu’on puisse faire le lien.

L’idée : décorer le code avec les positions du source

La solution tient en une phrase : en mode debug, le Studio décore le code désucré avec, pour chaque instruction, sa position dans votre source d’origine.

Le device, lui, garde ces informations avec l’instruction pendant qu’il l’exécute. Avant chaque instruction qui peut servir de point de pause, il regarde ce qu’on lui a demandé :

  • Une pause est demandée : il envoie au Studio la position de l’instruction à venir, le Studio la surligne dans l’éditeur, et le device attend.
  • Un délai de trace est actif : il envoie la position de la même façon, puis patiente le temps demandé avant de continuer.

Deux détails ont leur importance :

  • C’est le device qui fait tout le travail. Le parser sait lire les informations de debug quand elles sont là, et les ignore quand elles n’y sont pas. Un programme lancé normalement se comporte exactement comme avant : pas de surcoût, pas de changement.
  • La pause a lieu avant l’instruction surlignée. Si le ? est surligné, il n’a pas encore été exécuté. C’est le comportement habituel d’un débogueur : on regarde l’état du programme juste avant de faire le pas.

Lancer une session : nano.debug et nano.debugall

Pour exécuter un programme en mode debug, il suffit de remplacer nano.run par nano.debug :

{
0 -> 'n'
while (n 5 <) do {
n 1 + -> 'n'
n ?
}
}

nano.debug

Il existe deux modes, qui ne changent rien côté device (c’est le Studio qui décide de la quantité d’informations ajoutées) :

  • nano.debug : le mode minimal. On ne peut s’arrêter que sur les instructions qui comptent vraiment pour suivre un programme : les primitives, les mots, et les listes et records en auto-évaluation.
  • nano.debugall : tous les tokens sont marqués, littéraux compris, pour un pas à pas beaucoup plus fin. Seuls en sont exclus ceux que MOGWAI lui-même déclare non pausables. Le prix à payer : davantage d’informations à parser, donc un démarrage plus long.

Dès qu’une session démarre, un petit panneau de debug apparaît par-dessus l’éditeur, avec trois boutons : Pause, Resume et Step. Il disparaît tout seul à la fin du programme.

Piloter l’exécution

Le panneau

Les boutons s’activent et se désactivent selon l’état de la session, à partir des informations que renvoie le device à chaque instruction :

ÉtatPauseResumeStep
Programme en cours d’exécutionactifinactifinactif
Programme en pause (y compris après un Step)inactifactifactif

Step exécute une seule instruction, puis remet le programme en pause sur la suivante qui peut en être le siège. Resume relance l’exécution normale.

Capture d'écran de l'interface MOGWAI NANO Studio affichant un code de débogage et une console, avec des instructions pour interagir avec un appareil connecté.
Panneau de debug.

Les commandes

Chaque bouton a son équivalent en commande. Elles passent par la connexion TCP comme toutes les commandes nano.*, et pendant qu’un programme tourne, on les tape dans l’onglet Console MOGWAI du Studio :

CommandeEffet
nano.debug.haltDemande une pause à la prochaine instruction
nano.debug.stepAvance d’une seule instruction
nano.debug.resumeReprend l’exécution
150 nano.debug.tronRalentit l’exécution à une instruction toutes les 150 ms, en affichant l’instruction courante dans l’éditeur, comme une trace
nano.debug.troffArrête la trace (équivaut à 0 nano.debug.tron)

Hors session de debug, toutes ces commandes sont simplement ignorées.

Les points d’arrêt : ¤

Vous pouvez aussi demander une pause depuis votre code. La primitive debug.halt fait exactement cela, et pour l’écrire plus vite, elle a un alias : ¤ (comme ? est un alias de console.println).

{
0 -> 'n'
while (n 5 <) do {
n 1 + -> 'n'
¤ # point d'arrêt : le programme se met en pause ici
n ?
}
}

nano.debug

debug.tron et debug.troff existent aussi comme primitives : on peut ainsi encadrer la portion de programme qu’on veut tracer, sans toucher au reste. Bonne nouvelle, ces trois primitives sont communes à MOGWAI desktop et à MOGWAI NANO et se comportent de la même façon. Et comme elles ne font strictement rien hors mode debug, on peut les laisser dans un programme sans conséquence.

Capture d'écran de l'interface de studio MOGWAI NANO montrant un code de débogage dans la console. Les instructions incluent des commandes pour réinitialiser et exécuter des actions sur un appareil connecté.
Utilisation d’un point d’arrêt.

Voir la pile : debug.stack et nano.stack

Dans un langage à pile, savoir ce qu’elle contient est la moitié du débogage. Deux outils s’en chargent, utilisables en run normal comme en debug :

  • debug.stack (sur le device) empile une chaîne qui représente la pile telle qu’elle était à l’appel.
  • nano.stack (côté Studio) demande au device, par TCP, cette représentation et l’affiche dans la console MOGWAI.

Avec 50 60 « Hello » (1 2 3) true [x: 50 y: 20] sur la pile du device :

> nano.stack

STACK
0: [x: 50 y: 20]
1: true
2: (1 2 3)
3: "Hello"
4: 60
5: 50

Le sommet de la pile est en 0:. C’est particulièrement pratique pendant une pause.

Exécuter du code pendant une pause

C’est le détail qui change tout : quand le programme est en pause, la console reste utilisable. Vous pouvez y taper du code et l’exécuter pour inspecter ou modifier des variables, ajouter ou retirer des éléments de la pile, appeler une fonction du programme…

Ce code s’exécute normalement, sans pause ni surlignage, et tout ce qu’il fait reste effectif à la reprise : ce qui est fait est fait. Vous pouvez donc corriger une valeur à la volée, puis reprendre pour voir si le programme se comporte mieux.

Capture d'écran de l'interface du logiciel MOGWAI NANO Studio affichant un code de débogage et des instructions de console.
Exécuter du code pendant une pause.

Et les événements ?

Un gestionnaire d’événement peut être suivi pas à pas comme s’il s’agissait d’une fonction du code source. Vous pouvez donc déboguer la réaction à un changement de GPIO, à un timer ou à un message du Studio exactement comme le reste du programme.

Les limites actuelles

Cette première version du débogueur est déjà très utilisable, mais il vaut mieux être honnête sur ce qu’elle ne fait pas encore :

  • Le parsing est plus long en mode debug. Il y a beaucoup plus d’informations à lire, et le device a des moyens limités. C’est le prix à payer pour pouvoir déboguer, et il est plus élevé avec nano.debugall qu’avec nano.debug. Je continue à travailler sur la question.
  • Le pas à pas est de type « step-into » uniquement : on avance jusqu’à la prochaine instruction pausable, sans possibilité de passer par-dessus un appel de fonction (step-over) ni d’en sortir (step-out).
  • Les tâches ne peuvent pas encore s’exécuter en mode debug.
  • Les unités sont chargées sans informations de debug, elles ne peuvent donc pas être parcourues pas à pas pour l’instant.
  • debug.step et debug.resume n’existent que comme commandes du Studio, pas comme primitives utilisables dans un script. Un programme peut se mettre en pause lui-même avec ¤, mais ne peut pas se relancer tout seul.

Le BLE en mode périphérique (ESP32)

L’autre nouveauté majeure de la 0.8 : votre ESP32 peut maintenant se comporter comme un périphérique Bluetooth Low Energy, c’est-à-dire se faire découvrir par un téléphone ou un ordinateur, exposer des services et des caractéristiques, et échanger des données avec eux.

La philosophie est la même que pour le reste de MOGWAI NANO : un seul record décrit tout le périphérique (son nom, ses services, ses caractéristiques, les propriétés de chaque caractéristique et sa valeur initiale). Un petit jeu de primitives le pilote :

  • ble.peripheral.create crée le périphérique à partir de la description ;
  • ble.peripheral.start et ble.peripheral.stop le démarrent et l’arrêtent ;
  • ble.peripheral.setValue et ble.peripheral.getValue écrivent et lisent la valeur d’une caractéristique ;
  • ble.peripheral.notify prévient un appareil connecté qu’une valeur a changé.

Trois événements, BLE_PERIPHERAL_DID_START, BLE_PERIPHERAL_DID_STOP et BLE_PERIPHERAL_VALUE_DID_CHANGE, permettent à votre programme de réagir aux connexions et aux écritures venant de l’extérieur. L’appareil déclare le skill 'BLE-P', que l’on peut tester avec hasSkill.

Quelques précisions importantes :

  • Pour l’instant, c’est réservé à l’ESP32. Le support est dans son propre assemblage (MogwaiNanoBlePeripheral), référencé uniquement par MogwaiNanoEsp32. Il n’est pas disponible sur Pico W, et d’autres cibles viendront.
  • Seul le premier service défini est annoncé. Tous les services et toutes les caractéristiques sont bien créés et accessibles une fois connecté, mais la découverte BLE n’en expose qu’un seul.

Un petit confort au passage

View > Font... s’applique désormais à tous les volets et plus seulement à l’éditeur : l’éditeur et les quatre onglets de sortie (Console MOGWAI, Debug MOGWAI, Console NANO, Debug NANO) suivent la police et la taille choisies. Il suffit d’augmenter la taille une fois pour que tout soit lisible, par exemple pour un enregistrement d’écran.

Où la trouver

Et ensuite ?

Le débogueur est un début. Les prochaines étapes naturelles sont le step-over et le step-out, l’ouverture des tâches et des unités au debug, la réduction du temps de parsing en mode debug, et sans doute de vraies primitives pour debug.step et debug.resume. Côté BLE, l’idée est d’élargir les cibles et, plus tard, de couvrir aussi le mode « central ».

Si vous avez un ESP32 dans un tiroir, c’est le bon moment pour l’essayer : un programme qui plante ou qui se comporte bizarrement, c’est exactement ce pour quoi on a besoin d’un débogueur. Et si vous croisez un cas qui ne se laisse pas déboguer, ou un comportement surprenant, n’hésitez pas à l’ouvrir sur le suivi des tickets.

Bon débogage !

Laisser un commentaire

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture