Сценарий 15: Помощь в отладке¶
Разработчик сталкивается с проблемами отладки, связанными с навигацией по коду и его анализом.
Содержание¶
- Быстрый старт
- Трассировка путей вызовов
- Трассировка выполнения функции
- Обратная трассировка (вызываемые функции)
- Анализ путей ошибок
- Поиск обработчиков ошибок
- Поток исключений
- Анализ состояния
- Отслеживание переменных
- Рабочие процессы отладки
- Быстрая проверка при отладке
- Примеры вопросов
- Связанные сценарии
Быстрый старт¶
# Выберите сценарий отладки
/select 15
Трассировка цепочки вызовов¶
Трассировка выполнения функции¶
> Трассировка цепочки вызовов до heap_insert
╭─────────────── Цепочка вызовов ─────────────────────────────────────────────╮
│ │
│ Цель: heap_insert() │
│ Расположение: src/backend/access/heap/heapam.c:2156 │
│ │
│ Цепочки вызовов (топ-3 по частоте): │
│ │
│ Путь 1 (оператор INSERT): │
│ exec_simple_query() │
│ └── PortalRun() │
│ └── ExecutorRun() │
│ └── ExecModifyTable() │
│ └── ExecInsert() │
│ └── table_tuple_insert() │
│ └── heap_insert() ← │
│ │
│ Путь 2 (команда COPY): │
│ DoCopy() │
│ └── CopyFrom() │
│ └── CopyMultiInsertBufferFlush() │
│ └── heap_insert() ← │
│ │
│ Путь 3 (выполнение триггера): │
│ ExecCallTriggerFunc() │
│ └── heap_insert() ← │
│ │
╰────────────────────────────────────────────────────────────────────────────╯
Обратная трассировка (вызываемые функции)¶
> Какие функции вызывает heap_insert?
╭─────────────── Вызываемые функции ────────────────────────────────────────╮
│ │
│ heap_insert() вызывает: │
│ │
│ Память/Буфер: │
│ RelationGetBufferForTuple() - Получить целевой буфер │
│ BufferGetPage() - Получить страницу из буфера │
│ PageAddItem() - Добавить элемент на страницу │
│ MarkBufferDirty() - Пометить для записи │
│ │
│ Транзакции: │
│ GetCurrentTransactionId() - Получить XID │
│ TransactionIdStore() - Сохранить XID в кортеже │
│ │
│ Блокировки: │
│ LockBuffer() - Заблокировать буфер │
│ UnlockReleaseBuffer() - Разблокировать и освободить │
│ │
│ Ведение логов: │
│ XLogInsert() - Запись в журнал WAL │
│ log_heap_insert() - Специфичная для кучи запись в WAL │
│ │
│ Всего прямых вызовов: 23 │
│ │
╰────────────────────────────────────────────────────────────────────────────╯
Анализ путей ошибок¶
Поиск обработчиков ошибок¶
> Какие обработчики ошибок существуют для операций с кучей?
╭─────────────── Обработчики ошибок ──────────────────────────────╮
│ │
│ Обработка ошибок в модуле кучи: │
│ │
│ Места вызова ereport(ERROR): │
│ │
│ heap_insert(): │
│ Строка 2178: "tuple too big" (слишком большой кортеж) │
│ Строка 2189: "no free space on page" (нет свободного места на странице) │
│ │
│ heap_update(): │
│ Строка 3456: "concurrent update detected" (обнаружено параллельное обновление) │
│ Строка 3478: "tuple updated by different transaction" (кортеж обновлен другой транзакцией) │
│ │
│ heap_delete(): │
│ Строка 2890: "tuple already deleted" (кортеж уже удалён) │
│ │
│ Блоки PG_TRY/PG_CATCH: │
│ heap_vacuum_rel(): 2 обработчика исключений │
│ heap_prepare_insert(): 1 обработчик исключений │
│ │
│ Всего путей ошибок: 15 │
│ │
╰────────────────────────────────────────────────────────────────╯
Поток исключений¶
> Проследить, что происходит при сбое heap_insert
╭─────────────── Поток исключений ──────────────────────────────╮
│ │
│ Исключение: "tuple too big" в функции heap_insert() │
│ │
│ Путь исключения: │
│ heap_insert() вызывает ERROR │
│ ↓ │
│ PG_CATCH в ExecModifyTable() │
│ ↓ (нет специфического обработчика, повторное возбуждение) │
│ PG_CATCH в ExecutorRun() │
│ ↓ (очистка, повторное возбуждение) │
│ PG_CATCH в PortalRun() │
│ ↓ (прерывание транзакции) │
│ PG_CATCH в PostgresMain() │
│ → Отправка ошибки клиенту │
│ → Откат транзакции │
│ → Продолжение основного цикла │
│ │
│ Освобождённые ресурсы: │
│ - Освобождение буферов │
│ - Освобождение блокировок │
│ - Сброс контекстов памяти │
│ │
╰──────────────────────────────────────────────────────────────╯
Анализ состояния¶
Отслеживание переменных¶
> Отслеживание состояния переменной 'tuple' в функции heap_insert
╭─────────────── Состояние переменной ──────────────────────────────╮
│ │
│ Переменная: tuple (HeapTuple) │
│ Функция: heap_insert() │
│ │
│ Переходы состояний: │
│ │
│ Строка 2160: [ВХОД] Получена от вызывающего кода │
│ Состояние: Инициализирована, OID отсутствует │
│ │
│ Строка 2165: [ИЗМЕНЕНО] HeapTupleSetOid() │
│ Состояние: Присвоен OID │
│ │
│ Строка 2170: [ИЗМЕНЕНО] HeapTupleHeaderSetXmin() │
│ Состояние: Xmin установлен в текущий XID │
│ │
│ Строка 2178: [ПРОВЕРЕНО] Проверка размера кортежа │
│ Состояние: Проверено или ОШИБКА │
│ │
│ Строка 2190: [ИЗМЕНЕНО] PageAddItem() │
│ Состояние: Сохранено на странице буфера │
│ │
│ Строка 2210: [ВЫХОД] Возврат вызывающему коду │
│ Состояние: Полностью вставлено, записано в WAL │
│ │
╰───────────────────────────────────────────────────────────────────╯
Отладка рабочих процессов¶
Быстрая контрольная панель отладки¶
> Создать контрольный список отладки для проблем с heap_insert
╭────────────── Контрольный список отладки ─────────────────────────────╮
│ │
│ Отладка проблем с heap_insert(): │
│ │
│ 1. Проверка корректности входных данных │
│ □ Корректна ли и открыта ли реляция? │
│ □ Правильно ли сформирован кортеж? │
│ □ Укладывается ли размер кортежа в допустимые пределы? │
│ │
│ 2. Проверка состояния транзакции │
│ □ Имеется ли активная транзакция? │
│ □ Действителен ли идентификатор транзакции? │
│ □ Проверить возможность переполнения идентификаторов транзакций │
│ │
│ 3. Проверка состояния буфера │
│ □ Правильно ли установлены блокировки буфера? │
│ □ Имеется ли свободное место на странице? │
│ □ Установлен ли флаг "грязного" буфера? │
│ │
│ 4. Проверка состояния WAL │
│ □ Включено ли ведение журнала WAL? │
│ □ Успешно ли выполнена запись в WAL? │
│ □ Проверить наличие свободного места в буфере WAL │
│ │
│ 5. Типичные ошибки │
│ □ "кортеж слишком большой" → Проверить размер кортежа │
│ □ "нет свободного места" → Проверить автосборку мусора │
│ □ "взаимоблокировка" → Проверить порядок установки блокировок │
│ │
╰─────────────────────────────────────────────────────────────────────╯
Примеры вопросов¶
- «Проследить путь вызова до [функции]»
- «Какие функции вызывает [функция]?»
- «Найти обработчики ошибок в [модуле]»
- «Что происходит, если [функция] завершается с ошибкой?»
- «Отследить переменную [имя] внутри [функции]»
- «Создать контрольный список отладки для [проблемы]»
Связанные сценарии¶
- Введение в работу — Понимание структуры кода
- Производительность — Диагностика производительности
- Разработка функций — Понимание цепочек вызовов