< | 1. Wstęp | 2. printk | 3. klogd i syslogd | 4. strace i ltrace | 5. gdb i UML |
KDB
to odpluskwiacz, który umożliwia badanie pamięci i struktur danych
podczas pracy systemu. Nie wymaga do działania dodatkowego komputera. Nie jest możliwa przy jego
pomocy praca na poziomie kodu źródłowego (nie można np. ustawić pułapki w konkretnym pliku,
na określonej linii kodu jądra).
Umożliwia m.in następujące operacje:
kdb
) w katalogu /usr/src
i skopiować
do niego źródła linuksa (np. 2.6.17.13)
ln -s kdb linux
(po usunięciu poprzedniego
poleceniem rm linux
)
kdb-v4.4-2.6.17-rc5-common-2.bz2
ikdb-v4.4-2.6.17-rc5-i386-1.bz2
,bzcat kdb-v4.4-2.6.17-rc5-common-2.bz2 | patch -p1 bzcat kdb-v4.4-2.6.17-rc5-i386-1.bz2 | patch -p1Po ich nałożeniu w katalogu
Documentation/kdb
można znaleźć dokumentację kdb
.
make mrproper
make xconfig
.
CONFIG_KDB
. Warto także wybrać opcję
CONFIG_FRAME_POINTER
.
CONFIG_KDB_OFF
- kdb
- domyślnie wyłączoneCONFIG_KDB_MODULES
- umożliwia rozszerzanie funkcjonalności kdb
poprzez dodanie własnych modułów
(opisane w http://www.goldenbits.com/newsletter/issue3/gbtechnews_issue3.pdf)
make bzImage
make modules
bzImage
(jako vmlinuz
), System.map
do katalogu /boot
make modules_install
lilo.conf
/sbin/lilo
Jeśli podczas kompilacji jądra wybraliśmy opcję CONFIG_KDB_OFF
to w celu aktywacji
kdb
należy przy bootowaniu podać parametr kdb=on
.
Można to również zrobić poprzez wpisanie 1
do odpowiedniego pliku:
$ echo "1" >/proc/sys/kernel/kdb
Wpisanie do niego 0
spowoduje deaktywację kdb
.
Po aktywowaniu kdb
mozna wywołać naciskając klawisz Pause/Break
.
Pojawia się wtedy komunikat postaci:
Entering kdb (current=0xc03ff280, pid 0) due to Keyboard Entry. kdb>
i wsztrzymywana jest wszelka aktywność systemu operacyjnego.
Odpluskwiacz jest również wywoływany, gdy pojawia sie oops
(wyjątek wyzwalany w przypadku anomalii w zachowaniu systemu,
mający na celu ochronę danych, sprzętu przed uszkodzeniem,
zapamiętujący dane przydatne przy diagnozowaniu problemu), kernel panic
,
a także po napotkaniu pułapki (breakpoint).
Można go również wywołać z poziomu kodu źródłowego, korzystając z makra KDB_ENTER()
z pliku
/include/asm/kdb.h
np.
if (warunek()) KDB_ENTER()
Działanie linuksa wznawia się wpisując go
i naciskając enter
.
kdb> go
Można wymusić wznowienie od konkretnego miejsca, wstawiając
adres za go
. Pozwala to np. ominąć pewne instrukcje.
Polecenia:
md adres [liczba_linii] [podstawa]
- wyświetla zawartość pamięci od podanego adresu.
Adresem może być numer, symbol np. sys_write
, nazwa rejestru...mdr adres [liczba_bajtow]
- wyświetla określoną liczbę bajtów począwszy od podanego adresumdp adres [liczba_bajtow]
- wyświetla określoną liczbę bajtów fizycznej pamięci począwszy od podanego adresumds
- wyświetla zawartość pamięci po jednym słowie w każdej linii,
próbując przyporządkować każdemu słowu odpowiedni symbol z tablicy symboli.
przykład 1:
mds %esp
- wypisuje zawartość stosu
0xc0487fd8 c0487fe4 init_thread_union+0x1fe4 0xc0487fdc c0101139 cpu_idle+0x59 0xc0487fe0 00000000 .... 0xc0487fe4 c0487fec init_thread_union+0x1fec 0xc0487fe8 c0100261 _stext+0x21 0xc0487fec c0487ff8 init_thread_union+0x1ff8 0xc0487ff0 c04888af start_kernel+0x18f 0xc0487ff4 c04bb9e0 command_line
mm adres nowa_wartosc
- zmienia zawartość pamięci pod podanym adresemrd
- wyświetla zawartość rejestrów procesorard c
- wyświetla zawartość rejestrów kontrolnychrd u
- wyświetla zawartość rejestrów aktualnego procesu w momencie,
gdy ostatni raz odwoływał się do kernelarm rejestr nowa_zawartosc
- zmienia zawartość rejestru procesoraid adres
- dessasembluje instrukcje pod podanym adresem
przykład 2:
kdb> id sys_write
wypisze (fragment):
0xc01521b0 sys_write: push %ebp 0xc01521b1 sys_write+0x1: mov %esp,%ebp 0xc01521b3 sys_write+0x3: push %esi 0xc01521b4 sys_write+0x4: push %ebx 0xc01521b5 sys_write+0x5: sub $0xc,%esp ...
bp
- ustawia pułapkę na adresie lub instrukcjibpa
- to co wyżej, ale na wszystkich procesorach w architekturze SMP
przykład 3:
Ustawiamy pułapkę na funkcji do_fork
:
kdb> bp do_fork Instruction(i) BP #0 at 0x0117c30 (do_fork) is enabled globally adjust 1
Tworzymy prosty skrypt w bashu:
#!/bin/bash exec ps
Po jego uruchomieniu wywołane zostanie kdb
:
Instruction breakpoint #0 at 0xc0117c30 (adjusted) 0xc0117c30 do_fork: int3 Entering kdb (current=0xc6220a50, pid 2053) due to Breakpoint @ 0xc0117c30
Wpisując bt
, możemy teraz prześledzić jak doszło do wywołania do_fork
:
kdb> bt Stack traceback for pid 2053 0xc6220a50 2053 1 1 0 R 0xc6220bf0 *bash EBP EIP Function (args) 0xc59affb4 0xc0117c30 do_fork (0x1200011, 0xbfd7c640, 0xc59affbc, 0x0, 0x0) 0xc0101b76 sys_clone+0x36 0xc0102e87 syscall_call+0x7
bl
- wypisuje listę ustawionych pułapekbc 0
- usuwa pierwszą pułapkę z listybd *
- wyłącza (nie usuwa) wszystkie pułapkibe *
- włącza wszystkie pułapkibt
- wypisuje ścieżkę wywołań (stack trace)btp pid
- wypisuje scieżkę wywołań procesu o podanym pidziebta
- wypisuje scieżki wywołań wszystkich procesówbph adres [DATAR|DATAW|DATAA|IO] [liczba_bajtów]
- pułapka sprzętowa,
umożliwia dodatkowo wywołanie debuggera podczas następujących sytuacji:
DATAW
- zapis pod adres podanej liczby bajtówDATAR
- odczyt spod adresu określonej liczby bajtówDATAA
- dostęp (zapis lub odczyt) do podanego adresuIO
- instrukcja wejścia/wyjścia dotyczy podanego adresu I/O (nie działa na x86)przykład 4:
bph 0xc0000000 dataw 7
- wywołuje debugger za każdym razem, gdy pod adres 0xc0000000
zostanie zapisane 7 bajtów
ss
- wywołuje jedną instrukcję i wraca do kdb
ssb
- wywołuje instrukcje aż do zmiany przebiegu sterowania (call
, interrupt
)
przykład 5:
Ustawiamy pułapkę na funkcji do_fork
:
kdb> bp do_fork
Wznawiamy działanie systemu:
kdb> go
Uruchamiamy polecenie ls
:
root@darkstar:~# ls
Wywołane zostanie kdb
:
Instruction breakpoint #0 at 0xc0117c30 (adjusted) 0xc0117c30 do_fork: int3 Entering kdb (current=0xc6220a50, pid 2053) due to Breakpoint @ 0xc0117c30 kdb>
Wykonujemy jedną instrukcję:
kdb> ss SS trap at 0xc0117c31 (do_fork+0x1) 0xc0117c31 do_fork+0x1: mov %esp,%ebp kdb>
A następnie kilka, aż do zmiany przebiegu sterowania:
kdb> ssb 0xc0117c33 do_fork+0x3: push %edi 0xc0117c34 do_fork+0x4: push %esi 0xc0117c35 do_fork+0x5: push %ebx 0xc0117c36 do_fork+0x6: sub $0x28,%esp 0xc0117c39 do_fork+0x9: mov 0x8(%ebp),%esi 0xc0117c3c do_fork+0xc: movl $0x0,0xffffffd0(%ebp) 0xc0117c43 do_fork+0x13: call 0xc0127ea0 alloc_pid kdb>
Warto wspomnieć, że kdb
umożliwia również definiowanie własnych
komend przy użyciu polecenia defcmd
:
defcmd nazwa uzycie pomoc
np.
kbd> defcmd bcd "" "wyswietla zawartosc rejestrow ebx, ecx i edx" kbd> [defcmd] md %ebx kbd> [defcmd] md %ecx kbd> [defcmd] md %edx kbd> [defcmd] endefcmd
Polecenie bcd
pojawi się na liście komend wyświetlanej po wpisaniu help
.
KDGB
- odpluskwiacz jądra wykorzystywany wraz z gdb
,
który działa na poziomie źródeł. Pozwala na wstawianie pułapek w kodzie kernela,
wykonywanie kodu instrukcja po instrukcji, obserwację zmiennych.
Do działania wymaga dwóch komputerów połączonych kablem szeregowym.
(obecne wersje KGDB
umożliwiają również pracę poprzez siec Ethernet
).
Na pierwszym komputerze, testowym znajduje sie jądro, które chcemy badać,
na drugim działa gdb
.
Instalacja (maszyna deweloperska)
Kernel hacking
:KGDB: kernel debuging with remote gdb
KGDB: On generic serial port (8250)
lubKGDB: On Ethernet
KGDB: On generic serial port (8250)
Simple selection of KGDB serial port
(wybieramy)
Console messages through GDB
: komunikaty z konsoli maszyny
testowej bedą się pojawiały na maszynie deweloperskiej.
Thread analysis
: po włączeniu tej opcji, możliwe staje
sie wylistowywanie wątkow i pobieranie dla wątku ścieżki wywołań.
Instalacja (maszyna testowa)
bzImage
(jako vmlinuz
) i System.map
na maszynę testową do katalogu /boot
.
grub'a
lub lilo
na maszynie testowej.
przykład 6 (wpis dla lilo):
image = /boot/vmlinuz label = Linux-KGDB append = "kgdbwait kgdb8250=0,115200"
Uruchamianie:
Waiting for connection from remote gdb...
gdb
:
$ gdb ./vmlinux
Ustalić prędkość transmisji:
(gdb) set remotebaud 115200
Spróbować nawiązać połączenie z maszyną testową:
(gdb) target remote /dev/ttyS0
Poczekać na odpowiedź:
Remote debugging using /dev/ttyS0 breakpoint () at kernel/kgdb.c:1212 1212 atomic_set(&kgdb_setting_breakpoint, 0); warning: shared library handler failed to enable breakpoint (gdb)
Wywołać continue:
(gdb) c
Często nie dysponujemy dwoma komputerami. W takiej sytuacji wygodnie jest skorzystać z maszyny wirtualnej.
Uruchamianie przy użyciu VMware Workstation
:
bzImage
(jako vmlinuz
)
i System.map
do katalogu /boot
.
Dodać 2 wpisy do grub'a
lub lilo
.
Jeden dla maszyny testowej, drugi dla deweloperskiej.
przykład 7 (lilo):
Pierwszy wpis taki jak w omówionym wyżej przypadku instalacji dla maszyny testowej, drugi podobny, ale bez parametruappend = "kgdbwait kgdb8250=0,115200"
.
\\.\\pipe\com_1
.
Named Pipe TCP Proxy
ze strony
shvechkov.tripod.com/nptp.html a następnie zainstalować.
Named Pipe TCP Proxy
, stworzyć połączenie między jakimś portem na komputerze
np. 8250 a \\.\pipe\com_1
.enable non-local systems access
.
Waiting for connection from remote gdb...
$ gdb ./vmlinux
target remote adres_ip_komputera_na_ktorym_dziala_vmware:8250
continue
.
Jako, że debugując przy pomocy kgdb
korzystamy z gdb
, odpluskwianie
jądra przypomina debugowanie zwykłego programu przy pomocy gdb
(o czym była mowa wcześniej).
Nie będę w związku z tym rozpisywał się zbytnio o dostępnych poleceniach.
Niemniej jednak:
Ctrl + C wywołane w gdb
wstrzymuje wykonanie kernela
na maszynie testowej. Przechodzi on pod kontrolę kgdb
i można go debugować
przy pomocy gdb
(innym powodem wstrzymania działania może być np. napotkanie pułapki).
Wpisanie continue
lub c
w gdb
powoduje wznowienie
działania kernela na maszynie testowej.
Kilka poleceń:
break nazwa_funkcji
- ustawia pułapkę na funkcjibreak plik : linia
- ustawia pułapkę na konkretnej linii w podanym pliku np.
(gdb) break fork.c : 1167
disable n
- wyłącza pułapkę o numerze ninfo breakpoints
- lista ustawionych pułapekbacktrace
- wypisuje ścieżkę wywołań (stack trace)set var v=expr
- ustawia wartość zmiennej v
na wartość wyrażenia expr
list plik : linia
- pozwala wypisać kod wokół wskazanej linii danego pliku
np. list fork.c : 1167
Przy pomocy kgdb
możliwe jest również debugowanie modułów, ale jest
ono nieco bardziej skomplikowane, jako że pliki obiektowe modułów nie zawierają adresów absolutnych.
Adresy są zmieniane na absolutne przez insmod
przed załadowaniem modułu.
Aby debugować moduły należy zainstalować specjalną wersję gdb
na maszynie deweloperskiej
dostępną pod adresem http://kgdb.linsyssoft.com/downloads/gdbmod-2.4.bz2.
Trzeba też odpowiednio zbudować moduł (w przypadku gcc
opcja -g
) i podać
gdb
ścieżkę do katalogu z plikami modułu:
(gdb) set solib-search-path /home/mm/moduly
Ma to na celu udostępnienie gdb
symboli modułu.
http://oss.sgi.com/projects/kdb/
http://linuxdevices.com/articles/AT3761062961.html
http://www.luv.asn.au/overheads/embedded/kdb.pdf
http://www.goldenbits.com/newsletter/issue3/gbtechnews_issue3.pdf
http://www-128.ibm.com/developerworks/linux/library/l-debug/
prezentacje dotyczące odpluskwiania z ubiegłego roku
Documentation/kdb
http://kgdb.linsyssoft.com/
http://oslab.info/index.php/Misc/KGDB
http://shvechkov.tripod.com/nptp.html
< | 1. Wstęp | 2. printk | 3. klogd i syslogd | 4. strace i ltrace | 5. gdb i UML |