Привет! Меня зовут Алексей Маряхин, я работаю разработчиком на Oracle и пишу много, очень много кода. И когда программа ведёт себя не так, как ожидалось, на помощь приходит отладка.
Не так давно выяснил, что не все разработчики владеют функционалом отладки или знают её фичи. А если код сложный и баги искать всё равно надо? Литературы на русском языке про отладку практически нет.
Тогда я собрал подробный гайд для коллег и провёл внутренний семинар по обмену опытом. Материал получился настолько подробным и полезным, что решил поделиться им с сообществом программистов. На примере инструментов для работы с СУБД Oracle, которые используются у нас в компании, посмотрим, как работает отладка, сравним их в теории и узнаем, что внутри.
В серии из двух статей подробно расскажу о способах, инструментах и нюансах отладки кода PL/SQL. Первая часть — про инструмент PL/SQL Developer. Поехали!
Отладка: что и зачем. Настройка окружения
Отладка программного кода значительно упрощает его написание: можно сразу отлаживать алгоритмы и проверять, что происходит на каждом шаге. В существующем коде она ускоряет поиск ошибок и, как следствие, их исправление. Возможность полноценной пошаговой отладки существует и для PL/SQL.
Oracle предоставляет интерфейс для отладки в виде DBMS-пакетов — DBMS_DEBUG и DBMS_DEBUG_JDWP. Отлаживать можно хранимые объекты Oracle с SQL-кодом: procedure, function, package, package body, trigger, anonymous block, object type, object type body.
Помимо своих сессий, можно удалённо отлаживать код «чужих» сессий, а ещё по описанию в документации Oracle — хранимый Java-код. Но это отдельная тема со своими инструментами, и нужна другая среда разработки, её мы не проверяли.
Чтобы отладка стала функциональным инструментом, сэкономила время и упростила поиск критичных ошибок, перед началом работы нужно выполнить ряд настроек.
Права
Для запуска отладки необходимо выдать права пользователю, в сессии которого хотим выполнять отладку. Это обязательный пункт, без него отладка работать не будет:
-- Под SYS:
GRANT DEBUG CONNECT SESSION TO <USER>;
Эти права дают пользователю возможность выполнения отладки объектов своей схемы.
Если есть необходимость отладки объектов, расположенных в других схемах, то можно отдельно дать права на отладку конкретных объектов:
-- Под пользователем схемы-владельца объекта, который нужно отлаживать:
GRANT DEBUG ON <object_name> TO <USER>;
Также есть возможность выдать права на отладку любого объекта БД, доступного для пользователя в его или других схемах:
-- Под SYS:
GRANT DEBUG ANY PROCEDURE TO <USER>;
Включение отладки для объектов
Чтобы отладка хранимого объекта стала возможной, необходимо скомпилировать его с добавлением отладочной информации.
Сделать это можно через контекстное меню объекта в PL/SQL Developer — пункт «Add debug information» (важно: данное действие приведёт к перекомпиляции объекта!):
![](https://habrastorage.org/getpro/habr/upload_files/618/52a/62f/61852a62f2dea1f5c990f7a1b6e281ed.png)
Те же действия можно выполнить командой компиляции (ALTER PACKAGE Statement):
-- На примере пакетов, но для хранимых процедур или функций синтаксис аналогичный:
ALTER PACKAGE <object> COMPILE PLSQL_DEBUG = TRUE;
-- Или
ALTER PACKAGE <object> COMPILE DEBUG;
-- Или
ALTER PACKAGE <object> COMPILE PLSQL_CCFLAGS = 'debug:TRUE';
Рекомендованный Oracle вариант (см. COMPILE clause):
ALTER PACKAGE <object> COMPILE PLSQL_OPTIMIZE_LEVEL = 1;
Также включить отладочную информацию можно на уровне сессии:
ALTER SESSION SET PLSQL_CCFLAGS = 'debug:TRUE';
-- Или
ALTER SESSION SET PLSQL_DEBUG = TRUE;
-- Или (рекомендованный враиант)
ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL = 1;
Эти команды не перекомпилируют существующие объекты, а только сообщают компилятору, что всё, что будет далее скомпилировано в этой сессии, будет снабжено отладочной информацией. То есть, чтобы включить отладку для какого-то уже существующего объекта, его всё равно нужно будет перекомпилировать в этой сессии.
Проверить включение отладки для объекта можно запросом:
SELECT * FROM USER_STORED_SETTINGS s WHERE s.object_name = 'PK_DEBUG_DEMO';
![](https://habrastorage.org/getpro/habr/upload_files/5aa/736/808/5aa736808e0cc594af93aa8bbcd5f0c1.png)
Шаги отладки
Подготовка скрипта
Для отладки в PL/SQL Developer есть специальный тип окна — «Test window».
Создаём отладочный скрипт одним из способов:
Вручную: открываем тестовое окно — меню File > New > Test Window. Код скрипта пишем вручную.
Из контекстного меню программного объекта: заходим в нужный пакет, вызываем контекстное меню для нужной процедуры или функции (только для public-методов), выбираем пункт «Test»:
![](https://habrastorage.org/getpro/habr/upload_files/8c7/0ee/b7c/8c70eeb7cd04aa71f196179ee510cb8f.png)
В этом случае также откроется тестовое окно, но скрипт отладки будет сформирован автоматически. При этом все параметры будут автоматически добавлены в список bind-переменных скрипта (только для простых типов, сложные типы будут вынесены в блок DECLARE с пометкой, что для них нужна специальная обработка):
![](https://habrastorage.org/getpro/habr/upload_files/b19/caa/e60/b19caae6069f96b2d65858e3dee69c74.png)
Настройка переменных скрипта
В тестовом скрипте есть возможность задания bind-переменных. Синтаксис стандартный для Oracle: двоеточие + имя переменной, например, «:1» или «:res».
Список bind-переменных отображается в таблице в нижней части тестового окна. Для обновления списка переменных можно использовать контекстное меню таблицы — пункт «Scan Variables»:
![](https://habrastorage.org/getpro/habr/upload_files/359/9c7/dc8/3599c7dc860f2d26a0d0680c7103ac93.png)
При этом все bind-переменные будут добавлены в таблицу, нужно будет только при необходимости поменять для них тип (по умолчанию добавляются с типом String). Также, при запуске скрипта все переменные будут добавлены в таблицу автоматически (если в настройках включён соответствующий параметр; по умолчанию включён).
Для некоторых типов переменных существуют такие особенности:
LOB. Есть две категории таких типов — обычные CLOB / BLOB, а также Temporary CLOB / Temporary BLOB. Первые требуют дополнительной инициализации на сервере, и простой ввод данных в текстовое поле для них не имеет эффекта — в скрипт эти данные передаваться не будут (хотя сами данные ввести можно). Вторые могут содержать данные ещё до выполнения скрипта.
Cursor. После выполнения скрипта содержимое курсора можно посмотреть, нажав кнопку «...» справа в поле с этой переменной.
Запуск
Для запуска пошаговой отладки нажимаем «Start debugger»:
![](https://habrastorage.org/getpro/habr/upload_files/44e/854/1b9/44e8541b9a79b4642b755c04d144f723.png)
После этого окно перейдёт в режим отладки: станут активными кнопки управления отладкой, и будет подсвечена текущая строка, на которой остановлено выполнение:
![](https://habrastorage.org/getpro/habr/upload_files/72a/970/d1d/72a970d1da7ca73c3db0d423676f035e.png)
Пошаговая отладка
Команды пошаговой отладки
Команда | Описание |
Run | Выполнить скрипт до конца или до следующей точки остановки |
Step into | Зайти внутрь процедуры или функции, находящейся на текущем шаге отладки. Для DML-операторов данная кнопка приведёт ко входу в соответствующий триггер, если он есть для таблицы |
Step over | Пройти шаг отладки без захода в процедуру или функцию (внутрь зайдём, только если во вложенном методе есть точка остановки) |
Step out | Выйти из текущей процедуры или функции на уровень выше — в код, из которого зашли в эту процедуру или функцию в режиме Step into |
Run to cursor line | Выполнить до строки, в которой установлен курсор |
Run to next exception | Выполнить до следующего исключения. Выполнение прервётся на строке, где на следующем шаге будет поднято исключение |
Execute SQL in debug session... | Выполнить SQL-запрос в текущей сессии отладки |
Точки прерывания
Для прерывания выполнения кода в нужном месте можно использовать точки прерывания. По ним разработчик может уточнить состояние исполняемого кода (значения переменных, состояние данных, текущий стек вызовов), чтобы определить, правильно ли ведёт себя программа. После остановки на точке прерывания есть возможность продолжить выполнение кода в режиме пошаговой отладки или выполнить до конца (или до следующей точки).
Что важно знать про работу с точками прерывания:
Нужны для прерывания выполнения кода на указанной строке.
Имеют эффект только на строках, содержащих исполняемые инструкции. То есть не будут работать в комментариях или в середине инструкции.
Имеют эффект только для объектов, скомпилированных с добавлением отладочной информации (см. Включение отладки для объектов). То есть в анонимных блоках ставить нельзя.
Точка применяется к номеру строки объекта (согласно номеру из dba_source). То есть, если установить точку в неактуальном объекте (в ещё не скомпилированной версии объекта), то она будет учитываться при отладке (если в dba_source на этой строке находится исполняемая инструкция), но поведение будет отличаться от ожидаемого, так как фактически точка установлена на другой строке.
Ставятся локально для данного экземпляра PL/SQL Developer. Другие пользователи не будут видеть установленные вами точки прерывания.
Установка
Установка точки происходит стандартно, достаточно нажать на номер строки в объекте, в результате чего отобразится соответствующая иконка:
![](https://habrastorage.org/getpro/habr/upload_files/ad6/ccd/87e/ad6ccd87ec857acd01c8c0833456d128.png)
Установить точку можно как непосредственно в нужном объекте, открыв его для просмотра или редактирования, так и из скрипта отладки, когда отладка уже в процессе.
В самом тестовом скрипте точки остановки устанавливать нельзя — только в скомпилированных объектах. Поэтому, если код самого скрипта сложный и для его отладки требуются точки прерывания, нужно оформить его как хранимую процедуру, и уже её вызывать из тестового скрипта.
Управление точками прерывания также доступно через контекстное меню (правой кнопкой на номер строки). Через это же меню можно включить или отключить ранее установленную точку, а также открыть панель управления точками прерывания.
Настройка параметров
Для настройки параметров точки нужно открыть окно управления точками прерывания через контекстное меню «Modify breakpoint». Откроется окно со списком всех установленных точек и их параметрами:
![](https://habrastorage.org/getpro/habr/upload_files/54b/034/615/54b034615646414987be5feb0bdc2602.png)
Параметры:
Use Condition. Можно задать условие прерывания в данной точке в виде SQL-выражения. Например,
:var = 'hello'
, где var — имя переменной в отлаживаемом коде.Use Message. При проходе через точку в output будет выводиться указанное здесь сообщение. Можно указывать подстановочные переменные типа «[Date]» (текущая дата), «[Pass]» (количество проходов в этой точке) (полный список см. в выпадающем меню под текстовым полем), а также значения переменных из PL/SQL-кода в формате «[:var]», где var — имя переменной (в том числе поддерживаются переменные сложных типов — массивы, структуры).
Don't Break. Не прерывать выполнение. Используется в сочетании с Use Message, когда нужно только вывести сообщение в лог (output скрипта; так называемые Logpoint или Tracepoint).
Use Pass Count. Условие на количество проходов. Например, если указать 10, то выполнение будет останавливаться только каждый 10-й проход. Удобно для отладки циклов.
Возникновение исключений
Для поиска мест в коде, в которых возникает исключение, может быть полезен функционал остановки выполнения по видам ошибок Oracle.
Точки прерывания для ошибок (Error breakpoint) позволяют остановить выполнение на инструкции, в которой возникло исключение. При этом остановка происходит на шаг раньше поднятия исключения, что позволяет посмотреть место в коде и значения переменных, которые привели к исключению.
Для установки таких точек нужно задать коды ошибок, при возникновении которых требуется остановить выполнение. Делается это через меню «Modify breakpoints...» на вкладке «Error breakpoints». Сами точки на строке кода при этом ставить не нужно.
Например: создадим пакет с функцией, где происходит деление. Создаём скрипт, который приведёт к делению на ноль, и в «Error breakpoints» указываем ошибку деления на ноль:
![](https://habrastorage.org/getpro/habr/upload_files/057/0b9/d5d/0570b9d5dd5651e3224c0c5cb488fbc7.png)
Запустим скрипт под отладкой и выполним его до конца. Видим, что выполнение прервалось на инструкции, где возникло исключение деления на ноль, но отладка ещё активна, и можно посмотреть значения переменных:
![](https://habrastorage.org/getpro/habr/upload_files/159/282/9bb/1592829bb200e7c093854f12bb38cbe2.png)
Значения переменных
Просмотр в режиме отладки
Посмотреть значения переменных в режиме отладки можно двумя способами: 1) навести курсор на переменную (значение отобразится во всплывающей подсказке); 2) добавить переменную в область просмотра в нижней таблице окна отладки, либо прописав там имя переменной вручную, либо добавив её через меню «Add variable to Watches».
Переменные сложных типов
Довольно часто в PL/SQL-коде приходится использовать переменные сложных типов — коллекции и структуры. Возможности отладчика для таких типов ограничены, но, тем не менее, есть несколько полезных функций и для них:
Коллекцию простых типов можно посмотреть целиком через контекстное меню «View collection variable»:
![](https://habrastorage.org/getpro/habr/upload_files/7bc/126/9b2/7bc1269b2241ed74eb3ae992718935fb.png)
Если навести курсор на переменную, то значение будет вычисляться по всей подстроке. Например, для la_str_arr(i)
отладчик попытается вычислить значение всей конструкции с учётом индекса i
. Если нужно посмотреть значение не всей строки, а только её части, то эту часть нужно выделить — тогда отладчик будет пытаться вычислить значение только для выделенного текста. Например, если навести курсор на переменную типа «структура», то отобразится значение конкретного свойства:
![](https://habrastorage.org/getpro/habr/upload_files/244/eb3/963/244eb3963104af944a0ab2a8354bad0e.png)
Но если выделить саму структуру и навести курсор на выделенный текст, то отобразится вся структура целиком:
![](https://habrastorage.org/getpro/habr/upload_files/c95/404/08a/c9540408a2e105ecfb03d98387cc5ae5.png)
Для коллекций добавить переменную в область просмотра можно как в виде конкретного элемента с указанием его индекса, например
la_str_arr(1)
, так и с переменной индекса,la_str_arr(i)
. Если в контексте текущей исполняемой строки кода индекс задан, то значение будет вычислено.Если для структуры выполнить Add variable to watches из контекстного меню, то в список переменных будут добавлены все поля структуры:
![](https://habrastorage.org/getpro/habr/upload_files/97d/b23/b12/97db23b120408f2d949ba1ea59b01789.png)
Имеет эффект только для плоской (одноуровневой) структуры, без вложенных других структур или массивов.
Установка значений переменных
В режиме отладки есть возможность установки значений для переменных простых типов и для коллекций простых типов. Делается это через контекстное меню, команда «Set variable». В окне в поле ввода отобразится текущее значение переменной — указываем новое, нажимаем Enter (без этого значение не применится):
![](https://habrastorage.org/getpro/habr/upload_files/02a/e86/3b1/02ae863b116088793761317182daf0ec.png)
Выполнение запросов в сессии отладки
Ещё одна крайне полезная функция — возможность выполнения SQL- или PL/SQL-кода прямо в сессии отладки.
Позволяет просматривать и изменять данные непосредственно в процессе отладки, и таким образом проверять, как отлаживаемый скрипт влияет на данные. Также при необходимости можно изменять данные.
Для выполнения запроса в сессии отладки нажимаем «Execute SQL in debug session...», в открывшемся окне вводим текст запроса:
![](https://habrastorage.org/getpro/habr/upload_files/adb/25d/23f/adb25d23f568062db940d50be53046eb.png)
Результат выполнения откроется в отдельном окне:
![](https://habrastorage.org/getpro/habr/upload_files/99b/b82/4a7/99bb824a7d82ddd463a5354e435ac587.png)
Важно учитывать, что все выводимые в таком случае поля будут приведены к строке с ограничением в 1000 символов. Числовые поля и даты будут сконвертированы в строку согласно NLS-настройкам сессии отладки.
Для выражений SELECT есть ограничения к наименованиям полей: у них должны быть уникальные имена. Для повторяющихся названий полей необходимо присвоить уникальный псевдоним.
В сессии отладки можно выполнять любые DML-конструкции — SELECT, INSERT, UPDATE, DELETE, MERGE, изменять состояние сессии (ALTER SESSION SET...), а также управлять транзакцией (COMMIT / ROLLBACK).
Кроме DML можно выполнять и PL/SQL-код. Например:
BEGIN
DBMS_OUTPUT.put_line(PK_DEBUG_DEMO.get_order(19014));
END;
Результат такого запроса будет выведен в output сессии отладки.
Стоит заметить, что парсер этого окна довольно капризный. Он может выдавать ошибки на вполне правильный с точки зрения синтаксиса код, поэтому писать его лучше как можно более просто. Например, если в начале скрипта будет закомментированная строка, то такой код уже будет считаться некорректным. И также ошибки могут возникать при добавлении в код блочных комментариев, при слишком большом количестве полей в запросе и т. д.
Особенности отладки в PL/SQL Developer
1. В самом тестовом скрипте нельзя ставить точки остановки, поэтому если нужно проверить сложный скрипт с большим количеством вызовов и циклов, то лучше оформить его в виде пакета или хранимой процедуры или функции.
2. В некоторых случаях при отладке может не работать отображение значений переменных. Связано это с наличием в коде определённых конструкций (использование пользовательских типов), см. SQL Developer doesn't step into code (для SQL Developer проблема также актуальна). В таком случае можно сделать копию пакета, удалив мешающий код и оставив только те процедуры или функции, которые собираемся отлаживать.
3. Если уже открытый в Test window объект изменился, нужно обновить его, закрыв соответствующую ему вкладку (переключиться на вкладку объекта, закрыв его через контекстное меню «Close page»). Иначе для него будет отображаться неактуальное содержимое, и текущая отображаемая исполняемая строка может не совпадать с фактической. После закрытия вкладки она снова будет открыта при следующем запуске, когда отладчик дойдёт до кода этого объекта.
4. Есть проблема с зависанием приложения при отладке. В этом случае может помочь убийство сессии отладки из другого экземпляра PL/SQL Developer. По поводу зависания есть даже баг, где в качестве решения проблемы Oracle просто рекомендует не использовать PL/SQL Developer для отладки…
5. В качестве механизма отладки в PL/SQL Developer используется системный пакет DBMS_DEBUG, который помечен как deprecated начиная с 19-й версии Oracle. Актуальным механизмом отладки на данный момент является Java Debug Wire Protocol (пакет DBMS_DEBUG_JDWP). Он, например, используется в SQL Developer.
6. Окно Test window имеет удобный функционал с bind-переменными, которые можно использовать для запуска обычных скриптов, без отладки. Особенно это актуально для переменных типа CLOB и BLOB: их можно легко передать на вход скрипта через bind-переменные без необходимости вставлять их прямо в скрипт. И так же с выходными переменными: не нужно заморачиваться с выводом результатов в output, результат можно увидеть прямо в этих переменных. Особенно актуально для типа BLOB, например, когда функция возвращает бинарный файл, типа PDF или изображения. В таком случае объявляем bind-переменную типа BLOB, передаём в неё результат функции, сохраняем результат в файл или открываем содержимое для просмотра прямо из PL/SQL Developer.
Подведём итоги
Функционал отладки в PL/SQL Developer достаточно прост и интуитивно понятен. Есть удобные функции, вроде bind-переменных и выполнения запросов в сессии отладки. Есть подробная документация.
Но есть и минусы — нестабильная работа (зависания), ограничения по просмотру значений переменных. Также вызывает вопросы использование устаревшего механизма отладки DBMS_DEBUG. И стоит ещё учитывать, что инструмент платный.
Итак, мы рассмотрели базовый функционал отладки в PL/SQL Developer и некоторые нюансы использования отладки в этом инструменте. Надеюсь, что статья была полезна и как-то упростит жизнь разработчикам на Oracle; делитесь мнением в комментариях!
Дальше в планах — ещё одна статья про отладку. Посмотрим ближе на другой инструмент — SQL Developer, у которого есть свои особенности и возможности, типа удалённой отладки. Не переключайтесь!