the watchpoint of kgdb 大致流程为:
A:using gdb to set a watchpoint
B:send out a set watchpoint protocol packet to kgdb from gdb
C:kgdb receive/parse the protocol packet
D:kgdb set a watchpoint hardware breakpoint on kernel
E:Once kernel hit a watchpoint breakpoint, kgdb will collect the watchpoint breakpoint
info, fill them to a Stop-Reply-Packets with watchpoint format, and send out to gdb
前言
前面我们在《
gdb 和 watchpoint
》 文章
里讨论了在gdb的watchpoint,这次我们来讨论下如何让kgdb也支持watchpoint特性。
KGDB 相当于一个gdb server,只是这个server是跑在内核里面。所以KGDB支持watchpoint实现和
gdb server的实现如出一辙,即通过
GDB远程串行协议
里的Stop-Reply-Packets来传达
watchpoint信息给gdb,让gdb知道那个watchpoint击中了。
其运行的大致流程为:
A:using gdb to set a watchpoint
B:send out a set watchpoint protocol packet to kgdb from gdb
C:kgdb receive/parse the protocol packet
D:kgdb set a watchpoint hardware breakpoint on kernel
E:Once kernel hit a watchpoint breakpoint, kgdb will collect the
watchpoint breakpoint info, fill them to a Stop-Reply-Packets with
watchpoint format, and send out to gdb
|
1: 设置watchpoint 断点 in kgdb
在gdb敲入watch命令后,会将发送一个设置watchpoint的数据包给kgdb,kgdb收该数据包后就执行设置断点动作.
(gdb) watch xxx (假设我们要监视断点地址为 xxx)
gdb发送数据包"Z2,xxx,4" 给kgdb
kgdb 收到协议数据包后,调用gdb_cmd_break()(kernel/debug/gdbstub.c)
来解析设置断点协议数据
gdb_cmd_break()
------------------------------------------
parsing "Z2,xxx,4"
--> 'Z' 设置断点
--> '2' 断点类型为write watchpoint
--> 'xxx' 断点地址为 xxx
--> '4' 断点长度为 4
------------------------------------------
--> arch_kgdb_ops.set_hw_breakpoint(xxx, 4, BP_WRITE_WATCHPOINT)
--> kgdb_set_hw_break(xxx, 4, BP_WRITE_WATCHPOINT) (arch/x86/kernel/kgdb.c)
这个函数完成了将断点信息加入到breakinfo[HBP_NUM]数组里和设置其enabled标志为1,
真正的断点使能是在kgdb退出,让系统正常运行时,调用kgdb_correct_hw_break()函数
来每个cpu上使能硬件断点.
|
2: watchpoint触发捕获
watchpoint 断点隶属于硬件断点,所以我们先来谈谈硬件断点的触发的整个流程和细节。
硬件断点的触发后,会产生一个调试异常
,
因此会进入do_debug异常的
处理程序
(
arch
/
x86
/
kernel
/
traps.
c
)
.
do_debug
(
)
{
unsigned
long
dr6
;
get_debugreg
(
dr6
,
6
)
;
//保持dr6寄存器值到dr6变量
/* DR6 may or may not be cleared by the CPU */
set_debugreg
(
0
,
6
)
;
//手动清除dr6寄存器
//通过notify_die通知链告诉大家"debug"异常发生了,同时把dr6寄存器值作为参数传递出去。
notify_die
(
DIE_DEBUG
,
"debug"
,
regs
,
PTR_ERR
(
&
dr6
)
,
error_code
,
SIGTRAP
)
}
|
如果kgdb处于早期调试模式(那时候Hardware Breakpoint Layer还没初始化),则是由kgdb自己处理
DIE_DEBUG的notify通知,如果是普通模式,即硬件断点管理都是从Hardware Breakpoint Layer获取的,
则是由Hardware Breakpoint Layer处理,然后通过回调函数kgdb_hw_overflow_handler()进入kgdb.
Hardware Breakpoint Layer处理流程
:
static
struct
notifier_block hw_breakpoint_exceptions_nb
=
{
.
notifier_call
=
hw_breakpoint_exceptions_notify
,
/* we need to be notified first */
.
priority
=
0x7fffffff
}
;
register_die_notifier
(
&
hw_breakpoint_exceptions_nb
)
;
触发硬件断点
:
do_debug
(
)
--
notify_die
(
DIE_DEBUG
)
->
arch
/
x86
/
kernel
/
hw_breakpoint.
c
hw_breakpoint_exceptions_notify
(
)
->
hw_breakpoint_handler
(
)
{
for
(
i
=
0
;
i
<
HBP_NUM
;
++
i
)
{
if
(
likely
(
!
(
dr6
&
(
DR_TRAP0
<<
i
)
)
)
)
continue
;
perf_bp_event
(
bp
,
args
->
regs
)
;
-->
perf_swevent_overflow
(
event
)
->
event
->
overflow_handler
(
event
,
nmi
,
data
,
regs
)
;-->
kgdb_hw_overflow_handler
(
)
. 这里最终跑到kgdb的代码里了.
}
/* end of for()*/
}
|
3: watchpoint 断点信息获取
kgdb只需要知道是哪个watchpoint断点被踩中了,并把它的地址传递给gdb就可以了。
如果kgdb处于早期调试模式,则可以挨个判断dr6寄存器值的bit位来看哪个watchpoint击中了。
但是普通模式的Hardware Breakpoint Layer回调函数并没有传递dr6值给我们,不过还好,它传
递struct perf_event的event对象给我们,通过这个对象,我们可以得到发生硬断点的地址,然后
根据地址和kgdb里保存的硬断点数组对比,相同的,则是踩中的断点..
static
void
kgdb_hw_overflow_handler
(
struct
perf_event
*
event
,
int
nmi
,
struct
perf_sample_data
*
data
,
struct
pt_regs
*
regs
)
{
for
(
i
=
0
;
i
<
HBP_NUM
;
i
++
)
{
if
(
!
breakinfo
[
i
]
.
enabled
)
continue
;
if
(
breakinfo
[
i
]
.
addr
==
event
->
attr.
bp_addr
)
//查找和判断踩中断点信息
break
;
}
}
|
在得到是哪个breakinfo[i]被触发后,在查看下breakinfo[i].type,如果是
X86_BREAKPOINT_WRITE/X86_BREAKPOINT_RW 就可以认定是watchpoint断点了,
而断点地址则保存在breakinfo[i].addr.
4: 填充Stop-Reply-Packets来通知gdb踩中watchpoint断点了
根据watchpoint断点的类型,返回给gdb的Stop-Reply-Packets可以有如下格式:
type
=
breakinfo
[
i
]
.
type
;
addr
=
breakinfo
[
i
]
.
addr
;
switch
(
type
)
{
case
KST_READ_WATCHPOINT
:
ptr
+=
strlen
(
strcpy
(
ptr
,
"rwatch:"
)
)
;
break
;
case
KST_ACCESS_WATCHPOINT
:
ptr
+=
strlen
(
strcpy
(
ptr
,
"awatch:"
)
)
;
break
;
case
KST_WRITE_WATCHPOINT
:
ptr
+=
strlen
(
strcpy
(
ptr
,
"watch:"
)
)
;
break
;
}
ptr
+=
kgdb_long2hex
(
addr
,
ptr
)
;
*
ptr
++
=
';'
;
|
具体packgets包格式,可以阅读
Stop-Reply-Packets
5: test watchpoint support on x86
After gdb connect successly to kgdb
,
setting a watchpoint
at softlockup_thresh address.
(
gdb
)
watch softlockup_thresh
Then change the watchdog_thresh value to
24
on target
:
# echo "24" > /proc/sys/kernel/watchdog_thresh
Then the gdb will stop and show
:
----------------------------------------------------------
Old value
=
60
New value
=
24
0xffffffff810481f2
in do_proc_dointvec_minmax_conv
(
negp
=
0xffff88066cbb9e17
,
lvalp
=
0xffffffff8190e718
,
valp
=
0xffffffff8190e718
,
write
=<
value optimized out
>,
data
=
0xffff88066cbb9e68
)
at kernel
/
sysctl.
c
:
2408
2408
*
valp
=
val
;
(
gdb
)
----------------------------------------------------------
Change the watchdog_thresh value to
24
on target again
:
# echo "24" > /proc/sys/kernel/watchdog_thresh
As the previous watchdog_thresh value is
24
,
thus gdb will not stop..
|
6: 扩展阅读
更多有关kgdb和Hardware Breakpoint Layer的内容,可阅读:《
抓虫日记之 kgdb 和 删除硬断点
》
更多有关x86硬件调试寄存器信息,可阅读:《
x86 调试寄存器
》
本文转载自:
http://www.kgdb.info/kgdb/kgdb_watchpoint_support_on_x86/
暂时没有评论