kernel 内存泄漏和自旋锁调试

时间:2022-11-16 20:58:04
作者:下家山(请尊重原创,转载请注明) http://www.xiajiashan.com   一直以来我(每个从事linux开发的人)深受“bug”的困扰,好像“bug”不足以描述这种被问题困扰的无奈。因为当在驱动或BSP的开发过程中,所碰到的问题比解决一个bug难得多。

       Linux因其庞大,复杂(也许不是)至今并没有一个类似VB,VC,ADS等可视化调试工具,交叉编译环境的“麻烦”把很多想学linux的人挡在门外。      

       言归正传,下面介绍第一个调试技术

一:SLAB调试技术      1.1 SLAB调试的意义

       下面引用《linux设备驱动程序第三版》第四章的一段话:

       “这个重要的选项打开了内核内存分配函数的几类检查; 激活这些检查, 就可能探测到一些内存覆盖和遗漏初始化的错误. 被分配的每一个字节在递交给调用者之前都设成 0xa5, 随后在释放时被设成 0x6b. 你在任何时候如果见到任一个这种"坏"模式重复出现在你的驱动输出(或者常常在一个 oops 的列表), 你会确切知道去找什么类型的错误. 当激活调试, 内核还会在每个分配的内存对象的前后放置特别的守护值; 如果这些值曾被改动, 内核知道有人已覆盖了一个内存分配区, 它大声抱怨. 各种的对更模糊的问题的检查也给激活了。”

     1.2 CONFIG_DEBUG_SLAB在哪里打开

  书上说CONFIG_DEBUG_SLAB在kernel hacking配置菜单中

  #make menuconfig

 

kernel 内存泄漏和自旋锁调试

进入kernel hacking:

kernel 内存泄漏和自旋锁调试

 

但是并没有看到CONFIG_DEBUG_SLAB,

       这需要熟悉linux内核配置菜单的生成(自己去琢磨,顺藤摸瓜也不难发现)。

       如图:

kernel 内存泄漏和自旋锁调试

知道了吧!(不同的版本名字有不同)

=======================================================================================

By 下家山 QQ 408452271                                            Email:ximenpiaoxue4016@sina.com 

7/30/2012                                5/25                               7:20:06 PM

Linux Kernel 调试技术      上海索漫科技  http://www.xiajiashan.com       专注ARM+Linux

1.3 根本就没有相关选项

有些版本在make menuconfig 后进入kernel hacking根本就没有SLAB的相关选项,此时你需要仔细研究lib/kconfig.debug了,把SLAB相关选项打开。Linux-2.6.19中kernel hacking里面还有DEBUG_SLAB_LEAK(内存泄漏)功能,应该是一个很好的调试功能,我还没有去试,哪位有时间去试一下,烦请把结果贴出来共享。下面是linux-2.6.19下的kernel hacking菜单:

kernel 内存泄漏和自旋锁调试

 

Memory leak debugging需要在Debug slab memory allocations打开的情况下才显示。

=======================================================================================

By 下家山 QQ 408452271                                            Email:ximenpiaoxue4016@sina.com 

7/30/2012                                5/25                               7:20:06 PM

Linux Kernel 调试技术      上海索漫科技  http://www.xiajiashan.com       专注ARM+Linux

1.4验证0x5a和0x6b

“在将已分配内存返回给调用者之前,内核将把其中的每个字节设置成0xa5,而在释放后将其设置成0x6b,如果读者在自己驱动程序的输出中,或者在OOPS信息中看到上述“毒剂”字符,则可以轻松判断问题所在”

真是如此吗?下面是我做的实验:

我写了个单独的模块,这个模块首先申请一块空间,然后释放。成功申请后即打印这块空间的值,看是否为0x5a,释放后再一次打印这块空间的值,看其是否为0x6b。其代码如下:

#include

#include

#include    

unsigned char    *slab_wr;

static int test _init(void)

{

         int i;

   printk(KERN_EMERG "slab_wr address before call kmalloc:0x%08lx \n",slab_wr);

         slab_wr = (unsigned char  *)kmalloc(512, GFP_KERNEL);

    printk(KERN_EMERG "slab_wr address affter call kmalloc :0x%08lx \n",slab_wr);

         for( i=0; i<200; i++)

                   {

                             printk(KERN_EMERG "0x%x  ",*(slab_wr+i));

                   }      

    printk(KERN_EMERG "\nHello\n");       

    return 0;

}

 

static void test_exit(void)

{

   int i;

      kfree(slab_wr);

      for( i=0; i<200; i++)

                   {

                             printk(KERN_EMERG "0x%x  ",*(slab_wr+i));

                   }

    printk(KERN_EMERG"\nGoodbye,\n"); 

}

module_init(test _init);

module_exit(test _exit);

下面是运行结果

=======================================================================================

By 下家山 QQ 408452271                                            Email:ximenpiaoxue4016@sina.com 

7/30/2012                                5/25                               7:20:06 PM

Linux Kernel 调试技术      上海索漫科技  http://www.xiajiashan.com       专注ARM+Linux

kernel 内存泄漏和自旋锁调试

 

证明完毕。

1.5 验证越界

         还是刚才那个程序:

//slabtest.c

#include

#include

#include    

unsigned char    *slab_wr;  /* I/O data buffer             */

static int slabtest_init(void)

{

         int i;

   printk(KERN_EMERG "slab_wr address before call kmalloc:0x%08lx \n",slab_wr);

         slab_wr = (unsigned char  *)kmalloc(100,GFP_USER/*GFP_KERNEL*/);

    printk(KERN_EMERG "slab_wr address affter call kmalloc :0x%08lx \n",slab_wr);

         for( i=0; i<101; i++)

                   {

                             *(slab_wr+i)=0xcc;

                   }

#if 0

         for( i=0; i<200; i++)

                   {

                             printk(KERN_EMERG "0x%x  ",*(slab_wr+i));

                   }

#endif

    printk(KERN_EMERG "\nHello\n");

        

    return 0;

}

=======================================================================================

By 下家山 QQ 408452271                                            Email:ximenpiaoxue4016@sina.com 

7/30/2012                                5/25                               7:20:06 PM

Linux Kernel 调试技术      上海索漫科技  http://www.xiajiashan.com       专注ARM+Linux

 

static void slabtest_exit(void)

{

   int i;

      kfree(slab_wr);

#if 0

      for( i=0; i<100; i++)

                   {

                             printk(KERN_EMERG "0x%x  ",*(slab_wr+i));

                   }

#endif

    printk(KERN_EMERG"\nGoodbye\n");

 

        

}

module_init(slabtest_init);

module_exit(slabtest_exit);

       运行结果

kernel 内存泄漏和自旋锁调试

即看到了”毒剂”字符

 

二:SPINLOCK调试       2.1 spin_lock调试作用

       打开该选项,内核将捕获对未初始化自旋锁的操作,也会捕获两次解开同一锁的操作等其他错误。

kernel 内存泄漏和自旋锁调试
2.2 捕获未初始化自旋锁        2.2.1实验代码:

       //spinlock.c

#include <linux/init.h>

#include <linux/module.h>

#include  <linux/spinlock.h>

unsigned char aa;

   spinlock_t SpinLockTest ;

static int spinlock_init(void)

{

       int i;

       unsigned long flags;

       // spin_lock_init(&SpinLockTest);//进行未初始化自旋锁检测

        spin_lock_irqsave(&SpinLockTest, flags);

        aa =1;

       spin_unlock_irqrestore(&SpinLockTest, flags);

    printk(KERN_EMERG "\nHello\n");  

    return 0;

}

 

=======================================================================================

By 下家山 QQ 1209050967       上海松江文汇路928号258室    松江大学城  7/31/2012  

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训 

 

static void spinlock_exit(void)

{

    printk(KERN_EMERG"\nGoodbye\n");     

}

module_init(spinlock_init);

module_exit(spinlock_exit);

2.2.2 运行结果

 

kernel 内存泄漏和自旋锁调试
2.3.1  代码

       //spinlock.c

#include <linux/init.h>

#include <linux/module.h>

#include  <linux/spinlock.h>

unsigned char aa;

   spinlock_t SpinLockTest ;

static int spinlock_init(void)

{

       int i;

       unsigned long flags;

       spin_lock_init(&SpinLockTest);

        spin_lock_irqsave(&SpinLockTest, flags);

        aa =1;

       spin_unlock_irqrestore(&SpinLockTest, flags);

    printk(KERN_EMERG "\nHello\n");  

    return 0;

}

static void spinlock_exit(void)

{

     printk(KERN_EMERG"\nGoodbye\n");    

}

=======================================================================================

By 下家山 QQ 1209050967       上海松江文汇路928号258室    松江大学城  7/31/2012  

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训

 

module_init(spinlock_init);

module_exit(spinlock_exit);

2.3.2 运行结果
kernel 内存泄漏和自旋锁调试
2.4 如果不打开spin_lock调试功能

       如果不打开spin_lock调试功能,则检测不到自旋锁未初始化错误。

2.4.1实验代码      

测试代码与2.2.1实验代码同。

2.4.2实验结果

       测试结果与2.3.2 运行结果同。

2.5 检测两次解开同一锁         2.5.1 关闭此功能

       即在”kernel hacking”中不选择“spinlock debugging”,看能否检测两次解开同一锁的错误。

      ●测试代码

       //spinlock.c

#include <linux/init.h>

#include <linux/module.h>

#include  <linux/spinlock.h>

unsigned char aa;

   spinlock_t SpinLockTest ;

static int spinlock_init(void)

{

       int i;

       unsigned long flags;

       spin_lock_init(&SpinLockTest);

        spin_lock_irqsave(&SpinLockTest, flags);

        aa =1;

       spin_unlock_irqrestore(&SpinLockTest, flags);

        spin_unlock_irqrestore(&SpinLockTest, flags);

    printk(KERN_EMERG "\nHello\n");  

    return 0;

}

static void spinlock_exit(void)

{

       printk(KERN_EMERG"\nGoodbye\n");     

}

module_init(spinlock_init);

module_exit(spinlock_exit);

=======================================================================================

By 下家山 QQ 1209050967       上海松江文汇路928号258室    松江大学城  7/31/2012  

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训

kernel 内存泄漏和自旋锁调试

检测不到此错误!

2.5.2 打开此功能

       即在”kernel hacking”中选择“spinlock debugging”,看能否检测两次解开同一锁的错误。

      ●测试代码

       //spinlock.c

#include <linux/init.h>

#include <linux/module.h>

#include  <linux/spinlock.h>

unsigned char aa;

   spinlock_t SpinLockTest ;

static int spinlock_init(void)

{

       int i;

       unsigned long flags;

       spin_lock_init(&SpinLockTest);

        spin_lock_irqsave(&SpinLockTest, flags);

        aa =1;

       spin_unlock_irqrestore(&SpinLockTest, flags);

        spin_unlock_irqrestore(&SpinLockTest, flags);

    printk(KERN_EMERG "\nHello\n");  

    return 0;

}

static void spinlock_exit(void)

{

       printk(KERN_EMERG"\nGoodbye\n");     

}

module_init(spinlock_init);

module_exit(spinlock_exit);

●运行结果
kernel 内存泄漏和自旋锁调试

检测到了此错误!