Journal [POC] microprofilage en python avec yahi

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
1
24
mai
2026

User story

J'utilise profile de python et j'aimerais ne voir que les fonctions appelées dans un fichier et sortir les graphs correspondants.

Et accessoirement, t'as envie de montrer comment yahi peut s'utiliser pour d'autres graphs que les analyses de pages web.

Résultat

Le résultat est ici sous forme de page web

 Le chemin

Patcher profile

D'abord, il faut commencer à patcher profile de la stdlib pour sortir les données sous forme d'un log « regexpable » :

+++ mprofile/__init__.py    2026-05-24 09:24:37.185491502 +0200
@@ -330,6 +330,7 @@
             callers[pfn] = 1

         timings[rfn] = cc, ns - 1, tt + rit, ct, callers
+        print(f"{time.time()} {rfn} {rit} {ct} {pfn}")

         return 1

NB : je n'ai absolument pas compris ce que je renvoyais comme données.

Pour moi :

rfn = fonction appelée
rit = temps passé dans le frame où la fonction est appelée
ct = temps passé dans le frame parent (?????)
pfn = fonction appelante

Écrire un script spécifique à yahi

Ensuite après avoir récupéré les journaux d'un profilage, il faut écrire un script de sélection des données.

Ici ; on ne souhaite qu'inclure les entrées dont le chemin vers le fichier qui contient la fonction contient archery.

#!/usr/bin/env python
from yahi import notch, shoot
from json import dumps
from archery import mdict

date_formater= lambda dt :"%s-%02d-%02d %02d-%02d-%02d.%02d" % ( 
    dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond
    )


######################## Setting UP ##################################
# parsing command line & default settings. Return a not fully qualified object
context=notch(
    log_format="custom",
    off="geo_ip,user_agent",
    output_format="json",
# ne sélectionner que les fonction dans le fichier archery/trait.py
    include='''{ "from_callee" : ".*archery/trait.py" }''',
    date_pattern="%s",
# datetime en format timestamp (spécifique à yahi) 
    log_pattern="""^(?P<datetime>\S+)\s
    \('(?P<from_callee>[^']+)',\s
    (?P<lineno_callee>[^']+),\s
    '(?P<callee>[^']+)'\)\s
    (?P<time>\S+)\s
    (?P<ctime>\S+)\s
    \('(?P<from_caller>[^']+)',\s
    (?P<lineno_caller>[^']+),\s
    '(?P<caller>[^']+)'\)$""")
# log sample
# 1779465928.7602208 ('<frozen importlib._bootstrap>', 911, '_load_unlocked') 1 1.267900000000155e-05 0.0015263490000001073 ('<frozen importlib._bootstrap>', 1304, '_find_and_load_unlocked')

##### OKAY, now we can do the job ##########################################

context.output_file.write(
    dumps(
        shoot(
            context,
            lambda data : mdict({
                'date_calls': 
                     mdict({ date_formater(data["_datetime"])
                : float(data["time"]) }),
                "by_overhead_callee" : mdict({ data["callee"]: float(data["ctime"])  }),
                "by_overhead_caller" : mdict({ data["caller"]: float(data["ctime"])  }),
                "by_time_callee" : mdict({ data["callee"]: float(data["time"])  }),
                "by_time_caller" : mdict({ data["caller"]: float(data["time"])  }),
                'by_callee': mdict({ data["callee"] : 1 }),
                'by_caller': mdict({ data["caller"] : 1 }),
                'by_transition' : mdict({ "%(caller)s=>%(callee)s" % data : 1 }),
                'by_transition_time' : 
                    mdict({ "%(caller)s=>%(callee)s" % data : float(data["ctime"]) }),
                'total_line' : 1,
            }),
        ),
        indent=4
    )
)

Ensuite une petite invocation de yahi suffit

python mprofile.py  < ../mprofile.log > data.js && yahi_all_in_one_maker && firefox aio.html

Conclusion

Ben, j'ai été le premier surpris par la simplicité de l'exercice, et j'ai trouvé ça gratifiant.

Yahi est peut être utile pour faire de l'aggrégation de données. :)

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.