Как я могу сказать, где мой сценарий Python зависает?

Таким образом, я отлаживаю свою программу Python и встретился с ошибкой, которая заставляет программу зависнуть, как будто в бесконечном цикле. Теперь, у меня была проблема с бесконечным циклом прежде, но когда это зависло, я мог закрыть программу, и Python выложил полезное исключение, которое сказало мне, где программа завершилась, когда я отправил ему команду уничтожения. Теперь, однако, когда программа зависает и я ctrl-c это, это не прерывает, но продолжает работать. Есть ли какой-либо инструмент, который я могу использовать для определения местоположения зависания? Я плохо знаком с профилированием, но от того, что я знаю, профилировщик может только предоставить Вам информацию о программе, которая успешно завершилась. Или можно ли использовать профилировщика для отладки такого подвешивать взлеты?

47
задан Johnny 9 August 2010 в 19:46
поделиться

8 ответов

Предположим, вы запускаете свою программу как:

python YOURSCRIPT.py

Попробуйте запустить свою программу как:

python -m trace --trace YOURSCRIPT.py

И проявите немного терпения, пока много чего напечатаны на экране. Если у вас есть бесконечный цикл, он будет длиться вечно (проблема с остановкой). Если он где-то застревает, то в основном вы застряли на вводе-выводе или это тупик.

92
ответ дан 26 November 2019 в 19:19
поделиться

Вау! Уже 5 ответов, и никто не предложил наиболее очевидного и простого:

  1. Попробуйте найти воспроизводимый тестовый пример, который вызывает зависание.
  2. Добавьте логирование в свой код. Это может быть просто печать «** 010» , печать «** 020» и т.д.
  3. Выполнить код. Посмотри где висит. Не могу понять почему? Добавьте больше журналов. (То есть, если между ** 020 и ** 030, добавьте ** 023, ** 025, ** 027 и т. Д.)
  4. Перейти к 3.
24
ответ дан 26 November 2019 в 19:19
поделиться

Если ваша программа слишком велика и сложна, чтобы ее можно было использовать для пошагового выполнения с помощью pdb или печати каждой строки с помощью модуля трассировки, вы можете попробовать уловку из моих дней программирования 8-битных игр. Начиная с Python 2.5, pdb имеет возможность связывать код с точкой останова с помощью команды commands . Вы можете использовать это, чтобы распечатать сообщение и продолжить работу:

(Pdb) commands 1
(com) print "*** Breakpoint 1 ***"
(com) continue
(com) end
(Pdb)

Это напечатает сообщение и продолжит работу, когда будет достигнута точка останова 1. Определите аналогичные команды для нескольких других точек останова.

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

Между прочим, на 8-битных микросхемах (Commodore 64, Spectrum и т. Д.) Вы могли вставить значение в реестр, чтобы изменить цвет рамки вокруг экрана. Раньше я устанавливал несколько точек останова, чтобы сделать это разными цветами, поэтому, когда программа запускалась, она отображала психоделическую радугу до тех пор, пока она не зависала, а затем граница менялась на один цвет, который сообщал вам, какая была последняя точка останова. Вы также можете получить хорошее представление об относительной производительности различных разделов кода по количеству каждого цвета радуги. Иногда мне не хватает этой простоты в этих новомодных машинах с «Windows».

9
ответ дан 26 November 2019 в 19:19
поделиться

Эти зависания легче предотвратить, чем отлаживать их.

Во-первых: for циклы очень и очень трудно застрять в ситуации, когда цикл не завершится. Очень тяжело.

Во-вторых: while циклы относительно легко застрять в цикле.

Первый проход - это проверка каждого цикла while , чтобы убедиться, что он должен быть циклом while . Часто вы можете заменить конструкции , а на для , и вы исправите свою проблему, переосмыслив цикл.

Если вы не можете заменить цикл while на для , то вам просто нужно доказать, что выражение в операторе while должно ] меняются каждый раз в цикле. Это не так уж и сложно доказать.

  1. Посмотрите на все условия в цикле. Назовите это T .

  2. Посмотрите на все логические ветви в теле цикла. Есть ли способ пройти цикл без изменения условия T ?

    • Да? Это твоя ошибка. Этот логический путь неверен.

    • Нет? Отлично, этот цикл должен завершиться .

3
ответ дан 26 November 2019 в 19:19
поделиться

Сам не пользовался, но слышал, что Eric IDE хороша и имеет хороший отладчик. Это также единственная известная мне IDE, которая имеет отладчик для Python

.
0
ответ дан 26 November 2019 в 19:19
поделиться

Если в вашей программе более одного потока, она может игнорировать ctrl-c, потому что один поток подключен к обработчику ctrl-c, но живой (убегающий?) поток глух к нему. GIL (глобальная блокировка интерпретатора) в CPython означает, что обычно только один поток может быть запущен в любой момент времени. Я думаю, что решил свою (возможно) похожую проблему, используя this

0
ответ дан 26 November 2019 в 19:19
поделиться

Ух ты! Кажется, вы добавили столько кода за один раз, не тестируя его, что вы не можете сказать, какой код был добавлен непосредственно перед тем, как программа начала зависать ... (наиболее вероятная причина проблемы).

Серьезно, вы должны кодировать небольшими шагами и тестировать каждый в отдельности (в идеале, выполняя TDD).

Для решения вашей точной проблемы определения того, какой код Python запущен и ctrl-c не работает, я попробую предположить: вы использовали некоторые , кроме: , неявно улавливающих все исключения. Если вы сделали это в цикле (и продолжили цикл после управления исключением), это очень вероятная причина, по которой ctrl-c не работает: он перехватывается этим исключением. Измените на , кроме Exception: , и его больше не следует перехватывать (есть и другие возможности для ctrl + c, не работающего, как управление потоками, как предлагал другой плакат, но я считаю, что вышеуказанная причина более вероятна).

исключение KeyboardInterrupt

 Возникает, когда пользователь нажимает клавишу прерывания (обычно Control-C или Delete).

Во время выполнения регулярно выполняется проверка прерываний. Прерывания, набираемые, когда встроенная функция input () или raw_input () установлена ожидание ввода также вызывает это исключение. Исключение наследует из BaseException, чтобы его случайно не поймал код, перехватывает Exception и, таким образом, предотвращает выход интерпретатора.

 Изменено в версии 2.5: Изменено наследование от BaseException.
-9
ответ дан 26 November 2019 в 19:19
поделиться

Нет ничего лучше старого доброго pdb

import pdb
pdb.run('my_method()',globals(),locals())

Затем просто нажмите (n), чтобы перейти к следующей команде, (s), чтобы перейти к следующей команде. Следуйте за своей программой шаг за шагом, и вы, вероятно, разберетесь с ней достаточно быстро.

2
ответ дан 26 November 2019 в 19:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: