Firefly开源社区

标题: linux内核调试常用方法 [打印本页]

作者: zouxf    时间: 2014-10-11 09:49
标题: linux内核调试常用方法
在firefly开发板内核调试中,碰到一下问题无法定位时,常常感觉束手无策,对于接触linux时间不长的朋友们更甚,其实lunix有一套很完整的调试方法,我稍稍整理一下。1.    查看内核log的两种方式
1.1.       接串口,使用kermit(linux)或者SecureCRT(windows),直接就能够看到完整的开机log和机器休眠时的log
1.2.       使用adb shell。
缺点:只能在机器开起来之后,打开USB调试,并且在连接USB之后,机器不会进入深度休眠。
命令: cat proc/kmsg &    或者 dmesg(不会实时更新)
2.    内核中添加调试信息  printk函数。
printk( ) 函数中可以指定日志的级别
#define KERN_EMERG   "<0>"  /* system is unusable           */
#define KERN_ALERT   "<1>"  /* action must be taken immediately    */
#define KERN_CRIT "<2>"  /* critical conditions      */
#define KERN_ERR  "<3>"  /* error conditions         */
#defineKERN_WARNING "<4>"  /* warning conditions       */
#define KERN_NOTICE  "<5>"  /* normal but significant condition    */
#define KERN_INFO "<6>"  /* informational         */
#define KERN_DEBUG   "<7>"  /* debug-level messages  */
值越小,其级别越高。
printk(KERN_WARNING“abcdefg\n”)指定了此日志的级别为KERN_WARNING
printk(“abcdefg\n”)没有指定日志的输出级别,则使用系统默认的日志级别DEFAULT_MESSAGE_LOGLEVEL
printk.c中,定义DEFAULT_MESSAGE_LOGLEVEL
/*printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
在内核驱动中,我们常使用的打印函数是
intdev_printk(const char *level,const struct device*dev, const char *fmt,...)
这个函数相比printk的好处是,它能够打印出设备和驱动名称的信息。
还有一个函数dev_dbg(dev,format, arg...)它实际上是一个dev_printk的简化宏
#ifdefined(DEBUG)
#define dev_dbg(dev,format, arg...)    \
   dev_printk(KERN_DEBUG,dev, format, ##arg)
这个函数指定了dev_printk的输出级别为KERN_DEBUG。由其定义可知,要使用此宏,必须在代码中#define DEBUG 1

在内核中,有几个与dev_dbg类似的函数,对应各自不同的日志级别。不过,使用下面这些函数,不需要#define DEBUG 1
define_dev_printk_level(dev_emerg, KERN_EMERG);
define_dev_printk_level(dev_alert, KERN_ALERT);
define_dev_printk_level(dev_crit,KERN_CRIT);
define_dev_printk_level(dev_err, KERN_ERR);
define_dev_printk_level(dev_warn, KERN_WARNING);
define_dev_printk_level(dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);
#define dev_info(dev, fmt,arg...) _dev_info(dev, fmt, ##arg)
另有两个在debug是很有用的函数
#define dev_WARN(dev, format,arg...) \
   WARN(1, "Device: %s\n" format, dev_driver_string(dev), ## arg);
#define dev_WARN_ONCE(dev,condition, format, arg...) \
   WARN_ONCE(condition, "Device %s\n" format, \
         dev_driver_string(dev), ## arg)
代码里面的对这两个函数的注释是:
*dev_WARN*() acts like dev_printk(), but with the key difference of using aWARN/WARN_ON to get the message out, including the file/line information and a backtrace.
它的日志中包含了文件名 行号 及堆栈等信息。而dev_WARN_ONCE则可以通过condition来指定输出日志的条件。
以上都是关于日志输出及其级别的,控制台通过输出日志级别控制来决定各级别的日志是否需要从控制台输出,例如串口等虚拟控制台。
#defineMINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#defineDEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
intconsole_printk[4] = {
   DEFAULT_CONSOLE_LOGLEVEL,   /* console_loglevel */
   DEFAULT_MESSAGE_LOGLEVEL,   /* default_message_loglevel */
   MINIMUM_CONSOLE_LOGLEVEL,   /* minimum_console_loglevel */
   DEFAULT_CONSOLE_LOGLEVEL,   /* default_console_loglevel */
};
console_printk4个数据分别对应
控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别。
可用下面的命令设置当前控制台输出日志级别:
# cat  /proc/sys/kernel/printk
# echo 8> /proc/sys/kernel/printk

关闭所有内核log输出
echo 0 >/proc/sys/kernel/printk
另一个比较有效控制log的方法的是使用module_param(debugLevel,int,0)
module_param可以在模块中传入参数,这个参数是可以在终端中通过
echo x >/sys/module/xxx/parameters/debugLevel 来修改的,因此,我们可以此来动态的控制单独某一个模块的log输出。例如:
#define tchip_dev_dbg(dbg_level, dev, format,arg...) \
do {                                      \
if(dbg_level <= debugLevel)       \
dev_info(dev, format, arg...)    \
}while(0)
C语言中有几个特定的宏,用来帮助代码定位的,通常编译器都是支持这些宏的。
__FILE__                       表示当前文件名
__FUNCTION__        表示当前函数名
__LINE__                       表示当前行数
1.  
2.  
3.  打印堆栈,回溯函数调用过程
在函数首加入dump_stack();即可。  
4.  查看机器进入休眠前的log
修改kernel/kernel/printk.c 中 intconsole_suspend_enabled = 0;使得休眠时,内核继续打印log。
5.  一些常用的命令(内核调试用)
3.        
4.        
5.        
5.1.       getevent                            (查看输入设备)
5.2.       iwconfig      iwlist  netcfg  (wifi调试)
5.3.       reboot loader            (机器重启并进入loder模式,升级模式)
5.4.       lsmod                  (查看系统加载的模块化的驱动)
5.5.       cat proc/wakelocks      (查看系统中当前的wakelocks)
5.6.       cat proc/clocks         (查看系统中的PLL频率)
5.7.       cat  proc/meminfo      (查看系统中的内存使用情况)
5.8.       cat proc/version          (查看内核版本,也可以查看内核的生成时间)

6.  实用技巧
6.   
6.1. 调试某些模块驱动时,采取模块编译,直接加载ko文件。可以避免频繁升级固件、开机,提高调试效率。步骤如下:
6.1.1. menuconfig中配置为模块M
6.1.2. 生成模块
make -C ~/rk29sdk/sdk21/kernel/ M=$(pwd) modules
6.1.3. 清除模块
make -C ~/rk29sdk/sdk21/kernel/ M=$(pwd) clean
6.1.4. andoird可一步执行编译并安装模块,如:
make -C ~/rk29sdk/sdk21/kernel/ M=$(pwd) modules && adb push rk29_headset.ko /flash/ && (adb shell rmmod rk29_headset.ko ; adb shell insmod /flash/rk29_headset.ko)

6.2.       对于某些器件,需要适时查询和修改寄存器值或者状态的,可以创建proc或sys文件,通过文件读写接口,实现对寄存器的操作。比较典型的应用就是codec调试中的codec_reg文件。
static ssize_t reg_show(struct device *dev,                         //读操作
       struct device_attribute *attr, char *buf)
{
       struct adxl34x *ac = dev_get_drvdata(dev);
       return sprintf(buf, "%u\n", ac->disabled);                    //返回其disabled值
}
static ssize_t reg_store(struct device *dev,                          //写操作
                              struct device_attribute *attr,
                              const char *buf, size_t count)
{
       struct adxl34x *ac = dev_get_drvdata(dev);
       int error;
       error = strict_strtoul(buf, 16, &val);
       if (error)
              return error;
       mutex_lock(&ac->mutex);
       AC_WRITE(ac, val >> 8, val & 0xFF);                        //写寄存器
       mutex_unlock(&ac->mutex);
       return count;
}
static DEVICE_ATTR(reg, 0644, reg_show, reg_store);
       ret = device_create_file(&rtd->dev, &dev_attr_reg);

作者: 暴走的阿Sai    时间: 2014-10-11 12:46
沙发,赞一个
作者: radxa    时间: 2014-10-17 11:29
很有用的,赞歌
作者: 东方青    时间: 2015-12-26 23:44
谢谢分享!!
作者: woody.lee    时间: 2016-3-30 16:47
謝謝分享
作者: xyh666168    时间: 2016-3-31 16:59
不错,楼主的多年linux、android开发经验的总结啊!
作者: dongyig    时间: 2020-4-10 15:38
111111111111111111111111111111111111
作者: cyu    时间: 2020-5-18 16:50
谢谢分享!!!!!!
作者: wzh598    时间: 2020-9-8 17:04
谢谢分享!!!!
作者: xiaoluo168899    时间: 2020-10-23 13:50

谢谢分享!!!!
作者: 圆木墩子    时间: 2020-11-9 09:17
看看咯
作者: 风清扬    时间: 2020-11-16 14:18
赞一个
作者: 繁_yh4uD    时间: 2020-11-19 09:53
11
作者: linuxjiahuo    时间: 2020-11-20 11:01
看看
作者: charle_duyw    时间: 2020-11-26 14:33
能否共享一下
作者: cjeeks    时间: 2020-11-30 13:01
谢谢分享!
作者: kwongchinang    时间: 2020-11-30 16:47
感谢楼主分享!
作者: 八龄先生    时间: 2020-12-2 19:14
咋屏蔽了呢?
作者: z2flood    时间: 2021-1-3 20:02
xiexie分享
作者: Brick    时间: 2021-1-4 17:55
看看
作者: hh3151128    时间: 2021-1-21 11:26
学习一下
作者: 大鲸鱼    时间: 2021-1-25 09:40
怎么作者账号都被删除了。。。。
作者: bigdaddy    时间: 2021-12-31 09:22
study
作者: norm9n    时间: 2022-1-4 14:34

作者: chaojidaotuguai    时间: 2022-1-7 13:52
感谢分享
作者: yku    时间: 2022-2-16 11:55

作者: wangchaoyu    时间: 2022-9-6 08:10
支持
作者: melon    时间: 2022-9-22 09:47
查看一下
作者: sleeping...    时间: 2023-11-6 15:41
优秀的少年




欢迎光临 Firefly开源社区 (https://dev.t-firefly.com/) Powered by Discuz! X3.1