[转]龙芯上内核开发、调试和优化小结


作者: falcon   发表日期: 2009-03-31 16:14   复制链接

by falcon <[email protected]>
2009-03-31

    [差点就要长篇大论(用E文写了大概要两个section了),想了想,还是直接从weekreport里头copy过来,整理一下得了!]

    最近移植了Linux的实时扩展补丁RT_PREEMPT到loongson2f平台上,现在基本完工,这里做个小结。

一、移植过程

    移植的过程大概如下(序号大体是推进的,但是有些可能是交叉并行的):

    0、查找Loongson2f相关的资料,资料收集如下:
       http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1986.html
       更多资料:http://dev.lemote.com/drupal/download
       Google + Baidu ==> 整个互联网
    1、学习mips汇编,学习笔记如下:
         Practical MIPS Assembly Language Programming In Linux
        http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1991.html
       汇编的学习没有任何诀窍,看cpu指令手册,看汇编规范,使用汇编工具,做练习!
    2、了解mips/loongson2f平台
       阅读see mips run v2和loongson2f用户手册,使用福珑6003的盒子
       光看是没有用,对照内核源代码来阅读会比较好一些!
    3、了解Linux内核中跟RT_PREEMPT相关的部分
       阅读lkd,ldd3等linux内核相关的书籍,仔细阅读了内核线程、中断处理、时钟管理、进程同步等部分。
       边看边练!
    4、使用RT_PREEMPT,阅读其源代码
       http://rt.wiki.kernel.org
       在移植之前,把要移植的东西了解透彻,但是不要在某些点钻得太深,点到为止(找到跟架构和目标相关的地方为止)!否则整个进程将不好控制!
    5、移植RT_PREEMPT到qemu/mips(malta)上
       主要是解决语法错误之类,这个时候需要cscope, objdump, nm之类的工具辅助
       指定cscope的架构: $ make cscope ARCH=mips
    6、移植RT_PREEMPT到福珑6003(loongson2f based)上
       在PMON这个BIOS和Bootloader级别的debugger的帮助下找出了一个bug,可以在loongson2f上跑起RT_PREEMPT了。
       关于添加配置选项和内核编译选项什么的,请仔细阅读:Documentation/kbuild/*
       如果运行不起来,不要恐惧!因为导致运行不起来的原因仅仅是一个非常小的bug,仔细分析,用各种调试器辅助解决之!详细的调试技巧看后面一节!
    7、移植RT_PREEMPT的实时内核调试工具Ftrace和perf_counter
       这个时候主要用的调试工具就是万能的printk了~~当然,还要对mips的sub-routine之类的实现有相当的清晰!特别是寄存器的保护!
       了解这些工具的原理,熟悉相关的硬件,然后移植之!
    8、整理源代码和文档
       整理源代码,最好是以补丁的形式发布你的东西,否则整个内核几十M不好弄!这个时候要熟练使用diff & patch,当然,还有vimdiff之类的!
        写文档,Latex is my love!
    9、做移植报告
       写幻灯(slides),不要用M$的PPT,用Latex的Beamer,非常酷:简单、漂亮、节约时间!

二、调试方法

    下面把移植过程中用到的和没有用到的一些调试方法都罗列出来:

    1、PMON

    PMON是loongson2f序列机器用的BIOS+Bootloader,但是对于开发人员来说,它也是个非常不错的调试工具,支持断点调试、单步调 试、内存和寄存器操作等,用法跟gdb非常相似。具体用法请在PMON的命令行下键入h命令,找到Debugger和Memory的相关信息仔细阅读。

    为什么要用到PMON来调试呢?因为在内核初始化硬件陷阱处理(trap_init)之前,内核执行过程中触发的异常还是要由PMON来处理。在移植过程 中遇到的一个典型问题是TLB MISS,PMON告诉我们大概的发生位置,然后通过PMON的断点调试和单步调试来定位问题发生的确切位置,并通过分析上下文查找原因。

    在调试的时候可能会有需要关掉loongson2f的缓存,以便调试,可以这么做:

    mfc0   t0, CP0_CONFIG     # 读取CP0之 conifg 寄存器的值于通用寄存器t0中
    and    t0, 0xfffffff8     # 低 3 位置 0
    or     t0, 0x2            # 低 3 位置 2,关闭 cache
    mtc0   t0, CP0_CONFIG     # 写回CP0之 conifg 寄存器 


     参考: http://comcat.blog.openrays.org/blog-htm-do-showone-tid-148.html

    2、串口调试

    利用串口调试有个明显的好处:可以查看和分析内核启动过程中的日志!因为显示器显示的信息有限,前面的数据会被后面的信息覆盖掉,无法查看完整的日志。
    福珑上有一个串口可用,如果想通过串口调试,那么请参照下面步骤来做:

    a. 用一跟串口线把它连接到另外一台机器上,在另外一个机器上启动一下诸如minicom之类的串口调试工具,配置一下波特率就行了,波特率是115200。minicom的用法见“man minicom”。
    b. 把福珑启动到PMON的命令行(启动的时候一直按着DEL键),然后键入"setvga 0",回车并重启,这样PMON重启后就会把信息输入到minicom这边了。
    c. 为了记录更多的日志信息,可以在启动minicom之前执行script或者screen -L,调试完以后,键入exit退出,这样就可以通过typescript或者screenlog.*文件查看操作日志了。当然,有个更简单的办法是配置 终端可以回滚的行数,改得足够大就行。

    3、puts, puthex, early_printk 等

    在控制台初始化(console_init)之前,printk是没有办法给我们打印日志的,怎么办呢?直接往串口写信息(thx to yanhua from Lemote):

// 32bit
*(char *)0xbfd002f8 = 'A'
// 64bit
*(char *)0xffffffffbfd002f8 = 'A'



    在福珑6003里头用的串口地址是0xbfd002f8,但是2e里头貌似是3f8,具体是什么你可以通过这个查看:

$ cat /proc/ioports  | grep serial
000002f8-000002ff : serial



    实际上我们还可以用一些封装好的串口操作函数,那就是puts, puthex或者直接开启内核里头的early_printk。开启early_printk的办法很简单:配置内核时把kernel hacking菜单下面的kernel debugging选项打开,然后再选择主菜单中的early_printk,这样内核就可以在console_init之前使用printk了。

    4、使用watch寄存器(thx to Mgr. Zhang from Lemote)

    loongson2f给我们提供了一个用于调试寄存器,即cp0(coprocessor 0)里头的第18号寄存器(见loongson2f手册)。它可以用来跟踪某个虚拟内存地址的读写操作,大概用法如下:

                63     3  2  1  0
                VADDR, 0, R, W


     设置(监测某个地址):load t0, (VADDR | 0RW); mtc0 t0, 18
     清除:mtc0 $ZERO, 18

    这个还是蛮有用的,典型的例子是:当你发现某个数据跟你预想的不一样的时候,你就可能想找出到底是哪个家伙把这个数据修改掉了!查找的办法很简单:
    先通过objdup -D vmlinux查找到内核中某个数据变量对应的虚拟地址,然后设置一下watch寄存器,一旦跟踪到该数据被修改(看你设置成读、写还是读写都监测了), 那么将触发一个watch例外,在例外处理程序中就会打印相关寄存器的信息,进而跟踪到那个罪魁祸首了!
    如果调试完了,记得清除掉,否则不管谁在操作那个虚拟地址,都给你报告,烦死你!清除的办法是往watch寄存器写了一个0。

    5、printk

    在控制台初始化(console_init)以后,printk就可以工作了,这个时候我们可以直接用它来调试。另外,在内核正常启动以后,如果要调试由应用程序触发的内核故障,也可以用printk来调试,但是有个地方需要注意一下。
    如果常用printk或者进行过内核模块编程,应该会注意到printk的第一个参数经常会包含诸如KERL_DEBUG或者<7>之类的字 符串,这个是告诉printk把日志打印到哪个地方,但是具体打印到哪个地方,还有一个console_loglevel用作参照。
    如果你指定的printk的日志级别比console_loglevel小的话,那么这些日志就会打印到控制台上,否则就可能送到klogd或者 /proc/kmsg里头了。建议在调试的时候把console_loglevel设为最大,可以通过/proc/sys/kernel/printk来 设置。

   6、koops

    内核挂掉时不会“一走了之”,总会给我们留下一些“遗言”,这些遗言包括backtrace信息,寄存器信息之类。通过这些信息,我们就可以分析内核挂掉的原因!这个分析过程可能需要ksymoops, objdump之类的工具来辅助。

    7、kernel internal debugging tools & API
   
    实际上,除了上述工具外,内核也有提供很多其他的调试方法,比如kernel hacking菜单下的很多调试选项,包括magic sysrq,tracer等。
    除了这些调试选项,我们还有一些内核API可用,比如上面提到的printk,以及其他的比如BUG, WARN_ON, show_registers, show_regs, dump_stack/show_trace/show_stack, print_symbol等。
     这里介绍一下BUG的用法:

BUG(!irq_disabled());



    上面这句翻译成自然语言是:如果现在irq没有disabled的话就可能产生比较严重的内核故障,所以得马上报告一下,并退出。

    8、其他调试方法

    如果没有真实机器,我们可能用到一些emulator来调试。因为loongson2f跟mips的兼容非常好,如果你做的工作跟loongson2f的 特性没有关系(loongson2f的特性见其手册),那么就可以直接用qemu里头的mips/malta来模拟做开发了,这样可以加速开发过程,保护 硬件和文件系统等。
    这里是用qemu启动一个mips内核的例子:

$ qemu-system-mipsel -kernel vmlinux-2.6.26-1-4kc-malta -initrd initrd.gz -append "root=/dev/ram0 init=/bin/sh console=ttyS0 ramdisk_size=3000"  -nographic -M malta -hda /dev/zero


    如果要调试,请在上述命令的后面加上-s 和 -S,之后再用gdb来连接qemu开启的用于调试的1234端口进行调试,调试过程跟嵌入式开发中常用的gdb server+gdb的调试模式相同。

    类似地,为了增加调试过程的交互性,我们也可以用kgdb来调试内核,基本过程跟qemu+gdb类似,但是需要在内核里头开启kgdb支持。

    9、补充: 用netconsole远程记录系统启动日志(thx taohl from lemote.com)

    为了跟踪某些莫名的内核panic问题,还有一个类似串口调试的办法,那就是netconsole。大概用法:
    a. 在内核里头,把下面的选项选上,直接编译进内核而不是模块,编译完以后安装好内核和模块

       Device Drivers -->
           
  • Network device support  -->
                      
  • Network console logging support
        b. 配置和监测

           配置之前先明确两个东西,一个是被监测的系统(A),一个是用来监测的系统(B)。为了能够监测某个系统的启动过程,我们要在A里头配置一下内核启动选 项,即通过/boot/boot.cfg来配置;另外,我们要是B里头启动一个监测程序来记录A的启动日志。

        在A里头,配置一下/boot/boot.cfg,在内核的启动参数后面加上这些:
  • no_auto_cmd debug netconsole=A-port@A-ip/eth0,B-port@B-ip/B-mach_addr



        在B里头,启动一下netcat进行监测

    $ netcat -l -u -p B-port



        现在重启A,就可以在B里头看到A的启动日志了。
       
        提示:
        a. 如果想让监测程序在后台运行,可以用nohup来执行netcat,即

    $ nohup netcat -l -u -p B-port &


        b. 如果想不停的监测系统的启动过程以便捕获可能的内核panic,那么可以在/etc/rc.local里头添加一个reboot,为了能够记录所有信息, 可以通过dmesg -n 8把控制台终端级别设置为最小,另外,如果想记录X等启动过程,在reboot之前可以sleep上一段时间。


    三、优化工具

        okay,调试工具就介绍到这里,下面介绍几个辅助内核优化的工具:

        1、kft

        内核函数跟踪工具,可以用来跟踪某个应用程序触发的内核函数的执行过程和每个内核函数执行时间,进而找出最值得优化的函数进行优化。
     
       已经移植到loongson2f上的kft:
        http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_2027.html

        2、kgcov

        kgcov是内核的profiling工具,可以统计内核运行时的代码执行情况,从而辅助开发人员找出利于优化的hotspots。这个可以辅助kft来使用。

         已经移植到loongson2f上的kgcov:
        http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_2028.html

        3、ftrace

        ftrace原意就是function trace,功能有点类似kft,但是目前ftrace已经发展成了一个内核tracer框架,已经有诸如irqsoff, preemptoff, wakeup, sched_switch等tracer出现,而且ftrace已经进入了内核,而kft和kgcov目前还只是以补丁的形式发布。ftrace来自 RT_PREEMPT。

        目前支持loongson2f的ftrace也已经移植完成,近期不会对外发布。实际上ftrace的部分功能已经随着RT_PREEMPT for loongson2f发布,可以从这里下载到:
        http://lkml.indiana.edu/hypermail/linux/kernel/0903.1/00707.html

        4、oprofile 和 perf_counter

        oprofile已经非常成熟了,是利用处理器提供的硬件性能计数器进行profile的工具,不同于kft,ftrace,kgcov等通过修改内核本 身来实现,它是通过硬件辅助实现,所以对内核的影响比较小,而且因为能够提供代码行级别对应的硬件事件统计信息,因此它还可以为我们提供更直接的优化“建 议”。
       
        因为loongson2f提供了两个性能计数器,而且已经有了oprofile的内核支持,所以oprofile可以直接在loongson2f上使用,不过编译内核的时候记得选上oprofile支持。

        perf_counter有点类似oprofile,目前能够进行基本的性能计数,但是还没有oprofile那么强大,不过它很有可能发展成为一个内核 profiling的框架,进而让oprofile之类的工具工作在它之上。perf_counter也来自RT_PREEMPT,目前也已经移植到 loongson2f上,但是近期也不会对外发布。

    你可能感兴趣的:(优化,汇编,assembly,工具,profiling,linux内核)