Присоединение процесса с помощью pdb

23

У меня есть скрипт на python, который я подозреваю, что есть тупик. Я пытался отладить с помощью pdb , но если я пошагово, он не получит тупик, и по возвращаемому выводу я вижу, что он не зависает на той же итерации. Я хотел бы присоединить мой скрипт к отладчику только когда он заблокирован, возможно ли это? Я открыт для использования других отладчиков, если это необходимо.

    
задан Hola Soy Edu Feliz Navidad 14.08.2014 в 15:09
источник
  • Может помочь code.activestate.com/recipes/... –  pavel_form 14.08.2014 в 17:03
  • Может помочь stackoverflow.com/a/2569696/2096752 –  shx2 15.08.2014 в 09:15
  • Поскольку вы приняли мой ответ, это заставило меня взглянуть на него снова. Я придумал еще пару вариантов, которые я добавил к моему ответу, если вас все еще интересует. Взгляните на раздел условных точек останова и циклов. –  skrrgwasme 10.11.2015 в 16:16

2 ответа

34

В настоящее время pdb не имеет возможности остановить и начать отладку работающей программы. У вас есть несколько других вариантов:

GDB

Вы можете использовать GDB для отладки на уровне C. Это немного более абстрактно, потому что вы работаете с исходным кодом Python C, а не с самим скриптом Python, но в некоторых случаях это может быть полезно. Инструкции находятся здесь: Ссылка . Они слишком вовлечены, чтобы резюмировать здесь.

Сторонние расширения & amp; Модули

Просто поиск «pdb attach process» показывает, что PDB имеет такую возможность:
Pyringe: Ссылка
Pycharm: Ссылка
На этой странице вики Python есть несколько альтернатив: Ссылка

Для вашего конкретного случая использования у меня есть несколько вариантов обходных путей:

Сигналы

Если вы работаете в Unix, вы можете использовать сигналы , как в этот пост в блоге , чтобы попытаться остановить его и присоединить к запущенному сценарию.

Этот блок цитат копируется непосредственно из связанного сообщения в блоге:

Of course pdb has already got functions to start a debugger in the middle of your program, most notably pdb.set_trace(). This however requires you to know where you want to start debugging, it also means you can't leave it in for production code.

But I've always been envious of what I can do with GDB: just interrupt a running program and start to poke around with a debugger. This can be handy in some situations, e.g. you're stuck in a loop and want to investigate. And today it suddenly occurred to me: just register a signal handler that sets the trace function! Here the proof of concept code:

import os
import signal
import sys
import time    

def handle_pdb(sig, frame):
    import pdb
    pdb.Pdb().set_trace(frame)    

def loop():
    while True:
        x = 'foo'
        time.sleep(0.2)

if __name__ == '__main__':
    signal.signal(signal.SIGUSR1, handle_pdb)
    print(os.getpid())
    loop()

Now I can send SIGUSR1 to the running application and get a debugger. Lovely!

I imagine you could spice this up by using Winpdb to allow remote debugging in case your application is no longer attached to a terminal. And the other problem the above code has is that it can't seem to resume the program after pdb got invoked, after exiting pdb you just get a traceback and are done (but since this is only bdb raising the bdb.BdbQuit exception I guess this could be solved in a few ways). The last immediate issue is running this on Windows, I don't know much about Windows but I know they don't have signals so I'm not sure how you could do this there.

Условные точки останова и циклы

Вы по-прежнему сможете использовать PDB, если у вас нет доступных сигналов, если вы оберните блокировку или получение семафора в цикл, который увеличивает счетчик, и останавливаются только тогда, когда счет достигает невероятно большого числа. Например, скажем, у вас есть блокировка, которая, как вы подозреваете, является частью вашей тупиковой ситуации:

lock.acquire() # some lock or semaphore from threading or multiprocessing

Перепишите его так:

count = 0
while not lock.acquire(False): # Start a loop that will be infinite if deadlocked
    count += 1

    continue # now set a conditional breakpoint here in PDB that will only trigger when
             # count is a ridiculously large number:
             # pdb> <filename:linenumber>, count=9999999999

Точка останова должна срабатывать, когда число очень велико (надеюсь), что указывает на возникновение тупика. Если вы обнаружите, что это срабатывает, когда блокирующие объекты, кажется, не указывают на тупик, тогда вам может понадобиться вставить небольшую временную задержку в цикл, чтобы он не увеличивался достаточно быстро. Вам также, возможно, придется поиграться с порогом срабатывания точки останова, чтобы заставить его срабатывать в нужное время. Число в моем примере было произвольным.

Другой вариант - не использовать PDB и намеренно вызывать исключение, когда счетчик становится большим, вместо того, чтобы вызывать точку останова. Если вы напишите свой собственный класс исключений, вы можете использовать его для объединения всех локальных состояний семафора / блокировки в исключении, а затем перехватить его на верхнем уровне вашего сценария, чтобы распечатать прямо перед выходом.

Индикаторы файлов

Другой способ, которым вы можете использовать свой тупиковый цикл, не полагаясь на правильное получение счетчиков, - записывать в файлы:

import time

while not lock.acquire(False): # Start a loop that will be infinite if deadlocked
    with open('checkpoint_a.txt', 'a') as fo: # open a unique filename
        fo.write("\nHit") # write indicator to file
        time.sleep(3)     # pause for a moment so the file size doesn't explode

Теперь позвольте вашей программе работать в течение минуты или двух. Убейте программу и просмотрите эти файлы «контрольных точек». Если за вашу заблокированную программу отвечает взаимоблокировка, то файлы, в которых написано слово «попадание», много раз указывают, какие приобретения блокировки ответственны за вашу взаимоблокировку.

Вы можете расширить полезность этого, используя переменные цикла печати или другую информацию о состоянии вместо просто константы. Например, вы сказали, что подозреваете, что тупик происходит в цикле, но не знаете, какая это итерация. Сделайте так, чтобы этот цикл блокировки сбрасывал управляющие переменные вашего цикла или другую информацию о состоянии, чтобы идентифицировать итерацию, на которой произошла взаимоблокировка.

    
ответ дан skrrgwasme 15.08.2014 в 17:38
  • Для этого требуется переписать мое приложение. Но я просто искал это, когда одна из моих программ python работает, и я хочу отлаживать ее прямо сейчас, не перезагружая. –  Shiplu Mokaddim 29.09.2016 в 13:30
  • @ shiplu.mokadd.im Ну, да ... Вам нужно будет добавить код для использования обходных решений, которые я предложил. Но см. Мое редактирование для некоторых предложений сторонних инструментов, которые вы можете использовать вместо этого. –  skrrgwasme 29.09.2016 в 20:17
  • уже был установлен PyCharm, просто хотел изменить уровень журнала для модуля протоколирования Python .. потребовалось минуту или два, чтобы процесс отображался как «приостановленный», но затем я увидел некоторые переменные в отладчике ... открыл интерпретатор кода, вставил, выполнил и bam ... после повторного запуска процесса, который показывал мой журнал отладки! –  nmz787 13.04.2018 в 23:05
10

Существует клон pdb, образно названный pdb-clone , который может присоединиться к запущенному процессу .

Вы просто добавляете from pdb_clone import pdbhandler; pdbhandler.register() в код основного процесса, а затем можете запустить pdb с pdb-attach --kill --pid PID .

    
ответ дан eaglebrain 10.01.2017 в 18:09