首页 > 深入kgdb > kgdb源代码分析(2.6.27)第二章-准备工作

kgdb源代码分析(2.6.27)第二章-准备工作

2010年7月1日 发表评论 阅读评论

kgdb源代码分析(2.6.27)

本文由penny编写, 你可以通过发邮件到lianglip@gmail.com联系penny,
也可以直接评论本文章与penny交流.

0. 概述
1. 异步通知
2. 准备工作
3.gdb 远程串行协议(GDB remote serial protocol)
4. 命令实现:
4.1 断点.
4.2 continue 和 step
5 初始化时机.

2. 准备工作

下面来看 kgdb 的主函数 kgdb_handle_exception(),这个函数前面一段只是在做准备
工作,为后面和远程的 gdb 通信打下基础。

在这个地方又有一个问题:在当今多线程和多 cpu 的情况下,到处都充满了竞态,那
个线程都怕被别人搞破坏,那 kgdb 又是怎样让自己立于不败之地,控制好 cpu 们的?

(kernel/kgdb.c)
1392 int
1393 kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs
*regs)
1394 {
1395        struct kgdb_state kgdb_var;
1396        struct kgdb_state *ks = &kgdb_var;
1397        unsigned long flags;
1398        int error = 0;
1399        int i, cpu;
1400
1401        ks->cpu                     = raw_smp_processor_id();
1402        ks->ex_vector               = evector;
1403        ks->signo                   = signo;
1404        ks->ex_vector               = evector;
1405        ks->err_code                = ecode;
1406        ks->kgdb_usethreadid         = 0;
1407        ks->linux_regs             = regs;
1408
1409        if (kgdb_reenter_check(ks))
1410                  return 0; /* Ouch, double exception ! */

参数 evector 是中断向量,当这个异常发生时 signo 是发送给当前进程的信号编号,
ecode 是错误码,regs 是当前进程的寄存器值。

ks 的类型为 kgdb_state, 它记录了这次断点发生的信息。不知道是别有用心还是不小
心作者给同一个变量 ks->ex_vector 赋了两次值.

kgdb_reenter_check()函数在检查是否出现了一种递归触发的现象,也就是当 kgdb
正在一个 cpu 上处理一个断点的过程中, 嵌套地触发了 kgdb, 再次进入
kgdb_handle_exception()中,kgdb 都已经拿到控制权了,谁还那么无聊自己抢自己的东
西。如果遇到这种情况,kgdb 尝试在 kgdb_reenter_check()里面纠正这个错误,并直接
结束这次调用,这只是一个特殊的错误处理,我们暂时不关心它。

(kernel/kgdb.c)
1412 acquirelock:
1413          /*
1414              * Interrupts will be restored by the 'trap return' code, except
when
1415           * single stepping.
1416           */
1417          local_irq_save(flags);
1418
1419          cpu = raw_smp_processor_id();
1420
1421          /*
1422           * Acquire the kgdb_active lock:
1423           */
1424 while (atomic_cmpxchg(&kgdb_active, -1, cpu) != -1)
1425          cpu_relax();

local_irq_save()保存当前中断状态,然后禁中断。raw_smp_processor_id()获得当前
CPU号。

atomic_cmpxchg()函数实现了一个比较+交换的原子操作(原子就是说cpu要不就不
做,要做就一定要做完某些操作才能干别的事情,对应这里就是比较和交换要一次过做完).
atomic_cmpxchg()比较kgdb_active->count的值是否等用-1,如果是则把cpu的值赋
给kgdb_active->count,否则不修改它的值,atomic_cmpxchg返回
kgdb_active->count赋值前的值.

kgdb_active是一个全局原子变量,定义在kernel/kgdb.c中,用来记录当前正在执行
kgdb代码的cpu号,它起到一个锁的作用,因为同一时间只能有一个cpu执行kgdb的代
码,这是可以想象得到的,如果两个cpu在两个不同断点被触发,那究竟是谁和远端gdb通
信呢?前一条命令被 cpu1拿了,后一条却去了cpu2那里,那还得了。

kgdb_active的初始值为-1,-1表示当前kgdb的处理函数并没有被触发,相反如果
kgdb已经在运行,那么kgdb_active就有它自己的值,这些处理都是针对多cpu的,如
果只有一个cpu,这个世界就简单多了。这里是防止多个kgdb的实例在不同cpu被触发
引起互相干扰。考虑这种情况,在cpu1上有一个断点让kgdb起来,这时,kgdb_active
还是-1,cpu1很顺利就给kgdb_active赋值然后进入后面的操作.这时cpu2中kgdb也被触发.
它也想进入后面的操作,但是这时候kgdb_active已经不再是-1,cpu2只能不断地比较
kgdb_active的值和执行cpu_relax(),宏cpu_relax()可以简化为一条pause汇编,通过引
入一个很短的延迟,加快了紧跟在锁后面的代码的执行并减少能源的消耗,实际上就是
让cpu2等。当cpu1在退出kgdb_handle_exception()前会把 kgdb_active赋回-1, 这样
cpu2就可以进行后面的操作了。kgdb使用大量的原子操作来完成锁的功能,后面还会
看到. atomic操作加上cpu_relax()跟一个自旋锁很相似。

(kernel/kgdb.c)
1427          /*
1428            * Do not start the debugger connection on this CPU if the last
1429            * instance of the exception handler wanted to come into the
1430            * debugger on a different CPU via a single step
1431            */
1432          if (atomic_read(&kgdb_cpu_doing_single_step) != -1 &&
1433                atomic_read(&kgdb_cpu_doing_single_step) != cpu) {
1434
1435                    atomic_set(&kgdb_active, -1);
1436                    touch_softlockup_watchdog();
1437                    clocksource_touch_watchdog();
1438                    local_irq_restore(flags);
1439
1440                    goto acquirelock;
1441          }

注释告诉了我们大部分事情.它在考虑这样一种情况:cpu1先进入kgdb,并和远端gdb
通信中确定要进行单步调试(step),设置好cpu1上面标志寄存器的X86_EFLAGS_TF位(也
就是trap位,或单步调试位)好让cpu在运行完后面一条指令后引起一个debug异常(1
号异常)再次进入kgdb,接下来cpu1让程序继续下一条指令.这时候cpu2闯进来了.当
cpu1退出kgdb时会把kgdb_active设置为-1,这样上面提到的那个锁就解开了。cpu2
就可以进来了,但是kgdb在一个cpu处在单步调试时不想让别的cpu进来扰乱,就让cpu2
放弃kgdb_active 锁,然后让它再拿一次锁,期望cpu1能够先处理,cpu2的事以后再说。
kgdb_cpu_doing_single_step用来记录当前那个cpu 处于单步调试的状态,-1代表没有
cpu在单步调试状态。后面我们将看到当kgdb收到远程gdb单步执行命令时会设置这个
变量.

(kernel/kgdb.c)
1443          if (!kgdb_io_ready(1)) {
1444                   error = 1;
1445                     goto kgdb_restore; /* No I/O connection, so resume the
system */
1446          }

检查 kgdb 要用的 I/O 驱动模块是否已经加载好了,如果没有准备好就没有必要往下走
直接返回,让系统起来。关于 I/O 的初始化这里暂时就不讨论了。

(kernel/kgdb.c)
1448          /*
1449            * Don't enter if we have hit a removed breakpoint.
1450            */
1451          if (kgdb_skipexception(ks->ex_vector, ks->linux_regs))
1452                   goto kgdb_restore;

kgdb_skipexception(),一个挺有意思的函数,注释写着”如果我们命中了一个已经被删
除的断点,下面的代码就不要走下去了”.这是什么意思?断点被删除了还能够被命中吗?迫不
及待看看它的代码,这是一个与体系结构相关的函数,暂时只有 x86 对它有实质性的实现:

(arch/x86/kernel/kgdb.c)
530 /**
531 *
532 *       kgdb_skipexception - Bail out of KGDB when we've been triggered.
533 *       @exception: Exception vector number
534 *       @regs: Current &struct pt_regs.
535 *
536 *           On some architectures we need to skip a breakpoint exception
when
537 *       it occurs after a breakpoint has been removed.
538 *
539 * Skip an int3 exception when it occurs after a breakpoint has been
540 * removed. Backtrack eip by 1 since the int3 would have caused it to
541 * increment by 1.
542 */
543 int kgdb_skipexception(int exception, struct pt_regs *regs)
544 {
545         if (exception == 3 && kgdb_isremovedbreak(regs->ip - 1)) {
546                  regs->ip -= 1;
547                  return 1;
548         }
549         return 0;
550 }

这段注释基本上没有说明什么问题,实现更是看不懂,kgdb_isremovedbreak()是干那
行的?为什么被删除了的断点还会起作用呢?google搜了一些,基本上只能找到一堆patch的
信息,还有什么线索呢?不要忘记邮件列表这个好东西,牛B门们都在上面.在kgdb的邮件列表
搜了一下,找到了一个位大侠06年发的这样一封信:

We keep track of removed entries to prevent breakpoints from occuring after
they were removed. Removed breakpoints might have occured on other processors
before there removal from code, which will pop up on continuing and cause
segfaults on i386. kgdb_skipexception function identifies them.

看到”other processors”让人恍然大悟,又是多cpu惹的祸.考虑这样一种情况:有两个cpu,
cpu1,cpu2,gdb在之前设置了两个两个断点 B1,B2,cpu1命中了B1,并且拿到了前面的
kgdb_active锁,在还没有停止其它cpu的活动前(后面我们马上会讲到),cpu2命中了 B2,但
是由于kgdb_active锁已经被cpu1拿了, cpu2将忙等kgdb_active. cpu1进入了kgdb
的执行代码,在和远程gdb通信过程中,gdb要求删除B2,cpu1没法知道正在门外等着的
cpu2正是因为B2而来的,cpu1直接把B2删除,然后gdb让程序继续.在cpu1放弃
kgdb_active锁之后,cpu2进来了,但是cpu2进来已经没有意义了,如果我们在这里不加以
判断,后面就会出大bug了,所以我们需要判断一下触发我们的这个断点是不是已经被删除
了,kgdb_isremovedbreak()正是用来作这个判断,如果真的是这种情况,pc寄存器
(regs->ip)就退一.

(kernel/kgdb.c)
1458          kgdb_info[ks->cpu].debuggerinfo = ks->linux_regs;
1459          kgdb_info[ks->cpu].task = current;
1460
1461          kgdb_disable_hw_debug(ks->linux_regs);

pre_exception()让驱动在进行连接前有机会去做一些自己特有的初始化,我们可以先
看看关于串口的这个函数.

(drivers/serial/kgdboc.c)
143 static void kgdboc_pre_exp_handler(void)
144 {
145   /* Increment the module count when the debugger is active */
146   if (!kgdb_connected)
147            try_module_get(THIS_MODULE);
148 }

看到了,对于串口来说根本就没有做什么事情,只是增加模块的使用计数。

回到我们的 kgdb_handle_exception(),kgdb 用 kgdb_info 数组记录当前进程和它的
寄存器, 方便日后和远程 gdb 通信时使用。kgdb_disable_hw_debug()对应到系统结构上
是调用 arch/x86/kernel/kgdb.c 中的 kgdb_disable_hw_debug:

(arch/x86/kernel/kgdb.c)
300 /**
301 *       kgdb_disable_hw_debug - Disable hardware debugging while we in
kgdb.
302 *       @regs: Current &struct pt_regs.
303 *
304 *       This function will be called if the particular architecture must
305 *       disable hardware debugging while it is processing gdb packets or
306 *       handling exception.
307 */
308 void kgdb_disable_hw_debug(struct pt_regs *regs)
309 {
310         /* Disable hardware debugging while we are in kgdb: */
311         set_debugreg(0UL, 7);
312 }

就正如注释所说的那样, 对于某些体系结构 kgdb在运行时需要设置禁止硬件调试,DR7
寄存器就是断点控制寄存器,他的低半个字用来允许断点和允许所选择的调试条件,清 0
就是禁止硬件调试。

再次回到主题 kgdb_handle_exception()

(kernel/kgdb.c)
1463          /*
1464            * Get the passive CPU lock which will hold all the non-primary
1465            * CPU in a spin state while the debugger is active
1466            */
1467          if (!kgdb_single_step) {
1468                    for (i = 0; i < NR_CPUS; i++)
1469                             atomic_set(&passive_cpu_wait[i], 1);
1470          }

在这里出来了一个十分重要的变量 kgdb_single_step,名字告诉我们这个变量和单步
调试有关,或者可以直接说当 kgdb 要进行单步调试时就会把这个变量设成 1,这个条件有些
让人费解,大家可以先想想为什么要有这样一个条件,我们在这里先忽略掉它,就当这里根本
没有这个条件,这是可以的.在这节最后我们会对这个条件作更多的说明.

这里又一个用原子变量作为锁的例子,我们先把下面一段代码也贴出来:

(kernel/kgdb.c)
1472          /*
1473            * spin_lock code is good enough as a barrier so we don't
1474            * need one here:
1475            */
1476          atomic_set(&cpu_in_kgdb[ks->cpu], 1);
1477
1478 #ifdef CONFIG_SMP
1479          /* Signal the other CPUs to enter kgdb_wait() */
1480          if ((!kgdb_single_step) && kgdb_do_roundup)
1481                    kgdb_roundup_cpus(flags);
1482 #endif
1483
1484          /*
1485            * Wait for the other CPUs to be notified and be waiting for us:
1486            */
1487          for_each_online_cpu(i) {
1488                    while (!atomic_read(&cpu_in_kgdb[i]))
1489                             cpu_relax();
1490          }

kgdb 把正在执行 kgdb 代码的 cpu 叫做主 cpu(primary cpu),其他的 cpu 叫从 cpu
(passive cpu). passive_cpu_wait[]和 cpu_in_kgdb[]两个数组存的都是原子变量,都是每
个 cpu 占一个元素。它们组合在一起又形成了一道锁,而这个锁锁的是从 cpu,让他们进
入忙等待的状态。具体看看它的实现过程。1469 行把所有 cpu 的 passive_cpu_wait[]都
设成了 1,从名字上就可以看出它的意思(passive:被动的), 就是 kgdb 想让那个 cpu 进
入等待状态,这里把自己也选上没关系吗?是的,没有关系,后面自然会明白。我们再跳到
1481, kgdb_roundup_cpus()被调用,现在来看看这个函数:

(arch/x86/kernel/kgdb.c)
348 void kgdb_roundup_cpus(unsigned long flags)
349 {
350         send_IPI_allbutself(APIC_DM_NMI);
351 }

再找 send_IPI_allbutself,发现一大堆的实现,什么 bigsmp,es7000,numaq,
summit。不过不用担心,其实它们所做的事情都是一样,只不过是硬件不同有不同的软件
实现,有兴趣的同学可以在 arch/x86/Kconfig 中可以看到他们的说明。从名字我们可以猜
出这个函数的作用: send 发送, IPI(interprocessor interrupt)处理器间的中断,allbutself
除了自己,那串起来就是向除了自己之外的 cpu 发中断,了解中断的同学应该不会对 APIC
(Advanced Programmable Interrupt Controller)陌生,APIC 能够让一个 cpu 向另外一
个发送一个中断。kgdb 向所有从 cpu(passive cpu)发送了一个 NMI(不可屏蔽)中断,用来
通知它们,kgdb 要正式干活了,其它同志们暂时歇会,不要进来捣乱.那么从 cpu 在被 NMI 中
断后有什么反应呢?让我们暂时离开主函数,在次回到 kgdb 在被触发时的情景.和 int3 一样,
对于NMI, x86对 NMI 也设立了相关的处理函数do_nmi()(定义在arch/x86/kernel/traps_32.c).
do_nmi()调用 default_do_nmi(),它也会引起 die_chain 的注意。我们又回到前面的 kgdb 在
die_chain 中注册的处理函数__kgdb_notify()

(arch/x86/kernel/kgdb.c)
442 static int __kgdb_notify(struct die_args *args, unsigned long cmd)
443 {
444           struct pt_regs *regs = args->regs;
445
446           switch (cmd) {
447           case DIE_NMI:
448                    if (atomic_read(&kgdb_active) != -1) {
449                             /* KGDB CPU roundup */
450                             kgdb_nmicallback(raw_smp_processor_id(), regs);
451                             was_in_debug_nmi[raw_smp_processor_id()] = 1;
452                             touch_nmi_watchdog();
453                             return NOTIFY_STOP;
454                    }
455                    return NOTIFY_DONE;
456
457           case DIE_NMI_IPI:
458                    /* Just ignore, we will handle the roundup on DIE_NMI. */
459                    return NOTIFY_DONE;

注释也说了,主 cpu 发过来的中断由 DIE_NMI 来处理。在 2.6.26 上面 DIE_NMI_IPI 和
DIE_NMI 的处理是一样的.kgdb_nmicallback()会被调用.究竟 kgdb 用什么方法让从 cpu
停下来的呢?

(kernel/kgdb.c)
1536 int kgdb_nmicallback(int cpu, void *regs)
1537 {
1538 #ifdef CONFIG_SMP
1539          if (!atomic_read(&cpu_in_kgdb[cpu]) &&
1540                           atomic_read(&kgdb_active) != cpu &&
1541
atomic_read(&cpu_in_kgdb[atomic_read(&kgdb_active)])) {
1542                   kgdb_wait((struct pt_regs *)regs);
1543                   return 0;
1544          }
1545 #endif
1546          return 1;
1547 }

看到名字我迫不及待地想看看 kgdb_wait

(kernel/kgdb.c)
 564 static void kgdb_wait(struct pt_regs *regs)
 565 {
 566          unsigned long flags;
 567          int cpu;
 568
 569          local_irq_save(flags);
 570          cpu = raw_smp_processor_id();
 571          kgdb_info[cpu].debuggerinfo = regs;
 572          kgdb_info[cpu].task = current;
 573          /*
 574            * Make sure the above info reaches the primary CPU before
 575            * our cpu_in_kgdb[] flag setting does:
 576            */
 577          smp_wmb();
 578          atomic_set(&cpu_in_kgdb[cpu], 1);
 579
 580          /* Wait till primary CPU is done with debugging */
 581          while (atomic_read(&passive_cpu_wait[cpu]))
 582                   cpu_relax();
 583
 584          kgdb_info[cpu].debuggerinfo = NULL;
 585          kgdb_info[cpu].task = NULL;
 586
 587          /* fix up hardware debug registers on local cpu */
 588          if (arch_kgdb_ops.correct_hw_break)
 589                   arch_kgdb_ops.correct_hw_break();
 590
 591          /* Signal the primary CPU that we are done: */
 592          atomic_set(&cpu_in_kgdb[cpu], 0);
 593          touch_softlockup_watchdog();
 594          clocksource_touch_watchdog();
 595          local_irq_restore(flags);
 596 }

代码都列出来了,估计大家也都看到这个锁是怎样让从 cpu 停止活动的,cpu_relax()前
面已经提到过了,它可以转换成一条 pause 汇编.在这里小弟再总结一下:主 cpu 在 kgdb 的
主函数中(kgdb_handle_exception)设置数组 passive_cpu_wait[]中所有的元素(包括描
述自己的那个元素),然后向除了自己以外的所有 cpu 发 NMI IPI 中断,因此所有的从 cpu 都
会进入 kgdb_wait(),进来后从 cpu先设置 cpu_in_kgdb[]中自己的那个元素,以通知主cpu,
自己已经准备好了,然后反复对自己的那个 passive_cpu_wait 元素进行判断然后 relax,而
另外一方面,主 cpu 也正反复检查 cpu_in_kgdb[]的元素,等待所有从 cpu 都进入等待状态.
这样 passive_cpu_wait 让从 cpu 等待,cpu_in_kgdb 让主 cpu 保证所有从 cpu 都进入了
等待状态.

当然,有上锁就有解锁,在主函数中,当处理完远端 gdb 请求后退出时就会解锁:

(kernel/kgdb.c)
1513          if (!kgdb_single_step) {
1514                   for (i = NR_CPUS-1; i >= 0; i--)
1515                             atomic_set(&passive_cpu_wait[i], 0);
1516                   /*
1517                     * Wait till all the CPUs have quit
1518                     * from the debugger.
1519                     */
1520                   for_each_online_cpu(i) {
1521                             while (atomic_read(&cpu_in_kgdb[i]))
1522                                       cpu_relax();
1523                   }
1524          }

一样的做法,只不过这次是解锁和等待所有从 cpu 离开 kgdb_wait()回到日常生活.

好了,大家应该不会忘记前面我们提的一个问题,就是为什么要有 kgdb_single_step 这
么一个判断,它在我们前面的代码中连续出现了 3 次.kgdb_single_step,就正如大家想的那
样,当 gdb 向 kgdb 发送一个’s’单步执行命令时这个变量的值就会变成 1.那为什么在 kgdb
处于单步调试的情况下反而不去停止其它 cpu 的活动呢?

在邮件列表里面,一位仁兄04年写的一封信里给出了答案,道理很简单.如果想看原文,可
以在邮件列表里面搜 debugger_step 这个关键字(在进内核树之前,kgdb_single_step 就
叫这个名字),在最老的几封信里面. 在 2.6.27 内核 x86 平台上,当 gdb 要求 kgdb 进行单
步调试时,kgdb 是会让所有从 cpu 都处于忙等待,只让被单步调试的那个 cpu 继续运行,一
直到 gdb 过来一个’c'(continue)为止,才让从 cpu 们继续.这样我们再回头看看前面的代码,
然后考虑这样一个情况:主 cpu 由于一个断点进入 kgdb 的代码,一开始,它并不是处于单步
调试的状态,所以 kgdb_single_step 为 0,这样 kgdb 把其它所有 cpu 都锁住,让它们等待,
然后和远程的 gdb 通信,假如这时候 gdb 要求单步调试,kgdb 就把 kgdb_single_step设成
1,然后让 程序继 续执 行,在 1513 行,也 是判断 kgdb_single_step(用于解 锁),这时 候,
kgdb_single_step 已经不是 0 了,所以主 cpu 并没有帮其它从 cpu 解锁,所以主 cpu 在下
一条指令完成后再次进入 kgdb 主函数,这时候,所有从 cpu 还在忙等待,所有没有必要在设
置 passive_cpu_wait[]和向 它们发 IPI NMI 中 断,不然从 cpu 为 了响应 这个 NMI 会 在
kgdb_wait 里面再调用一次 kgdb_wait,这正是之前 kgdb 解决过的一个 bug.

主函数还剩下一点点了,我们把他看完:

(kernel/kgdb.c)
1492          /*
1493           * At this point the primary processor is completely
1494           * in the debugger and all secondary CPUs are quiescent
1495           */
1496               kgdb_post_primary_code(ks->linux_regs, ks->ex_vector, ks-
>err_code);
1497          kgdb_deactivate_sw_breakpoints();
1498          kgdb_single_step = 0;
1499          kgdb_contthread = current;
1500          exception_level = 0;

这些是在进入和远程 gdb 通信之前 kgdb 所做的最后的初始化 ,
kgdb_post_primary_code 的意思就是在主 cpu 在控制整个机器后,它还有什么事情需要
做的,这是体系结构相关的,x86 对 kgdb_post_primary_code 实现非常简单,就是保存
了一下中断向量号和错误代码。在开始前我们先禁止所有断点, 把内存还原成原来的样子。
主函数在最后就是还原环境,中间夹着的是最重要的一行代码了,和 gdb 通信,执行他发
来的命令。

(kernel/kgdb.c)
1502          /* Talk to debugger with gdbserial protocol */
1503          error = gdb_serial_stub(ks);

主函数这样就差不多了,有些同志可能注意到了 kgdb_cpu_doing_single_step和前面
passive_cpu_wait[]/cpu_in_kgdb[]形成的锁在功能上有些重叠了,前者是当有一个 cpu
在单步调试时,其它 cpu 不能进 kgdb 的代码.而后者则是当 kgdb 在处在单步调试时让其它
cpu 处在忙等待中.忙等待有怎么会进 kgdb 的代码呢?对于这个问题,我没有很确切的回答,
如果大家知道就请指点指点小弟.这两段代码是一位大侠在一次提交中放到库里的。但是它
们的目标都是一样,就是保护单步调试的 cpu.

在离开之前,主函数在前面究竟做了什么事情?其实没干多少活,大部分时间都在控制
竞态,包括单步调试,停止从 cpu 的活动等。

本文地址:
http://www.kgdb.info/kgdb_source_chapter_two/
版权所有 © 转载时必须以链接形式注明作者和原始出处!