Для автоматизации отладки, GDB позволяет создавать макросы следующего вида:
define <commandname>
<commands>
end
document <commandname>
<help text>
end
Во время работы отладчика макрос вызывается по имени <commandname>
. При этом выполняются команды, указанные в секции define...end
. В секции document
...end
помещается справка по макросу, которую можно вызвать набрав
(gdb) help <commandname>
Переменные
Для работы с аргументами командной строки GDB используются переменные:
$argc # количество аргументов
$arg0 # первый аргумент
$arg1 # второй аргумент
$arg2 # ...
...
Пользователь может создавать собственные переменные, имена которых начинаются со знака доллара ($
). Значения переменным присваиваются командой set
, например:
set $var = 0
set $vec = $arg0
Вывод
Во время выполнения команд пользователя обычный вывод GDB подавляется. В то же время пользователь может организовать вывод самостоятельно. Для этого существуют команды echo
, output
и printf
.
echo <text>
Выводит текст, в том числе экранированные управляющие последовательности в стиле C, например, \n
.
output <expression>
Выводит значение выражения <expression>
и ничего сверх того (например, поясняющего текста или новой строки).
printf "format_string", <expression>, <expression>...
Аналогична функции printf()
в С: выводит значения выражений, отформатированные в соответствии с "format_string"
.
Пример: подсчет количества элементов std::vector
Теперь мы можем создать макрос, который выводит количество элементов std::vector
:
define p_stl_vector_size
set $vec = ($arg0)
set $vec_size = $vec._M_impl._M_finish - $vec._M_impl._M_start
printf "Vector Size: %d\n", $vec_size
end
Сохраняем макрос в текстовом файле
${HOME}/.gdbinit
Если такого файла нет, создаем его.
Имя макроса может показаться длинным, но в GDB работает автодополнение по Tab.
Команды управления
if
if <expression>
<commands>
else
<commands>
end
while
while <expression>
<commands>
end
Причем допустимо вложение этих команд.
Пример: вывод элементов std::vector
Рассмотрим следующий пример.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec;
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
cout << vec.size() << endl;
}
Если попытаться просмотреть в GDB содержимое vec
с помощью команды print
, то получим
(gdb) p vec
$1 = {<std::_Vector_base<int, std::allocator<int> >> = {
_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, _M_start = 0x804c028, _M_finish = 0x804c034,
_M_end_of_storage = 0x804c038}}, <No data fields>}
Вряд ли эта информация поможет при отладке. Кое-что, о том как посмотреть содержимое std::vector
мы уже знаем. Воспользуемся этим, и напишем следующий макрос:
define p_stl_vector
set $vec = ($arg0)
set $vec_size = $vec._M_impl._M_finish - $vec._M_impl._M_start
if ($vec_size != 0)
set $i = 0
while ($i < $vec_size)
printf "Vector Element %d: ", $i
p *($vec._M_impl._M_start+$i)
set $i++
end
end
end
Теперь, остановив выполнение в строке 14, получим:
(gdb) p_stl_vector vec
Vector Element 0: $1 = 10
Vector Element 1: $2 = 20
Vector Element 2: $3 = 30
Совсем другое дело!
Пример: односвязный список
Особенно полезными макросы GDB могут оказаться при отладке программ, содержащих объекты типов данных, заданных пользователем
Рассмотрим программу, заполняющую созданный пользователем односвязный список:
#include <cstdlib>
#include <cstdio>
typedef struct single_linked_list sll;
struct single_linked_list
{
int data;
sll *next;
};
sll* sll_mknode(int val)
{
sll* p = (sll*) malloc(sizeof(struct single_linked_list));
if (NULL != p)
{
p->data = val;
p->next = NULL;
}
return p;
}
int sll_insert_at_end(sll **head, int val)
{
sll* trav, *temp;
if (NULL == head)
{
printf("\nInvalid dhead");
return -1;
}
temp = sll_mknode(val);
trav = *head;
while (trav->next != NULL)
{
trav = trav->next;
}
trav->next = temp;
return 0;
}
int main()
{
sll* head;
head = sll_mknode(10);
sll_insert_at_end(&head,20);
sll_insert_at_end(&head,30);
sll_insert_at_end(&head,40);
return 0;
}
Просмотреть содержимое списка, на вершину которого указывает head
, поможет следующий макрос:
define p_sll
set $node = $arg0
while ($node)
printf "(%d,0x%X)-->", ((sll*)$node)->data, ((sll*)$node)->next
set $node = ((sll*)$node)->next
end
printf "Done\n"
end
В скобках выводятся данные, содержащиеся в узле, и значение указателя на следующий элемент списка.
Остановив выполнение в строке с return
, просмотрим содержимое списка
(gdb) p_sll head
(10,0x804B018)-->(20,0x804B028)-->(30,0x804B038)-->(40,0x0)-->Done
Комментарии
comments powered by Disqus