Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » micropython dans KhiCAS
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

micropython dans KhiCAS

Posté le 01/02/2022 14:22

J'ai avance sur le portage de MicroPython dans KhiCAS pour graph 90, ca compile, mais j'ai un crash lors de l'initialisation de MicroPython, lie a l'allocation memoire. Ca plante au 1er appel d'allocation dans la fonction mp_init (py/runtime.c) sur une demande d'allocation de 24 bits, lorsque le gc est actif, le pointeur renvoye est nul. Si je desactive le garbage collector, ca ne marche pas non plus, ca plante un peu plus tard a la 5eme allocation memoire (avec kmalloc de gint, mais c'est pas kmalloc la cause, car si on desactive les arena en ram en ne conservant que le malloc systeme ca plante tout autant).
C'est probablement lie a une mauvaise configuration. Mon mpconfigport.h est https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/mpconfigport.h, j'ai beau le comparer avec celui de CasioPython je ne vois rien qui explique le crash (https://github.com/Zezombye/casiopy/blob/master/ports/minimal/mpconfigport.h). La compilation se fait avec des commandes du type
sh3eb-elf-gcc -DNUMWORKS -DMICROPY_LIB -I. -I.. -I../py  -Ibuild -Wall  -ansi -std=gnu99 -DFFCONF_H=\"lib/oofatfs/ffconf.h\" -Os -g   -DMODULE_ULAB_ENABLED=1 -mb -m4a-nofpu  -mhitachi  -fdata-sections -ffunction-sections -fno-strict-aliasing -fno-exceptions  -c -MD -o build/./main.o main.c
(le flag NUMWORKS sert a contourner un bug de sprintf dans un module additionnel, ca n'a rien a voir avec le code de micropython)
Le source complet (giac+mp) est la
https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/giac2.tgz (se compile avec ./mklib dans micropython-1.12/fxcg et make dans giac2)
L'addin en 2 parties https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.g3a et https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.ac2
Pour lancer MicroPython depuis le shell de KhiCAS, faire shift-PRGM puis 8 pour mettre python( en ligne de commande et valider. Le crash apparait avec le print du malloc fautif que j'ai rajoute dans py/malloc.c, on devine aussi le msg d'erreur de micropython (FATAL uncaught NLR ...) genere suite au pointeur nul (non catche par un nlr_push).


Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 03/02/2022 18:53 | #


Ce n'est pas possible dans un add-in classique ; pour faire ce genre de manœuvres il faut contrôler les interruptions. Et de toute façon même quand on le peut on évite parce qu'en cas de plantage avec un MMU modifié on peut bricker une calto (AHelper a eu ce problème avec une Prizm à l'époque où il voulait implémenter des libs dynamiques).

La Graph 90+E est juste un successeur à la Prizm, qui n'avait que 2 Mo de RAM. Elle a beau avoir une puce RAM de 8 Mo, tout ce qu'on peut en déduire c'est que le R&D se sent à l'étroit dans 2 Mo et apprécie de pré-équiper le nouveau modèle avec une puce plus grosse pour qu'il soit compatible avec les futures mises à jour dont ils anticipent qu'elles utiliseront cet espace supplémentaire.

La même chose s'est produite avec la série monochrome ; elles ont eu des puces de 512 ko de RAM tout en n'en utilisant que 256 ko pendant 10 ans, et puis la Graph 35+E II est arrivée avec MicroPython et soudain la mémoire en plus est devenue nécessaire.

D'où le fait que j'insiste autant sur la stabilité de la manœuvre : cette RAM est probablement gratuite pour l'instante mais sera certainement utilisée plus tard, et on ne veut pas que quelqu'un dans 5 ans avec un OS plus lourd bricke quelque chose en utilisant un binaire qui traîne sur le forum.

Oui la fx-CG 50 a tout autant de mémoire, pour autant que je le sache matériellement elle est identique à la Graph 90+E.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 03/02/2022 19:00 | #


Merci pour toutes ces precisions, donc je continue comme maintenant. Je suppose que le test que tu m'as mis ci-dessus avec la branche Graph 90+e va aussi marcher pour les fxcg50?
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 03/02/2022 19:51 | #


Oui c'est celui ce que j'utilise pour gint. Note que je n'ai pas d'accès régulier à une fx-CG 50 pour tester mon code donc les informations que j'ai dessus sont toujours majorées à 80% de fiabilité.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 04/02/2022 09:34 | #


En fait il est impossible de faire le test des 0 pour avoir la memoire libre, car des que khicas a ete lance une fois, les fois suivantes la memoire correspondante n'est plus a 0... Tester la version d'OS ne va pas non plus etre tres utile, car on ne peut pas savoir a l'avance si l'OS va utiliser ces 3Mo, ou alors il faudrait bloquer l'addin a priori sur des versions d'OS non testees.
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 04/02/2022 09:41 | #


En fait il est impossible de faire le test des 0 pour avoir la memoire libre, car des que khicas a ete lance une fois, les fois suivantes la memoire correspondante n'est plus a 0...

Ce serait bon de la réinitialiser en quittant alors. Un petit SetQuitHandler() (je suppose que tu en as déjà un) pourrait le faire aisément.

Tester la version d'OS ne va pas non plus etre tres utile, car on ne peut pas savoir a l'avance si l'OS va utiliser ces 3Mo, ou alors il faudrait bloquer l'addin a priori sur des versions d'OS non testees.

Oui c'est exactement l'idée. Je sais que c'est restrictif, mais ce n'est pas très dommageable d'avoir une option "attention: instable" qu'on enlève après un test sur chaque nouvelle version d'OS, par rapport à bricker une calto.

Bien sûr, après... beaucoup d'années passées à chasser des paquets de bugs fourbes et/ou bas niveau dans gint, je suis un peu paranoïaque en termes de stabilité. Mais modulo tout le recul qui va bien, ma recommandation c'est ça (c'est ce que je fais pour CGDoom).
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 04/02/2022 12:38 | #


La reinitialisation a 0 en sortie ne marchera pas s'il y a un crash ou un reset, et ensuite tu ne pourrais plus lancer l'addin.
Pour la version d'OS, ca me parait quand meme un peu parano, mais bon je peux rajouter un test au lancement et arreter si la version d'OS est superieure a la derniere version testee, a condition que ce soit assez simple (en particulier pour l'addin light ou je n'ai quasiment plus de place disponible) et il faudrait un mecanisme permettant de passer outre (par exemple existence d'un fichier avec un nom specifique, par exemple nomemchk.ac2?)
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 04/02/2022 13:16 | #


La reinitialisation a 0 en sortie ne marchera pas s'il y a un crash ou un reset, et ensuite tu ne pourrais plus lancer l'addin.

Hmm... bonne remarque mais ça marche dans CGDoom, c'est curieux.

Comme mécanisme super minimal pour forcer le démarrage tu pourrais tester si une certaine touche est pressée au démarrage, genre (-). Mais si tu utilises cette zone pour charger l'add-in en deux parties la place ne devrait pas être un facteur limitant ?
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 04/02/2022 18:35 | #


Pour la place, je pensais a l'addin light, ca peut etre interessant de disposer de 3Mo de RAM.
Pour la version, si j'appelle GlibGetOSVersionInfo quels sont les caracteres a et b maxis? Est-ce que ce sont les memes sur fxcg50?
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 04/02/2022 18:38 | #


https://prizm.cemetech.net/index.php?title=GlibGetOSVersionInfo

Je suppose que c'est en entiers plutôt qu'en caractères (ie. 3/6 plutôt que '3'/'6'). L'autre option c'est d'utiliser GetOSVersion() qui te renvoie une chaîne de caractères. Dans CGDoom, je fais :

/* Allow the user to use memory past the 2 MB line on tested OS versions */
int allow_experimental_RAM = 0;
char const *osv = GetOSVersion();
if(!strncmp(osv, "03.", 3) && osv[3] <= '6') // 3.60 or earlier
        allow_experimental_RAM = 1;
else
        CGD_Options.EnableExperimentalMemory = 0;

Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 04/02/2022 20:30 | #


Je ne vois pas GetOSVersion() sur https://prizm.cemetech.net/index.php?title=Category:Syscalls
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 04/02/2022 20:32 | #


Ah oui lol désolé :

#define GetOSVersion() ((char *)0x80020020)

Oui la vérité c'est que c'est juste écrit en ROM cette affaire. (Attention la chaîne n'a pas de \0 final.)
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 04/02/2022 21:31 | #


Merci!
Voila, j'ai rajoute le test de version dans la version complete, j'espere que ca marche aussi sur fxcg50. Si le test echoue, il y a une boite de dialogue qui permet de forcer a continuer. Pour la version light, je laisse pour le moment la memoire a environ 350k, c'est deja plus de 3 fois plus de memoire qu'avant. Je n'ai pas encore ajoute la memoire restante des 3M sur l'addin en 2 morceaux, j'attends de voir combien il en restera finalement.

Ca commence a marcher pas mal
https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.ac2
https://www-fourier.univ-grenoble-alpes.fr/~parisse/tmp/khicas.g3a
Il faut faire shift-PRGM 8 ou taper python sur une ligne de commande vide puis valider pour passer de Xcas a MicroPython (et xcas pour revenir a Xcas). F1, F2 et shift-F1 a shift-F6 permet d'acceder a quelques modules (il faut encore que je retravaille certains menus).

Ajouté le 05/02/2022 à 14:52 :
Je me suis rejoui trop vite, il y a un probleme avec micropython. Des qu'on ecrit un script de plus de 2 a 3 lignes, on a des problemes, du genre erreur de memoire en essayant d'allouer plusieurs centaines de millions d'octets, ou bien des crashs. J'ai essaye de regarder ou ca se passe dans l'interpreteur, mais je ne trouve aucune piste. Je pense que la gestion de la memoire est en cause, ca rejoint le probleme de l'interpreteur qui crashe des le debut si on n'utilise pas le garbage collector et le printf que j'ai du rajouter a l'initialisation sans comprendre pourquoi. J'ai bien peur qu'il ne soit pas possible d'avoir micropython dans KhiCAS3d du coup parce que je n'ai aucune idee de ce qu'il faut faire, c'est bien dommage parce que j'ai pas mal bosse pour faire marcher les modules et ca ne servira a rien.

Ajouté le 05/02/2022 à 20:59 :
Analyse des crash (essaye avec micropython-1.12 et 1.18)
avec gc enabled, le parse de ((A[x+1])) pose probleme environ une fois sur 2, ca semble lie a l'utilisation de gc_realloc. Si on change les valeurs par defaut dans mpconfigport.h en

#define MICROPY_ALLOC_PARSE_RULE_INIT (256)
#define MICROPY_ALLOC_PARSE_RESULT_INIT (256)

ca parse. Mais on a des crash plus tard.

sans gc enable, le crash arrive dans mp_init (fichier runtime.c, je l'ai renomme mp_init_py dans mon arborescence de micropython pour eviter le clash avec tommath), plus precisement lors de l'appel de
mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(dict_main)), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));

qui appelle dans objdict.c mp_map_lookup(&self->map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
qui plante dans map.c (je n'ai pas regarde ou).

Casio a bien reussi a porter micropython, donc ca doit etre possible!
Mais perso, j'en reste la, car je n'ai pas de support debug avec addin en 2 parties (deja en 1 partie c'est parfois aleatoire).
Je pense qu'il faudrait porter micropython sans Xcas avec un shell pour trouver une config qui marche. C'est trop de travail... Et puis, avec les Ko octets economises je peux peut-etre porter la lib multi-precision de quickjs, voire meme l'interpreteur javascript de quickjs.

Ajouté le 06/02/2022 à 10:08 :
Je pense avoir trouve!
C'etait memset de la lib standard C qui utilise dans libfxcg le syscall 0x1511, qui a l'air foireux. En mettant du code C standard a la place, ca a l'air beaucoup mieux!
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 06/02/2022 10:24 | #


Désolé, tu vas assez vite je teste pas de façon assez réactive pour te donner les bons retours !

Je confirme 100% ce bug, j'ai eu exactement le même dans CGDoom. J'en ai eu d'autres même, donc pendant que j'y suis vérifie peut-être aussi les cas suivants. Il y a un bug dans le calloc() :

diff --git a/libfxcg/misc/calloc.c b/libfxcg/misc/calloc.c
index 295edd0..4dfda18 100644
--- a/libfxcg/misc/calloc.c
+++ b/libfxcg/misc/calloc.c
@@ -9,7 +9,7 @@ void *sys_calloc(int elements, int elementSize){
     if (tmp==0)
         return 0;

-    memsetZero(tmp, p);
+    memset(tmp, 0, p);
     return tmp;
}

Le memsetZero() est une fonction codée en assembler qui n'est techniquement pas du SuperH valide (manque un delay slot !!) et semble avoir été écrite par quelqu'un qui a plus d'expérience en x86. Elle ne marche pas.

J'ai aussi eu un problème d'alignement dans le linker script ; il me semble que tes scripts sont tous bons sur ce point, mais tant qu'à faire autant vérifier :

diff --git a/toolchain/prizm.x b/toolchain/prizm.x
index 57d7d50..3c3a145 100755
--- a/toolchain/prizm.x
+++ b/toolchain/prizm.x
@@ -36,7 +36,7 @@ SECTIONS
         } > rom

         /* RW initialized data, VMA in RAM but LMA in ROM */
-        .data : {
+        .data : ALIGN(4) {
                 _datald = LOADADDR(.data) ;
                 _sdata = . ;
                 *(.data)
@@ -45,7 +45,7 @@ SECTIONS
         } >ram AT>rom

         /* Uninitialized data (fill with 0), in RAM */
-        .bss : {
+        .bss ALIGN(4) : ALIGN(4) {
                 _bbss = . ;
                 *(.bss)
                 *(.bss*)

J'ai aussi eu un bug avec le sprintf() qui n'ajoute pas le NUL final :

diff --git a/libc/printf.c b/libc/printf.c
index de197db..954a59b 100755
--- a/libc/printf.c
+++ b/libc/printf.c
@@ -286,6 +286,7 @@ static int _v_printf(const char *fmt, va_list ap_in, writer_t writer, const void
         formatter(&ap, writer, warg, f);
     }
out:
+    writer(warg, 0);
     va_end(ap);
     return count;
}

Je croise les doigts pour que tu y arrives, ces bugs-là sont chiants mais la seule solution c'est de tester à fond pour localiser les problèmes. Moi aussi ça m'embêterait que tu passes tout ce temps dessus pour n'arriver à rien... merci d'avoir persévéré pour trouver ces problèmes qui sont dans l'environnement en tous cas.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 06/02/2022 12:19 | #


Voila, j'ai fait les modifs pour calloc (je ne l'utilise pas moi-meme, et apparamment micropython non plus, mais on ne sait jamais...) j'ai verifie les fichiers ld, pour printf j'avais deja ete confronte au probleme et j'etais arrive a la meme solution.

Ajouté le 07/02/2022 à 16:44 :
J'ai a nouveau un probleme, pour permettre l'interruption d'un programme Python. J'ai repris du code qui marche pour interrompre un programme Xcas. Le shell KhICAS appelle l'evaluateur de MicroPython de la facon suivante

...
  ctrl_c_py=0;
  execution_in_progress_py = 1;
  set_abort_py();
  int res= micropy_eval(line);
  clear_abort_py();
  execution_in_progress_py = 0;
  if (ctrl_c_py){
    while (confirm4("Interrupted","F1/F6: ok",true,100)==-1)
      ; // insure ON has been removed from keyboard buffer
  }  
...

ou les fonctions set_abort_py/clear_abort_py sont definies ci-dessous, et ou la VM de MicroPython appelle frequemment la fonction micropython_port_vm_hook_loop() ci-dessous (j'ai verifie que c'etait bien le cas en y mettant un printf).
set_abort_py est bien appele car le hourglass apparait.
Si je lance un programme un peu long et que j'appuie sur AC/ON, l'interruption ne semble pas detectee, tout se passe comme si ctrl_c_py restait a 0 (pas de message affiche par printf). Pourtant a la fin du programme, le message Interrupted apparait donc ctrl_c_py a bien ete mis a 1 et le printf("AC/ON pressed") est aussi execute. Je ne comprends pas ce qui se passe, on dirait que ctrl_c_py n'est pas teste dans micropython_port_vm_hook_loop, pourtant j'ai bien declare ctrl_c_py comme volatile. Ou bien le test clavier est desactive pendant l'execution, mais je ne vois pas comment. Ou comme si le timer ne fonctionnait pas pendant l'execution de la VM Python.


void mp_keyboard_interrupt(void) {
  printf("Keyboard Intr\n");
  MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
}

int execution_in_progress_py = 0; // set to 1 during program execution

int intr_key=KEY_PRGM_ACON;
volatile int ctrl_c_py=false;
static int PRGM_GetKey_() {
  unsigned char buffer[12];
  PRGM_GetKey_OS( buffer );
  return ( ( buffer[1] & 0x0F) *10 + ( ( buffer[2] & 0xF0) >> 4 ));
}
static void check_execution_abort() {
  if (execution_in_progress_py) {
    HourGlass();
    short unsigned int key = PRGM_GetKey_();
    if (key == intr_key){
      if (!ctrl_c_py)
    printf("ACON pressed\n");
      ctrl_c_py=1;
    }
  }
}

static int aborttimer = 0;
void set_abort_py(){
  aborttimer = Timer_Install(0, check_execution_abort, 100);
  if (aborttimer > 0) { Timer_Start(aborttimer); }
}

void clear_abort_py(){
  if (aborttimer > 0) {
    Timer_Stop(aborttimer);
    Timer_Deinstall(aborttimer);
  }
}


void mp_hal_set_interrupt_char(int c){ intr_key=c;}

int micropython_port_vm_hook_loop() {
  /* This function is called very frequently by the MicroPython engine. We grab
   * this opportunity to interrupt execution and/or refresh the display on
   * platforms that need it. */

  /* Doing too many things here slows down Python execution quite a lot. So we
   * only do things once in a while and return as soon as possible otherwise. */
  if (ctrl_c_py)
    mp_keyboard_interrupt();
  return 1;
}


Ajouté le 07/02/2022 à 19:53 :
Bon, je vais faire sans timer, en mettant le code de detection de touche directement dans la fonction micropython_port_vm_hook_loop().
J'ai un autre truc surprenant lie a la gestion de la memoire (toujours). D'abord le gc ne marche toujours pas. Ensuite, la memoire disponible diminue rapidement sous Python. J'utilise le code ci-dessous pour afficher la memoire disponible:

kmalloc_arena_t static_ram = { 0 },ram3M={0};
int get_free_memory(){
  kmalloc_gint_stats_t * s=kmalloc_get_gint_stats(&static_ram);
  if (!s) return 0;
  int res=s->free_memory;
  s=kmalloc_get_gint_stats(&ram3M);
  if (!s) return res;
  res += s->free_memory;
  return res;
}

Du coup, ma question, free_memory c'est le total de memoire disponible ou juste celle du plus grand bloc contigu?
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 07/02/2022 20:24 | #


Je ne vois pas non plus de problème avec ton code. Je l'ai regardé droit dans les yeux pendant un bon moment, mais non. Le timer doit avoir un truc qui coince. :x

free_memory c'est bien la mémoire totale disponible.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 07/02/2022 20:43 | #


Et en effet, quand on fait des boucles en MicroPython, la memoire diminue regulierement, et quand elle atteint 0 on a un crash.
Je soupconne que les gens utilisent MicroPython systematiquement avec GC, et donc qu'il y a des fuites memoire suite a des oublis d'appels de free (qui ne doit pas etre necessaire avec GC). Ou bien un autre bug dans la lib standard C Casio, Je ne pense pas que ca vient de kmalloc, car cote Xcas, je n'ai pas vu de fuite memoire.
Je vais peut-etre essayer de faire un build de MicroPython dans xcas desktop sans GC et tester avec valgrind.
Sinon, il faudrait arriver a comprendre pourquoi si on active GC ca crashe. Peut-etre une histoire d'endianness ?
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 07/02/2022 20:50 | #


Endianness je doute, tu l'as définie dans le fichier de config.

Ce message dit que MICROPY_GC_ALLOC_THRESHOLD doit absolument être à 1 dans le fichier de config, ce qui n'est pas le cas dans le tien. Sinon je tâtonne, comme toi.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 07/02/2022 21:27 | #


Je pensais que peut-etre ca n'avait pas ete (beaucoup) teste sur des processeurs big endian.
J'ai mis MICROPY_GC_ALLOC_THRESHOLD a 1, sans aucun changement ce qui semble assez logique au vu du code source, c'est utilise dans gc.c et le module correspondant.
En tout cas, il semble bien qu'il y a des fuites memoire, a commencer par mon propre code qui n'a ete teste qu'avec gc.
Lorsque j'entre 1 en ligne de commande, je perd 184 octets, lorsque je tape sur la touche VARS j'en perd 40. Je vais essayer de tracer pourquoi, avec un build sur PC en mode malloc, gdb et valgrind.

Ajouté le 07/02/2022 à 21:44 :
Pas sur que ce soit le meilleur plan, parce que tracer tous les objets Python qu'on cree pour les detruire avec free ca risque d'etre coton. Je me demande si la piste comprendre ce qui cloche avec GC n'est pas plus prometteuse...

Ajouté le 08/02/2022 à 10:16 :
J'ai une piste pour le probleme du garbage collector (je laisse pour l'instant tomber l'idee de fixer les memory leaks, ca serait trop de code a modifier et en plus je n'ai pas vu de routine pour liberer des objets micropython).
Si j'ai bien compris, le GC commence par lister les "root pointers" en regardant ses propres listes de dictionnaires, etc. et en ajoutant les objets references sur la pile et dans les registres. Or ce dernier point a l'air d'avoir une implementation specifique pour les processeurs "habituels" (x86 et arm) et je pense que tu (Lephenixnoir) es en mesure de voir ce qui ne va probablement pas avec l'implementation generique ou/et de creer le code equivalent pour sh.
Voici le fichier py/gccollect.c (hors en-tete de copyright)

#include <stdio.h>

#include "py/mpstate.h"
#include "py/gc.h"

#if MICROPY_ENABLE_GC

// Even if we have specific support for an architecture, it is
// possible to force use of setjmp-based implementation.
#if !MICROPY_GCREGS_SETJMP

// We capture here callee-save registers, i.e. ones which may contain
// interesting values held there by our callers. It doesn't make sense
// to capture caller-saved registers, because they, well, put on the
// stack already by the caller.
#if defined(__x86_64__)
typedef mp_uint_t regs_t[6];

STATIC void gc_helper_get_regs(regs_t arr) {
    register long rbx asm ("rbx");
    register long rbp asm ("rbp");
    register long r12 asm ("r12");
    register long r13 asm ("r13");
    register long r14 asm ("r14");
    register long r15 asm ("r15");
#ifdef __clang__
    // TODO:
    // This is dirty workaround for Clang. It tries to get around
    // uncompliant (wrt to GCC) behavior of handling register variables.
    // Application of this patch here is random, and done only to unbreak
    // MacOS build. Better, cross-arch ways to deal with Clang issues should
    // be found.
    asm("" : "=r"(rbx));
    asm("" : "=r"(rbp));
    asm("" : "=r"(r12));
    asm("" : "=r"(r13));
    asm("" : "=r"(r14));
    asm("" : "=r"(r15));
#endif
    arr[0] = rbx;
    arr[1] = rbp;
    arr[2] = r12;
    arr[3] = r13;
    arr[4] = r14;
    arr[5] = r15;
}

#elif defined(__i386__)

typedef mp_uint_t regs_t[4];

STATIC void gc_helper_get_regs(regs_t arr) {
    register long ebx asm ("ebx");
    register long esi asm ("esi");
    register long edi asm ("edi");
    register long ebp asm ("ebp");
#ifdef __clang__
    // TODO:
    // This is dirty workaround for Clang. It tries to get around
    // uncompliant (wrt to GCC) behavior of handling register variables.
    // Application of this patch here is random, and done only to unbreak
    // MacOS build. Better, cross-arch ways to deal with Clang issues should
    // be found.
    asm("" : "=r"(ebx));
    asm("" : "=r"(esi));
    asm("" : "=r"(edi));
    asm("" : "=r"(ebp));
#endif
    arr[0] = ebx;
    arr[1] = esi;
    arr[2] = edi;
    arr[3] = ebp;
}

#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)

typedef mp_uint_t regs_t[10];

STATIC void gc_helper_get_regs(regs_t arr) {
    register long r4 asm ("r4");
    register long r5 asm ("r5");
    register long r6 asm ("r6");
    register long r7 asm ("r7");
    register long r8 asm ("r8");
    register long r9 asm ("r9");
    register long r10 asm ("r10");
    register long r11 asm ("r11");
    register long r12 asm ("r12");
    register long r13 asm ("r13");
    arr[0] = r4;
    arr[1] = r5;
    arr[2] = r6;
    arr[3] = r7;
    arr[4] = r8;
    arr[5] = r9;
    arr[6] = r10;
    arr[7] = r11;
    arr[8] = r12;
    arr[9] = r13;
}

#else

// If we don't have architecture-specific optimized support,
// just fall back to setjmp-based implementation.
#undef MICROPY_GCREGS_SETJMP
#define MICROPY_GCREGS_SETJMP (1)

#endif // Arch-specific selection
#endif // !MICROPY_GCREGS_SETJMP

// If MICROPY_GCREGS_SETJMP was requested explicitly, or if
// we enabled it as a fallback above.
#if MICROPY_GCREGS_SETJMP
#include <setjmp.h>

typedef jmp_buf regs_t;

STATIC void gc_helper_get_regs(regs_t arr) {
    setjmp(arr);
}

#endif // MICROPY_GCREGS_SETJMP

// this function is used by mpthreadport.c
void gc_collect_regs_and_stack(void);

void gc_collect_regs_and_stack(void) {
    regs_t regs;
    gc_helper_get_regs(regs);
    // GC stack (and regs because we captured them)
    void **regs_ptr = (void**)(void*)&regs;
    gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)&regs) / sizeof(uintptr_t));
}

void gc_collect(void) {
    //gc_dump_info();

    gc_collect_start();
    gc_collect_regs_and_stack();
    #if MICROPY_PY_THREAD
    mp_thread_gc_others();
    #endif
    gc_collect_end();

    //printf("-----\n");
    //gc_dump_info();
}

#endif //MICROPY_ENABLE_GC


Si ca ne marche pas, on peut peut-etre utiliser un autre GC pour pouvoir se passer du GC de micropython. (je ne me vois pas fixer le code du GC de micropython...), par exemple
boehm's gc

Ajouté le 08/02/2022 à 10:42 :
La piste semble bonne. J'ai repris le code de casiopython, et la ca a l'air de marcher (je teste le Mandelbrot).

void gc_collect(void) {
    // WARNING: This gc_collect implementation doesn't try to get root
    // pointers from CPU registers, and thus may function incorrectly.
    void *dummy;
    gc_collect_start();
    gc_collect_root(&dummy, ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
    gc_collect_end();
    //gc_dump_info();
}


Ajouté le 08/02/2022 à 10:46 :
Bon, pas encore. Mandelbrot passe, mais j'ai un plantage a l'interpretation du triangle de Pascal...
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 08/02/2022 10:59 | #


Oui ok vu le truc là c'est pas surprenant que ça plante. Je pense que tu sais comment marche un GC de façon générale ; il part du principe que tous les objets sont accessibles à partir de certains "root pointers", qui sont en gros les registres actuels. Ça inclut le registre de pile, donc en fait c'est assez raisonnable.

Le code de gccollect.c (je ne sais pas exactement lequel tu utilises) contient du code spécifique à quelques archis pour récupérer les valeurs des registres, et sinon utilise setjmp(). L'idée c'est que généralement le jmp_buf c'est juste un tableau contenant la valeurs des registres importants, et c'est le cas avec libfxcg (gint aussi d'ailleurs).

Le doute que j'ai c'est comment gc_collect_root() se débrouille pour supposément déterminer ce qui est un pointeur et ce qui n'en est pas. Après tout, les valeurs peuvent être quelconques, y compris non initialisées, et je soupçonne que tes bugs viennent en partie de là.

// ptr should be of type void*
#define VERIFY_PTR(ptr) ( \
    ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0          /* must be aligned on a block */ \
    && ptr >= (void *)MP_STATE_MEM(gc_pool_start)        /* must be above start of pool */ \
    && ptr < (void *)MP_STATE_MEM(gc_pool_end)           /* must be below end of pool */ \
    )

Les zones de début et fin sont celles données dans gc_init(). Pour toi c'est quoi ?

Dans tous les cas si tu veux avoir un truc à peu près consistant tu peux ajouter cette version spécifique SuperH :

#elif defined(__sh3__)

typedef mp_uint_t regs_t[8];

STATIC void gc_helper_get_regs(regs_t arr) {
    register long r8 asm ("r8");
    register long r9 asm ("r9");
    register long r10 asm ("r10");
    register long r11 asm ("r11");
    register long r12 asm ("r12");
    register long r13 asm ("r13");
    register long r14 asm ("r14");
    register long r15 asm ("r15");
    arr[0] = r8;
    arr[1] = r9;
    arr[2] = r10;
    arr[3] = r11;
    arr[4] = r12;
    arr[5] = r13;
    arr[6] = r14;
    arr[7] = r15;
}

Là encore les registres concernés ne seront pas tous des pointeurs, mais c'est un problème pour VERIFY_PTR().
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Parisse Hors ligne Membre Points: 484 Défis: 0 Message

Citer : Posté le 08/02/2022 12:27 | #


Je ne sais pas comment fonctionne un GC, mais je devine comment il demarre, en partant de tous les pointeurs adressables et en parcourant les arborescences pour marquer ceux qui sont actifs.
J'appelle gc_init avec la zone allouee par malloc, ici 0x08134fa4 avec une taille de 64k

...
  char *heap = malloc(heap_size);
  printf("mp_init %d : %p\n", heap_size, heap);
  if(!heap)
    {
      return 0;
    }
  gc_init(heap, heap + heap_size - 1);

  mp_init_py();
...

J'ai essaye avec ton code mais ca ne marche toujours pas avec le triangle de Pascal. Le code Python est le suivant

# Triangle de Pascal pour calculer les coefficients binomiaux
# on calcule une ligne en fonction de la precedente
def Pascal(n):
    # local tableau,ligne,prec,j,k;
    tableau=[[1]]
    for j in range(1,n+1):
        ligne=[1]
        prec=tableau[j-1]
        for k in range(1,j):
            ligne.append(prec[k-1]+prec[k])
        ligne.append(1)
        tableau.append(ligne);
    return tableau

# test
#Pascal(6)

D'abord je suis oblige d'augmenter les valeurs par defaut suivantes pour que ca parse, sinon c'est le crash
#define MICROPY_ALLOC_PARSE_RULE_INIT (256)
#define MICROPY_ALLOC_PARSE_RESULT_INIT (128)
Ensuite Pascal(3) marche bien, mais Pascal(6) fait une erreur tres louche:
File "", line 10 in Pascal
TypeError 'int' object isn't subscriptable
Et Pascal(4) crashe. ADDRESS(R) PC=8C1DFC94
Lephenixnoir Hors ligne Administrateur Points: 24232 Défis: 170 Message

Citer : Posté le 08/02/2022 12:35 | #


Oui c'est ça, en fait le GC est capable de lire les contenus des objets pour trouver les sous-objets dedans, et ainsi déterminer tous les objets accessibles. C'est possible parce que c'est lui qui les alloue avec son malloc dédié, et c'est pour ça que je voulais vérifier le gc_init() - il faut absolument que tous les pointeurs pointant dans la zone concernée aient été créés par le GC.

Mon code n'aidera probablement pas pour les crashs par rapport à celui de CasioPython, il réduit juste le risque de fuite de mémoire en ajoutant quelques pointeurs. Si tu as des fuites de mémoire dans le futur, n'hésite pas à revenir là-dessus, d'autres registres pourraient être liés.

D'ici là, est-ce que par hasard le comportement change (ie. Pascal(4) marche par exemple) si tu prends un tas plus grand ? Histoire de voir si tu tombes ou pas à court de mémoire à un moment. J'en doute mais on sait jamais.
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 80 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd