Программа gdb – это популярный отладчик для программ, написанных на языке программирования Си и С++. Отладчик позволяет выполнить программу пошагово, посмотреть значения всех переменных на каждом из этапов выполнения, а если это необходимо, то и дизассемблировать код и посмотреть состояние регистров процессора.
В сегодняшней статье мы рассмотрим как пользоваться gdb для отладки и анализа выполнения программ, написанных на Си. Попытаемся разобраться с основными возможностями программы.
Содержание статьи:
Обычно, отладчик устанавливается вместе с другими инструментами для сборки программного обеспечения. Для установки всего необходимого в Ubuntu или Debian достаточно выполнить:
sudo apt install build-essential
Для того чтобы установить отладчик отдельно, выполните:
sudo apt install gdb
В CentOS или Fedora команда установки будет выглядеть следующим образом:
sudo dnf install gdb
А для Arch Linux надо выполнить:
sudo pacman -S gdb
Теперь отладчик gdb установлен и вы можете его использовать.
Для того чтобы получить максимум полезной информации во время отладки, например, имена переменных и номера строк кода программу следует скомпилировать особым образом. Для примеров из этой статьи мы будем использовать такую небольшую программу на Си, в процессе будем её изменять, но начнём с этого:
vi program.c
#include <stdio.h>int main(){int var1 = 0;int var2 = 2;char greeting[] = "Hello from losstn";printf("%s", greeting);}
Для того чтобы всё необходимое было включено в исполняемый файл, программу надо собрать с опцией -g. В данном случае команда будет выглядеть вот так:
gcc -g -o program program.c
Затем вы можете её выполнить:
./program
Для того чтобы запустить программу достаточно передать путь к ней отладчику. Какие-либо опции указывать не обязательно:
gdb ./program
После запуска отладчика вы попадаете в его командный интерфейс. Программа ещё не запущена, запущен только отладчик, в котором вы можете ею управлять с помощью специальных команд. Вот основные команды gdb:
Для того чтобы запустить программу надо воспользоваться командой run в консоли gdb. Просто выполните:
(gdb) run
И программа будет запущена и выполнена. Если вам надо передать программе какие-либо аргументы, то следует их передать команде run, например:
(gdb) run arg1 arg2
Если программа завершилась с ошибкой, вы можете вывести стек вызовов функций для того чтобы узнать в какой именно функции возникла ошибка:
(gdb) backtrace
Программа сообщает на какой строчке исходного кода возникла проблема. Чтобы посмотреть весь исходник выполните команду list:
(gdb) list
Для того чтобы вызвать ошибку Segmentation Fault и проверить как это работает можете добавить в программу такие строки и перекомпилировать её:
char *buffer = malloc(sizeof(char) * 10);while(1){*(++buffer) = 'c';}
Тут мы выделяем из памяти массив символов размером 10 элементов и заполняем его и память, которая находится за ним символами “с”, пока программа не упадёт.
Неотъемлемая часть отладки приложения – это точки останова. С помощью них можно остановить выполнение программы на любом месте исходного кода или функции, а затем проанализировать все переменные, а также что происходит с программой. Сначала смотрим исходник программы:
По умолчанию команда выводит первые десять строк. Ей можно передать в качестве аргумента номер строки, строки возле которой надо отобразить или имя функции, например:
(gdb) list 10
Например, давайте установим точку останова на восьмой строчке исходника:
(gdb) break 8
Теперь, когда вы запустите программу на выполнение она остановиться в указанной точке:
Для того чтобы выполнить следующую строчку без входа в функцию используйте команду next:
(gdb) next
Тогда выполнится следующая строка кода в программе. Если надо войти в функцию и посмотреть что в ней происходит следует использовать команду step:
(gdb) step
Для отладки циклов можно устанавливать точки останова на основе условия, например, на 11-той строке, если значение переменной var1 будет 20:
(gdb) break 11 if var1==20
Чтобы этот пример заработал добавьте в код цикла следующую строчку, как на снимке:
var1++;
Посмотреть все установленные точки останова можно такой командой:
info breakpoints
Сами по себе точки останова не очень полезны, но после каждой остановки программы можно посмотреть полностью её состояние и значения всех переменных, а это уже намного интереснее. Для просмотра информации используется команда info, а для вывода значения конкретной переменной – print. Для того чтобы посмотреть значения всех локальных переменных выполните:
(gdb) info locals
А для вывода значений аргументов функции:
(gdb) info args
Вывести значение определённой переменной можно с помощью print:
(gdb) print var1
Интересно, что с помощью print можно выполнять арифметические операции и их результат не только выводится на экран, но и присваивается внутренней переменной, таким образом его можно будет вывести ещё раз позже или использовать в других вычислениях. Например:
(gdb) print var1+30
Имя временной переменной, куда были записаны данные отображается перед самим результатом, например, в последней команде это $2. Теперь можно ещё раз вывести это значение:
(gdb) print $2
Таким образом вы можете анализировать состояние программы, на каждой точке останова и искать ошибки.
С помощью команды set можно изменить значение переменной прямо во время выполнения программы. Например:
(gdb) break 7(gdb) run(gdb) set var2 = 20(gdb) print var2
(gdb) break 7
(gdb) set var2 = 20
(gdb) print var2
С помощью команды ptype вы можете вывести тип переменной. Например:
(gdb) break 7(gdb) run(gdb) ptype var2
(gdb) run(gdb) ptype var2
Ещё интереснее исследовать как программы на Си работают с памятью. Команда print может выводить не только выводить значения переменных, но и их адреса в памяти. Приведите программу к такому виду и перекомпилируйте её:
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char *argv[]){int var1 = 0;int var2 = 2;char *buffer = malloc(sizeof(char) * 10);while(var1 < 10){var1++;*(++buffer) = 'c';}printf("%sn", buffer);}
Запустите отладчик и установите точку останова на девятой строке и запустите программу:
(gdb) break 9
Теперь вы можете вывести адреса всех переменных в памяти с помощью символа &. Например:
(gdb) print &argc(gdb) print &var1(gdb) print &buffer(gdb) print buffer
(gdb) print &argc
(gdb) print &var1
(gdb) print &buffer
(gdb) print buffer
Как видите, аргументы функции находятся в одном месте, локальные переменные в другому, но не очень далеко, а во память выделенная из кучи, на которую указывает указатель buffer – совсем далеко. Можете поменять программу и поэкспериментировать с разным количеством переменных.
С помощью команды x или eXamine можно посмотреть содержимое памяти, по определённому адресу. Например, смотрим содержимое переменной var2:
(gdb) x &var2
Если вы не знаете адрес переменной, можно передать её имя с оператором &, он извлечет её адрес. Программа выводит шеснадцатиричное значение, и оно обычно мало о чём нам может сообщить. Для того чтобы улучшить ситуацию можно воспользоваться опциями форматирования. Можно указать тип выводимых данных с помощью таких модификаторов:
Также можно указать размер выводимого блока:
Мы пытались вывести переменную типа int. Она занимает обычно четыре байта. Для её корректного вывода используйте такие параметры:
(gdb) x/wd &var2
Ещё можно указать количество блоков, которые надо выводить, например два:
(gdb) x/2wd &var2
Но поскольку там уже не наша переменная, эти данные не имеют смысла. Аналогично всё работает с строками, символами и другими значениями. Только обратите внимание, что если вы будете выводить переменную не одним блоком, то на результат повлияет порядок расположения байт. Значение будет выводиться задом на перед.
Мы рассмотрели основные возможности отладчика gdb. Но если этого вам не достаточно, вы можете посмотреть справку по любой команде программы в самой программе. Для этого используйте команду help. Например, для команды exemine:
help x
Теперь вы знаете как пользоваться gdb для отладки своих программ. Обычно, для отладки намного удобнее использовать графический интерфейс среды программирования, но консоль дает больше возможностей и больше гибкости.
Источник: https://losst.ru/ Материал распространяется под лицензией CC-BY-SA
Продолжая использовать данный сайт вы принимаете политику конфиденциальности и cookies