Teoria

Czego potrzeba do poprawienia buga?

1.Bug. Dobrze znany konkretny bug. (ang. bug - błąd w programie)
2.Kernel zawierający wspomnianego buga.
3.Odrobinę szczęścia.

Notka:

  • Błąd którego maożemy sprowokować to więcej niż 50% sukcesu
  • Wszystkie przykłady kodu pochodzą z wersji jądra 2.6.17.13(i386)

Funkcja printk()

printk() to bardzo użyteczna funkcja, podobna do printf(). Ta funkcja działa wszędzie i w każdym czasie (oprócz wczesnej fazy bootowania jądra gdy konsola nie jest jeszcze zainicjalizowana). Używa poziomów logowania (ang. loglevels) do powiadamiania konsoli o ważności poszczególnych wiadomości. Oto pełna lista poziomów:

1.KERN_EMERG <— najważniejsze (czyli praktycznie zawsze wyświetlane)
2.KERN_ALERT
3.KERN_CRIT
4.KERN_ERR
5.KERN_WARNING <— (standardowo dopiero ten poziom jest wyświetlany)
6.KERN_NOTICE
7.KERN_INFO
8.KERN_DEBUG <— najmniej ważne (standardowo nie wyświetlane)

Konsola wyświetli wiadomości jedynie z poziomem wyższym niż console_loglevel. Domyślnie printk używa DEFAULT_MESSAGE_LOGLEVEL==KERN_WARNING (może to ulec zmianie w przyszłości).

printk() używa cyklicznego bufora do zarządzania wiadomościami. Następnie klogd odczytuje wiadomości (używając /proc/kmsg) z bufora i przekazuje je do syslogd, który zapisuje je do pliku /var/log/messages. (Możesz konfigurować syslogd przez edytowanie pliku /etc/syslog.conf).

Przykłady:
printk(loglevel „messages”); Uwaga między wiadomością a poziomem logowania nie ma przecinka !!!

Źródło: Linux/arch/mips/sgi-ip27/ip27-berr.c

20 #if 1
321         printk("FIXME: disabling master aborts\n");
322         csrs->POx_MSK_HEI.csr &= ~(3UL << 14);
323 #endif

Błąd oops

Oops jest raportem buga zgłaszanym przez jądro. Gdy pojawia się błąd oops jądro wyświetli zawartość rejestrów i tzw. "back trace". Błąd oops nie musi oznaczać uszkodzenia systemu, a czasem system potrafi sam rozwiązać problem. Gdy system nie potrafi poradzić sobie z problemem, wyświetli komunikat kernel panic i przestanie działać. "Back trace" domyślnie będzie zawierał adresy wywołanych funkcji. Jeśli wkompilowałeś w jądro opcję CONFIG_KALLSYMS, błąd oops zostanie przetłumaczony i wyświetli nazwy funkcji, które go spowodowały. W serii jąder 2.4 możesz użyć ksymoops plik_z_treścią_oops.txt, w celu określenia nazw fukncji.

Dodatkowe opcje przy kompilacji

Te opcje są bardzo przydatne podczas debugowania jądra:

CONFIG_PREEMPT=y
CONFIG_DEBUG_KERNEL=y
CONFIG_KALLSYMS=y
CONFIG_SPINLOCK_SLEEP=y
CONFIG_MAGIC_SYSRQ=y

Powodowanie błędu oraz wyświetlanie dodatkowych informacji

Czasem możesz chcieć zobaczyć informacje oopsa na temat jakiegoś błędu. Użyj BUG() BUG_ON():

if(bad_thing)

BUG();

lub BUG_ON(bad_thing);

Przykłady:
Źródło: Linux/arch/arm/plat-omap/dma.c

732         if (omap_dma_in_1510_mode()) {
733                 printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
734                 BUG();
735                 return;
736         }
1221         BUG_ON(lcd_dma.active);

1.Czasem możesz chcieć zobaczyć informacje z oops i zaraz zatrzymać system. Użyj panic(): if(terrible_error)
panic(„var = %ld \n”, var);
2.Czasem możesz chcieć zobaczyć stos. Użyj: dump_stack();

if(debug_check)

dump_stack();

Przykłady:
Źródło: Linux/arch/cris/arch-v32/kernel/dma.c

40                 if (options & DMA_PANIC_ON_ERROR)
41                         panic("request_dma error!");

Źródło: Linux/drivers/scsi/hosts.c

398         if (!sht->detect) {
399                 printk(KERN_WARNING "scsi_register() called on new-style "
400                                     "template for driver %s\n", sht->name);
401                 dump_stack();
402         }

Magic SysRq Key

Jeżeli ustawiłeś CONFIG_MAGIC_SYSRQ=y lub wpiszesz 'echo 1 > /proc/sys/kernel/sysrq', możesz używać SysRq Key (na PPC lub i386) 'Alt+PrintScreen'.

1.SysRq+b Restartuje komputer
2.SysRq+e Wysyła SIGTERM do wszystkich zadań (poza init!!!)
3.SysRq+h Pomoc
4.SysRq+l wysyła SIGKILL do wszystkich zadań (poza init!!!)
5.SysRq+k Zabija wszystkie zadania uruchomione z tej konsoli
6.SysRq+l wysyła SIGTKILL do wszystkich zadań (poza init!!!)
7.SysRq+m Obraz pamięci procesu zapisywany w sytuacji awaryjnej na dysku(http://en.wikipedia.org/wiki/Core_Dump) i wyświetla go w konsoli.
8.SysRq+o zatrzymuje system i wyłącza go
9.SysRq+p Wyświetla rejestry procesora w konsoli
10.SysRq+r Zmienia klawiaturę z RAW na XLATE
11.SysRq+s Zapisuje brudne bufory na twardym dysku
12.SysRq+t Pokazuje informacje o obecnym zadaniu w konsoli
13.SysRq+u Odmontowanie wszystkich plików systemowych i ponowne zamontowanie w trybie tylko do odczytu

Zauważ, że każdy (nawet niepowołany) użytkownik może używać powyższych kombinacji i mogę one nie działać odpowiednio na niestabilnym systemie.

Jak używać debuggerów?

Zanim zaczną o nich opowiadać musisz wiedzieć jedną rzecz. Linus nie daje pozwolenia na używanie debuggerów, ponieważ nie zawsze pokazuję prawdziwe informacje.

gdb

gdb vmlinux /proc/kcore <—> vmlinux to skompresowany obraz jądra (poszukaj go w głównym katalogu źródeł kernela) /proc/kcore pozwala gdb obserwować pamięć Linuksa

http://sourceware.org/gdb/current/onlinedocs/gdb_toc.html <— dokumentacja dla gdb
+ prosty w użyciu
- nie możesz dokonywać zmian w danych działającego jądra

kgdb

kgdb jest paczem dla jądra pozwalającą Ci na połączenie dwóch komputerów, jeden z kgdb i drugi z gdb
+ możesz modyfikować dane i zmienne
- musisz skonfigurować połączenie
http://kgdb.linsyssoft.com/

kdb

kdb jest jest podobny do kgdb ale wszystko odbywa się na jednym komputerze.

Różne sztuczki które umilają życie

Powiązanie kodu z wartością UID

Wykonywanie eksperymentalnego kodu można powiązać z UID'em użytkownika:

if(current->uid == 7777) {
    /* eksperymentalny kod */
}else {
    /* start stabilny kod */

Kod eksperymentalny uaktywni się[++ będzie dostępny++] tylko dla użytkownika o UID = 7777. Reszta użytkowników może spać spokojne ;) .

Prowadzenie statystyk

Czasami gdy do analizy danego błędu jest potrzebna wiedza o częstotliwości występowania jakiegoś zdarzenia wystarczy zadeklarować zmienną (typ int zwykle wystarczy) i zwiększać ją przy każdym zdarzeniu. A dane przesyłać do jakiegoś pliku w /proc lub przez wywołanie systemowe, w ostateczności możne przeglądać jego wartość przez debuggera. Trzeba jednak pamiętać, że aby w środowisku wieloprocesorowym/wielowątkowym zapewnić odpowiednią ochronę zmiennych liczników.

Przeszukiwanie binarne

Służy do szukania wersji kernela w której pojawił się błąd. Bierzemy 2 wersje z błędem i bez błędu (np.2.6.8 <- bez błędu i 2.6.19 <- z błędem) i wybieramy kernela który znajduję się po środku (czyli 2.6.13) i sprawdzamy czy i w nim występuje błąd. Jeżeli nie to błąd pojawił się w nowsze połówce (od 2.6.13) jeżeli jednak środkowa wersja posiada błąd to błąd pojawił się w starszej połówce (od 2.6.8 do 2.6.13). Po otrzymaniu połówki znowu bierzemy środkowy element (dla 2.6.8 i 2.6.13 będzie to 2.6.10) i znowu sprawdzamy czy jest to wersja z błędem czy bez. I tak w kółko aż znajdziemy to pierwszą błędną wersję.

Gdy wszystko się sypnie

Nikt nie lubi bugów. Więc gdy spędzasz godziny/dni na poprawianiu bugów, możesz wysłać krótki i opisowy e-mail zawierający wszystkie informacje, które udało Ci się zebrać i wysłać je do LKML. Powodzenia w polowaniu na bugi!

Powrót do Spisu treści.

O ile nie zaznaczono inaczej, treść tej strony objęta jest licencją Creative Commons Attribution-Share Alike 2.5 License.