11 Cze

Kiedy parsowanie XML w Pythonie crashuje Apache...

Ostatnio podczas próby napisania webowej aplikacji w Pythonie, która miała między innymi parsować pliki XML natrafiłem na bardzo dziwny problem. Skrypt oparty był o framework Django. Podczas użycia metody xml.dom.minidom.parse() aplikacja się wykładała - nie zwracała żadnych błędów, a w przeglądarce widać było tylko białe okno. Jak zwykle przekopywanie polskiego internetu w poszukiwaniu odpowiedzi nic nie dało.

Okazało się, że problem tkwi w bibliotece Expat. Jest ona używana przez moduł xml.dom do parsowania kodu XML. W logach Apache można znaleźć taki oto wpis:

[notice] child pid 3238 exit signal Segmentation fault (11)

Okazuje się, że Apache może domyślnie używać innej wersji expat`a niż Python. Dzieją się wtedy różne dziwne rzeczy, w które nie będę się wgłębiał, bo właściwie nie ma po co. Ale jak sprawdzić, czy to jest rzeczywiście źródłem naszego problemu?

Najpierw należy sprawdzić jakiej wersji biblioteki używa Apache. W tym celu wydajemy polecenie (ścieżka do httpd może się nieco różnić od podanej):

ldd /usr/sbin/httpd | grep expat

Jako wynik powinniśmy otrzymać coś w rodzaju:

libexpat.so.0 => /usr/lib/libexpat.so.0 (0xb7e8c000)

Interesuje nas tylko ścieżka podana po strzałce. Wskazuje ona na plik używany przez nasz serwer. Sama nazwa nam nic nie powie, dlatego należy wyciągnąć numer wersji:

strings /usr/lib/libexpat.so.0 | grep expat_

Otrzymana odpowiedź powinna być podobna do tej:

expat_1.95.8

Musimy się jeszcze upewnić, że żadne zmienne systemowe nie wpływają na ścieżkę bibliotek ładowanych do Apache (np. LD_LIBRARY_PATH) i dowiedzieć jaki proces uruchomił serwer. W tym celu wykonujemy polecenie:

ps aux | grep http | head -3

Uwagę należy zwrócić na drugą kolumnę, w której to pokazany jest identyfikator procesu. Następnie wykonujemy komendę:

sudo lsof -p PID | grep expat

, gdzie PID należy zastapić identyfikatorem właściwego procesu. W ten sposób otrzymaliśmy listę plików, używanych przez dany proces. Przykładowy wynik polecenia:

httpd   3625 root  mem    REG     253,0   123552    6409040 /usr/lib/libexpat.so.0.5.0

Jak widać plik używany przez Apache okazał się nieco inny niż otrzymany wcześniej. Aby sprawdzić ostatecznie wersję Expat`a wykonujemy wcześniej już używane polecenie strings:

strings /usr/lib/libexpat.so.0.5.0 | grep expat_

Sprawdzenie wersji expat`a używanej przez Pythona jest nieco łatwiejsze:

python
>>> import pyexpat
>>> pyexpat.version_info

W odpowiedzi możemy dostać:

(1, 95, 7)

Jak widać Apache i Python używają różnych wersji Expat (odpowiednio 1.95.8 i 1.95.7). Sprawienie, aby oba programy ładowały odpowiednią wersji biblioteki wykonujemy polecenie:

LD_PRELOAD=/usr/lib/libexpat.so.0.5.0

Można teraz ponownie sprawdzić wersję ładowaną przez Pythona wykonując kroki opisane w poprzednim paragrafie.

Źródło: http://www.dscpl.com.au/wiki/ModPython/Articles/ExpatCausingApacheCrash

Wpis nie posiada jeszcze komentarzy

Napisz komentarz