DDD (Data Display Debugger)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 17:50, 24 декабря 2016.
Display Data Debugger
Ddd.png
Разработчики: GNU
Постоянный выпуск: 3.3.12 (11 февраля 2009)
Операционная система: Linux, FreeBSD, NetBSD, OpenBSD, Tru64, UNIX
Тип ПО: пакет GNU
Лицензия: GNU GPL
Веб-сайт gnu.org/software/ddd/

Data Display Debugger (сокращённо DDD) — популярное свободное программное обеспечение проекта GNU (распространяемое по лицензии GNU GPL), графический интерфейс пользователя (фронт-энд/front-end) для таких утилит командной строки, как GDB, DBX, JDB, WDB, XDB, отладчик Perl, отладчик Bash, отладчик Python и отладчик GNU Make.

Используется в основном в Unix-подобных системах.

Среди функций DDD: отладка программ на языках Ада, Bash, Си, C++, Chill, Фортран, Java, Modula, Pascal, Perl и Python Отладка на машинном уровне; гипертекстовая навигация по исходникам; брэйкпоинты, обратное исполнение, и история редактирования; редактор; выполнение программ в окне эмулятора терминала; отладка на удаленной машине; on-line-справка; интерактивная помощь; GDB/DBX/XDB command-line интерфейс с редактированием, историей, дополнением.

Техническое описание

DDD имеет фронт-энд свойства GUI, такие как отображение исходного текста и интерактивное графическое отображение данных, где структуры данных отображены в виде графов.

Кликом мыши можно разыменовывать указатели, а также открывать содержимое структуры, обновляемое при каждой остановке программы. Используя DDD вы можете анализировать ваше приложение, наблюдая за его данными, а не только за исполняемыми строками исходного кода.

DDD в основном используется в системах Unix, и польза от него дополняется множеством доступных в открытом доступе плагинов.

Пример работы отладчика

Программа-пример `sample.c' обнаруживает следующую ошибку. Программа sample должна сортировать и печатать свои аргументы в виде чисел, как в этом примере:

$ ./sample 8 7 5 4 1 3
1 3 4 5 7 8

Однако, при некоторых значениях аргументов она ошибается:

$ ./sample 8000 7000 5000 1000 4000
1000 1913 4000 5000 7000

Хотя вывод отсортирован и содержит верное число аргументов, некоторые аргументы пропущены и заменены на странные числа; в данном случае пропущено 8000, а вместо него стоит 1913.

Давайте применим DDD, чтобы увидеть, что происходит. Сначала вы должны скомпилировать `sample.c' для отладки, задав при компиляции флаг -g:

$ gcc -g -o sample sample.c

Теперь вы можете вызвать DDD для исполняемого файла sample:

$ ddd sample

Через несколько секунд появляется DDD. Окно с исходным текстом содержит код отлаживаемой программы; для прокрутки по файлу используйте полоску прокрутки.

Консоль отладчика (в самом низу) содержит информацию о версии DDD, а также подсказку GDB.

GNU DDD Version 3.2.1, by Dorothea L@"utkehaus and Andreas Zeller.
Copyright (C) 1999 Technische Universit@"at Braunschweig, Germany.
Copyright (C) 1999 Universit@"at Passau, Germany.
Reading symbols from sample...done.
(gdb) 

Первое, что нужно сейчас сделать -- установить точку останова, что заставит sample остановиться в интересующем вас месте. Щелкните на пустом месте слева от инициализации a. Поле аргумента теперь содержит позицию ('sample.c:31'). Теперь щелкните на 'Break', чтобы создать точку останова в позиции. Вы увидите, что на строке 31 появился маленький знак "стоп".

Следующее, что нужно сделать, -- действительно запустить программу, чтобы вы могли исследовать ее поведение. Для запуска программы выберите `Program => Run'; появится диалоговое окно 'Run Program'.

Теперь вы можете ввести в поле 'Run with Arguments' аргументы программы sample. Введите здесь аргументы, приводящие к ошибочному поведению -- то есть, '8000 7000 5000 1000 4000'. Щелкните на 'Run', чтобы началось выполнение с заданными вами аргументами.

Теперь GDB запускает sample. Через несколько мгновений, когда достигнута точка останова, выполнение останавливается. Об этом сообщается в консоли отладчика.

(gdb) break sample.c:31
Breakpoint 1 at 0x8048666: file sample.c, line 31.
(gdb) run 8000 7000 5000 1000 4000
Starting program: sample 8000 7000 5000 1000 4000
Breakpoint 1, main (argc=6, argv=0xbffff918) at sample.c:31
(gdb) 

Выполняемая в текущий момент строка обозначена зеленой стрелкой.

=> a = (int *)malloc((argc - 1) * sizeof(int)); Сейчас вы можете проверить значения переменных. Чтобы проверить простую переменную, вы можете просто поместить указатель мыши над ее именем и задержать его там. Спустя секунду всплывет маленькое окно со значением этой переменной. Попробуйте проделать это с 'argv', чтобы увидеть ее значение . Локальная переменная `a' пока не проинициализирована; вы, вероятно, увидите 0x0 или какое-то другое значение неверного указателя.

Чтобы выполнить текущую строку, щелкните на кнопке 'Next' из командной панели. Стрелка продвинется на следующую строку. Теперь снова укажите на 'a' и увидите, что значение изменилось, и переменная 'a' действительно стала инициализированной.

Чтобы исследовать отдельные значения массива 'a', введите в поле аргумента 'a[0]' (вы можете заранее очистить его), а затем щелкните на кнопку 'Print'. Это напечатает текущее значение в консоли отладчика. В нашем случае вы получите

(gdb) print a[0]
$1 = 0
(gdb) 

или какое-то другое значение (заметьте, что 'a' была только размещена, но ее содержимое еще не проинициализировано.)

Чтобы увидеть все члены 'a' одновременно, вы должны применить особый оператор GDB. Поскольку 'a' была размещена динамически, GDB не знает ее размера; вы должны явно указать его, используя оператор '@' . Введите в поле аргумента 'a[0]@(argc - 1)' и щелкните кнопку 'Print'. Вы получите первые argc - 1 элементов 'a', или

(gdb) print a[0]@(argc - 1)
$2 = {0, 0, 0, 0, 0}
(gdb) 

Вместо того чтобы использовать 'Print' для просмотра текущего значения 'a' на каждом останове, вы можете также отобразить 'a', то есть сделать так, чтобы ее значение показывалось автоматически. Щелкните на 'Display' оставив в поле аргумента 'a[0]@(argc - 1)'. Содержимое 'a' теперь показывается в другом окне, окне данных. Для горизонтального поворота массива щелкните на 'Rotate'.

Далее идет присваивание значений членам 'a':

=> for (i = 0; i < argc - 1; i++)

       a[i] = atoi(argv[i + 1]);

Теперь вы можете щелкать на 'Next' и снова на 'Next', чтобы увидеть, как происходит присваивание отдельным членам 'a'. Измененные члены подсвечиваются.

Для продолжения выполнения цикла используйте кнопку 'Until'. Она велит GDB выполнять программу до тех пор, пока не будет достигнута строка, большая текущей. Щелкайте на 'Until', пока не окажетесь на вызове 'shell_sort':

=> shell_sort(a, argc); В этом месте содержимое 'a' должно быть равно '8000 7000 5000 1000 4000'. Снова щелкните на 'Next', чтобы пройти через вызов 'shell_sort'. DDD остановится на цикле

=> for (i = 0; i < argc - 1; i++)

       printf("%d ", a[i]);

и вы увидите, что после окончания 'shell_sort' содержимое 'a' стало равным '1000, 1913, 4000, 5000, 7000' -- то есть 'shell_sort' каким-то образом испортил его.

Чтобы выяснить, что же случилось, выполните программу снова. На этот раз не проходите инициализацию, а перескочите прямо на вызов 'shell_sort'. Удалите старую точку останова, выбрав ее и щелкнув на 'Clear'. Затем создайте новую точку останова в строке 35, перед вызовом 'shell_sort'. Для повторного выполнения программы выберите 'Program => Run Again'.

Опять же, DDD остановится перед вызовом 'shell_sort':

=> shell_sort(a, argc); На этот раз вы хотите ближе исследовать, что делает 'shell_sort'. Щелкните на 'Step', чтобы войти в вызов 'shell_sort'. Это оставит вашу программу на первой исполняемой строке,

=> int h = 1; тогда как консоль отладчика говорит нам, что произошел вход в функцию:

(gdb) step
shell_sort (a=0x8049878, size=6) at sample.c:9
(gdb)

Такой вывод, показывающий функцию (и ее аргументы), где остановлено выполнение 'sample', называется отображением стека фреймов. Он дает представление о содержимом стека. Вы можете использовать 'Status => Backtrace', чтобы узнать, в каком месте стека вы находитесь; если выбрать строку (или щелкнуть на 'Up' или 'Down'), вы сможете перемещаться по стеку. Обратите внимание на то, что отображение 'a' исчезает, когда вы покидаете его фрейм.

Давайте теперь проверим правильность аргументов 'shell_sort'. Вернувшись в самый нижний фрейм, введите в поле аргумента 'a[0]@size' и щелкните на 'Print':

(gdb) print a[0] @ size
$4 = {8000, 7000, 5000, 1000, 4000, 1913}
(gdb) 

Откуда взялось это лишнее значение 1913? Ответ прост: размер массива, передаваемый в функцию 'shell_sort' как 'size', больше чем нужно на единицу -- странное число 1913 оказалось в памяти после 'a'. И это значение также сортируется.

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

Измените значение 'size' на 5 и щелкните на `OK'. Затем щелкните на 'Finish', чтобы продолжить выполнение функции 'shell_sort':

(gdb) set variable size = 5
(gdb) finish
Run till exit from #0  shell_sort (a=0x8049878, size=5) at sample.c:9
0x80486ed in main (argc=6, argv=0xbffff918) at sample.c:35
(gdb) 

Получилось! Для 'a' теперь показаны корректные значения '1000, 4000, 5000, 7000, 8000'.

Вы можете убедиться, что эти значения будут на самом деле напечатаны на стандартный вывод, выполнив программу дальше. Щелкните на 'Cont', чтобы продолжить выполнение.

(gdb) cont
1000 4000 5000 7000 8000 
Program exited normally.
(gdb) 

Сообщение 'Program exited normally.' исходит от GDB; оно говорит, что программа sample завершила выполнение.

Найдя причину ошибки, вы теперь можете исправить исходный код. Щелкните на 'Edit', чтобы отредактировать 'sample.c', и измените строку

shell_sort(a, argc); на корректный вызов

shell_sort(a, argc - 1); Теперь вы можете перекомпилировать sample

$ gcc -g -o sample sample.c

и проверить (через 'Program => Run Again'), что sample работает хорошо.

(gdb) run
'sample' has changed; re-reading symbols.
Reading in symbols...done.
Starting program: sample 8000 7000 5000 1000 4000
1000 4000 5000 7000 8000 
Program exited normally.
(gdb) 

Все готово; сейчас программа работает правильно. Вы можете завершить этот сеанс DDD с помощью 'Program => Exit' или Ctrl+Q.

Источники