Odpluskwianie jądra

Odpluskwianiem nazywamy wyszukiwanie błędów (błąd to z ang. bug, czyli pluskwa) w danym programie,
określenie miejsca ich wystąpienia oraz, w najczęstszym przypadku, ich usunięcie.

W tej prezentacji skupimy się na odpluskwianiu jądra.

Każdorazowo po dodaniu jakiegoś nowego kawałka kodu do jądra istnieje spora szansa na to, że zawiera on błędy lub źle współgra z resztą kodu.
Wraz z rozwojem jądra, pracuje nad nim coraz więcej osób, co może powodować coraz większe trudności w jego utrzymaniu. Aby ułatwić pozbycie się błędów zostało stworzone specjalne oprogramowanie mające na celu wspomóc wyszukiwanie i usuwanie błędów w jądrze.

Podczas ładowania systemu mogą się zdarzyć różne błędy, przykładowo:

  1. Oops - dziwo, o którym będzie za chwilę
  2. kernel panic - nazwa komunikatu wyświetlanego przez system operacyjny po wykryciu wewnętrznego błędu jądra systemu, którego same nie jest w stanie obsłużyć; często jest następstwem pojawienia się wielu komunikatów "Oops"
Informują one nas o zajściu błędu, czasem też o jego miejscu wystąpienia. Nie jest to jednak wystarczające. Najwygodniej byłoby zastosować debugger, tak jak dla zwykłego programu. Niestety, ze względu na specyfikę jądra, nie jest to takie proste.
Z pomocą przychodzą nam takie narzędzia jak:

Żeby odpluskwianie jądra było możliwe warto włączyć w konfiguracji jądra następującą opcję:

CONFIG_DEBUG_KERNEL = y

Można ją znaleźć w menu konfiguracyjnym w dziale Kernel Hacking.

W pierwszej części prezentacji zostaną omówione komunikaty Oops oraz KDB, natomiast w drugiej KGDB oraz UML.

Oops (i co teraz?)

Co to jest ten Oops?

Oops - raport jądra systemu świadczący o jego odstępstwie od poprawnego zachowania.

Gdy jądro znajdzie problem, wypisuje komunikat Oops oraz zabija każdy proces, który "zawinił". Wiadomość ta jest wykorzystywana przez inżynierów jądra Linuksa do znalezienia przyczyn błędu i naprawy problemu. Po wystąpieniu Oopsa niektóre zasoby mogą zostać utracone. Gdy system będzie chciał ich użyć, może to doprowadzić do stanu kernel panic.

Oops może się zdarzyć, gdy wystąpi:

Przykłady:

  1. dla architektury x86
  2. BUG: unable to handle kernel paging request at virtual address eaa34b3c
    
     printing eip:
    
    b0161cdd
    
    *pde = 0048a067
    
    *pte = 3aa34000
    
    Oops: 0000 [#1]
    
    PREEMPT SMP DEBUG_PAGEALLOC
    
    last sysfs file: /devices/pci0000:00/0000:00:1d.1/usb3/idVendor
    
    Modules linked in: snd_intel8x0 snd_ac97_codec snd_ac97_bus
    
    snd_pcm_oss snd_mixer_oss snd_pcm snd_timer ide_cd cdrom intel_agp
    
    agpgart snd i2c_i801 hw_random soundcore snd_page_alloc unix
    
    CPU:    0
    
    EIP:    0060:[<b0161cdd>]    Not tainted VLI
    
    EFLAGS: 00010282   (2.6.16-rc1-mm3 #4)
    
    EIP is at do_path_lookup+0x22b/0x259
    
    eax: eaa34b20   ebx: eb328000   ecx: 00000000   edx: eb328f4c
    
    esi: ffffff9c   edi: fffffffe   ebp: eb328f24   esp: eb328f0c
    
    ds: 007b   es: 007b   ss: 0068
    
    Process udevd (pid: 731, threadinfo=eb328000 task=eb30ca80)
    
    Stack: <0>00000000 eb5cb000 b015fab1 eb5cb000 eb5cb000 00000000
    
    eb328f40 b01621f3
    
          eb328f4c ffffff9c afbf6dec afbf6dec 00000100 eb328f9c b015bf69 eb328f4c
    
          eaa34b20 b23d5f28 00000000 eb329003 b015f8ce 00000000 00000001 00000000
    
    Call Trace:
    
     [<b0103917>] show_stack_log_lvl+0xaa/0xb5
    
     [<b0103a54>] show_registers+0x132/0x19d
    
     [<b0103d91>] die+0x171/0x1fb
    
     [<b02ab110>] do_page_fault+0x3be/0x568
    
     [<b010343f>] error_code+0x4f/0x54
    
     [<b01621f3>] __user_walk_fd+0x2d/0x41
    
     [<b015bf69>] sys_readlinkat+0x26/0x93
    
     [<b015bfe9>] sys_readlink+0x13/0x15
    
     [<b01028bf>] sysenter_past_esp+0x54/0x75
    
    Code: 00 83 c0 04 e8 9a 82 14 00 8b 03 c7 80 e4 01 00 00 00 00 00 00
    
    8b 55 08 8b 45 ec e8 55 fa ff ff 89 c7 8b 55 08 8b 02 85 c0 74 24 <8b>
    
    50 1c 85 d2 74 1d b8 00 f0 ff ff 21 e0 8b 00 83 b8 d4 04 00
    
  3. dla innej architektury

Zazwyczaj raport Oops składa się z (dla architektury x86):

Gdzie ten Oops się teraz znajduje?

  1. Zazwyczaj komunikat Oops jest wczytywany z buforów jądra przez demona klogd i przekazywany dalej do demona syslogd. Ten z kolei umieszcza komunikat w pliku /var/log/messages.
  2. Gdy klogd nie działa można samemu spróbować wczytać zawartość bufora jądra jednym ze sposobów:
    1. dmesg > plik
    2. cat /proc/kmsg > plik (należy tu pamiętać, by przerwać to, gdyż kmsg jest "niekończącym się" plikiem
  3. Jest jeszcze możliwość, że system zupełnie nie rozumie komend, które chcemy mu wprowadzić, więc zanim zmienimy dysk, wyląduje za oknem, itp. można spróbować jeszcze zrobić tak:
    1. przepisać ręcznie zawartość ekranu lub zrobić mu zdjęcie i wpisać zapisany tekst po zrestartowaniu systemu (można też ewentutalnie zmienić rozdzielczość ekranu, by dostać to co trzeba)
    2. uruchomić komputer ponownie zdalnie z drugiej maszyny używając kabla szeregowego (null-modemu)
    3. wykorzystać Kdump (który to pozwala na tworzenie tzw. zrzutów podczas padu system; taki zrzut pozwala na łatwe odpluskwianie)

Możliwe sposoby radzenia sobie z Oopsem:

  1. Metoda "zrób to sam":
  2. (Żeby jej użyć, warto włączyć w konfiguracji następujące opcje:

    CONFIG_DEBUG_SLAB = y

    CONFIG_FRAME_POINTER = y)

    1. sprawdzamy co jest napisane (nazwa funkcji) przy "EIP is at ..." (np. w przykładzie powyżej jest to funkcja do_path_lookup)
    2. wyszukujemy nazwy tej funkcji w drzewie jądra
    3. tworzymy zdeassemblowany plik modułu, w którym się znajduje dana funkcja
    4. w pliku z rozszerzeniem .s szukamy numeru podanego po "+" i przed "\" w linii z "EIP is at ..."
    5. za pomocą funkcji printf "0x%x\n" $((0xCOS+0x22b)), wyszukujemy numer linii, który mówi nam, która to linjka kodu, itd. itd. ...
  3. Metoda "dla inżyniera jądra":
    1. przesłać informacje o Oops wraz z plikiem konfiguracyjnego jądra, z którym było kompilowane oraz ze sposobem odtworzenia, jeśli jest znany, na LKML(Linux Kernel Mailing List)
    2. inżynier jądra sprawdzi o co chodzi wydając komendy:
    3. następnie używając swojej znajomości jądra danej dystrybucji systemu i używając przeróżnych sztuczek inżynier naprawia usterkę :)
  4. Metody Linusa Torvaldsa (który to jest przeciwnikiem odpluskwiaczy dla jądra Linuksa):
    1. gdb /usr/src/linux/vmlinux
    2. gdb> disassemble <offending_function> (gdzie offending_function to nazwa funkcji sprawiająca problem (czyli ta przy napisie "EIP is at ..."))
    3. napisać program w C:
    4. char str[] = "\xXX\xXX\xXX...";
      main(){}
      
      ,gdzie spację należało zamienić na "\x" z kodu (napis po "Code: ...") oznaczonego jako XX XX ...
    5. następnie skompilować ów program z gcc -g oraz zdeassemblować łańcuch "str"
    6. Linus Torvalds twierdzi, że mając te informacje wraz z raportem Oopsa można często znaleźć usterkę metodą "znajdź i przypasuj", a jak nie to szukając, co nie pasuje.

Jednakże, zamiast się trudzić, lepiej czasem skorzystać z gotowego oprogramowania do odpluskwiania. np.


KDB

Do czego służy KDB?

KDB - jest to łata (z ang. patch) na jądro, która po dodaniu do niego daje możliwość dokładnego zbadania "co się stało, że nie działa?"

Cechy KDB:

Instalacja

KDB jest projektem Silicon Graphics i można go ściągnąć z ich strony internetowej: Silicon Graphics - KDB.


Na stronie w dziale Download/Sources należy znaleźć odpowiednie pliki, np. dla jądra w wersji 2.6.17 i architektury i386 trzeba załadować 2 pliki:
     kdb-v4.4-2.6.17-common-1.bz2

     kdb-v4.4-2.6.17-i386-1.bz2 

Obecnie najnowszą wersją KDB jest 4.4.
Teraz pliki te kopiujemy do katalogu /usr/src/linux i tam rozpakowujemy programem bzip2:
     #bzip2 -d kdb-v4.4-2.6.17-common-1.bz2

     #bzip2 -d kdb-v4.4-2.6.17-i386-1.bz2

Zastosowujemy patche:
     #patch -p1 <kdb-v4.4-2.6.17-common-1

     #patch -p1 <kdb-v4.4-2.6.17-i386-1 

Należy teraz skonfigurować jądro (np. przez użycie make menuconfig, gdzie można ustawić następujące opcje:
Po konfiguracji jądra należy zapisać zmiany, skompilować jądro, skopiować do katalogu /boot i uruchomić ponownie komputer. Można też przed ponownym uruchomieniem wydać polecenie make clean.

Dodatkowe przygotowania

Można samemu zdefiniować komendy dla KDB. Powinny one być umieszczone w pliku kdb_cmds, który znajduje się w katalogu KDB w drzewie żródłowym Linuksa.

Plik ten służy także do ustawiania zmiennych środowiskowych, które mają wpływ na wyświetlanie komunikatów przy działaniu KDB. Jednakże, po jego zmodyfikowaniu należy przekompilować i przeinstalować jądro.

Aktywacja KDB

Po ustawieniu CONFIG_KDB_OFF KDB nie będzie uruchamiane domyślnie po starcie systemu, więc należy je teraz włączyć, by móc z niego korzystać.

Można to zrobić tak:
       #echo "1" >/proc/sys/kernel/kdb 

Jeśli chcemy wyłączyć go to należy napisać:
       #echo "0" >/proc/sys/kernel/kdb 

Istnieją trzy sposoby, KDB zostało uruchomione:

Obsługa KDB

Obsługę można podzielić na kilka rodzajów:

  1. WYŚWIETLANIE PAMIĘCI I JEJ MODYFIKACJA
  2. np.:
    wyświetlenie zawartości 15 linii zaczynając od adresu 0xc000000:
    [0]kdb> md 0xc000000 15
    zmiana zawartości pamięci dla adresu 0xc000000 na 0x10:
    [0]kdb> mm 0xc000000 0x10
  3. WYŚWIETLANIE REJESTRÓW I ICH MODYFIKACJA
  4. np.
    ustawienie zawartości rejestru ebx na 0x25:
    [0]kdb> rm %ebx 0x25
  5. PUNKTY ZATRZYMANIA ("BREAKPOINTS")
  6. np.
    ustawienie punktu zatrzymania na instrukcji sys_read:
    [0]kdb> bp sys_write
  7. ŚLEDZENIE STOSU
  8. np.
    śledzenie procesu o nr PID równym 575:
    [0]kdb> btp 575
  9. INNE KOMENDY
  10. np.
    deassemblacja instrukcji począwszy od "schedule", liczba linii wyświetlanych zależy od IDCOUNT:
    [0]kdb> id schedule
    wykonywanie instrukcji do napotkania rozgałęzienia (warunku) (w tym przypadku "jne"):
        [0]kdb> ssb
    
        0xc0105355 default_idle+0x25: cli
        0xc0105356 default_idle+0x26: mov 0x14(%edx),%eax
        0xc0105359 default_idle+0x29: test %eax, %eax
        0xc010535b default_idle+0x2b: jne 0xc0105361 default_idle+0x31 
    
    
    

Podsumowanie

Jak widać, istnieją różne sposoby na odpluskwianie jądra: od metod "dla wytrwałych" w przypadku rozwiązywaniu problemów z Oopsem do wygodnego użytkowania odpluskwiacza KDB. Od użytkownika systemu zależy, które z nich uzna za lepsze.

Przydatne linki