A: kgdb江湖背景分析
其实关于kgdb和removed breakpoint的关系已经在 《kgdb源代码分析(2.6.27)第二章
-准备工作》的’kgdb_skipexception()‘的一节讲得很清楚了,这里我就再唠叨一遍.
kgdb_skipexception()函数的引入是为了避免kgdb踩中已经删除的断点而做的特殊处理。
"踩中已经删除的断点"-也许大家看到这句话觉得有点前后矛盾,第一反应也许是. "既然已
经把断点删除了,怎么会被踩中呢?如果被踩中了,说明删除操作没成功!". 是的,这个情
况在单CPU的系统下,是不会出现的。但如果放到多CPU的系统下,就很有可能发生.
考虑这样一种情况:系统有两个cpu,[cpu1,cpu2],gdb在之前设置了两个断点 B1,B2,在
cpu1命中了B1时,cpu2命中了B2。(这是情况是很有可能的哈,多CPU嘛)然后cpu1在命中B1
后,马上进入了kgdb的地盘,抢到了kgdb掌门令牌(kgdb_active),成为kgdb的掌门CPU了.
而CPU2进kgdb的地盘慢了点,没拿到kgdb掌门令牌(kgdb_active), 因此最后很惨的被CPU1
借kgdb门号发出的江湖号令(NMI中断)给召集过去做kgdb的门徒和奴隶了。
这时候系统的状态是: cpu1踩中B1断点,kgdb的掌门人,CPU2踩中B2断点,是kgdb的门徒和奴隶,行动完全受到限制。
由于CPU1是掌门人,因此和gdb这厮的外交事宜由CPU1全权处理,gdb早就看断点B2很不爽,于是
密谋让CPU1把B2断点给干掉,CPU1不好驳gdb的面子,做个顺水人情把B2断点给做了,但为了避免
别人口舌说它不公,就给B2打了个已干掉标签(BP_REMOVED),"光明正大"的告诉大家不要在用它了。
gdb把对操作系统偷鸡摸狗的事做完后,觉得kgdb帮派可以暂时解散了,于是发出"continue"外交令给kgdb,
迫于gdb的强势,CPU1不得不解散kgdb,并释放了CPU2等人,交还kgdb掌门令牌(kgdb_active)。
从此系统恢复原来状态了.
因kgdb帮派解散而被释放的CPU2很不服,它也击中了断点的呀,却被召集去做kgdb的奴隶,于是奋发图强,
再次进军kgdb的地盘,这次没人和它抢kgdb掌门令牌(kgdb_active),就成了kgdb的掌门。虽然成了掌门人,
但出于居安思危的考虑,CPU2得去求证它踩中B2断点是否有效,如果有效就说明失业可以正常运作下去,如
果无效就说明根基不稳,日后必将在江湖起风浪,酿成大祸,须早早离开.
由于前面说到,B2已经被gdb和CPU1合伙给拔掉了,而且CPU1为此还留下了个标记,因此CPU2看到这个标记后,
就知道B2断点靠不住了. CPU2这哥们很稳重,一见有问题,就立刻放弃kgdb掌门的位置,避免了大祸,并返回江东,
期待时机后续大业.
B: 现象 & 现场分析
时空穿梭,咱们回到CPU1做掌门时期,即:
系统的状态是: cpu1踩中B1断点,并是kgdb的掌门人,CPU2踩中B2断点,是kgdb的门徒和奴隶,行动完全受到限制。
话说有次gdb这厮和kdgb掌门人CPU1因某事给闹僵了,gdb火了,要和kgdb绝交,于是对kgdb帮派发出"detach"外交令,
要知道gdb上下都很有关系的,黑白统吃的那种,最后kgdb不仅和gdb绝交了,最后还害得家破人亡,帮派也被上头强制解散了.
解散帮派需要做很多善后工作的,包括对所有断点的善后处理,按常理来说,善后处理的断点都要给贴上已干掉标签(BP_REMOVED),
但CPU1一忙,竟贴错了,贴成了未使用的标签(BP_UNDEFINED)...
int dbg_remove_all_break(void)
{
unsigned long addr;
int error;
int i;
/* Clear memory breakpoints. */
for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) {
if (kgdb_break[i].state != BP_ACTIVE)
goto setundefined;
addr = kgdb_break[i].bpt_addr;
error = kgdb_arch_remove_breakpoint(addr,
kgdb_break[i].saved_instr);
if (error)
printk(KERN_ERR "KGDB: breakpoint remove failed: %lx\n",
addr);
setundefined:
kgdb_break[i].state = BP_UNDEFINED;
}
/* Clear hardware breakpoints. */
if (arch_kgdb_ops.remove_all_hw_break)
arch_kgdb_ops.remove_all_hw_break();
return 0;
}
话说kgdb帮派解散后,CPU2自由了,由于CPU2在做kgdb门派/奴隶期间,信息封闭,竟然不知道CPU1和gdb那档子事,
还一直在积极的想当kgdb掌门,于是再次进军kgdb的地盘,并成了kgdb掌门,由于前面CPU1无心的错误,使得CPU2在检测
B2断点时,没有发现是已干掉的状态,还以为B2是正常的,由于它就竖起kgdb大旗,并发出发江湖号令(NMI中断),让其它
CPU们来归附于它,最后因为B2的根基不稳导致无法预料的后果...
C: 问题解决和女娲大婶的出场
那时,女娲大婶看不下去了,祸虽然是在CPU2头上,但根本原因是在CPU1的粗心,所以女娲大婶决定来个时光逆转,
重新改造dbg_remove_all_break,避免CPU1的犯错误,从而保世间太平...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | commit d70f9db504d43d929c53cfb6fd01b17f7166a2be Author: Dongdong Deng <dongdong.deng@windriver.com> Date: Wed Aug 25 15:28:38 2010 +0800 debug_core: track the removed breakpoints of remove_all_break() The debugger has to keep track of removed breakpoint to prevent breakpoints occurring after they were removed. Removed breakpoints might have occurred on other processors before there removal from code, which will pop up on continuing and cause segfaults on x86. kgdb uses BP_REMOVED to mark an removed breakpoint, and provides kgdb_isremovedbreak() to judge the given breakpoint is an removed breakpoint or not. This patch changes the default removed breakpoint state to BP_REMOVED from BP_UNDEFINED for tracking the removed breakpoints in remove_all_break(), and flush the removed breakpoint address memory after breakpoint removed. Signed-off-by: Dongdong Deng <dongdong.deng@windriver.com> int dbg_remove_all_break(void) { unsigned long addr; int error; int i; /* Clear memory breakpoints. */ for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { if (kgdb_break[i].state == BP_UNDEFINED) continue; if (kgdb_break[i].state == BP_ACTIVE) { addr = kgdb_break[i].bpt_addr; error = kgdb_arch_remove_breakpoint(addr, kgdb_break[i].saved_instr); kgdb_flush_swbreak_addr(addr); if (error) printk(KERN_ERR "KGDB: breakpoint remove" " failed: %lx\n", addr); } kgdb_break[i].state = BP_REMOVED; } /* Clear hardware breakpoints. */ if (arch_kgdb_ops.remove_all_hw_break) arch_kgdb_ops.remove_all_hw_break(); return 0; } |