Команды GDB, о которых вы возможно не знали

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.


Отладка кода — это как охота. Охота на баги.
— Amit Kalantri

Что такое GDB


GNU Debugger — переносимый отладчик проекта GNU, который работает на многих UNIX-подобных системах и умеет производить отладку многих языков программирования, включая Си, C++, Free Pascal, FreeBASIC, Ada, Фортран и Rust. GDB — свободное программное обеспечение, распространяемое по лицензии GPL.
Источник: GNU Debugger — Википедия


Проще говоря GDB — отладчик, который работает прямо из консоли.


В данной статье я опишу комманды, о которых раньше не знал, однако, они оказались очень полезными при отладке в GDB.


Рассматривать работу отладчика будем на одной из моих курсовых работ, её тема — шифр Цезаря, так что не нужно удивляться названиям переменных или классов. Самого кода вы тут не увидите, ибо это будет лишним для понимания сути действия, однако стоит привести несколько замечаний для полного понимания:


Объект Что значит
Caesar::shift Свойство Caesar
Caesar::ChangeShift Метод Caesar
Caesar::Crypt Метод Caesar

Интерфейс в консоли (TUI)


Я всегда включаю TUI, как только зайду в GDB, он позволяет посмотреть где именно находится выполняемая строка, что идёт перед ней, что дальше. Включается TUI в GDB командой tui enable


С помощью колесика мыши (или стрелок) вы можете перемещаться по псевдо-интерфейсу вверху. С помощью комбинации клавиш Ctrl + p (previous), Ctrl + n (next), вы можете перемещаться между введёнными командами в терминале (окно внизу).


Тезис: Для включения псевдо-интерфейса используют команду tui enable, для выключения — tui disable.


Точки останова


Точки останова можно ставить с помощью команды breakpoint (сокращение — b).
Можно ставить точки останова исходя из следующих принципов:


  • Точкой останова может являться имя функции (метода) или переменной
  • Точкой останова может являться номер строки
  • Точкой останова может являться адрес памяти, в котором располагается инструкция
  • Точкой останова может являться условие

Если с тремя первыми всё понятно, то третий принцип — достаточно интересный.


b Method if variable == value — примерно такой синтаксис используется для задания точки останова исходя из выполнения условия.


Например:


(gdb) b Crypt if Caesar::shift == 1
Note: breakpoint 2 also set at pc 0x4026e3.
Breakpoint 4 at 0x4026e3: file Caesar.h, line 55.

Тут сказано поставить точку останова на методе Crypt(), если свойство класса Caesar под названием shift будет равно 1.


Дополнительно: точки останова можно удалять с помощью команды delete, или её сокращения d.


Тезис: Точки останова можно ставить четыремя способами: с помощью имени объекта, с помощью номера строки, с помощью адреса, с помощью условия.


Шаг с входом в Scope, шаг без входа


Различие между командами next и step (сокращения: n и s) состоит в том, что next выполняет код не входя в Scope (не входя внутрь функции или метода), тогда как step просто выполняет шаг и аналогичен команде Step In в обычных дебагерах.


Дополнительно: для выполнения программы до точки останова используют continue, для выхода из функции в которую вы вошли используют finish или fin.


Тезис: Для вхождения в функцию используется step, для обычного выполнения — next.


Просмотр переменных разными способами


Одной из самых главных частей в процессе отладки является просмотр переменных в разных частях программы. Можно делать это с помощью команды print или p, однако есть и более удобные методы для просмотра переменных.


Команда display


Команда display будет показывать значение переменных каждый раз, когда терминал будет доступен для ввода


(gdb) display Caesar::shift
1: Caesar::shift = <error: Cannot reference non-static field "shift">
(gdb) n

Breakpoint 2, Caesar::Crypt (this=0x7fffffffdc30, text="Crypt this text! Btw, xyz") at Caesar.h:55
1: Caesar::shift = 1
(gdb) n
1: Caesar::shift = 1
(gdb)

Таким образом вы можете просматривать что находится внутри одной переменной или даже целого массива:


(gdb) display *stringArrayForOutput@lengthOfArray
1: *stringArrayForOutput@lengthOfArray = {"Etarv vjku vgzv! Dvy, zab", "Fubsw wklv whaw! Ewz, abc", "Gvctx xlmw xibx! Fxa, bcd"}
(gdb) 

Думаю не стоит говорить, что массив может быть динамический и по мере выполнения программы lengthOfArray (длина массива) может изменяться. GDB сам разберётся с этим и всё время будет выводить актуальную информацию.


Команда explore


Иногда бывает такое, что информации до того много, что путаешься какой объект за что отвечает, для этого создали explore.


explore помогает найти что за объект мелькает перед вашими глазами и рассказывает о нём всё, что может


(gdb) explore Caesar::shift
'Caesar::shift' is a scalar value of type 'int'.
Caesar::shift = 6

(gdb) explore Caesar
'Caesar' is a struct/class with the following fields:

letters = <Enter 0 to explore this field of type 'std::string'>
shift = <Enter 1 to explore this field of type 'int'>

Тезис: Кроме команды print можно использовать команды explore и display.


Запись логов работы программы


Иногда нужно "перемотать вспять" момент, где случилась ошибка, для этого придумана команда record, которая может полностью записывать логи программы в файл или просто в память, дабы перемотать некоторые моменты вспять или посмотреть что именно вызвало ошибку


(gdb) target record-full
(gdb) p Caesar::shift
$1 = 6
(gdb) n
(gdb) p Caesar::shift
$2 = 7
(gdb) reverse-next

No more reverse-execution history.
(gdb) p Caesar::shift
$3 = 6

Для того, чтобы остановить запись и удалить все логи — нужно ввести record stop, для того, чтобы сохранить все логи в файл нужно ввести record save <имя файла>.


Тезис: GDB имеет возможность создать лог работы программы, с помощью которого, мы можем перемещаться по программе вспять.


Просмотр информации


Просмотреть сразу все локальные переменные можно с помощью info local:


(gdb) info local
mainObj = {letters = "abcdefghijklmnopqrstuvwxyz", shift = 1}
textForC = "Crypt this text! Btw, xyz"
cryptedText = "Dszqu uijt ufyu! Cux, yza"
outputVariants = 0x402280 <_start>

Просмотреть все переменные, которые инициализированы можно с помощью info variables:


(gdb) info variables
All defined variables:

File /usr/include/c++/10/bits/basic_string.h:
101:    const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::size_type std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::npos;

File /usr/include/c++/10/bits/stl_pair.h:
83:     static const std::piecewise_construct_t std::piecewise_construct;

File /usr/include/c++/10/iostream:
74:     static std::ios_base::Init std::__ioinit;

Non-debugging symbols:
0x00000000004002e8  __abi_tag
0x0000000000403000  _IO_stdin_used
0x00000000004031b8  typeinfo for std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
0x00000000004031e0  typeinfo name for std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
--Type <RET> for more, q to quit, c to continue without paging--

Чтобы посмотреть все точки останова можно ввести info b:


(gdb) info b   
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000402633 in Caesar::ChangeShift() at Caesar.h:31
        breakpoint already hit 1 time
2       breakpoint     keep y   0x00000000004026e3 in Caesar::Crypt(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) at Caesar.h:55
        breakpoint already hit 1 time

Вы также можете посмотреть тип переменной с помощью команды whatis:


(gdb) whatis Caesar::shift
type = int
(gdb) whatis Caesar
type = Caesar

Чтобы посмотреть адрес функции, вы можете использовать info address:


(gdb) info address Caesar::Crypt
Symbol "Caesar::Crypt(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)" is a function at address 0x4026be.

Заключение


Есть ещё куча комманд, которые я не привёл в данной статье, однако, на мой взгляд они являются столь полезными, как те, что я перечислил выше. Вы можете почитать больше о разработке на Linux в моём телеграм-канале, если вам это нужно. Я периодически пишу о фулл-стак разработке, а также о Unix-подобных системах.


В данной статье не указаны некоторые комманды, которые легки для понимания, например: run, stop, quit, и т.д. Не указал я их специально, так как статья пытается нести информацию, о которой пользователи GDB возможно могли не знать. Если у вас есть предложения чем можно дополнить статью, с радостью почитаю комментарии.

Источник: https://habr.com/ru/post/547824/


Интересные статьи

Интересные статьи

В 2021 версиях управлять данными стало еще проще, а сэкономленное при этом время инженеры могут посвятить повышению качества продукции, которую они разрабатывают. В семей...
Меня зовут Андрей Гладков, я разработчик в направлении беспилотных автомобилей. Сегодня я поделюсь с сообществом Хабра задачей, которая связана с важнейшим сенсором беспилотника —...
В этой статье мы рассмотрим, как система управления 1С-Битрикс справляется с большими нагрузками. Данный вопрос особенно актуален сегодня, когда электронная торговля начинает конкурировать по обороту ...
Примечание: это не полноценная статья-руководство, а скорее напоминание/подсказка для тех, кто уже пользуется ConfigMap в Kubernetes или только готовит своё приложение для работы в нём. Пр...
Доброго времени суток, друзья! Представляю вашему вниманию перевод статьи Apal Shah «Common Javascript Promise mistakes every beginner should know and avoid». Распространенные ошибки пр...