Portál AbcLinuxu, 31. října 2025 00:35
Pythoní attach-to-process pohodlněji
    27.11.2015 23:30
    | Přečteno: 1365×
            | programování
        |  | poslední úprava: 27.11.2015 23:58
        | poslední úprava: 27.11.2015 23:58
Největší nedostatek pythonu z hlediska programátora jsem považoval absenci připojení k běžícímu procesu, pokud nebyl předtím nastartován v debuggeru. Před pár lety jsem na to našel hack a nedávno jsem zjistil, že jeho podobu implementovali do PyCharm IDE, kde to jde teď na pár kliknutí.
Je to už 6 let, co jsem psal návod, jak ohackovat Python přes gdb a dostat funkcionalitu známou jako attach-to-process z gdb. PyCharm to umí a má to implementováno dost podobným hackem, taky přes gdb. Jenže pro programátora mnohem pohodlněji, protože je to integrováno jako featura IDE. I v komunitní free verzi.
PyCharm vs můj starý hack
Hlavní rozdíl je, že se člověk nemusí starat o debug symboly. A implementace je o poznání složitější. GDB musí mít zabudovanou podporu pythonu, přes něj se pythoní proces interně ovládá. K tomu je potřebná knihovna závislá na platformě/architektuře. Přímo podporují x86/amd64, ale k dispozici jsou zdrojáky, takže by to šlo nejspíš použít i na jinou architekturu (pro nějaké embedded věci jako Raspberry Pi). Kombinaci remote debugger + attach-to-process jsem ještě nezkoušel, protože remote debugger je jenom v placené verzi - ale teoreticky by to asi mohlo fungovat.
Oproti mé staré metodě používá jiný způsob zabezpečení konzistentního stavu interpreteru - přes PyGILState_Ensure a PyGILState_Release. Pravděpodobně je to korektnější než můj původní hack. Pak se použije trik s PyRun_SimpleString, případně interní trasovací funkce _PYDEVD_ExecWithGILSetSysStrace.
Musím říct, že z funkcionality jsem příjemně překvapen. Asi jedinou chybu na kráse, co jsem zatím našel je, že po použití attach-to-process a odpojení od procesu mi ten proces nešel zabít bez SIGKILL.
Memory profiling s injekcemi
Nový největší nedostatek je místo původního chybějícího attach-to-process neexistence rozumného memory profileru pro python. Především pokud je potřeba z nějakého několik dní běžícího procesu vydumpovat stav objektů v alokátoru. O vizualizaci škoda mluvit, objgraph sice funguje, ale jeho použitelnost je dost slabá. Hlavně pokud těch objektů máte několik miliard. Podobně RunSnakeMem.
Jediné, co jsem v dané situaci považoval za marginálně použitelné, je meliae. V repozitářích se vyskuje, bohužel na druhé straně to taky už vypadá na mrtvý projekt. S gdb hackem lze statistiku udělat takhle (běžícího procesu, jinak nepotřebujete GDB):
# Stary hack s breaknutim uvnitr VM, viz puvodni zapisek
define breakvm
        tbreak ceval.c:1099
end
define meliae_dump
        breakvm
        continue
        call PyRun_SimpleString("from meliae import scanner; scanner.dump_all_objects('/tmp/meliae_dump.json')")
end
V GDB nebo přes GDB machine interface pak stačí zadat meliae_dump. Statistiku vypíšeme následovně:
from meliae import loader
om = loader.load('/tmp/meliae_dump.json')
s = om.summarize()
print s
Příklad výstupu (na jedné staré GUI aplikaci, co jsem měl zrovna po ruce):
Total 188023 objects, 654 types, Total size = 36.4MiB (38211601 bytes)
 Index   Count   %      Size   % Cum     Max Kind
     0    4211   2   8094536  21  21 3146008 dict
     1    3342   1   7016535  18  39 2097152 numpy.ndarray
     2   50560  26   5511918  14  53   19388 str
     3   51086  27   4423192  11  65    3040 tuple
     4     366   0   1662048   4  69   49488 module
     5    1237   0   1375544   3  73    1112 Path
     6    9534   5   1144080   2  76     120 function
     7    9016   4   1081920   2  79     120 code
     8     778   0    703312   1  81     904 type
     9     686   0    642096   1  82     936 PyQt4.QtCore.pyqtWrapperType
    10     180   0    614880   1  84    3416 Line2D
    11     529   0    482448   1  85     912 sip.enumtype
    12    2259   1    472160   1  86   36992 list
    13     134   0    457744   1  88    3416 Text
    14   15621   8    374904   0  89      24 sip.methoddescriptor
    15    3898   2    311840   0  89      80 wrapper_descriptor
    16    3222   1    283536   0  90      88 weakref
    17    9871   5    236904   0  91      24 float
    18     594   0    209088   0  91     352 WeakKeyDictionary
    19     138   0    153456   0  92    1112 Distribution
Vedlejší efekty meliae injekce
Samotný kód meliae způsobí, že procesu ještě vzroste používaná RAM (RSS) a to značně (řádově klidně 25-50%). Před použitím tam musí být rezerva. Když už proces swapuje, je pozdě. Nebo si počkáte.
Dumpy paměti celkem trvají a výsledkem je dost velký soubor, řádově jsem se pohyboval asi tolik GB, kolik měl sledovaný proces. Mnoho malých objektů. Po načtení a zpracování statistiky lze očekávat, že to spolkne asi tolik paměti, kolik měl soubor na disku.
S pydev gdb helper knihovnou z PyCharm by to šlo skombinovat lépe, aby to bylo přenositelnější, ale zatím jsem to nepotřeboval.
    
    
    
        Tiskni
            
                Sdílej:
                 
                 
                 
                 
                 
                 
            
    
    Komentáře
    
    
            
                Vložit další komentář
            
    
    
    
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.