Testowanie

Proces testowania możemy podzielić na trzy fazy:
1. Używanie wersji testowej do normalnej pracy.
2. Użycie programów testujących system np. LTP.
3. Wykonywanie działań niekonwencjonalnych.

2.1 „Faza 1”

Pierwsza faza jest prosta - próbujemy uruchomić system:
• na początku w minimalnej konfiguracji z parametrem init=/bin/bash i w trybie bez sieci (przekazujemy 1 lub 2 do parametrów jądra - zależy od dystrybucji (sprawdź w dokumentacji dystrybucji lub w pliku /etc/inittab, który poziom init uruchamia system bez obsługi sieci) (przydaje się grub jako bootloader))
• dopiero po sprawdzeniu działania konfiguracji minimalnych uruchamiamy normalnie system
Następnie ściągamy jakiś plik z sieci, odbieramy pocztę, nagrywamy płytę z danymi/audio (przydaje się CD,DVD-RW) itd.

2.2 „Faza 2 (AutoTest)”

W następnej fazie używamy wyspecjalizowanych programów służących do sprawdzania poprawności działania różnych podsystemów jądra oraz przeprowadzamy testy regresyjne i wydajnościowe. Te ostatnie są bardzo wartościowe dla deweloperów (i dla nas) - jeżeli przesiadka z wersji 2.6.x-rc1 na wersję 2.6.x-rc2 zaowocowała 10% spadkiem wydajności naszego systemu plików, to warto wyśledzić, która łatka to spowodowała.

Do przeprowadzania zautomatyzowanych testów polecam użycie platformy AutoTest. Składają się na nią różnego rodzaju testy i narzędzia służące do profilowania systemu obudowane w proste w użyciu API. Na początku powinniśmy przejść do zalecanego w dokumentacji katalogu /usr/local i jako użytkownik uprzywilejowany wykonać polecenie

# svn checkout svn://test.kernel.org/autotest/trunk autotest

Podczas pracy z AutoTestem będziemy wykorzystywać konto użytkownika uprzywilejowanego, wbrew wszystkim zaleceniom, które mówią, żeby nie pracować na takim koncie. Powód jest bardzo prosty, duża część testów wymaga uprawnień administratora, a AutoTest z założenia ma być platformą nieinteraktywną. Dzięki temu możemy zostawić go na kilka godzin bez opieki i nie martwić się, czy wyskoczy prośba o podanie hasła administratora. (Jest to też kolejny dobry argument za używaniem odseparowanych systemów testowych - gdy coś pójdzie nie tak, to zniszczymy tylko system przeznaczony do testów, a nie do pracy :) Najprostszym sposobem uruchomienia testu jest wykonanie polecenia „bin/autotest tests/nazwa_testu/control” po przejściu do katalogu client. AutoTest wczytuje plik control i wykonuje zawarte w nim instrukcje. Najprostszą postacią pliku control jest:
job.run_test(’ltp’)
Nie podaje on żadnych parametrów, które mają być przekazane do testu, zawiera tylko jego nazwę. Bardziej „skomplikowane” pliki control mają postać:
job.run_test(’pktgen’, ’eth0’, 50000, 0, tag=’clone_skb_off’)
job.run_test(’pktgen’, ’eth0’, 50000, 1, tag=’clone_skb_on’)
Przekazują one parametry do programów testowych. Możemy zmodyfikować te parametry po zapoznaniu się z dokumentacją testu, oraz plikiem tests/nazwa_testu/nazwa_testu.py. Gdy AT dowie się już jaki test ma uruchomić i jakie przekazać do niego parametry, uruchamia plik tests/nazwa_testu/nazwa_testu.py. Najczęściej jest on trochę dłuższy, niż przedstawiony poniżej, ale ten dobrze obrazuje, co w takim pliku powinno się znaleźć.

import test, os_dep
from autotest_utils import *
class isic(test.test):
        version = 1
        # http://www.packetfactory.net/Projects/ISIC/isic-0.06.tgz
        # + http://www.stardust.webpages.pl/files/crap/isic-gcc41-fix.patch
        def setup(self, tarball = ’isic-0.06.tar.bz2’):
                tarball = unmap_url(self.bindir, tarball, self.tmpdir)
                extract_tarball_to_dir(tarball, self.srcdir)
                os.chdir(self.srcdir)
                os_dep.library(’libnet.so’)
                system(’./configure’)
                system(’make’)
        def execute(self, args = ’-s rand -d 127.0.0.1 -p 10000000’):
                system(self.srcdir + ’/isic ’ + args)

Pierwszą funkcją zawartą w skrypcie jest funkcja setup(), która ma za zadanie przygotować test do działania, czyli wykonuje za nas konfigurację i kompilacje testu. Jak sama nazwa wskazuje, funkcja execute() uruchamia test z zadanymi parametrami. Rezultaty wykonania testu można obejrzeć w katalogu results/default/nazwa_testu/. Plik status zawiera informację o tym, czy test zakończył się pomyślnie, lub czy coś poszło nie tak.

Jeżeli chcemy uruchomić kilka testów jeden po drugim, to najlepiej jest przygotować plik z instrukcjami dla autotestu. Powinien on zawierać te same informacje, które można znaleźć w plikach control. Na przykład pierwsze pięć linijek pliku „samples/all_tests” wygląda tak:

job.run_test(’aiostress’)
job.run_test(’bonnie’)
job.run_test(’dbench’)
job.run_test(’fio’)
job.run_test(’fsx’)

Następnie uruchamiamy autotest i jako parametr podajemy nasz plik z informacjami kontrolnymi. Możemy też po prostu wykonać polecenie „bin/autotest samples/all_tests”, jednak musimy się przygotować na to, że wykonanie wszystkich zawartych w nim testów będzie trwało bardzo długo.

Jeżeli chcemy uruchomić równolegle kilka testów, to plik kontrolny powinien mieć składnie taką, jak przykładowy plik „samples/parallel

def kernbench():
          job.run_test(’kernbench’, 2, 5)
def dbench():
          job.run_test(’dbench’)
job.parallel([kernbench], [dbench])

(W każdej chwili możemy przerwać wykonywanie testu przy użyciu kombinacji Ctrl+c)

Dla ludzi, którzy nie lubią linii poleceń i plików konfiguracyjnych powstał ATCC (AutoTest Control Center). Można go uruchomić poleceniem „ui/menu”. Dostajemy do dyspozycji system prostych menu w, których możemy wybrać testy, programy służące do profilowania, obejrzeć rezultaty ich wykonania, oraz przeprowadzić prostą konfigurację. Jeśli zestaw narzędzi dostarczony z AutoTestem Ci się znudził, to zawsze możesz odwiedzić stronę http://ltp.sourceforge.net/tooltable.php. Znajduje się na niej pokaźny spis narzędzi, które można używać do testowania systemu.

2.3 „Faza 3”

Ok, system przeszedł bez problemów dwie pierwsze fazy testów? Możemy zacząć improwizować tzn. robić głupie rzeczy, których nikt nie robi, dlatego nie wie, że można nimi spowodować oops’a :). Ale co takiego należy robić? Gdyby istniał jakiś standardowy sposób, to na pewno trafiłby do jakiegoś zestawu testów.

W fazie trzeciej możemy zacząć od wyciągania i przekładania wtyczek urządzeń podpiętych do magistrali USB. O ile przekładanie wtyczek teoretycznie nie powinno nic zmienić, o tyle notoryczne wyciąganie i wkładanie wtyczki może wywołać oops’a z lasu (pod warunkiem, że ktoś inny tego wcześniej nie próbował przy podobnej konfiguracji PC). Następnie możemy napisać skrypt, który będzie czytał w pętli zawartość plików znajdujących się w katalogu /proc. W telegraficznym skrócie faza trzecia polega na robieniu rzeczy, których nie robią zwykli użytkownicy (lub robią bardzo rzadko - po co ktoś miałby w nieskończoność montować i odmontowywać jakiś system plików? :).

2.4 Pomiar wydajności

Jak już wcześniej wspomniałem, warto jest sprawdzać wpływ zmian wprowadzanych w systemie na jego wydajność (nawiasem mówiąc - może to być świetne zajęcie dla początkujących testerów, którzy jeszcze nie chcą testować bardziej rozwojowych wersji systemu, a chcą bardzo pomóc w jego tworzeniu). Ale od czego zacząć i co warto wiedzieć?

Na początku warto jest się zdecydować na testowanie konkretnego podsystemu - wybierzmy jeden, który będziemy testować regularnie - wtedy nasze dane będą o wiele bardziej wartościowe dla deweloperów. Czasami ktoś wysyła na LKML list w stylu: „Witam, zauważyłem spory spadek wydajności mojej karty sieciowej po zaktualizowaniu jądra z 2.6.8 na 2.6.20. Ktoś ma jakiś pomysł, co z tym zrobić?”. Oczywiście, że nikt nie może mieć żadnego pomysłu co się stało, ponieważ pomiędzy wydaniem jądra 2.6.8 a 2.6.20 upłynęło ponad dwa i pół roku (i gadzilion różnych losowych łatek). Jeśli z kolei napiszemy, że pomiędzy wersją 2.6.x-rc3 a 2.6.x-rc4 wydajność naszej karty graficznej spadła o 50%, to łatwo będzie wyśledzić, dlaczego tak się stało. Dlatego regularne wykonywanie pomiarów jest ważne.

Drugą sprawą, na którą trzeba zwrócić uwagę, są szczegóły dotyczące poszczególnych testów - musimy się dowiedzieć jak najwięcej o danym benchmarku. Chodzi przede wszystkim o to, żeby uzyskane wyniki były wiarygodne. Czasami, w różnych publikacjach prasowych/internetowych możemy się spotkać z testem wydajności pod tytułem „time make”. Chodzi o sprawdzenie szybkości budowania jądra systemu z pomocą programu time. Jest to test, który należy zaliczyć do grupy niewiarygodnych. Dlaczego? Odpowiedź na to pytanie jest bardzo prosta, jeśli zwrócimy uwagę na to, że pliki przed kompilacją są wczytywane z dysku. Różne dyski mają różną prędkość odczytu, zależy ona również od tego w jakim miejscu znajdują się dane i jak są porozrzucane. O ile prędkość procesora i pamięci RAM można przyjąć za stałą, o tyle szybkość odczytu danych z dysku jest na tyle losowa, że pomiędzy poszczególnymi wynikami mogą występować znaczące różnice. Aby uzyskać wiarygodne wyniki, trzeba więc jakoś ominąć niedoskonałości dysków twardych - najprostszym sposobem będzie buforowanie danych w pamięci operacyjnej (Linux bardzo intensywnie buforuje dane odczytywane z dysków, czym przyspiesza ich późniejszy ponowny odczyt). Jeżeli chcemy przeprowadzać testy pt. „time make” i otrzymywać wiarygodne wyniki, to najlepiej jest użyć skryptu kernbench http://ck.kolivas.org/kernbench/ lub jego nowszej wersji wchodzącej w skład platformy AutoTest.

Kolejną sprawą, o której warto pamiętać, jest stabilność (niezmienność) środowiska, w którym wykonujemy pomiary - czyli nie powinniśmy aktualizować programów, które mogą mieć wpływ na uzyskiwane dane. Jeśli nasze pomiary polegają na kompilacji jądra systemu, to niech to cały czas będzie ta sama wersja źródeł i ten sam plik konfiguracyjny (kompilator i wszystkie potrzebne przy budowaniu jądra narzędzia też nie powinny zmieniać swoich wersji).

Dobrym przykładem na złe testowanie, mogą być porównania wydajności systemów plików, w których nie wzięto pod uwagę czynnika budowy dysku twardego. Jak wcześniej wspomniałem, prędkość odczytu/zapisu w różnych miejscach dysku jest różna - dlatego nie powinniśmy tworzyć pięciu partycji do wykonywania testów pięciu różnych systemach plików. Jedynym sposobem na otrzymanie wiarygodnych wyników jest utworzenie jednej partycji (i nie zmienianie jej położenia), na której będziemy tworzyć różne systemy plików. Pomiędzy kolejnymi pomiarami najlepiej jest też zrestartować system, aby pominąć czynnik buforowania danych.

Na stronie http://ltp.sourceforge.net/tooltable.php można znaleźć kilka dobrych benchmarków (część z nich znajduje się również w AutoTest), jednak powinniśmy pamiętać o tym, że nie zależy nam na mierzeniu wydajności sprzętu tylko systemu. Dlatego polecam zapoznanie się z dokumentacją wybranego benchmarku - zazwyczaj znajduje się w niej kilka użytecznych informacji.
Podsumowując, w mierzeniu wydajności najważniejsze są:
• regularne pomiary
• znajomość „szczegółów” pozwalających na uzyskiwanie wiarygodnych wyników
• stabilność środowiska testowego
Jeśli wszystkie trzy czynniki zostaną spełnione, to dane przez nas dostarczane będą nieocenionym źródłem informacji na temat wydajności konkretnego podsystemu.

2.5 Witaj świecie, czyli czego właściwie szukamy?

Czego właściwie szukamy? Poniżej zamieszczam kilka przykładów błędów na które można natrafić podczas testowania systemu.

=============================================
[ INFO: possible recursive locking detected ]
---------------------------------------------
idle/1 is trying to acquire lock:
 (lock_ptr){....}, at: [<c021cbd2>] acpi_os_acquire_lock+0x8/0xa
but task is already holding lock:
 (lock_ptr){....}, at: [<c021cbd2>] acpi_os_acquire_lock+0x8/0xa
other info that might help us debug this:
1 lock held by idle/1:
 #0: (lock_ptr){....}, at: [<c021cbd2>] acpi_os_acquire_lock+0x8/0xa
stack backtrace:
 [<c0103e89>] show_trace+0xd/0x10
 [<c0104483>] dump_stack+0x19/0x1b
 [<c01395fa>] __lock_acquire+0x7d9/0xa50
 [<c0139a98>] lock_acquire+0x71/0x91
 [<c02f0beb>] _spin_lock_irqsave+0x2c/0x3c
 [<c021cbd2>] acpi_os_acquire_lock+0x8/0xa
 [<c0222d95>] acpi_ev_gpe_detect+0x4d/0x10e
 [<c02215c3>] acpi_ev_sci_xrupt_handler+0x15/0x1d
 [<c021c8b1>] acpi_irq+0xe/0x18
 [<c014d36e>] request_irq+0xbe/0x10c
 [<c021cf33>] acpi_os_install_interrupt_handler+0x59/0x87
 [<c02215e7>] acpi_ev_install_sci_handler+0x1c/0x21
 [<c0220d41>] acpi_ev_install_xrupt_handlers+0x9/0x50
 [<c0231772>] acpi_enable_subsystem+0x7d/0x9a
 [<c0416656>] acpi_init+0x3f/0x170
 [<c01003ae>] _stext+0x116/0x26c
 [<c0101005>] kernel_thread_helper+0x5/0xb

Powyżej znajduje się błąd znaleziony przez mechanizm sprawdzający poprawność użycia blokad w systemie (lockdep).

BUG: sleeping function called from invalid context at /usr/src/linux-mm/sound/core/info.c:117
in_atomic():1, irqs_disabled():0
 <c1003ef9> show_trace+0xd/0xf
 <c100440c> dump_stack+0x17/0x19
 <c10178ce> __might_sleep+0x93/0x9d
 <f988eeb5> snd_iprintf+0x1b/0x84 [snd]
 <f988d808> snd_card_module_info_read+0x34/0x4e [snd]
 <f988f197> snd_info_entry_open+0x20f/0x2cc [snd]
 <c1067a17> __dentry_open+0x133/0x260
 <c1067bb7> nameidata_to_filp+0x1c/0x2e
 <c1067bf7> do_filp_open+0x2e/0x35
 <c1068bf2> do_sys_open+0x54/0xd7
 <c1068ca1> sys_open+0x16/0x18
 <c11dab67> sysenter_past_esp+0x54/0x75
BUG: using smp_processor_id() in preemptible [00000001] code: init/1
caller is __handle_mm_fault+0x2b/0x20d
 [<c0103ba8>] show_trace+0xd/0xf
 [<c0103c7a>] dump_stack+0x17/0x19
 [<c0203bcc>] debug_smp_processor_id+0x8c/0xa0
 [<c0160e60>] __handle_mm_fault+0x2b/0x20d
 [<c0116f7b>] do_page_fault+0x226/0x61f
 [<c0103959>] error_code+0x39/0x40
 [<c019d4c1>] padzero+0x19/0x28
 [<c019e716>] load_elf_binary+0x836/0xc02
 [<c017db53>] search_binary_handler+0x123/0x35a
 [<c019d3b9>] load_script+0x221/0x230
 [<c017db53>] search_binary_handler+0x123/0x35a
 [<c017deee>] do_execve+0x164/0x215
 [<c0101e7a>] sys_execve+0x3b/0x7e
 [<c02fabc3>] syscall_call+0x7/0xb

Poniżej znajduje się tzw. oops - po wystąpieniu którego, bardzo często nasz system kończy działanie tzw. kernel panic. Czyli stało się coś naprawdę złego i nasz system przestaje działać, żeby nie ryzykować utraty danych lub innych przykrych doświadczeń. (W artykule OSWeekly.com Puru Govind wyjaśnił skąd się wziął kernel panic i co może oznaczać http://www.osweekly.com/index.php?option=com_content&task=view&id=2241&Itemid=449 (ang.))

BUG: unable to handle kernel paging request at virtual address 6b6b6c07
 printing eip:
c0138722
*pde = 00000000
Oops: 0002 [#1]
4K_STACKS PREEMPT SMP
last sysfs file: /devices/pci0000:00/0000:00:1d.7/uevent
Modules linked in: snd_timer snd soundcore snd_page_alloc intel_agp agpgart
ide_cd cdrom ipv6 w83627hf hwmon_vid hwmon i2c_isa i2c_i801 skge af_packet
ip_conntrack_netbios_ns ipt_REJECT xt_state ip_conntrack nfnetlink xt_tcpudp
iptable_filter ip_tables x_tables cpufreq_userspace p4_clockmod speedstep_lib
binfmt_misc thermal processor fan container rtc unix
CPU:     0
EIP:     0060:[<c0138722>]     Not tainted VLI
EFLAGS: 00010046     (2.6.18-rc2-mm1 #78)
EIP is at __lock_acquire+0x362/0xaea
eax: 00000000    ebx: 6b6b6b6b    ecx: c0360358  edx: 00000000
esi: 00000000    edi: 00000000    ebp: f544ddf4  esp: f544ddc0
ds: 007b    es: 007b    ss: 0068
Process udevd (pid: 1353, ti=f544d000 task=f6fce8f0 task.ti=f544d000)
Stack: 00000000 00000000 00000000 c7749ea4 f6fce8f0 c0138e74 000001e8 00000000
         00000000 f6653fa4 00000246 00000000 00000000 f544de1c c0139214 00000000
         00000002 00000000 c014fe3a c7749ea4 c7749e90 f6fce8f0 f5b19b04 f544de34
Call Trace:
[<c0139214>] lock_acquire+0x71/0x91
[<c02f2bfb>] _spin_lock+0x23/0x32
[<c014fe3a>] __delayacct_blkio_ticks+0x16/0x67
[<c01a4f76>] do_task_stat+0x3df/0x6c1
[<c01a5265>] proc_tgid_stat+0xd/0xf
[<c01a29dd>] proc_info_read+0x50/0xb3
[<c0171cbb>] vfs_read+0xcb/0x177
[<c017217c>] sys_read+0x3b/0x71
[<c0103119>] sysenter_past_esp+0x56/0x8d
DWARF2 unwinder stuck at sysenter_past_esp+0x56/0x8d
Leftover inexact backtrace:
[<c0104318>] show_stack_log_lvl+0x8c/0x97
[<c010447f>] show_registers+0x15c/0x1ed
[<c01046c2>] die+0x1b2/0x2b7
[<c0116f5f>] do_page_fault+0x410/0x4f0
[<c0103d1d>] error_code+0x39/0x40
[<c0139214>] lock_acquire+0x71/0x91
[<c02f2bfb>] _spin_lock+0x23/0x32
[<c014fe3a>] __delayacct_blkio_ticks+0x16/0x67
[<c01a4f76>] do_task_stat+0x3df/0x6c1
[<c01a5265>] proc_tgid_stat+0xd/0xf
[<c01a29dd>] proc_info_read+0x50/0xb3
[<c0171cbb>] vfs_read+0xcb/0x177
[<c017217c>] sys_read+0x3b/0x71
[<c0103119>] sysenter_past_esp+0x56/0x8d
Code: 68 4b 75 2f c0 68 d5 04 00 00 68 b9 75 31 c0 68 e3 06 31 c0 e8 ce 7e fe ff
e8 87 c2 fc ff 83 c4 10 eb 08 85 db 0f 84 6b 07 00 00 <f0> ff 83 9c 00 00 00 8b
55 dc 8b 92 5c 05 00 00 89 55 e4 83 fa
EIP: [<c0138722>] __lock_acquire+0x362/0xaea SS:ESP 0068:f544ddc0

Poniższy typ błędu wynika najczęściej z sytuacji o której deweloperzy mówią, że nie powinna
mieć miejsca :).

KERNEL: assertion ((int)tp->lost_out >= 0) failed at net/ipv4/tcp_input.c (2148)
KERNEL: assertion ((int)tp->lost_out >= 0) failed at net/ipv4/tcp_input.c (2148)
KERNEL: assertion ((int)tp->sacked_out >= 0) failed at net/ipv4/tcp_input.c (2147)
KERNEL: assertion ((int)tp->sacked_out >= 0) failed at net/ipv4/tcp_input.c (2147)
BUG: warning at /usr/src/linux-mm/kernel/cpu.c:56/unlock_cpu_hotplug()
 [<c0103e41>] dump_trace+0x70/0x176
 [<c0103fc1>] show_trace_log_lvl+0x12/0x22
 [<c0103fde>] show_trace+0xd/0xf
 [<c01040b0>] dump_stack+0x17/0x19
 [<c0140e19>] unlock_cpu_hotplug+0x46/0x7c
 [<fd9560b0>] cpufreq_set+0x81/0x8b [cpufreq_userspace]
 [<fd956109>] store_speed+0x35/0x40 [cpufreq_userspace]
 [<c02ac9f2>] store+0x38/0x49
 [<c01aec16>] flush_write_buffer+0x23/0x2b
 [<c01aec69>] sysfs_write_file+0x4b/0x6c
 [<c01770af>] vfs_write+0xcb/0x173
 [<c0177203>] sys_write+0x3b/0x71
 [<c010312d>] sysenter_past_esp+0x56/0x8d
 [<b7fbe410>] 0xb7fbe410
 [<c0103fc1>] show_trace_log_lvl+0x12/0x22
 [<c0103fde>] show_trace+0xd/0xf
 [<c01040b0>] dump_stack+0x17/0x19
 [<c0140e19>] unlock_cpu_hotplug+0x46/0x7c
 [<fd9560b0>] cpufreq_set+0x81/0x8b [cpufreq_userspace]
 [<fd956109>] store_speed+0x35/0x40 [cpufreq_userspace]
 [<c02ac9f2>] store+0x38/0x49
 [<c01aec16>] flush_write_buffer+0x23/0x2b
 [<c01aec69>] sysfs_write_file+0x4b/0x6c
 [<c01770af>] vfs_write+0xcb/0x173
 [<c0177203>] sys_write+0x3b/0x71
 [<c010312d>] sysenter_past_esp+0x56/0x8d

Tutaj mamy wynik skanowania pliku /sys/kernel/debug/memleak (kernel memory leak detector jeszcze nie jest częścią standardowego jądra)

orphan pointer 0xf5a6fd60 (size 39):
c0173822: <__kmalloc>
c01df500: <context_struct_to_string>
c01df679: <security_sid_to_context>
c01d7eee: <selinux_socket_getpeersec_dgram>
f884f019: <unix_get_peersec_dgram>
f8850698: <unix_dgram_sendmsg>
c02a88c2: <sock_sendmsg>
c02a9c7a: <sys_sendto>

Dzięki tym informacjom, oraz plikowi konfiguracyjnemu naszego jądra deweloperzy mogą poprawić błąd, który udało się nam znaleźć.

Poza wymienionymi wyżej błędami możemy spotkać się z sytuacją, że jądro nie będzie się chciało zbudować z powodu błędu na etapie kompilacji. Jest to bardzo rzadko spotykany problem i świadczy o wyjątkowej niedbałości autora kodu. Dobrym przykładem może być moja poprawka do sterownika bufora ramki dla kart firmy SIS, która nie kompilowała się jako moduł…

Czasami powodem wystąpienia błędu jest błędne działanie np. naszego kompilatora, który w niewłaściwy sposób buduje jądro. Również programy, które wykorzystują niektóre części ABI (Application Binary Interface) mogą przestać działać poprawnie po jego zmianie w nowej wersji - w takich wypadkach ważne jest ustalenie czy zmiana ABI była zamierzona czy też przypadkowa.

Część problemów nie zawsze jest widoczna od razu, niektóre występują tylko w określonych sytuacjach i mogą się objawiać np. zawieszaniem losowych procesów, wyrzucaniem lub wrzucaniem losowych danych do zapisywanych plików etc. Jednym słowem trzeba mieć oczy i uszy szeroko otwarte.

Błędy możemy klasyfikować na kilka różnych sposobów, jednak najbardziej użyteczna dla nas klasyfikacja dzieli je na dwa typy:
• łatwe do odtworzenia - takie, o których dokładnie wiemy kiedy i gdzie występują
• trudne do odtworzenia - występują (pozornie) losowo i nie wiemy dokładnie jak je odtworzyć
Pierwszy typ jest najłatwiejszy do naprawienia, bardzo łatwo można znaleźć łatkę która dany błąd wprowadziła. Drugi typ jest tym typem, którego nikt nie lubi. Jeżeli nie mamy odpowiedniego doświadczenia, to będziemy musieli zdać się na doświadczenie deweloperów.

2.6 Sterowniki binarne i jądra dystrybucyjne

Bardzo często słyszymy, że sterowniki binarne są złe i nie należy ich stosować - ale dlaczego? Powód jest prosty - jeżeli znajdziemy błąd i wyślemy raport na Linux Kernel Mailing List, to deweloperzy nie będą nam w stanie pomóc, ponieważ nie mają dostępu do zamkniętego kodu sterownika. Informację o użytym sterowniku można otrzymać z linijki „EIP: 0060:[<c046c7c3>] Tainted: P VLI”, litera „P” informuje nas, że został użyty sterownik z zamkniętym kodem. Taki błąd należy spróbować odtworzyć bez załadowanego binarnego bloba. Powinniśmy zgłosić błąd do twórców sterownika i niech oni się martwią o jego poprawienie. W pliku Documentation/oops-tracing.txt możemy znaleźć listę powodów, dla których dane jądro zostało uznane za „skażone”. Podsumowując: sterowniki binarne są bardzo przydatne - dostrzeżone błędy należy wysyłać do ich producentów, nie na LKML.

Czasami ludzie wysyłają na LKML raporty błędów znalezionych w jądrach dystrybucyjnych, nie należy tego robić z kilku powodów:
• jądra dystrybucyjne zawierają modyfikacje, które nie są dostępne w standardowej wersji
• często te modyfikacje zawierają błędy (czasami są bardzo eksperymentalne)
• błąd najprawdopodobniej nie występuje w wersji oryginalnej
• to nieładnie obarczać ludzi odpowiedzialnością za błędy, których najprawdopodobniej nie popełnili :)
Powinniśmy spróbować odtworzyć taki błąd na jądrze z kernel.org, jeżeli się nie uda, to jest to błąd producenta dystrybucji i do niego powinien zostać zgłoszony.

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