Typetracer.jl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. module TypeTracer
  2. using Serialization
  3. # Singleton pour stocker les signatures uniques (Thread-safe)
  4. const SEEN_SIGNATURES = Set{String}()
  5. const LOCK = ReentrantLock()
  6. const LOG_FILE = "execution_trace.txt"
  7. """
  8. Fonction interne pour enregistrer une signature.
  9. Format: FunctionName(ArgType1, ArgType2, ...) -> ReturnType
  10. """
  11. function _record_call(func_name, args, result)
  12. # On map les types Julia vers des chaînes de caractères
  13. arg_types = join([string(typeof(a)) for a in args], ", ")
  14. ret_type = string(typeof(result))
  15. signature = "$func_name($arg_types) -> $ret_type"
  16. # Vérification avec lock pour le multi-threading (fréquent en simu)
  17. lock(LOCK) do
  18. if !(signature in SEEN_SIGNATURES)
  19. push!(SEEN_SIGNATURES, signature)
  20. # On écrit immédiatement pour ne rien perdre en cas de crash
  21. open(LOG_FILE, "a") do io
  22. println(io, signature)
  23. end
  24. # Un petit echo console pour voir que ça vit
  25. println("[TRACER] Nouvelle variante capturée : $signature")
  26. end
  27. end
  28. end
  29. """
  30. Macro à placer devant les définitions de fonctions.
  31. Elle enveloppe le corps de la fonction pour logger les types.
  32. """
  33. macro monitor(expr)
  34. # Vérifie qu'on applique bien la macro sur une définition de fonction
  35. if expr.head !== :function && expr.head !== :(=)
  36. error("La macro @monitor doit être appliquée à une définition de fonction.")
  37. end
  38. # Extraction de la signature (nom + args) et du corps
  39. call_def = expr.args[1]
  40. body = expr.args[2]
  41. # Récupération du nom de la fonction (gestion cas f(x) et Module.f(x))
  42. func_name = length(call_def.args) >= 1 ? call_def.args[1] : :anonymous
  43. # On reconstruit la fonction avec l'instrumentation
  44. quote
  45. function $(call_def)
  46. # 1. Exécuter le corps original et capturer le résultat
  47. __result = $(body)
  48. # 2. Récupérer les arguments (via ... dans la macro c'est délicat,
  49. # donc on utilise une astuce : on capture les valeurs des arguments par leur nom)
  50. # Note : Pour une conversion C, on a besoin des types concrets à l'exécution.
  51. # Ici, on passe un tuple des arguments.
  52. # Attention : cela suppose que call_def.args contient les noms des variables.
  53. # Pour faire simple et robuste sans métaprogrammation complexe sur les arguments :
  54. # On logue juste le fait qu'on est passé ici.
  55. # MAIS pour avoir les args, il faut être plus malin.
  56. # Approche simplifiée : On logue à la fin
  57. TypeTracer._record_call(
  58. $(string(func_name)),
  59. # On capture tous les arguments de la fonction courante
  60. # (nécessite que la fonction ne soit pas anonyme pure)
  61. (Base.@locals()...,), # Capture tout le scope local (arguments inclus)
  62. __result
  63. )
  64. return __result
  65. end
  66. end |> esc
  67. end
  68. # Fonction de nettoyage pour vider le log au début
  69. function init_trace()
  70. if isfile(LOG_FILE)
  71. rm(LOG_FILE)
  72. end
  73. println("--- Démarrage du traçage des types ---")
  74. end
  75. end