Зовсім недавно на одному з клієнтських проектів (http://www.ihoppers.com) ми мали проблемку із зависанням Python сервера. Під “зависанням” мається на увазі ситуація, коли процес “з’їдає” весь ресурс процесора і забирає необмежену кількість часу. Після того як усі потоки в межах даного процесу сервера зайняті аплікація повністю перестає обслуговувати відвідувачів.
Проект написаний на Plone CMS і обслуговується на Ubuntu сервері. Тому дана техніка дебагу була застосована саме для дослідження Python процесу на Лінуксі.
Зазвичай, коли є потреба подебажити те чи інше місце в Пітон коді я використовую pdb – Python дебагер. З допомогою нього ставлю точку зупинки в програмі, і вже там на місці досліджую змінні середовища, що не так, і т.д… Зазвичай працює добре! Чому ж тоді цього разу я звернувся за допомогою до GDB інструменту?
Проблему із зависанням процесу було важко відтворити як на розробницьких так і на продакшин машинах з реальною базою даних. Могло бути так що 3 рази на день трапляється після того як хтось поредагував контент на сайті, а могло бути так що 2 тижні все спокійно і ніяких глюків.
Тому прийшлось шукати інших способів розбору проблеми, зокрема можливості залісти напряму в процес, що завис і використовує 100% процесора, без його попередньої зупинки чи рестарту.
Для цього я дізнався про новий для себе інструмент – GDB – GNU Project Debugger. Є кілька сценаріїв як можна використовувати цей інструмент, але для моєї задачі я його використовував саме для того, щоб залізти всередину запущеного процесу і подивитися, що ж там відбувається.
Далі даю інструкцію як я це зробив для дебагу Пітон процесу на Лінуксі (Ubuntu). Вкінці статті ви знайдете наглядний скрінкаст усієї процедури.
1. існталюємо наш GNU GDB дебагер. Зауважте: весь процес дебагу на лінуксовій машині я робив під рутовим (root) користувачем. Я не впевнений чи можна обійтися тут без рутового користувача. Якщо знаєте більше про це – будь-ласка, коментуйте і діліться вашим досвідом.
1 |
$ apt-get install gdb |
2. запускаємо дебагер і приєднуємося до нашого процесу, який власне хочемо дебажити
- з допомогою команди top визначаємо програму, яка запустила наш процес, а також id процесу. У моєму випадку це був Python інтерпретатор, який постійно висів у самому верху списку процесів посортованому по CPU Usage % колонці:
у даному прикладі це /home/devel/python-2.6.7/bin/python Пітон інтерпретатор запустив наш процес під ID 16355.
- запускаємо gdb з назвою або шляхом до програми що запустила наш процес:
1 |
$ gdb /home/devel/python-2.6.7/bin/python |
- тепер приєднаємо наш дебагер до конкретного процесу запущеного нашими Пітон інтерпретатором. ID процесу ми визначили на попередньому кроці.
1 |
(gdb) attach 16355 |
3. тепер знаходимо “завислий” потік у процесі:
- виводимо список усіх потоків у даному процесі
1 |
(gdb) info threads |
- якщо потоків більше, ніж один, тоді або треба пройтись по кожному; або, якщо ви маєте уявлення про нутрощі аплікації/сервера, що дебажите – по перших стрічках навпроти виводу кожного потоку вище вказаною командою ви зможете побачити чи це нормальний стан потоку, а чи він зайнятий незвичною для себе річчю. Наприклад, у моєму випадку, працюючи з Python сервером Plone CMS я знаю, що вивід типу: sigsuspend(), poll(), or select() вказує на правильну поведінку потоку.
- отже, вибираємо перший з потоків, спробуємо почати з нього і розібрати чи у ньому зараз проблема. Наступна команда переключить нас у контекст конкретного потоку:
1 |
(gdb) thread 1 |
1 |
(gdb) call PyRun_SimpleString("import sys, traceback; sys.stderr=open('/tmp/tb','w',0); traceback.print_stack()") |
Ось як це виглядало у моєму випадку:
Після цього я зміг визначити, що є проблемка у функції _placeLink всередині модуля events.py пакету ihoppers.contenttypes. А саме – зависає складний регулярний пітонівський вираз. Це допомогло вирішити проблему!
І коротке відео-скрінкаст як це виглядає:
Був цей пост корисний вам? Якщо так – тисність Like 🙂
А як ви вирішуєте, розрулюєте подібні проблеми?
Будьмо дружніми!