Product SiteDocumentation Site

6.2. Удалённая отладка программ

Иногда нельзя запустить GDB в том же текстовом окне, что и программу для отладки —  например, если эта программа использует Ncurses. Однако можно запустить программу под управлением «удаленного» варианта отладчика —  GDB server, интерфейс которого доступен по сети или с другого терминала. Подключаться к GDB server можно обычным GDB, отдельно указав расположение удалённого сеанса.
Мы рассмотрим слегка усложнённую ситуацию: попробуем повести сеанс удалённой отладки, пользуясь только пакетами, установленными в окружении hasher. Запустить gdbserver в hasher, а потом зайти туда же, чтобы пользоваться GDB, нам не удастся: настройки доступа hasher дают возможность единовременного доступа к окружению только одному процессу зарегистрированного пользователя. Для единовременной работы двух (и более) hasher-пользователей необходимо создать дополнительные окружения с другой парой builder-rooter-пользователей.
Для создания другой пары необходимо воспользоваться ключами --names для именования этих пользователей и --names для описания дополнительного идентификатора пары (без этого ключа дополнительные пары на одного и того же изначального пользователя не создадутся):
@root
[root@VM ~]# id user
uid=1000(user) gid=1000(user) группы=1000(user),10(wheel),100(users),997(hashman),1001(user_a),1002(user_b),36(vmusers)

[root@VM ~]# hasher-useradd --names=user_c:user_d --number=2 user
useradd: Warning: missing or non-executable shell '/dev/null'
useradd: Warning: missing or non-executable shell '/dev/null'
Добавление пользователя user в группу user_c
Добавление пользователя user в группу user_d
Добавление пользователя user в группу hashman
hasher-useradd: enabling hasher-privd
Внимание: Отправляется запрос 'systemctl enable hasher-privd.service'.
Synchronizing state of hasher-privd.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable hasher-privd
hasher-useradd: starting hasher-privd

[root@VM ~]# id user
uid=1000(user) gid=1000(user) группы=1000(user),10(wheel),100(users),997(hashman),1001(user_a),1002(user_b),1003(user_c),1004(user_d),36(vmusers)
[root@VM ~]#
Создание дополнительного окружения аналогично созданию первого: необходимо создать директорию и в ней собрать окружение. Все команды по работе с дополнительными окружениями должны сопровождаться явным указанием директории расположения окружения с помощью ключа --workdir (команды по умолчанию будут выполнять действия над директорией hasher и окружением в ней):
@user
[user@VM ~]$ mkdir hasher2
[user@VM ~]$ hsh --init --workdir=hasher2
<...>
[user@VM ~]$
Теперь при помощи двух процессов-пользователей (с помощью, например, двух ssh-подключений к машине) будем работать с двумя окружениями. Установим в них необходимые пакеты: gdbserver для серверной стороны, gdb для клиентской стороны:
@user
[user@VM ~]$ hsh-install gdbserver
<...>
<13>Jul 16 11:57:37 rpmi: gdbserver-14.1.0.56.d739d4fd457-alt1 sisyphus+338901.300.3.1 1706109034 installed
[user@VM ~]$ hsh-install --workdir=hasher2 gdb
<...>
<13>ul 16 11:58:08 rpmi: gdb-14.1.0.56.d739d4fd457-alt1 sisyphus+338901.300.3.1 1706109034 installed
[user@VM ~]$
Для совместной работы клиенту нужно будет подключаться к серверу по сети. Для этого необходимо добавить в hasher доступ в интернет. Для этого необходимо:
  • указать разрешение доступа export share_network=1;
  • в hasher указать адрес DNS-сервера (например, 8.8.8.8, но сгодится и содержимое /etc/resolv.conf на хост-системе):
    @user
    [user@VM ~]$ hsh-copy --rooter /etc/resolv.conf /etc/resolv.conf
    [user@VM ~]$ hsh-copy --workdir=hasher2 --rooter /etc/resolv.conf /etc/resolv.conf
    
Запустим удалённую отладку (не забываем, что для работы GDB необходимо на входе в hsh-shell указывать --mountpoints=proc, а для работы сети — share_network=1).
Будем заниматься отладкой той же самой программы:
@user: ex.c
#include <stdio.h>
int N = 42;

int fun2(int n) {
	return n*2+1;
}

int fun1(int c) {
	c += 1;
	return fun2(c) + fun2(c*2);
}

int main(int argc, char *argv[]) {
	int i;
	for(i=0; i <10; i++)
		printf("%d\n", fun1(N+i));
	return 0;
}
На сервере для работы должен располагаться исполняемый файл, собранный с отладочными флагами. Текст программы не обязателен:
@user
[user@VM ~]$ share_network=1 hsh-shell --mountpoints=/proc
@builder
[builder@localhost ~]$ cat > ex.c
<текст программы>
[builder@localhost ~]$ cc -g -O0 ex.c -o ex
[builder@localhost ~]$ rm -f ex.c
На клиенте достаточно запустить сам GDB, но так как исходные тексты показывает именно клиент, рекомендуется иметь их под рукой:
@user
[user@VM ~]$ share_network=1 hsh-shell --mountpoints=/proc --workdir=hasher2
@builder2
[builder@localhost ~]$ cat > ex.c
<текст программы>
[builder@localhost ~]$
Запустим gdbserver и подключимся к нему с клиента с помощью target remote:
@builder
[builder@localhost ~]$ gdbserver localhost:5000 ex
Process /usr/src/ex created; pid = 188151
Listening on port 5000

@builder2
[builder@localhost ~]$ gdb
GNU gdb (GDB) 14.1.0.56.d739d4fd457-alt1 (ALT Sisyphus)
<...>
(gdb) target remote localhost:5000
Remote debugging using localhost:5000
Reading /usr/src/ex from remote target...
<...>
0x00007ffff7fe4e40 in _start () from target:/lib64/ld-linux-x86-64.so.2
(gdb)
@builder
<...>
Remote debugging from host 127.0.0.1, port 48528

@builder2
(gdb) b 9
Breakpoint 1 at 0x555555555156: file ex.c, line 9.
(gdb) cont
Continuing.
<...>
Breakpoint 1, fun1 (c=42) at ex.c:9
warning: Source file is more recent than executable.
9		c += 1;
(gdb) list
4	int fun2(int n) {
5		return n*2+1;
6	}
7
8	int fun1(int c) {
9		c += 1;
10	      return fun2(c) + fun2(c*2);
11      }
12
13      int main(int argc, char *argv[]) {

(gdb) delete breakpoints
Delete all breakpoints? (y or n) y
(gdb) cont
Continuing.
[Inferior 1 (process 188103) exited normally]
(gdb) quit
[builder@localhost ~]$
@builder
[builder@localhost ~]$ gdbserver localhost:5000 ex
Process /usr/src/ex created; pid = 188151
Listening on port 5000
Remote debugging from host 127.0.0.1, port 48528
[builder@localhost ~]$
Для удобства отладки программ на основе GDB создано множество GUI-приложений (например, плагины к различным IDE). Сам GDB поддерживает представление кода при отладке с помощью флага -tui.
Отдельно упомянем класс UI, интерфейсом для которых выступает браузер. В качестве примера можно рассмотреть Gdbgui и GDBFrontend  — python-пакеты, которые достаточно установить в отладочное окружение с помощью pip install, прав суперпользователя при этом не нужно. После запуска нужно зайти браузером на заданный порт, где будет ожидать крошечный веб-сервер, управляющий GDB.