目錄[-]html
爲了實現內核調試,在內核配置上增長了幾項:linux
Kernel hacking ---> [*] Magic SysRq key [*] Kernel debugging [*] Debug slab memory allocations [*] Spinlock and rw-lock debugging: basic checks [*] Spinlock debugging: sleep-inside-spinlock checking [*] Compile the kernel with debug info Device Drivers ---> Generic Driver Options ---> [*] Driver Core verbose debug messages General setup ---> [*] Configure standard kernel features (for small systems) ---> [*] Load all symbols for debugging/ksymoops
啓用選項例如: shell
slab layer debugging(slab層調試選項) high-memory debugging(高端內存調試選項) I/O mapping debugging(I/O映射調試選項) spin-lock debugging(自旋鎖調試選項) stack-overflow checking(棧溢出檢查選項) sleep-inside-spinlock checking(自旋鎖內睡眠選項)
2 調試原子操做編程
從內核2.5開發,爲了檢查各種由原子操做引起的問題,內核提供了極佳的工具。CONFIG_PREEMPT = y CONFIG_DEBUG_KERNEL = y CONFIG_KLLSYMS = y CONFIG_SPINLOCK_SLEEP = y
一些內核調用能夠用來方便標記bug,提供斷言並輸出信息。最經常使用的兩個是BUG()和BUG_ON()。ubuntu
定義在<include/asm-generic>中:windows
#ifndef HAVE_ARCH_BUG #define BUG() do { printk("BUG: failure at %s:%d/%s()! ", __FILE__, __LINE__, __FUNCTION__); panic("BUG!"); /* 引起更嚴重的錯誤,不但打印錯誤消息,並且整個系統業會掛起 */ } while (0) #endif #ifndef HAVE_ARCH_BUG_ON #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0) #endif
#ifndef __WARN_TAINT #ifndef __ASSEMBLY__ extern void warn_slowpath_fmt(const char *file, const int line, const char *fmt, ...) __attribute__((format(printf, 3, 4))); extern void warn_slowpath_fmt_taint(const char *file, const int line, unsigned taint, const char *fmt, ...) __attribute__((format(printf, 4, 5))); extern void warn_slowpath_null(const char *file, const int line); #define WANT_WARN_ON_SLOWPATH #endif #define __WARN() warn_slowpath_null(__FILE__, __LINE__) #define __WARN_printf(arg...) warn_slowpath_fmt(__FILE__, __LINE__, arg) #define __WARN_printf_taint(taint, arg...) \ warn_slowpath_fmt_taint(__FILE__, __LINE__, taint, arg) #else #define __WARN() __WARN_TAINT(TAINT_WARN) #define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0) #define __WARN_printf_taint(taint, arg...) \ do { printk(arg); __WARN_TAINT(taint); } while (0) #endif #ifndef WARN_ON #define WARN_ON(condition) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ __WARN(); \ unlikely(__ret_warn_on); \ }) #endif #ifndef WARN #define WARN(condition, format...) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ __WARN_printf(format); \ unlikely(__ret_warn_on); \ }) #endif
if (!debug_check) { printk(KERN_DEBUG 「provide some information…/n」); dump_stack(); }
#define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */ #define KERN_DEFAULT "<d>" /* Use the default kernel loglevel */
mtj@ubuntu :~$ cat /proc/sys/kernel/printk 4 4 1 7 mtj@ubuntu :~$ cat /proc/sys/kernel/printk_delay 0 mtj@ubuntu :~$ cat /proc/sys/kernel/printk_ratelimit 5 mtj@ubuntu :~$ cat /proc/sys/kernel/printk_ratelimit_burst 10
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
CONFIG_LOG_BUF_SHIFT=18
※ 這個紀錄緩衝區之因此稱爲環形,是由於它的讀寫都是按照環形隊列的方式進行操做的。api
在標準的Linux系統上,用戶空間的守護進程klogd從紀錄緩衝區中獲取內核消息,再經過syslogd守護進程把這些消息保存在系統日誌文件中。klogd進程既能夠從/proc/kmsg文件中,也能夠經過syslog()系統調用讀取這些消息。默認狀況下,它選擇讀取/proc方式實現。klogd守護進程在消息緩衝區有新的消息以前,一直處於阻塞狀態。一旦有新的內核消息,klogd被喚醒,讀出內核消息並進行處理。默認狀況下,處理例程就是把內核消息傳給syslogd守護進程。syslogd守護進程通常把接收到的消息寫入/var/log/messages文件中。不過,仍是能夠經過/etc/syslog.conf文件來進行配置,能夠選擇其餘的輸出文件。數組
-源文件名緩存
-函數名sass
-行號(包括指定範圍的行號)
-模塊名
-格式化字符串
將要打印信息的格式寫入<debugfs>/dynamic_debug/control中。nullarbor:~ # echo 'file svcsock.c line 1603 +p' > <debugfs>/dynamic_debug/control
#include <stdlib.h> #include <stdio.h> #include "memwatch.h" int main(void) { char *ptr1; char *ptr2; ptr1 = malloc(512); ptr2 = malloc(512); ptr2 = ptr1; free(ptr2); free(ptr1); }
gcc -DMEMWATCH -DMW_STDIO test1.c memwatchc -o test1
清單 2. test1 memwatch.log 文件
MEMWATCH 2.67 Copyright (C) 1992-1999 Johan Lindh ... double-free: <4> test1.c(15), 0x80517b4 was freed from test1.c(14) ... unfreed: <2> test1.c(11), 512 bytes at 0x80519e4 {FE FE FE FE FE FE FE FE FE FE FE FE ..............} Memory usage statistics (global): N)umber of allocations made: 2 L)argest memory usage : 1024 T)otal of all alloc() calls: 1024 U)nfreed bytes totals : 512
gcc -g test1.c -o test1
YAMD version 0.32 Executable: /usr/src/test/yamd-0.32/test1 ... INFO: Normal allocation of this block Address 0x40025e00, size 512 ... INFO: Normal allocation of this block Address 0x40028e00, size 512 ... INFO: Normal deallocation of this block Address 0x40025e00, size 512 ... ERROR: Multiple freeing At free of pointer already freed Address 0x40025e00, size 512 ... WARNING: Memory leak Address 0x40028e00, size 512 WARNING: Total memory leaks: 1 unfreed allocations totaling 512 bytes *** Finished at Tue ... 10:07:15 2002 Allocated a grand total of 1024 bytes 2 allocations Average of 512 bytes per allocation Max bytes allocated at one time: 1024 24 K alloced internally / 12 K mapped now / 8 K max Virtual program size is 1416 K End.
#include <stdlib.h> #include <stdio.h> int main(void) { char *ptr1; char *ptr2; char *chptr; int i = 1; ptr1 = malloc(512); ptr2 = malloc(512); chptr = (char *)malloc(512); for (i; i <= 512; i++) { chptr[i] = 'S'; } ptr2 = ptr1; free(ptr2); free(ptr1); free(chptr); }
./run-yamd /usr/src/test/test2/test2
Running /usr/src/test/test2/test2 Temp output to /tmp/yamd-out.1243 ********* ./run-yamd: line 101: 1248 Segmentation fault (core dumped) YAMD version 0.32 Starting run: /usr/src/test/test2/test2 Executable: /usr/src/test/test2/test2 Virtual program size is 1380 K ... INFO: Normal allocation of this block Address 0x40025e00, size 512 ... INFO: Normal allocation of this block Address 0x40028e00, size 512 ... INFO: Normal allocation of this block Address 0x4002be00, size 512 ERROR: Crash ... Tried to write address 0x4002c000 Seems to be part of this block: Address 0x4002be00, size 512 ... Address in question is at offset 512 (out of bounds) Will dump core after checking heap. Done.
execve("/sbin/mkfs.jfs", ["mkfs.jfs", "-f", "/dev/test1"], & ... open("/dev/test1", O_RDWR|O_LARGEFILE) = 4 stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0 ioctl(4, 0x40041271, 0xbfffe128) = -1 EINVAL (Invalid argument) write(2, "mkfs.jfs: warning - cannot setb" ..., 98mkfs.jfs: warning - cannot set blocksize on block device /dev/test1: Invalid argument ) = 98 stat64("/dev/test1", {st_mode=&, st_rdev=makedev(63, 255), ...}) = 0 open("/dev/test1", O_RDONLY|O_LARGEFILE) = 5 ioctl(5, 0x80041272, 0xbfffe124) = -1 EINVAL (Invalid argument) write(2, "mkfs.jfs: can\'t determine device"..., ..._exit(1) = ?
※ 做爲內核的開發者,一定將會常常處理OOPS。
※ OOPS中包含的重要信息,對全部體系結構的機器都是徹底相同的:寄存器上下文和回溯線索(回溯線索顯示了致使錯誤發生的函數調用鏈)。
※ 如:回溯線索中的地址,會經過ksymoops轉化成名稱可見的函數名。
ksymoops須要幾項內容:Oops 消息輸出、來自正在運行的內核的 System.map 文件,還有 /proc/ksyms、vmlinux和/proc/modules。ksymoops 2.4.0 on i686 2.4.17. Options used ... 15:59:37 sfb1 kernel: Unable to handle kernel NULL pointer dereference at virtual address 0000000 ... 15:59:37 sfb1 kernel: c01588fc ... 15:59:37 sfb1 kernel: *pde = 0000000 ... 15:59:37 sfb1 kernel: Oops: 0000 ... 15:59:37 sfb1 kernel: CPU: 0 ... 15:59:37 sfb1 kernel: EIP: 0010:[jfs_mount+60/704] ... 15:59:37 sfb1 kernel: Call Trace: [jfs_read_super+287/688] [get_sb_bdev+563/736] [do_kern_mount+189/336] [do_add_mount+35/208] [do_page_fault+0/1264] ... 15:59:37 sfb1 kernel: Call Trace: [<c0155d4f>]... ... 15:59:37 sfb1 kernel: [<c0106e04 ... ... 15:59:37 sfb1 kernel: Code: 8b 2d 00 00 00 00 55 ... >>EIP; c01588fc <jfs_mount+3c/2c0> <===== ... Trace; c0106cf3 <system_call+33/40> Code; c01588fc <jfs_mount+3c/2c0> 00000000 <_EIP>: Code; c01588fc <jfs_mount+3c/2c0> <===== 0: 8b 2d 00 00 00 00 mov 0x0,%ebp <===== Code; c0158902 <jfs_mount+42/2c0> 6: 55 push %ebp
109 printk("%d\n",*ptr); objdump jfs_mount.o jfs_mount.o: file format elf32-i386 Disassembly of section .text: 00000000 <jfs_mount>: 0:55 push %ebp ... 2c: e8 cf 03 00 00 call 400 <chkSuper> 31: 89 c3 mov %eax,%ebx 33: 58 pop %eax 34: 85 db test %ebx,%ebx 36: 0f 85 55 02 00 00 jne 291 <jfs_mount+0x291> 3c: 8b 2d 00 00 00 00 mov 0x0,%ebp << problem line above 42: 55 push %ebp
#cat /proc/kallsyms c0100240 T _stext c0100240 t run_init_process c0100240 T stext c0100269 t init …
3.1 Kdump 的基本概念
3.1.1 什麼是 kexec ?
Kexec 是實現 kdump 機制的關鍵,它包括 2 個組成部分:一是內核空間的系統調用 kexec_load,負責在生產內核(production kernel 或 first kernel)啓動時將捕獲內核(capture kernel 或 sencond kernel)加載到指定地址。二是用戶空間的工具 kexec-tools,他將捕獲內核的地址傳遞給生產內核,從而在系統崩潰的時候可以找到捕獲內核的地址並運行。沒有 kexec 就沒有 kdump。先有 kexec 實現了在一個內核中能夠啓動另外一個內核,才讓 kdump 有了用武之地。kexec 原來的目的是爲了節省 kernel 開發人員重啓系統的時間,誰能想到這個「偷懶」的技術卻孕育了最成功的內存轉存機制呢?
3.1.2 什麼是 kdump ?
Kdump 的概念出如今 2005 左右,是迄今爲止最可靠的內核轉存機制,已經被主要的 linux™ 廠商選用。kdump是一種先進的基於 kexec 的內核崩潰轉儲機制。當系統崩潰時,kdump 使用 kexec 啓動到第二個內核。第二個內核一般叫作捕獲內核,以很小內存啓動以捕獲轉儲鏡像。第一個內核保留了內存的一部分給第二內核啓動用。因爲 kdump 利用 kexec 啓動捕獲內核,繞過了 BIOS,因此第一個內核的內存得以保留。這是內核崩潰轉儲的本質。
kdump 須要兩個不一樣目的的內核,生產內核和捕獲內核。生產內核是捕獲內核服務的對像。捕獲內核會在生產內核崩潰時啓動起來,與相應的 ramdisk 一塊兒組建一個微環境,用以對生產內核下的內存進行收集和轉存。
3.1.3 如何使用 kdump
構建系統和 dump-capture 內核,此操做有 2 種方式可選:
1)構建一個單獨的自定義轉儲捕獲內核以捕獲內核轉儲;
2) 或者將系統內核自己做爲轉儲捕獲內核,這就不須要構建一個單獨的轉儲捕獲內核。
方法(2)只能用於可支持可重定位內核的體系結構上;目前 i386,x86_64,ppc64 和 ia64 體系結構支持可重定位內核。構建一個可重定位內核使得不須要構建第二個內核就能夠捕獲轉儲。可是可能有時想構建一個自定義轉儲捕獲內核以知足特定要求。
3.1.4 如何訪問捕獲內存
在內核崩潰以前全部關於核心映像的必要信息都用 ELF 格式編碼並存儲在保留的內存區域中。ELF 頭所在的物理地址被做爲命令行參數(fcorehdr=)傳遞給新啓動的轉儲內核。
在 i386 體系結構上,啓動的時候須要使用物理內存開始的 640K,而無論操做系統內核轉載在何處。所以,這個640K 的區域在從新啓動第二個內核的時候由 kexec 備份。
在第二個內核中,「前一個系統的內存」能夠經過兩種方式訪問:
1) 經過 /dev/oldmem 這個設備接口。
一個「捕捉」設備可使用「raw」(裸的)方式 「讀」這個設備文件並寫出到文件。這是關於內存的 「裸」的數據轉儲,同時這些分析 / 捕捉工具應該足夠「智能」從而能夠知道從哪裏能夠獲得正確的信息。ELF 文件頭(經過命令行參數傳遞過來的 elfcorehdr)可能會有幫助。
2) 經過 /proc/vmcore。
這個方式是將轉儲輸出爲一個 ELF 格式的文件,而且可使用一些文件拷貝命令(好比 cp,scp 等)將信息讀出來。同時,gdb 能夠在獲得的轉儲文件上作一些調試(有限的)。這種方式保證了內存中的頁面都以正確的途徑被保存 ( 注意內存開始的 640K 被從新映射了 )。
3.1.5 kdump 的優點
1) 高可靠性
崩潰轉儲數據可從一個新啓動內核的上下文中獲取,而不是從已經崩潰內核的上下文。
2) 多版本支持
LKCD(Linux Kernel Crash Dump),netdump,diskdump 已被歸入 LDPs(Linux Documen-tation Project) 內核。SUSE 和 RedHat 都對 kdump 有技術支持。
3.2 Kdump 實現流程
圖 1. RHEL6.2 執行流程
圖 2. sles11 執行流程
3.3 配置 kdump
3.3.1 安裝軟件包和實用程序
Kdump 用到的各類工具都在 kexec-tools 中。kernel-debuginfo 則是用來分析 vmcore 文件。從 rhel5 開始,kexec-tools 已被默認安裝在發行版。而 novell 也在 sles10 發行版中把 kdump 集成進來。因此若是使用的是rhel5 和 sles10 以後的發行版,那就省去了安裝 kexec-tools 的步驟。而若是須要調試 kdump 生成的 vmcore文件,則須要手動安裝 kernel-debuginfo 包。檢查安裝包操做:
3.3.2 參數相關設置 uli13lp1:/ # rpm -qa|grep kexec kexec-tools-2.0.0-53.43.10 uli13lp1:/ # rpm -qa 'kernel*debuginfo*' kernel-default-debuginfo-3.0.13-0.27.1 kernel-ppc64-debuginfo-3.0.13-0.27.1
1) 修改內核引導參數,爲啓動捕獲內核預留內存
經過下面的方法來配置 kdump 使用的內存大小。添加啓動參數"crashkernel=Y@X",這裏,Y 是爲 kdump 捕捉內核保留的內存,X 是保留部份內存的開始位置。
在 ia64, 編輯 /etc/elilo.conf,添加"crashkernel=256M"到內核行。
2) kdump 配置文件
kdump 的配置文件是 /etc/kdump.conf(RHEL6.2);/etc/sysconfig/kdump(SLES11 sp2)。每一個文件頭部都有選項說明,能夠根據使用需求設置相應的選項。
3.3.3 啓動 kdump 服務
在設置了預留內存後,須要重啓機器,不然 kdump 是不可以使用的。啓動 kdump 服務:
Rhel6.2:
# chkconfig kdump on
# service kdump status
Kdump is operational
# service kdump start
SLES11SP2:
# chkconfig boot.kdump on
# service boot.kdump start
3.3.4 測試配置是否有效
能夠經過 kexec 加載內核鏡像,讓系統準備好去捕獲一個崩潰時產生的 vmcore。能夠經過 sysrq 強制系統崩潰。
# echo c > /proc/sysrq-trigger
這形成內核崩潰,如配置有效,系統將重啓進入 kdump 內核,當系統進程進入到啓動 kdump 服務的點時,vmcore 將會拷貝到你在 kdump 配置文件中設置的位置。RHEL 的缺省目錄是 : /var/crash;SLES 的缺省目錄是 : /var/log/dump。而後系統重啓進入到正常的內核。一旦回覆到正常的內核,就能夠在上述的目錄下發現 vmcore 文件,即內存轉儲文件。可使用以前安裝的 kernel-debuginfo 中的 crash 工具來進行分析(crash 的更多詳細用法將在本系列後面的文章中有介紹)。
# crash /usr/lib/debug/lib/modules/2.6.17-1.2621.el5/vmlinux /var/crash/2006-08-23-15:34/vmcore crash> bt
3.4 載入「轉儲捕獲」內核
須要引導系統內核時,可以使用以下步驟和命令載入「轉儲捕獲」內核:
kexec -p <dump-capture-kernel> \ --initrd=<initrd-for-dump-capture-kernel> --args-linux \ --append="root=<root-dev> init 1 irqpoll"
裝載轉儲捕捉內核的注意事項:
3.5 後記
Kdump 是一個強大的、靈活的內核轉儲機制,可以在生產內核上下文中執行捕獲內核是很是有價值的。本文僅介紹在 RHEL6.2 和 SLES11 中如何配置 kdump。望拋磚引玉,對閱讀本文的讀者有益。
參考:
1 kallsyms的分析2.1 軟硬件準備
如下軟硬件配置取自筆者進行試驗的系統配置狀況:stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
echo hello > /dev/ttyS0
cat /dev/ttyS0
2.2 安裝與配置
下面咱們須要應用kgdb補丁到Linux內核,設置內核選項並編譯內核。這方面的資料相對較少,筆者這裏給出詳細的介紹。下面的工做在開發機(developement)上進行,以上面介紹的試驗環境爲例,某些具體步驟在實際的環境中可能要作適當的改動:I、內核的配置與編譯
[root@lisl tmp]# tar -jxvf linux-2.6.7.tar.bz2 [root@lisl tmp]#tar -jxvf linux-2.6.7-kgdb-2.2.tar.tar [root@lisl tmp]#cd inux-2.6.7
[root@lisl tmp]#patch -p1 <../linux-2.6.7-kgdb-2.2/core-lite.patch
[root@lisl tmp]#make menuconfig
[*] KGDB: kernel debugging with remote gdb Method for KGDB communication (KGDB: On generic serial port (8250)) ---> [*] KGDB: Thread analysis [*] KGDB: Console messages through gdb [root@lisl tmp]#make
[root@lisl tmp]#scp arch/i386/boot/bzImage root@192.168.6.13:/boot/vmlinuz-2.6.7-kgdb [root@lisl tmp]#scp System.map root@192.168.6.13:/boot/System.map-2.6.7-kgdb
[root@lisl tmp]#mkinitrd /boot/initrd-2.6.7-kgdb 2.6.7 [root@lisl tmp]#scp initrd-2.6.7-kgdb root@192.168.6.13:/boot/ initrd-2.6.7-kgdb
title 2.6.7 kgdb root (hd0,0) kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait kgdb8250=1,115200
image=/boot/vmlinuz-2.6.7-kgdb label=kgdb read-only root=/dev/hda3 append="gdb gdbttyS=1 gdbbaud=115200"
gdb file vmlinux set remotebaud 115200 target remote /dev/ttyS0
[root@lisl boot]#gdbstart -s 115200 -t /dev/ttyS0
[*]KGDB: kernel debugging with remote gdb Method for KGDB communication (KGDB: On ethernet) ---> ( ) KGDB: On generic serial port (8250) (X) KGDB: On ethernet
title 2.6.7 kgdb root (hd0,0) kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait kgdboe=@192.168.5.13/,@192.168. 6.13/
[root@lisl tmp]# insmod -m hello.ko >modaddr
.this 00000060 c88d8000 2**2 .text 00000035 c88d8060 2**2 .rodata 00000069 c88d80a0 2**5 …… .data 00000000 c88d833c 2**2 .bss 00000000 c88d833c 2**2 ……
(gdb) Add-symbol-file hello.o 0xc88d8060 -s .data 0xc88d80a0 -s .rodata 0xc88d80a0 -s .bss 0x c88d833c
…… …… if (mod->init != NULL) ret = mod->init(); …… ……
II、在Linux 2.6.x內核中的內核模塊調試方法
Linux 2.6以後的內核中,因爲module-init-tools工具的更改,insmod命令再也不支持-m參數,只有採起其餘的方法來獲取模塊加載到內核的地址。經過分析ELF文件格式,咱們知道程序中各段的意義以下:…… int bss_var; static int hello_init(void) { printk(KERN_ALERT "Text location .text(Code Segment):%p\n",hello_init); static int data_var=0; printk(KERN_ALERT "Data Location .data(Data Segment):%p\n",&data_var); printk(KERN_ALERT "BSS Location: .bss(BSS Segment):%p\n",&bss_var); …… } Module_init(hello_init);
3.2 VMware的使用技巧
VMware虛擬機是比較佔用資源的,尤爲是象上面那樣在Windows中使用兩臺虛擬機。所以,最好爲系統配備512M以上的內存,每臺虛擬機至少分配128M的內存。這樣的硬件要求,對目前主流配置的PC而言並非太高的要求。出於系統性能的考慮,在VMware中儘可能使用字符界面進行調試工做。同時,Linux系統默認狀況下開啓了sshd服務,建議使用SecureCRT登錄到Linux進行操做,這樣能夠有較好的用戶使用界面。
[root@lisl tmp]#chmod +x arm-uclinux-tools-base-gcc3.4.0-20040713.sh [root@lisl tmp]#./arm-uclinux-tools-base-gcc3.4.0-20040713.sh
[root@lisl tmp]# tar -jxvf uClinux-dist-20041215.tar.bz2 [root@lisl uClinux-dist]# tar -jxvf linux-2.6.9.tar.bz2 [root@lisl uClinux-dist]# gzip -dc linux-2.6.9-hsc0.patch.gz | patch -p0
[root@lisl uClinux-dist]# gunzip linux-2.6.9-hsc0.patch.gz [root@lisl uClinux-dist]patch -p0 < linux-2.6.9-hsc0.patch
[root@lisl uClinux-dist]# rm -rf linux-2.6.x/ [root@lisl uClinux-dist]# mv linux-2.6.9 linux-2.6.x
[root@lisl uClinux-dist]# cd linux-2.6.x [root@lisl linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux- atmel_deconfig
[root@lisl linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux- oldconfig
[root@lisl linux-2.6.x]# make ARCH=armnommu CROSS_COMPILE=arm-uclinux- v=1
CFLAGS += -g
Export PATH=$PATH:/root/bin/arm-linux-tool/bin
#bzip2 -d kdb-v4.2-2.4.20-common-1.bz2 #bzip2 -d kdb-v4.2-2.4.20-i386-1.bz2
#patch -p1 <kdb-v4.2-2.4.20-common-1 #patch -p1 <kdb-v4.2-2.4.20-i386-1
#echo "1" >/proc/sys/kernel/kdb
#echo "0" >/proc/sys/kernel/kdb
[0]kdb> md 0xc000000 15
[0]kdb> mm 0xc000000 0x10
[0]kdb> rd [0]kdb> rm %ebx 0x25
[0]kdb> bp sys_write
[0]kdb> bl
[0]kdb> bc 1
[0]kdb> bt
[0]kdb> btp 575
[0]kdb> id schedule
[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
[0]kdb> 0xc013db4c 0xc013db4c = 0xc013db4c (sys_read)
[0]kdb> sys_write sys_write = 0xc013dcc8 (sys_write)
[0]kdb> md %ebp 0xc74c9f38 c74c9f60 c0136c40 000001f0 00000000 0xc74c9f48 08053328 c0425238 c04253a8 00000000 0xc74c9f58 000001f0 00000246 c74c9f6c c0136a25 0xc74c9f68 c74c8000 c74c9f74 c0136d6d c74c9fbc 0xc74c9f78 c014fe45 c74c8000 00000000 08053328 [0]kdb> 0xc0136c40 0xc0136c40 = 0xc0136c40 (__alloc_pages +0x44) [0]kdb> 0xc0136a25 0xc0136a25 = 0xc0136a25 (_alloc_pages +0x19) [0]kdb> 0xc0136d6d 0xc0136d6d = 0xc0136d6d (__get_free_pages +0xd)
[0]kdb> defcmd name "usage" "help" [0]kdb> [defcmd] type the commands here [0]kdb> [defcmd] endefcmd
[0]kdb> defcmd hari "" "no arguments needed" [0]kdb> [defcmd] md 0xc000000 1 [0]kdb> [defcmd] rd [0]kdb> [defcmd] md %ebp 1 [0]kdb> [defcmd] endefcmd
[0]kdb> hari [hari]kdb> md 0xc000000 1 0xc000000 00000001 f000e816 f000e2c3 f000e816 [hari]kdb> rd eax = 0x00000000 ebx = 0xc0105330 ecx = 0xc0466000 edx = 0xc0466000 .... ... [hari]kdb> md %ebp 1 0xc0467fbc c0467fd0 c01053d2 00000002 000a0200 [0]kdb>
[0]kdb> bph 0xc0204060 dataw 4
[0]kdb> bph 0xc000000 datar 2
$tar -xvzf kprobes-2.6.8-rc1.tar.gz $cd /usr/src/linux-2.6.8-rc1 $patch -p1 < ../kprobes-2.6.8-rc1-base.patch
$patch -p1 < ../kprobes-2.6.8-rc1-sysrq.patch
/* pre_handler: this is called just before the probed instruction is * executed. */ int handler_pre(struct kprobe *p, struct pt_regs *regs) { printk("pre_handler: p->addr=0x%p, eflags=0x%lx\n",p->addr, regs->eflags); return 0; } /* post_handler: this is called after the probed instruction is executed * (provided no exception is generated). */ void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags) { printk("post_handler: p->addr=0x%p, eflags=0x%lx \n", p->addr, regs->eflags); } /* fault_handler: this is called if an exception is generated for any * instruction within the fault-handler, or when Kprobes * single-steps the probed instruction. */ int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) { printk("fault_handler:p->addr=0x%p, eflags=0x%lx\n", p->addr, regs->eflags); return 0; }
/* specify pre_handler address */ kp.pre_handler=handler_pre; /* specify post_handler address */ kp.post_handler=handler_post; /* specify fault_handler address */ kp.fault_handler=handler_fault; /* specify the address/offset where you want to insert probe. * You can get the address using one of the methods described above. */ kp.addr = (kprobe_opcode_t *) kallsyms_lookup_name("do_fork"); /* check if the kallsyms_lookup_name() returned the correct value. */ if (kp.add == NULL) { printk("kallsyms_lookup_name could not find address for the specified symbol name\n"); return 1; } /* or specify address directly. * $grep "do_fork" /usr/src/linux/System.map * or * $cat /proc/kallsyms |grep do_fork * or * $nm vmlinuz |grep do_fork */ kp.addr = (kprobe_opcode_t *) 0xc01441d0; /* All set to register with Kprobes */ register_kprobe(&kp);
$tail -5 /var/log/messages Jun 14 18:21:18 llm05 kernel: pre_handler: p->addr=0xc01441d0, eflags=0x202 Jun 14 18:21:18 llm05 kernel: post_handler: p->addr=0xc01441d0, eflags=0x196
$objdump -D /usr/src/linux/kernel/fork.o > fork.dis
000022b0 <do_fork>: 22b0: 55 push %ebp 22b1: 89 e5 mov %esp,%ebp 22b3: 57 push %edi 22b4: 89 c7 mov %eax,%edi 22b6: 56 push %esi 22b7: 89 d6 mov %edx,%esi 22b9: 53 push %ebx 22ba: 83 ec 38 sub $0x38,%esp 22bd: c7 45 d0 00 00 00 00 movl $0x0,0xffffffd0(%ebp) 22c4: 89 cb mov %ecx,%ebx 22c6: 89 44 24 04 mov %eax,0x4(%esp) 22ca: c7 04 24 0a 00 00 00 movl $0xa,(%esp) 22d1: e8 fc ff ff ff call 22d2 <do_fork+0x22> 22d6: b8 00 e0 ff ff mov $0xffffe000,%eax 22db: 21 e0 and %esp,%eax 22dd: 8b 00 mov (%eax),%eax
void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags) { struct task_struct *task; read_lock(&tasklist_lock); for_each_process(task) { printk("pid =%x task-info_ptr=%lx\n", task->pid, task->thread_info); printk("thread-info element status=%lx,flags=%lx,cpu=%lx\n", task->thread_info->status, task->thread_info->flags, task->thread_info->cpu); } read_unlock(&tasklist_lock); }
$tail -10 /var/log/messages Jun 22 18:14:25 llm05 kernel: thread-info element status=0,flags=0, cpu=1 Jun 22 18:14:25 llm05 kernel: pid =5e4 task-info_ptr=f5948000 Jun 22 18:14:25 llm05 kernel: thread-info element status=0,flags=8, cpu=0 Jun 22 18:14:25 llm05 kernel: pid =5e5 task-info_ptr=f5eca000
$echo 1 > /proc/sys/kernel/sysrq
Jun 23 10:24:48 linux-udp4749545uds kernel: SysRq : Show kprobes Jun 23 10:24:48 linux-udp4749545uds kernel: Jun 23 10:24:48 linux-udp4749545uds kernel: [<c011ea60>] do_fork+0x0/0x1de