Date Редакция Категория comp Теги gdb / Cpp

Для автоматизации отладки, 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