continue
【 模块调试 】
『 内联函数 』
对于模块的步骤是有些不同的,因为模块目标文件中是不包含绝对地址的。
( 步骤的相关说明和实例将被添加 http://kgdb.linsyssoft.com/inlinemodules.htm )
『 卸载和装载模块 』
同第三节的相关内容
『 loadmodule.sh的工作 』
相关内容只针对kgdb1.8或早些的。
---目标文件段---
假设是 trfs 模块目标文件。文件中包含的段可以使用 objdump 来列出。
$ objdump -h trfs
trfs: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000162c 00000000 00000000 00000040 2**4
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .text.lock 0000010a 00000000 00000000 0000166c 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .rodata 000002a4 00000000 00000000 00001780 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 __ksymtab 00000020 00000000 00000000 00001a24 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
4 .kstrtab 0000006f 00000000 00000000 00001a60 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .modinfo 00000111 00000000 00000000 00001acf 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .data 00000178 00000000 00000000 00001be0 2**5
CONTENTS, ALLOC, LOAD, RELOC, DATA
7 .bss 00000010 00000000 00000000 00001d58 2**2
ALLOC
8 .stab 0003b8b0 00000000 00000000 00001d58 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
9 .stabstr 000e32bd 00000000 00000000 0003d608 2**0
CONTENTS, READONLY, DEBUGGING
10 .comment 000001ab 00000000 00000000 001208c5 2**0
CONTENTS, READONLY
11 .note 0000008c 00000010 00000010 00120a70 2**0
CONTENTS, READONLY
.text 是代码段, .data 包含初始化数据, .bss包含 未初始化数据。调试信息包含在 .stab 和 .stabstr段中。
VMA 是 段的虚拟地址。因为文件仍未重定位,所以 所有段的VMAs都为 0 。
内核目标文件--vmlinux ,被重定位。所以vmlinux中段的VMAs出现在进程虚拟地址空间的地方,拥有实际地址。
$ objdump -h vmlinux
vmlinux: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0014ed2b c0100000 c0100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .text.lock 00009579 c024ed30 c024ed30 0014fd30 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .rodata 00048061 c02582c0 c02582c0 001592c0 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .kstrtab 0000a200 c02a0340 c02a0340 001a1340 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 __ex_table 00001848 c02aa540 c02aa540 001ab540 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 __ksymtab 000023f0 c02abd88 c02abd88 001acd88 2**2
CONTENTS, ALLOC, LOAD
, READONLY, DATA
6 .data 0001a200 c02ae180 c02ae180 001af180 2**5
CONTENTS, ALLOC, LOAD, DATA
7 .data.init_task 00002000 c02ca000 c02ca000 001ca000 2**5
CONTENTS, ALLOC, LOAD, DATA
8 .text.init 00018718 c02cc000 c02cc000 001cc000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
9 .data.init 0001ed18 c02e4720 c02e4720 001e4720 2**5
CONTENTS, ALLOC, LOAD, DATA
10 .setup.init 00000170 c0303440 c0303440 00203440 2**2
CONTENTS, ALLOC, LOAD, DATA
11 .initcall.init 000000bc c03035b0 c03035b0 002035b0 2**2
CONTENTS, ALLOC, LOAD, DATA
12 .data.page_aligned 00000800 c0304000 c0304000 00204000 2**5
CONTENTS, ALLOC, LOAD, DATA
13 .data.cacheline_aligned 00004520 c0304800 c0304800 00204800 2**5
CONTENTS, ALLOC, LOAD, DATA
14 .bss 00041f54 c0308d20 c0308d20 00208d20 2**5
ALLOC
15 .stab 00615708 00000000 00000000 00208d20 2**2
CONTENTS, READONLY, DEBUGGING
16 .stabstr 001b4ec1 00000000 00000000 0081e428 2**0
CONTENTS, READONLY, DEBUGGING
17 .comment 00005bfa 00000000 00000000 009d32e9 2**0
CONTENTS, READONLY
18 .note 00001e28 c034ac74 c034ac74 009d8ee3 2**0
CONTENTS, READONLY
如以上输出所见,代码段.text ,开始于 地址 0xc0100000。
『 装载模块 』
当模块装载进内核,其首先被重定位。模块的重定位意思是将所有相对地址转化为绝对地址。
模块重定位后,insmod 命令打印模块map文件,如果命令行需要的话。
模块map文件包含 模块中的段和所有符号的地址。
模块trfs的部分模块map文件如下所示:
$ head -n 20 /tmp/trfs.map
Sections: Size Address Align
.this 00000060 c1808000 2**2
.text 0000162c c1808060 2**4
.text.lock 0000010a c180968c 2**0
.rodata 000002a4 c18097a0 2**5
__ksymtab 00000048 c1809a44 2**2
.kstrtab 00000109 c1809aa0 2**5
.data 00000178 c1809bc0 2**5
.bss 00000010 c1809d38 2**2
__archdata 00000000 c1809d50 2**4
Symbols:
00000000 a translators.c
00000000 a linkinfo.c
00000000 a view.c
00000000 a super.c
00000000 a dirinfo.c
00000000 a link.c
00000000 a dir.c
c1808000 d __this_module
代码段.text在地址0xc1808060处装入。
『 gdb脚本的产生 』
loadmodule.sh产生一个gdb脚本,用于装载目标文件到gdb中。
脚本告诉gdb装载模块目标文件,给出不同段装载之处的地址。
以上模块map的gdb脚本如下:
$ cat /mnt/work/gdbscripts/loadtrfs
add-symbol-file /mnt/work/build/old-pc/trfs/modules/trfs/trfs 0xc1808060 -s .text.lock 0xc180968c -s .rodata 0xc18097a0 -s __ksymtab
0xc1809a44 -s .data 0xc1809bc0
loadmodule.sh 从模块map文件中获得段的地址。
--将目标文件载入gdb --
含有地址的模块目标文件载入后,gdb可以计算一个符号的地址,通过提供相对地址给包含此符号的段的绝对地址来实现。
例如 trfs模块中有一个函数 tr_initviewtree 。 包含他以及函数相对地址的段,可以通过 objdump 来找到。
$ objdump -t trfs | grep tr_initviewtree
000014f0 g F .text 00000016 tr_initviewtree
.text 是包含此函数的段,字符 F 表示其是一个函数,0x14f0 是此函数在.text段中的相对地址。0x16是此函数的代码长度。
此函数载入的地址是0xc1808060 + 0x14f0 = 0xc1809550。
此地址在模块目标文件中也给出了。
$ grep tr_initviewtree /tmp/trfs.map
c1809550 T tr_initviewtree
$ gdbmod
GNU gdb 20000204
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu".
(gdb) source loadtrfs
add symbol table from file "/mnt/work/build/old-pc/trfs/modules/trfs/trfs" at
.text_addr = 0xc1808060
.text.lock_addr = 0xc180968c
.rodata_addr = 0xc18097a0
__ksymtab_addr = 0xc1809a44
.data_addr = 0xc1809bc0
(gdb) p tr_initviewtree
$1 = {void ()} 0xc1809550
『 init_module()之调试 』
从一个模块中调试init_module()函数需要特殊的步骤。
当gdb被模块通报( 针对kgdb 1.9之后的 ),或者当loadmodule.sh结束 (针对kgdb 1.8之前的),模块调试信息才是可用的。
init_module()函数那时已经被执行过,因此init_module()不能通过原来的方式来调试。
方法是:在调用init_module()处之前的位置 设置断点于内核中。
对于kgdb 1.9/之后的版本:
modprobe 或者 insmod 载入模块,等到以上断点的产生。这时你可以在模块中任何地方设置断点了。
针对kgdb 1.8/之前的版本:
一旦内核侵入调试器,等到loadmodule.sh退出。 此时 init_module()未被调用,所有调试信息是可用的。现在载入模块目标文件。一旦载入,断点可设置于模
块中任何地方,如同init_module()的一条语句一般。
余下的显示实例参见 http://kgdb.linsyssoft.com/initmodule.htm
************************************************************************************************************************************************
【 体系依赖 】
『 x86_64 』
Kgdb patch已经在双opteron CPU上测试过。包括线程支持、控制台信息的所有功能测试可用。
x86_64 gdb对运行kgdb的CPU上d的o_IRQ函数不能正确显示堆栈跟踪。为了得到这个堆栈跟踪,一个隐蔽线程被执行。
Press Ctrl+C in gdb
Program received signal SIGTRAP, Trace/breakpoint trap.
breakpoint () at kernel/kgdbstub.c:1056
1056 atomic_set(&kgdb_setting_breakpoint, 0);
(gdb) bt
#0 breakpoint () at kernel/kgdbstub.c:1056
#1 0xffffffff801e3f17 in kgdb8250_interrupt (irq=3, dev_id=0x0, regs=0x1)
at drivers/serial/kgdb_8250.c:143
#2 0xffffffff80113341 in handle_IRQ_event (irq=3, regs=0x10001a1dc48,
action=0x1001ff29840) at arch/x86_64/kernel/irq.c:219
#3 0xffffffff801134e1 in do_IRQ (regs=0x10001a1dc48)
at arch/x86_64/kernel/irq.c:387
#4 0xffffffff80110eab in common_interrupt () at elfcore.h:92
Previous frame inner to this frame (corrupt stack?)
GDB不能正确跟踪这一点,检查一下线程列表中是否包含一个隐蔽线程。其可以通过在中断进入点处的tag stack标识。
(gdb) info thr
10 Thread 32769 (Stack at interrupt entrypoint) __delay (loops=1384000)
at arch/x86_64/lib/delay.c:30
9 Thread 32768 (Shadow task 0 for pid 0) 0xffffffff8010eb3e in cpu_idle ()
at sched.h:914
8 Thread 8 (aio/0) 0xffffffff8013f41c in worker_thread (
__startup=0x1001ff28d60) at sched.h:914
7 Thread 7 (kswapd0) kswapd (p=0xffffffff803170e8) at mm/vmscan.c:1046
6 Thread 6 (pdflush) __pdflush (my_work=0x1001fd71f18) at current.h:11
5 Thread 5 (pdflush) __pdflush (my_work=0x1001fd73f18) at current.h:11
4 Thread 4 (kblockd/0) 0xffffffff8013f41c in worker_thread (
__startup=0x1001ff28ae0) at sched.h:914
3 Thread 3 (events/0) 0xffffffff8013f41c in worker_thread (
__startup=0x1001ff3cd60) at sched.h:914
2 Thread 2 (ksoftirqd/0) ksoftirqd (__bind_cpu=0xffffffff8030be20)
at current.h:11
* 1 Thread 1 (swapper) breakpoint () at kernel/kgdbstub.c:1056
如上面的列表,这个隐蔽线程是10,我们可以从线程10中得到丢失的回溯跟踪。
(gdb) bt
#0 __delay (loops=1384000) at arch/x86_64/lib/delay.c:30
#1 0xffffffff80212bfd in ide_delay_50ms () at drivers/ide/ide.c:1451
#2 0xffffffff8020b252 in actual_try_to_identify (drive=0xffffffff803c05e8,
cmd=236 '' '') at drivers/ide/ide-probe.c:351
#3 0xffffffff8020b67a in try_to_identify (drive=0xffffffff803c05e8,
cmd=236 '' '') at drivers/ide/ide-probe.c:405
#4 0xffffffff8020b7ef in do_probe (drive=0xffffffff803c05e8, cmd=236 ''?)
at drivers/ide/ide-probe.c:497
#5 0xffffffff8020bcaa in probe_hwif (hwif=0xffffffff803c04a0)
at drivers/ide/ide-probe.c:613
#6 0xffffffff8020bf46 in probe_hwif_init (hwif=0xffffffff803c04a0)
at drivers/ide/ide-probe.c:868
#7 0xffffffff8021a10d in ide_setup_pci_device (dev=0x1f9e8, d=0x1f7)
at drivers/ide/setup-pci.c:740
....
『 PowerPC 』
这个支持的提供还只是基于试验基础。仍然为进行测试,所以要小心使用。欢迎提交相关问题报告和bug修复。
---编译一个包括kgdb的内核---
启用 [ Standard / Generic serial ] 支持。如果kgdb选项中 通过gdb显示控制台信息的选项被选中,启用 串口控制台支持 。
『 Linux / s390 』
---运行于 VM 下的 Linux/s390 的 kgdb stub (ver 0.2.0 )---
这里试图提供有关 VM下,运行在s390平台上的Linux的内核源码级调试工具。其提供的功能组和x86 kgdb很相似。
如图:

x3270连接 s390 VM ,提供一个控制台接口给Linux客户机。 gdbstub.pl 通过x3270 告知VM , 并使用 VM的 调试命令 (如 cp trace 、cp dispay ......)
来实现gdb远程协议。
VM调试命令可用于 提取和修改 运行没有发生变化的内核的Linux客户机。
图中的 B和C通常是相同机器。
注意:其只能工作于32位的s390平台。64位的s390平台还不支持,这需要全部重写代码。
---必要条件---
目标机:
运行在VM下的Linux/s390
主机 :
* 运行Linux的x86或s390
* 打有下面附加补丁的gdb5.2 (x86上本地的 或 交叉平台的)。在下载页有一个预编译版本(x86 binary)。
* x3270。 使用基于文本的3270客户端-c3270来取代x3270是可能的。使用只基于脚本的客户机是不怎么可取的,因为有时需要手工干预。
* stub ---- 由三个perl文件组成,gdbstub.pl 、cpinter.pl 、 cpstrings.pl
最新的发布在这里 http://sourceforge.net/projects/kgdb
---基本使用---
(交叉)编译内核 时加上 -g -O2 。 拷贝到你的目标机并引导之。 主机则需要一份包含完整调试信息的vmlinux拷贝。
在目标机端,你不能从标准的kernel rpm中简单地使用 一个plain(平坦)的 -O2 内核,不能在主机端使用-g -O2的内核。当使用-g 选项时,地址将有轻微的不同。
一个遗憾,因为这对于调试 运行着标准发布内核的产品服务器而言 是非常有用的。
编辑 cpstrings.pl 文件,以使输出与你的VM配置相匹配。
在你解开stub的目录下,创建两个fifo: mkfifo ip op
运行命令:
$ sleep 1000000 > ip < op &
这个可以让让x3270保持“陶”醉状态。
按照下面来启动x3270:
x3270 -script -port blah hostname op
注意控制台大小至少需要80x25 。 现在手动登录到你的虚拟机,启动编译时带有-g选项的内核。
按照下面来运行 stub :
perl -w ./gdbstub.pl >ip <OP
如果所有一切进行的顺利,其将迫使目标机进入 CP ,并释放命令组。
在控制台你可以进行观察。一旦上面结束,启动gdb。
s390-ibm-linux-gdb /path/to/vmlinux
set remotetimeout 20 /* 如果通过网络来调试目标机,需要运行这条命令。你可以将此 加入到 .gdbinit 中。
set trust-readonly-sections 1 /*这个可以避免一些不必要的麻烦。gdb will memory. target remote stubhost:5513
stubhost之处即是你运行gdbstub.pl之处(通常在本地主机)
你将得到下面信息:
$ s390-ibm-linux-gdb vmlinux
GNU gdb 5.2
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=i386-pc-linux-gnu --target=s390-ibm-linux"...
(gdb) set remotetimeout 20
(gdb) ta re linux-3:5513
Remote debugging using linux-3:5513
cpu_idle (unused=0x0) at process.c:76
76 }
warning: shared library handler failed to enable breakpoint
(gdb)
这时你可以设置断点,检查内存、寄存器、查看回溯跟踪……
---线程调试---
你现在可以观察所有线程任务。可以切换到特殊线程做回溯跟踪。这对于调试死锁等状况是有用的。
这里有两种方法来做。
你可以根据自己的特殊环境,选择其中一个较简单的。
--方法1--
Helper Module :
第一步需要编译一个调用gdbmod的helper module,然后在目标机上载入。
你可以在任何时间来做,甚至是stub 和 gdb启动之后。但是,这一步需要在运行第一个"info threads"命令之前来完成。
编辑Makefile样品,改变诸如内核源码路径、交叉编译等等之后,gdbmod.c 文件才可以被编译。
$ make
s390-ibm-linux-gcc -D__KERNEL__ -I/home/ganesh/work/usr/src/linux-2.4.9-37/include -Wall -Wstrict-prototypes -Wno-trigraphs -O2
-fno-omit-frame-pointer -fno-strict-aliasing -fno-common -Wno-unused -pipe -fno-strength-reduce -DMODULE -DEXPORT_SYMTAB -c
gdbmod.c
$
现在拷贝它至目标机:
$ scp -C gdbmod.o root@target:
gdbmod.o 100% |***************************************************| 106 KB 00:02
载入模块:
$ ssh root@target "/sbin/insmod /root/gdbmod.o;/sbin/lsmod"
Module Size Used by
gdbmod 107616 0 (unused)
netiucv 18096 1 (autoclean)
af_packet 16560 0 (autoclean)
$
获得目标机的/proc/ksyms,在你的perl stub目录下dump一下ksyms:
[......gdbstub]$ ssh root@target cat /proc/ksyms >ksyms
[......gdbstub]$
现在你可以运行 info threads ,大约半分钟之后如果目标机连通网络,你应该获得一个运行在目标机上的线程列表。
使用 thread 和 bt 命令来切换 线程和回溯跟踪。
但是,无法设置线程上下文中的变量、寄存器。 只可观,不可触焉。
[............gdb]$ ~/work/gdb-build/gdb/gdb
GNU gdb 5.2
[.....]
(gdb) ta re localhost:5513
cpu_idle (unused=0x0) at process.c:76
76 }
warning: shared library handler failed to enable breakpoint
(gdb) info threads
22 Thread 575 (mingetty) schedule_timeout (timeout=2147483647) at sched.c:425
21 Thread 568 (xfs) schedule_timeout (timeout=282645) at sched.c:453
20 Thread 516 (crond) schedule_timeout (timeout=231187) at sched.c:453
19 Thread 497 (xinetd) schedule_timeout (timeout=2147483647) at sched.c:425
18 Thread 464 (sshd) schedule_timeout (timeout=2147483647) at sched.c:425
17 Thread 427 (klogd) 0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095) at printk.c:178
16 Thread 422 (syslogd) schedule_timeout (timeout=2147483647) at sched.c:425
15 Thread 110 (kjournald) interruptible_sleep_on (q=0xc6d468) at sched.c:838
14 Thread 109 (kjournald) interruptible_sleep_on (q=0xc6d668) at sched.c:838
13 Thread 108 (kjournald) interruptible_sleep_on (q=0xc6d268) at sched.c:838
12 Thread 107 (kjournald) interruptible_sleep_on (q=0xc6d068) at sched.c:838
11 Thread 106 (kjournald) interruptible_sleep_on (q=0xf6a868) at sched.c:838
10 Thread 10 (mdrecoveryd) md_thread (arg=0x0) at md.c:3010
9 Thread 9 (kupdated) schedule_timeout (timeout=228645) at sched.c:453
8 Thread 8 (bdflush) interruptible_sleep_on (q=0x200c30) at sched.c:838
7 Thread 7 (kreclaimd) interruptible_sleep_on (q=0x1f9a0c) at sched.c:838
6 Thread 6 (kswapd) schedule_timeout (timeout=228682) at sched.c:453
5 Thread 5 (ksoftirqd_CPU1) ksoftirqd (__bind_cpu=0x0) at softirq.c:387
4 Thread 4 (ksoftirqd_CPU0) ksoftirqd (__bind_cpu=0x0) at softirq.c:387
3 Thread 3 (keventd) context_thread (dummy=0x0) at context.c:99
2 Thread 2 (kmcheck) __down_interruptible (sem=0x321020) at semaphore.c:128
1 Thread 1 (init) schedule_timeout (timeout=228644) at sched.c:453
(gdb) thread 17
[Switching to thread 17 (Thread 427)]#0 0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095)
at printk.c:178
178 error = wait_event_interruptible(log_wait, (log_start - log_end));
(gdb) bt 3
#0 0x0001f8d4 in do_syslog (type=0, buf=0x406b38 "", len=4095) at printk.c:178
#1 0x0007a6aa in kmsg_read (file=0x0, buf=0x0, count=0, ppos=0x0) at kmsg.c:35
#2 0x0004d5c8 in sys_read (fd=0, buf=0x406b38 "", count=4095) at read_write.c:162
(More stack frames follow...)
(gdb)
--方法2--
另一个办法耗费更长的时间,比需要做的更加痛苦, 但是,有一个优点: 不需要 helper module 。
你需要在 .gdbinit 中加入用户定义的命令两条:
【code】
define addcontext
maintenance packet Qa,$arg0,$arg1,,
end
define findthreads
set $init_thread = init_tasks[0]
set $athread = $init_thread->next_task
while $athread != $init_thread
set $frameptr = (unsigned long *)(((unsigned long *)$athread->thread.ksp)[0])
printf "%s %x ", (char *)$athread->comm, $frameptr
set $athread = $athread->next_task
end
end
【/code】
(gdb) findthreads
init 7f5d80
kmcheck 7f1eb8
keventd df7f08
ksoftirqd_CPU0 df5f38
ksoftirqd_CPU1 df3f38
kswapd de5e48
kreclaimd de3ec0
bdflush de1eb8
kupdated ddfeb8
mdrecoveryd ddbf18
kjournald f00bea0
kjournald f003ea0
kjournald efffea0
kjournald eff9ea0
kjournald eff5ea0
syslogd e975d80
klogd eb11d80
sshd e7ffd80
xinetd e733d80
crond 1003e28
xfs e567d80
mingetty e4dfd08
(gdb) addcontext syslogd e975d80
sending: "Qa,syslogd,e975d80,,"
received: "OK"
(gdb) info threads
1 Thread 65536 (syslogd) schedule_timeout (timeout=2147483647) at sched.c:425
(gdb) thread 1
[Switching to thread 1 (Thread 65536)]#0 schedule_timeout (timeout=2147483647) at sched.c:425
425 goto out;
(gdb) bt 3
#0 schedule_timeout (timeout=2147483647) at sched.c:425
#1 0x00063178 in do_select (n=1, fds=0xe975ed8, timeout=0xe975ef0) at select.c:223
#2 0x00063548 in sys_select (n=1, inp=0x7ffff800, outp=0x0, exp=0x0, tvp=0x0) at select.c:318
(More stack frames follow...)
(gdb) addcontext klogd eb11d80
sending: "Qa,klogd,eb11d80,,"
received: "OK"
(gdb) info threads
2 Thread 65537 (klogd) 0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "", len=4095)
at printk.c:178
* 1 Thread 65536 (syslogd) schedule_timeout (timeout=2147483647) at sched.c:425
(gdb) thread 2
[Switching to thread 2 (Thread 65537)]#0 0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "",
len=4095) at printk.c:178
178 error = wait_event_interruptible(log_wait, (log_start - log_end));
(gdb) bt 3
#0 0x0001f8d4 in do_syslog (type=129048040, buf=0x406b38 "", len=4095) at printk.c:178
#1 0x0007a6aa in kmsg_read (file=0x7b11de8, buf=0x7b11de8 "", count=0, ppos=0x0) at kmsg.c:35
#2 0x0004d5c8 in sys_read (fd=129048040, buf=0x406b38 "", count=4095) at read_write.c:162
(More stack frames follow...)
(gdb) The program is running. Exit anyway? (y or n) y
如果gdb的伪语言(pseudo-language)在传递参数之前可以实际求出它们,这个方法就更容易一些。
然后我们可以简单地从 findthreads loop 中运行 addcontext ,而不是手动做这一切。
addcontext 命令也可北用于 backtrack任意帧指针,比如 一个 panic 之后在控制台上打印出的。
『 提示 』
* 不要做不受限制的backtrace ,esp 。 如果你调试的是一个非常远的系统,Do a bt 5 or something 。
* 避免使用 n 命令 和 单步进single stepping 。(这也许在将来会得到修复)。
在必要的地方设置断点。
* 在gdb上击打 ^C 来强迫机器进入调试器。作为选择, 在控制台键入 PA1 或者 #cp 将起到同样的效果。
* 有时会发生这种情况: 机器已进入CP ,但是 在控制台上没有讯息--VM Read/Running 的状态遗留着,没有断点地址。
或者CP讯息出现在屏幕上。如果你怀疑机器在CP中,击打 Enter 。This is usually sufficient to get the ball rolling.
* 当机器进入CP之中,你可以在控制台运行任何命令 而不影响调试器。 只是不要改变状态 (如 寄存器、内存)。
* 不要在stub运行时从控制台手动恢复VM。
* 你可以在任何时候杀死stub 和 gdb , do a #cpu all cp tr ,移除断点,重启stub 和 gdb 。
* 你可以调试模块,但是复杂的。我不能在这里解释的更多。在 kgdb.sourceforge.net 上查看调试模块说明。
---gdb patch---
stub使用 硬件断点 , 所以不必要去消耗PC资源。我确信这样做更好一些。
【patch code】
--- ./gdb/s390-tdep.c.ORG Wed Jun 5 01:59:18 2002
+++ ./gdb/s390-tdep.c Wed Jun 5 01:59:39 2002
@@ -1787,7 +1787,7 @@
/* Amount PC must be decremented by after a breakpoint.
This is often the number of bytes in BREAKPOINT
but not always. */
- set_gdbarch_decr_pc_after_break (gdbarch, 2);
+ set_gdbarch_decr_pc_after_break (gdbarch, 0);
set_gdbarch_pop_frame (gdbarch, s390_pop_frame);
/* Stack grows downward. */
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);