KGDB

Co to jest?

KGDB jest debugerem z poziomu kodu jądra linuksa (source level debugger). Do działania wykorzystuje gdb, dzięki czemu można debugować jądro w sposób analogiczny do innych aplikacji. Umożliwia ustawianie break-pointów, wykonywać kod krok po kroku, śledzić zmienne i zawartość rejestrów.

Aby skorzystać z KGDB potrzeba dwóch komputerów:

Komputery te muszą być połączone przez port szeregowy, ostatnie wersje KGDB umożliwiają także komunikację przez Ethernet (KGDB 2.3 i 2.4, dla jąder odpowiednio 2.6.13 i 2.6.15.5)

Debugowane jądro działa na maszynie testowej, zaś gdb na maszynie developerskiej.

KGDB jest łatką dodającą do kodu jądra następujące komponenty:

Zalety

Wady

KGDB jest dostępny na wiele architektur; i386, x86_64, ppc, arm, mips i ia64.

Maszyna developerska powinna mieć przynajmniej 128MB RAMu, tak by ładowanie informacji debuggera do gdb nie powodowało nadmiernego wykorzystania swapu.

Obie maszyny wymagają Red Hata 7.3 lub późniejszego. Maszyna testowa musi działać na kernelu który chcemy debugować, maszyna developerska może działać na dowolnym kernelu.

Instrukcja obsługi

Obsługa KGDB jest praktycznie identyczna z obsługą gdb. Poniżej znajdują się opisy wybranych instrukcji.

Dodatkowe funkcjonalności

KGDB umożliwia wyświetlanie informacji konsolowych w gdb, dzięki czemu wszystko mamy na jednym ekranie maszyny developerskiej.

Gdy korzystamy z systemu wieloprocesorowego KGDB dba o to, żeby wszystkie procesory znalazły się pod jego kontrolą.

W sytuacjach kernel panic KGDB przejmuje kontrolę, dzięki czemu istnieje możliwość analizy debugowanego jądra.

KGDB umożliwia debugowanie modułów. Do tego wymagane jest jednak zainstalowanie zmodyfikowanego gdb - gdbmod na maszynie develperskiej. Można je pobrać ze strony http://kgdb.sourceforge.net/.

Instalacja

Aplikowanie łatki i kompilacja jądra

  1. Logujemy się na maszynie developerskiej (o ile już nie jesteśmy zalogowani).
  2. Pobieramy kgdb z repozytorium CVS:

    cvs -z3 -d:pserver:anonymous@kgdb.cvs.sourceforge.net:/cvsroot/kgdb co -r linux2_6_17 .

    W bieżącym katalogu zostaną utworzone dwa katalogi: CVS i kgdb-2. Ten pierwszy możemy usunąć. W drugim oczywiście znajdują się źródła KGDB.
  3. Umieszczamy źródła jądra Linuksa w katalogu obok (np. linux-2.6.17.13-kgdb) i wchodzimy do niego:

    cd linux-2.6.17.13-kgdb

  4. Aplikujemy łatki (kolejność znajduje się w pliku kgdb-2/series):

    for p in $(grep patch ../kgdb-2/series); do patch -p1 -i ../kgdb-2/$p; done

  5. Modyfikujemy Makefile

    EXTRAVERSION = .13-kgdb

  6. Odpalamy menu konfiguracyjne (wymagane pakiety: gcc i libncurses5-dev):

    make menuconfig

  7. Zaznaczamy następujące opcje:

    Kernel hacking ->
       [*] KGDB: kernel debugging with remote gdb ->
       [*] KGDB: Console messages through gdb
       Method for KGDB communication (KGDB: On generic serial port (8250)) --->
         ( ) KGDB: Use only kernel modules for I/O
         (X) KGDB: On generic serial port (8250)
         ( ) KGDB: On ethernet - in kernel
       [*] Simple selection of KGDB serial port
       (115200) Debug serial port baud rate
       (0) Serial port number for KGDB

  8. Kompilujemy:

    make && make modules_install

  9. Czekamy z jakąś godzinkę aż się wszystko raczy skompilować.
  10. Jeżeli podczas kompilacji wyskoczy nam błąd podobny do poniższego:

    init/built-in.o: In function `try_name':
    /usr/src/linux-2.6.17.13-kgdb/init/do_mounts.c:115: undefined reference to `__stack_chk_fail'

    trzeba zmodyfikować Makefile, tak by definicja CFLAGS wyglądała następująco:
        CFLAGS          := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
                           -fno-strict-aliasing -fno-common -fno-stack-protector
        CFLAGS          += $(call cc-option, -fno-stack-protector)
        
  11. Kopiujemy wyniki kompilacji na maszynę testową:

    scp arch/i386/boot/bzImage user@host:/boot/vmlinuz-2.6.17.13-kgdb
    scp System.map user@host:/boot/System.map-2.6.17.13-kgdb

  12. Może się okazać że nowe jądro nie będzie działać bez początkowego ramdysku, więc o ile mamy dwie identyczne maszyny, trzeba go więc utworzyć:

    mkinitramfs -o initrd.img-2.6.17.13-kgdb 2.6.17.13-kgdb

    Kopiujemy na maszynę testową nowoutworzony ramdysk:

    scp initrd.img-2.6.17.13-kgdb user@host:/boot/initrd.img-2.6.17.13-kgdb

  13. Na maszynie testowej dodajemy wpis do gruba (lub odpalamy grub-update)

    title Kernel 2.6.17.13-kgdb
    root (hd0,0)
    kernel /boot/vmlinuz-2.6.17.13-kgdb root=/dev/hda1 ro kgdbwait
    initrd initrd.img-2.6.17.13-kgdb
    boot

    Opcja kgdbwait spowoduje, że debugowane jądro będzie czekać na połączenie od gdb z maszyny developerskiej.

Emulacja portu szeregowego

Ciąg dalszy instalacji dotyczy VMware zainstalowanego w systemie Windows. Instalacja w systemie Linux opisana jest w internecie (hasła kgdb vmware).

  1. W VMware dodajemy do maszyny testowej port szeregowy i nazywamy go \\.\pipe\com_1. Należy zaznaczyć opcję Yield CPU on poll, pozostałe opcje bez zmian.

    VMware

  2. Ściągamy i instalujemy program Named Pipe TCP Proxy z http://shvechkov.tripod.com/nptp_setup.zip. Tworzymy łącze z \\.\pipe\com_1 i podpinamy je do jakiegoś portu (np. 6666).



    Dzięki temu port szeregowy maszyny testowej jest osiągalny poprzez

    ip_naszego_komputera:port

  3. Odpalamy maszynę testową i modlimy się żarliwie żeby zadzialało. Ładowanie jądra powinno zatrzymać się na:

    Uncompressing Linux... Ok, booting the kernel.

  4. Na maszynie developerskiej wchodzimy do katalogu, w którym dokonywaliśmy kompilacji:

    cd /usr/src/linux-2.6.17.13-kgdb

    i uruchamiamy gdb:

    gdb ./vmlinux

    otrzymujemy odpowiedź:

    GNU gdb 6.4.90-debian
    Copyright (C) 2006 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or 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.
    This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

    (gdb)

    Każemy mu się połączyć z debugowanym jądrem:

    (gdb) target remote ip_naszego_komputera:port

    O ile gdb uda się połączyć wypisuje komunikat:

    Remote debugging using ip_naszego_komputera:port
    breakpoint () at kernel/kgdb.c:1691
    1691           atomic_set(&kgdb_setting_breakpoint, 0);

    Widzimy, że debugowane jądro zatrzymało się na wbudowanym breakpoincie.
  5. No i sobie debugujemy dalej...

Demonstracja:

Filmik (2.98 MB) pokazujący komunikację dwóch maszyn wirtualnych i ładowanie jądra z wykorzystaniem KGDB.

Źródła: