<!--
     The FreeBSD Russian Documentation Project

     $FreeBSD$
     $FreeBSDru: frdp/doc/ru_RU.KOI8-R/books/handbook/kerneldebug/chapter.sgml,v 1.2 2000/11/10 11:53:52 phantom Exp $

     Original revision: 1.25
-->

<chapter id="kerneldebug">
  <title>Отладка ядра</title>

  <para><emphasis>Текст предоставили &a.paul; и &a.joerg;</emphasis></para>

  <sect1>
    <title>Отладка аварийных образов ядра при помощи
      <command>kgdb</command></title>

    <para>Вот некоторые указания по работе с отладкой ядра с аварийными
      образами памяти.	При этом предполагается, что у вас достаточно места
      на разделе подкачки для аварийного образа памяти.  Если у вас есть
      несколько разделов подкачки и первый слишком мал для размещения образа
      памяти, вы можете настроить ядро на использование другого устройства
      для сброса образа памяти (в строке <literal>config kernel</literal> или
      указать его при помощи команды &man.dumpon.8;.  Лучше всего
      использовать &man.dumpon.8;, задав переменную
      <literal>dumpdev</literal> в файле <filename>/etc/rc.conf</filename>.
      Как правило, вам нужно будет задать одно из устройств подкачки,
      перечисленных в файле <filename>/etc/fstab</filename>.  Сброс образов
      памяти на устройства, не являющиеся устройствами подкачки, напрмер,
      ленты, в данный момент не поддерживаются.  Настройте ваше ядро при
      помощи команды <command>config -g</command>.  Обратитесь к разделу
      <link linkend="kernelconfig">Настройка ядра</link> за более подробной
      информацией о настройке ядра FreeBSD.</para>

    <para>Используйте команду &man.dumpon.8; для указания ядру места, куда
      нужно помещать образ памяти (отметьте, что это будут сделано после
      настройки соответствующего раздела как раздел подкачки через
      &man.swapon.8;).	Обычно это делается через
      <filename>/etc/rc.conf</filename> и <filename>/etc/rc</filename>.
      Либо вы можете задать устройство для сброса образа памяти явно через
      параметр <literal>dump</literal> в строке <literal>config</literal>
      конфигурационного файла вашего ядра.  Такой способ использовать не
      рекомендуется и он должен использоваться, только если вы хотите
      получать аварийные образы памяти ядра, которое аварийно завершает свою
      работу при загрузке.</para>

    <note>
      <para>Далее термин <command>kgdb</command> означает утилиту
	<command>gdb</command>, которая запущена в <quote>режиме отладки
	ядра</quote>.  Этот режим достигается либо при запуске
	<command>gdb</command> с параметром <option>-k</option>, либо
	компоновкой и запуском его под именем <command>kgdb</command>.	По
	умолчанию этого, однако, не делается и эта идея, в общем-то, не
	поддерживается, потому что разработчикам GNU не нравится, когда их
	утилиты работают по-другому, будучи вызванными по другому имени.  В
	будущих релизах такая возможность может исчезнуть.</para>
    </note>

    <tip>
      <para>Если вы используете FreeBSD версии 3 или более раннюю, вы должны
	выполнить усечение отладочного ядра командой strip, а не
	устанавливать большое отладочное ядро:</para>

      <screen>
&prompt.root; <userinput>cp kernel kernel.debug</userinput>
&prompt.root; <userinput>strip -g kernel</userinput>
      </screen>

      <para>Этот шаг не так уж и необходим, но рекомендуем.  (Во FreeBSD 4
	и более поздних релизах этот шаг выполняется автоматически в конце
	процесса построения ядра <command>make</command>.)  Когда ядро
	усечено, автоматически или при помощи команд выше, вы можете
	установить его обычным образом, набрав <command>make
	install</command>.</para>

      <para>Заметьте, что в старых версиях FreeBSD (до 3.1, не включая этот
	релиз), используется ядра в формате a.out, поэтому их таблицы
	символов должны располагаться постоянно в памяти.  С большой таблицей
	символов в не усеченном отладочном ядре это излишняя трата.  Последние
	релизы FreeBSD используют ядра в формате ELF, где это не является
	проблемой.</para>
    </tip>

    <para>Если вы тестируете новое ядро, скажем, набирая имя нового ядра в
      приглашении загрузчика, но вам нужно загружать и работать с другим
      ядром, чтобы снова вернуться к нормальному функционированию, загружайте
      его только в однопользовательском режиме при помощи флага
      <option>-s</option>, указываемого при загрузке, а затем выполните такие
      шаги:</para>

    <screen>
&prompt.root; <userinput>fsck -p</userinput>
&prompt.root; <userinput>mount -a -t ufs</userinput>	   # so your file system for /var/crash is writable
&prompt.root; <userinput>savecore -N /kernel.panicked /var/crash</userinput>
&prompt.root; <userinput>exit</userinput>		   # ...to multi-user
    </screen>

    <para>Эта последовательность указывает программе &man.savecore.8; на
      использование другого ядра для извлечения символических имен.  Иначе
      она будет использовать ядро, работающее в данный момент и, скорее
      всего, ничего не сделает, потому что аварийный образ памяти и символы
      ядра будут отличаться.</para>

    <para>А теперь, после сброса аварийного дампа, перейдите в каталог
      <filename>/sys/compile/WHATEVER</filename> и запустите команду
      <command>kgdb</command>.	Из программы <command>kgdb</command> сделайте
      вот что:

      <screen>
<userinput>symbol-file kernel.debug</userinput>
<userinput>exec-file /var/crash/kernel.0</userinput>
<userinput>core-file /var/crash/vmcore.0</userinput>
      </screen>

      и вуаля - вы можете отлаживать аварийный дамп, используя исходные
      тексты ядра точно также, как вы это делаете с любой другой
      программой.</para>

    <para>Вот журнал команд сеанса работы <command>kgdb</command>,
      иллюстрирующий эту процедуру.  Длинные строки были разорваны для
      улучшения читабельности и для удобства строки были пронумерованы.
      Все остальное является трассировкой ошибки, реально возникнувшей во
      время работы над драйвером консоли pcvt.</para>

    <screen>
 1:Script started on Fri Dec 30 23:15:22 1994
 2:&prompt.root; <userinput>cd /sys/compile/URIAH</userinput>
 3:&prompt.root; <userinput>kgdb kernel /var/crash/vmcore.1</userinput>
 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel
...done.
 5:IdlePTD 1f3000
 6:panic: because you said to!
 7:current pcb at 1e3f70
 8:Reading in symbols for ../../i386/i386/machdep.c...done.
 9:<prompt>(kgdb)</prompt> <userinput>where</userinput>
10:#0  boot (arghowto=256) (../../i386/i386/machdep.c line 767)
11:#1  0xf0115159 in panic ()
12:#2  0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698)
13:#3  0xf010185e in db_fncall ()
14:#4  0xf0101586 in db_command (-266509132, -266509516, -267381073)
15:#5  0xf0101711 in db_command_loop ()
16:#6  0xf01040a0 in db_trap ()
17:#7  0xf0192976 in kdb_trap (12, 0, -272630436, -266743723)
18:#8  0xf019d2eb in trap_fatal (...)
19:#9  0xf019ce60 in trap_pfault (...)
20:#10 0xf019cb2f in trap (...)
21:#11 0xf01932a1 in exception:calltrap ()
22:#12 0xf0191503 in cnopen (...)
23:#13 0xf0132c34 in spec_open ()
24:#14 0xf012d014 in vn_open ()
25:#15 0xf012a183 in open ()
26:#16 0xf019d4eb in syscall (...)
27:<prompt>(kgdb)</prompt> <userinput>up 10</userinput>
28:Reading in symbols for ../../i386/i386/trap.c...done.
29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\
30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\
31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\
32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\
33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\
34:ss = -266427884}) (../../i386/i386/trap.c line 283)
35:283				   (void) trap_pfault(&amp;frame, FALSE);
36:<prompt>(kgdb)</prompt> <userinput>frame frame-&gt;tf_ebp frame-&gt;tf_eip</userinput>
37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done.
38:#0  0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\
39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403)
40:403		   return ((*linesw[tp-&gt;t_line].l_open)(dev, tp));
41:<prompt>(kgdb)</prompt> <userinput>list</userinput>
42:398
43:399		   tp-&gt;t_state |= TS_CARR_ON;
44:400		   tp-&gt;t_cflag |= CLOCAL;  /* cannot be a modem (:-) */
45:401
46:402	   #if PCVT_NETBSD || (PCVT_FREEBSD >= 200)
47:403		   return ((*linesw[tp-&gt;t_line].l_open)(dev, tp));
48:404	   #else
49:405		   return ((*linesw[tp-&gt;t_line].l_open)(dev, tp, flag));
50:406	   #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */
51:407	   }
52:<prompt>(kgdb)</prompt> <userinput>print tp</userinput>
53:Reading in symbols for ../../i386/i386/cons.c...done.
54:$1 = (struct tty *) 0x1bae
55:<prompt>(kgdb)</prompt> <userinput>print tp-&gt;t_line</userinput>
56:$2 = 1767990816
57:<prompt>(kgdb)</prompt> <userinput>up</userinput>
58:#1  0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\
59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126)
60:	  return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p));
61:<prompt>(kgdb)</prompt> <userinput>up</userinput>
62:#2  0xf0132c34 in spec_open ()
63:<prompt>(kgdb)</prompt> <userinput>up</userinput>
64:#3  0xf012d014 in vn_open ()
65:<prompt>(kgdb)</prompt> <userinput>up</userinput>
66:#4  0xf012a183 in open ()
67:<prompt>(kgdb)</prompt> <userinput>up</userinput>
68:#5  0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\
69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\
70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \
71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \
72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673)
73:673		   error = (*callp-&gt;sy_call)(p, args, rval);
74:<prompt>(kgdb)</prompt> <userinput>up</userinput>
75:Initial frame selected; you cannot go up.
76:<prompt>(kgdb)</prompt> <userinput>quit</userinput>
77:&prompt.root; <userinput>exit</userinput>
78:exit
79:
80:Script done on Fri Dec 30 23:18:04 1994
    </screen>

    <para>Комментарии к вышеприведенному журналу:</para>

    <variablelist>
      <varlistentry>
	<term>строка 6:</term>

	<listitem>
	  <para>Это дамп, взятый при помощи DDB (смотри ниже), поэтому
	    комментарий к аварийному останову имеет именно вид <quote>because
	    you said to!</quote> и трассировка стека глубока; однако
	    изначальной причиной перехода в DDB была аварийная остановка при
	    возникновению ошибки страницы памяти.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>строка 20:</term>

	<listitem>
	  <para>Это местонахождение функции <function>trap()</function> в
	    трассировке стека.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>строка 36:</term>

	<listitem>
	  <para>Принудительное использование новой границы стека; теперь это
	    не нужно.  Теперь предполагается, что границы стека указывают на
	    правое расположение, даже в случае аварийного останова.  (У меня
	    нет под рукой новый аварийный дамп &lt;g&gt;, с моим ядром
	    аварийная ситуация не случалась давно.)  Глядя на строку
	    исходного кода 403, можно сказать, что весьма вероятно, что либо
	    виноват доступ по указателю <quote>tp</quote>, либо был выход за
	    границы массива.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>строка 52:</term>

	<listitem>
	  <para>Похоже, что виноват указатель, но он является допустимым
	    адресом.</para>
	</listitem>
      </varlistentry>

      <varlistentry>
	<term>строка 56:</term>

	<listitem>
	  <para>Однако, очевидно, что он указывает на мусор, так что мы нашли
	    нашу ошибку!  (Для тех, кто не знаком с этой частью кода:
	    <literal>tp-&gt;t_line</literal> служит для хранения режима
	    канала консольного устройства, и это должно быть достаточно
	    маленькое целое число.)</para>
	</listitem>
      </varlistentry>
    </variablelist>
  </sect1>

  <sect1>
    <title>Отладка аварийного дампа с помощью DDD</title>

    <para>Возможно также и исследование аварийного дампа ядра при помощи
      такого графического отладчика, как <command>ddd</command>.  Добавьте
      флаг <option>-k</option> к командной строке <command>ddd</command>,
      которую вы обычно используете для его вызова.  Например;</para>

    <screen>
&prompt.root; <userinput>ddd -k /var/crash/kernel.0 /var/crash/vmcore.0</userinput>
    </screen>

    <para>После этого у вас должно получиться исследование аварийного дампа
      при помощи графического интерфейса <command>ddd</command>.</para>
  </sect1>

  <sect1>
    <title>Посмертный анализ дампа</title>

    <para>Что делать, если ядро аварийно завершает работу, хотя этого вы не
      хотели и поэтому командой <command>config -g</command> его не
      компилировали?  Здесь не все еще потеряно.  Не паникуйте!</para>

    <para>Конечно, вам нужно включить создание аварийных дампов.  Смотрите
      выше, что вы должны для этого сделать.</para>

    <para>Перейдите в каталог конфигурации ядра
      (<filename>/usr/src/sys/<replaceable>arch</replaceable>/conf</filename>)
      и отредактируйте ваш конфигурационный файл.  Раскомментируйте (или
      добавьте, если она не существует) такую строку</para>

    <programlisting>
makeoptions    DEBUG=-g 	       #Build kernel with gdb(1) debug symbols
    </programlisting>

    <para>Перестройте ядро.  Из-за изменения метки времени в Makefile будут
      перестроены некоторые другие объектные файлы, например,
      <filename>trap.o</filename>.  К некоторому счастью, добавление опции
      <option>-g</option> не изменит все и вся в генерируемом коде, так что
      в конце концов вы получите новое ядро с тем же кодом, что и сбоящее
      ядро, за исключением наличия отладочной информации.  По крайней мере,
      вы можете сравнить старый и новый размеры ядер командой &man.size.1;.
      Если они не совпадают, то вам придется отказаться от вашей
      затеи.</para>

    <para>Исследуйте дамп так, как это описано выше.  Отладочной информации
      может не хватать в некоторых местах, как это можно видеть в трассировке
      стека примера выше, когда некоторые функции выводятся без номеров строк
      и списка аргументов.  Если вам нужно больше отладочной информации,
      удалите соответствующие объектные файлы и повторите сеанс работы с
      <command>kgdb</command>, пока не получите достаточно подробную
      информацию.</para>

    <para>Не гарантируется, что все это будет работать, однако в большинстве
      случаев все работает прекрасно.</para>
  </sect1>

  <sect1>
    <title>Отладка ядра в режиме реального времени с помощью DDB</title>

    <para>Хотя <command>kgdb</command> является отладчиком не реального
      времени с высокоуровневым пользовательским интерфейсом, есть несколько
      вещей, которые он сделать не сможет.  Самыми важными из них являются
      точки останова и пошаговое выполнение кода ядра.</para>

    <para>Если вам нужно выполнять низкоуровневую отладку вашего ядра, то на
      этот случай имеется отладчик реального времени, который называется DDB.
      Он позволяет устанавливать точки останова, выполнять функции ядра по
      шагам, исследовать и изменять переменные ядра и прочее.  Однако он не
      может использовать исходные тексты ядра и имеет доступ только к
      глобальным и статическим символам, а не ко всей отладочной информации,
      как <command>kgdb</command>.</para>

    <para>Чтобы отконфигурировать ваше ядро для включения DDB, добавьте
      строчку с параметром

      <programlisting>
options DDB
      </programlisting>

      в ваш конфигурационный файл, и перестройте ядро.	(Обратитесь к разделу
      о <link linkend="kernelconfig">конфигурации ядра</link> для выяснения
      деталей конфигурации ядра FreeBSD.</para>

    <note>
      <para>Заметьте, что, если у вас устаревшая версия загрузочных блоков,
	то отладочная информация может оказаться не загруженной.  Обновите
	блоки загрузки; самые новые загружают символы для DDB
	автоматически.)</para>
    </note>

    <para>После того, как ядро с DDB запущено, есть несколько способов войти
      в DDB.  Первый, и самый простой, способ заключается в наборе флага
      загрузки <option>-d</option> прямо в приглашении загрузчика.  Ядро
      будет запущено в режиме отладки и войдет в DDB до выполнения процедуры
      распознавания каких бы то ни было устройств.  Поэтому вы можете
      выполнить отладку даже функций распознавания/присоединения
      устройств.</para>

    <para>Второй способ - это особая комбинация клавиш на клавиатуре, как
      правило, Ctrl-Alt-ESC.  Для системной консоли syscon это может быть
      переопределено; некоторые из поставляемых раскладок это выполняют, так
      что присмотритесь.  Для последовательных консолей имеется параметр,
      позволяющий использовать последовательность BREAK на канале консоли для
      входа в DDB (<literal>options BREAK_TO_DEBUGGER</literal> в
      конфигурационном файле ядра).  По умолчанию этого не делается, так как
      существует множество паршивых последовательных адаптеров, которые
      генерируют последовательность BREAK, к примеру, при вытягивании
      кабеля.</para>

    <para>Третий способ заключается во входе в DDB при возникновении
      любой аварийной ситуации, если ядро его использует.  По этой причине
      не очень умно конфигурировать ядро с DDB для машины, которая работает
      без присмотра.</para>

    <para>Команды DDB примерно повторяют некоторые команды
      <command>gdb</command>.  Первым делом вам, наверное, нужно задать точку
      останова:</para>

    <screen>
<userinput>b function-name</userinput>
<userinput>b address</userinput>
    </screen>

    <para>Значения по умолчанию воспринимаются в шестнадцатиричном виде, но
      чтобы отличать их от имен символов; шестнадцатиричные числа,
      начинающиеся с букв <literal>a-f</literal>, должны предваряться
      символами <literal>0x</literal> (это опционально для других чисел).
      Разрешены простые выражения, например:
      <literal>function-name + 0x103</literal>.</para>

    <para>Чтобы продолжить работу прерванного ядра, просто наберите:</para>

    <screen><userinput>c</userinput></screen>

    <para>Чтобы получить трассировку стека, задайте:</para>

    <screen><userinput>trace</userinput></screen>

    <note>
      <para>Заметьте, что при входе в DDB по специальной комбинации, ядро
	в данный момент обслуживает прерывание, так что трассировка стека
	может не дать много информации.</para>
    </note>

    <para>Если вы хотите убрать точку останова, введите</para>

    <screen>
<userinput>del</userinput>
<userinput>del address-expression</userinput>
    </screen>

    <para>В первом варианте команда будет исполнена сразу же по достижении
      точки останова, а текущая точка останова будет удалена.  Во второй
      форме можно удалить любую точку останова, однако вам нужно будет
      указать ее точный адрес; его можно получить из:</para>

    <screen><userinput>show b</userinput></screen>

    <para>Чтобы выполнить один шаг ядра, попробуйте:</para>

    <screen><userinput>s</userinput></screen>

    <para>При этом будет осуществляться пошаговое выполнение функций, однако
      вы можете трассировать их с помощью DDB, пока не будет достигнуто
      соответствие возвращаемому значению:</para>

    <screen><userinput>n</userinput></screen>

    <note>
      <para>Это отличается от команды <command>next</command> отладчика
	<command>gdb</command>; это похоже на команду <command>gdb</command>
	<command>finish</command>.</para>
    </note>

    <para>Чтобы выводить значения в памяти, используйте, (к примеру):

      <screen>
<userinput>x/wx 0xf0133fe0,40</userinput>
<userinput>x/hd db_symtab_space</userinput>
<userinput>x/bc termbuf,10</userinput>
<userinput>x/s stringbuf</userinput>
      </screen>

      для доступа к данным типа слово/полуслово/байт и вывода в
      шестнадцатиричном/десятичном/символьном виде.  Число после запятой
      означает счетчик объектов.  Чтобы вывести следующие 0x10 объектов,
      просто укажите:</para>

    <screen><userinput>x ,10</userinput></screen>

    <para>Подобным же образом используйте

      <screen><userinput>x/ia foofunc,10</userinput></screen>

      для дизассемблирования и вывода первых 0x10 инструкций функции
      <function>foofunc</function> вместе с их адресом относительно
      начала <function>foofunc</function>.</para>

    <para>Чтобы изменить значения в памяти, используйте команду write:</para>

    <screen>
<userinput>w/b termbuf 0xa 0xb 0</userinput>
<userinput>w/w 0xf0010030 0 0</userinput>
    </screen>

    <para>Модификатор команды
      (<literal>b</literal>/<literal>h</literal>/<literal>w</literal>)
      указывает на размер записываемых данных, первое следующее за ним
      выражение является адресом для записи, а оставшаяся часть
      интерпретируется как данные для записи в доступные области
      памяти.</para>

    <para>Если вам нужно узнать текущее содержимое регистров,
      используйте:</para>

    <screen><userinput>show reg</userinput></screen>

    <para>Альтернативно вы можете вывести содержимое одного регистра по
      команде, скажем,

      <screen><userinput>p $eax</userinput></screen>

      и изменить его по:</para>

    <screen><userinput>set $eax new-value</userinput></screen>

    <para>Если вам нужно вызвать некоторую функцию ядра из DDB, просто
      укажите:</para>

    <screen><userinput>call func(arg1, arg2, ...)</userinput></screen>

    <para>Будет выведено возвращаемое значение.</para>

    <para>Для вывода суммарной статистики по всем работающим процессам в
      стиле команды &man.ps.1; воспользуйтесь такой командой:</para>

    <screen><userinput>ps</userinput></screen>

    <para>Теперь вы узнали, почему ядро работает с ошибками и хотите
      выполнить перезагрузку.  Запомните, что в зависимости от влияния
      предыдущих ошибок, не все части ядра могут работать так, как ожидается.
      Выполните одно из следующих действий для закрытия и перезагрузки вашей
      системы:</para>

    <screen><userinput>panic</userinput></screen>

    <para>Это приведет к созданию дампа ядра и перезагрузке, так что позже
      вы можете проанализировать дамп на более высоком уровне при помощи
      kgdb.  Как правило, эта команда должна следовать за другой командой
      <command>continue</command>.</para>

    <screen><userinput>call boot(0)</userinput></screen>

    <para>Это может оказаться хорошим способом для корректного закрытия
      работающей системы, <function>sync()</function> для всех дисков и
      напоследок перезагрузка.	Пока интерфейсы диска и файловой системы
      в ядре не повреждены, это может быть самым правильным способом закрытия
      системы.</para>

    <screen><userinput>call cpu_reset()</userinput></screen>

    <para>самый последнее средство при аварии и практически то же самое, что
      нажатие Большой Красной Кнопки.</para>

    <para>Если вам нужен краткий справочник по командам, просто
      наберите:</para>

    <screen><userinput>help</userinput></screen>

    <para>Однако настоятельно рекомендуем отпечатать копию страницы
      справочника по &man.ddb.4; при подготовке к сеансу отладки.  Помните,
      что трудно читать онлайновое руководство при пошаговом выполнении
      ядра.</para>
  </sect1>

  <sect1>
    <title>Отладка ядра в режиме реального времени при помощи удаленного
      GDB</title>

    <para>Эта возможность поддерживается во FreeBSD начиная с версии 2.2,
      и она на самом деле очень удобна.</para>

    <para>В GDB уже давно имеется поддержка <emphasis>удаленной
      отладки</emphasis>.  Это делается при помощи весьма простого протокола
      по последовательному каналу.  В отличие от других методов, описанных
      выше, для этого вам требуется наличие двух машин.  Одна из них является
      хостом, предоставляющим ресурсы для отладки, включая все исходные
      тексты и копию ядра со всеми символами в нем, а другая является целевой
      машиной, на которой запущена та же копия того же ядра (но без
      отладочной информации).</para>

    <para>Вы должны настроить исследуемое ядро при помощи команды
      <command>config -g</command>, включить <option>DDB</option> в
      конфигурацию и откомпилировать его обычным образом.  Это даст большой
      объем получаемого бинарного файла из-за отладочной информации.
      Скопируйте это ядро на целевую машину, усеките отладочную информацию
      командой <command>strip -x</command> и загрузите это ядро с
      использованием параметра загрузки <option>-d</option>.  Подключите
      последовательный канал целевой машины, имеющий установленные флаги
      "flags 080" на соответствующем устройстве sio к любому
      последовательному каналу отладочного хоста.  А теперь на отладочной
      машине перейдите в каталог компиляции целевого ядра и запустите
      gdb:</para>

    <screen>
&prompt.user; <userinput>gdb -k kernel</userinput>
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.16 (i386-unknown-freebsd),
Copyright 1996 Free Software Foundation, Inc...
<prompt>(kgdb)</prompt>
    </screen>

    <para>Проинициализируйте сеанс удаленной отладки (предполагается, что
      используется первый последовательный порт) такой командой:</para>

    <screen>
<prompt>(kgdb)</prompt> <userinput>target remote /dev/cuaa0</userinput>
    </screen>

    <para>Теперь на целевом хосте (тот, который перешел в DDB даже до
      начала процесса обнаружения устройств) наберите:</para>

    <screen>
Debugger("Boot flags requested debugger")
Stopped at Debugger+0x35: movb	$0, edata+0x51bc
<prompt>db&gt;</prompt> <userinput>gdb</userinput>
    </screen>

    <para>DDB ответит следующим:</para>

    <screen>Next trap will enter GDB remote protocol mode</screen>

    <para>Каждый раз, когда вы будете набирать <command>gdb</command>, режим
      будет меняться между удаленным GDB и локальным DDB.  Чтобы немедленно
      вызвать следующее прерывание, просто наберите <command>s</command>
      (step).  Ваш хостирующий GDB получит управление над целевым
      ядром:</para>

    <screen>
Remote debugging using /dev/cuaa0
Debugger (msg=0xf01b0383 "Boot flags requested debugger")
    at ../../i386/i386/db_interface.c:257
<prompt>(kgdb)</prompt>
    </screen>

    <para>Вы можете работать в этом сеансе точно также, как и в любом другом
      сеансе GDB, включая полный доступ к исходным текстам, запуск его в
      режиме gud-mode внутри окна Emacs (что дает вам автоматический вывод
      исходного кода в другом окне Emacs) и тому подобное.</para>

    <para>Удаленный GDB также может использоваться для отладки модулей LKM.
      Сначала откомпилируйте LKM с отладочной информацией:</para>

    <screen>
&prompt.root; <userinput>cd /usr/src/lkm/linux</userinput>
&prompt.root; <userinput>make clean; make COPTS=-g</userinput>
    </screen>

    <para>Затем установите эту версию модуля на целевой машине, загрузите его
      и воспользуйтесь командой <command>modstat</command> для определения
      того, куда он был загружен:</para>

    <screen>
&prompt.root; <userinput>linux</userinput>
&prompt.root; <userinput>modstat</userinput>
Type	 Id Off Loadaddr Size Info     Rev Module Name
EXEC	  0   4 f5109000 001c f510f010	 1 linux_mod
    </screen>

    <para>Возьмите адрес загрузки модуля и добавьте к нему 0x20 (для размера
      заголовка a.out).  Получится адрес, куда был перемещен код модуля.
      Воспользуйтесь командой <command>add-symbol-file</command> в GDB для
      указания отладчику на модуль:</para>

    <screen>
<prompt>(kgdb)</prompt> <userinput>add-symbol-file /usr/src/lkm/linux/linux_mod.o 0xf5109020</userinput>
add symbol table from file "/usr/src/lkm/linux/linux_mod.o" at
text_addr = 0xf5109020? (y or n) <userinput>y</userinput>
<prompt>(kgdb)</prompt>
    </screen>

    <para>Теперь вы имеете доступ ко всем символам в LKM.</para>
  </sect1>

  <sect1>
    <title>Отладка драйвера консоли</title>

    <para>Так как для работы DDB вам требуется драйвер консоли, то в случае
      неисправностей самого драйвера консоли все становится гораздо сложнее.
      Вы можете вспомнить об использовании последовательной консоли (либо с
      исправленными загрузочными блоками, либо при указании флага
      <option>-h</option> в приглашении <prompt>Boot:</prompt>) и подключить
      обычный терминал к первому последовательному порту.  DDB работает с
      любым отконфигурированным драйвером консоли, в том числе, конечно же,
      и с последовательной консолью.</para>
  </sect1>
</chapter>

<!--
     Local Variables:
     mode: sgml
     sgml-declaration: "../chapter.decl"
     sgml-indent-data: t
     sgml-omittag: nil
     sgml-always-quote-attributes: t
     sgml-parent-document: ("../book.sgml" "part" "chapter")
     End:
-->
