Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Снимки стека вызовов
dump

Снимки стека вызовов

Команда dump, отладка зависаний, анализ deadlock и long-running функций

Снимки стека вызовов с командой dump

Когда программа зависла, один снимок стека стоит тысячи догадок.

Команда dump делает мгновенный снимок стека вызовов всех потоков Python-процесса. Это ваш инструмент для отладки зависаний, deadlock и понимания «что программа делает прямо сейчас».

#Базовое использование

py-spy dump --pid <PID>

Пример вывода:

Thread 0x7FF895C3A5C0 (active)
    "MainThread"
    Func: process_data, File: my_app.py, Line: 45
    Func: fetch_from_api, File: api.py, Line: 23
    Func: time.sleep, File: :builtin, Line: 0

Thread 0x7FF895C3B700 (idle)
    "Worker-1"
    Func: task_queue.get, File: queue.py, Line: 176
    Func: worker_loop, File: worker.py, Line: 34

#Что показывает dump

Для каждого потока вы видите:

  1. ID потока (hex) и статус (active/idle)
  2. Имя потока (если задано через threading.setName())
  3. Стек вызовов: от текущей функции до корня (main)

Важно: Стек показывается «снизу вверх» — текущая функция внизу, вызвавшая её выше, и так далее до корня.

#Когда использовать dump

#1. Программа зависла

Пользователь ждёт, сервер не отвечает, процесс не потребляет CPU.

# Найдите PID ps aux | grep my_app # Сделайте снимок py-spy dump --pid 12345

Что искать:

  • time.sleep() — программа ждёт
  • threading.Lock.acquire() — ожидание блокировки
  • socket.recv() — ожидание сети
  • Бесконечный цикл в одной функции

#2. Deadlock (взаимная блокировка)

Два потока ждут друг друга.

Пример dump при deadlock:

Thread 0x7FF895C3A5C0 (active)
    "Thread-1"
    Func: lock_b.acquire, File: threading.py, Line: 234
    Func: task_a, File: tasks.py, Line: 12

Thread 0x7FF895C3B700 (active)
    "Thread-2"
    Func: lock_a.acquire, File: threading.py, Line: 234
    Func: task_b, File: tasks.py, Line: 28

Диагноз:

  • Thread-1 держит lock_a и ждёт lock_b
  • Thread-2 держит lock_b и ждёт lock_a
  • Классический deadlock!

#3. Понимание состояния перед ошибкой

Программа работает, но иногда выдаёт ошибку. Сделайте dump в момент перед ошибкой:

# В одном терминале запустите программу python my_app.py # В другом — сделайте снимок py-spy dump --pid 12345

#Практический пример: Отладка зависания

#Сценарий

Flask-приложение перестало отвечать на запросы. CPU не потребляет.

#Шаг 1: Найдите PID

lsof -i :5000 # или ps aux | grep flask

#Шаг 2: Сделайте dump

py-spy dump --pid 12345

#Шаг 3: Анализ вывода

Thread 0x7FF895C3A5C0 (active)
    "MainThread"
    Func: recv, File: socket.py, Line: 598
    Func: recv_into, File: socket.py, Line: 639
    Func: read, File: ssl.py, Line: 1234
    Func: request, File: requests.py, Line: 567
    Func: fetch_external_api, File: app.py, Line: 89
    Func: handle_request, File: app.py, Line: 34

Диагноз: Приложение ждёт ответ от внешнего API. Нет таймаута на сокете.

#Решение

Добавьте таймаут:

# Было response = requests.get(url) # Стало response = requests.get(url, timeout=5)

#Практический пример: Поиск бесконечного цикла

#Сценарий

Скрипт работает уже час, хотя должен завершиться за минуту.

#Шаг 1: Найдите PID

ps aux | grep my_script

#Шаг 2: Сделайте dump

py-spy dump --pid 12345

#Шаг 3: Анализ

Thread 0x7FF895C3A5C0 (active)
    "MainThread"
    Func: process_items, File: my_script.py, Line: 45
    Func: <module>, File: my_script.py, Line: 12

Функция process_items в строке 45 — это внутри цикла.

Возможные причины:

  • Условие выхода никогда не достигается
  • Офф-бай-ошибка в диапазоне
  • Бесконечная рекурсия

#Проверка кода

# Было — ошибка! def process_items(items): i = 0 while i < len(items): process(items[i]) # Забыли i += 1! # Стало def process_items(items): i = 0 while i < len(items): process(items[i]) i += 1 # Добавили инкремент

#Сравнение: dump vs top vs record

Характеристикаdumptoprecord
РежимМгновенный снимокРеальное времяЗапись в файл
Длительность0 секунд (мгновенно)Непрерывно30 секунд по умолчанию
Лучше дляОтладка зависанийLive-мониторингПоиск узких мест
ПоказываетТочный стек сейчасАгрегированные данныеСтатистика за время

Когда что использовать:

  • dump: «Программа зависла — что она делает прямо сейчас?»
  • top: «Что потребляет CPU прямо сейчас?»
  • record: «Где программа проводит больше всего времени?»

#Несколько снимков подряд

Для отслеживания изменений сделайте несколько снимков:

# Снимок каждые 2 секунды, 5 раз for i in {1..5}; do echo "=== Snapshot $i ===" py-spy dump --pid 12345 sleep 2 done

#Вывод в файл

py-spy dump --pid 12345 > stack_dump.txt

Удобно для отправки коллегам или анализа позже.

#Многопоточные приложения

dump показывает все потоки Python. Это особенно полезно для:

  • ThreadPoolExecutor: увидите, какие задачи выполняются
  • queue.Queue: увидите, какие потоки ждут очередь
  • Deadlock: увидите взаимные блокировки

#Пример: ThreadPoolExecutor

from concurrent.futures import ThreadPoolExecutor import time def task(n): time.sleep(1) return n * 2 with ThreadPoolExecutor(max_workers=4) as executor: futures = [executor.submit(task, i) for i in range(10)] time.sleep(10) # Долго

dump во время выполнения:

Thread 0x7FF895C3A5C0 (idle)
    "MainThread"
    Func: wait, File: concurrent/futures/_base.py, Line: 345

Thread 0x7FF895C3B700 (active)
    "ThreadPoolExecutor-0_0"
    Func: task, File: my_script.py, Line: 5

Thread 0x7FF895C3C800 (active)
    "ThreadPoolExecutor-0_1"
    Func: task, File: my_script.py, Line: 5

Видно: main ждёт завершения futures, рабочие потоки выполняют task.

#Ограничения dump

  1. Только Python: Не показывает C-стек (используйте --native для этого, но это уже не dump).

  2. Один момент времени: Не показывает историю. Для истории используйте record.

  3. Не работает с замороженными бинарниками: PyInstaller, cx_Freeze могут не иметь отладочной информации.

#Частые паттерны в dump

#Ожидание блокировки

Func: acquire, File: threading.py, Line: 234
Func: process_shared_resource, File: app.py, Line: 67

#Ожидание очереди

Func: get, File: queue.py, Line: 176
Func: worker, File: worker.py, Line: 23

#Ожидание сети

Func: recv, File: socket.py, Line: 598
Func: http_request, File: http.py, Line: 123

#Ожидание subprocess

Func: wait, File: subprocess.py, Line: 1234
Func: run_command, File: utils.py, Line: 45

#Бесконечный цикл

Thread 0x7FF895C3A5C0 (active)
    "MainThread"
    Func: process, File: my_script.py, Line: 34  # Одна и та же строка
    Func: <module>, File: my_script.py, Line: 12

#Практическое задание

  1. Создайте скрипт с намеренным deadlock:
import threading import time lock_a = threading.Lock() lock_b = threading.Lock() def task1(): with lock_a: time.sleep(0.1) with lock_b: print("Task 1 done") def task2(): with lock_b: time.sleep(0.1) with lock_a: print("Task 2 done") t1 = threading.Thread(target=task1) t2 = threading.Thread(target=task2) t1.start() t2.start() t1.join() t2.join()
  1. Запустите скрипт
  2. Быстро найдите PID и сделайте py-spy dump --pid <PID>
  3. Увидите deadlock в выводе

Ключевая идея: dump — это «рентгеновский снимок» вашей программы. Один снимок может мгновенно показать причину зависания, deadlock или бесконечного цикла.

Далее: Дочерние процессы