简图记录-驱动debug之打印总结(printk、dmesg、logcat)

简图记录总结~

        在驱动开发过程中,常会用到一些打印做问题定位,无论是提前设计或者调试过程中添加,打印都是一种常用的手段。以下为打印相关问题总结。

一、常见驱动打印的添加与查看。

1、内核打印printk

    概念:printk是内核中根据日志级别向控制台输出显示的函数

    用法:用法为printk(消息级别"a=%d\n",a);和printf用法类似。消息级别可省略。消息级别定义参考(0:系统崩溃前、1:系统必须响应的错误处理、2:严重错误、3:一般错误、4:警告、5:提醒、6:提示信息、7:调试信息)

    查看打印等级:使用cat /proc/sys/kernel/printk---->7 4 1 7;第一个等级7表示 控制台当前日志级别(必须要更高的级别才能打印出来);第二个等级4表示默认的消息级别(当printk没有指定打印等级时默认的打印等级);第三个等级1表示最低控制台级别;第四个等级7表示默认中断控制级别;

    设置打印等级:echo 目标级别 >/proc/sys/kernel/printk;

    常见无法打印原因:1、打印级别低于系统设置 2、控制台输出未指定到串口上(boot下bootargs中未设置控制台到串口,如console=ttySAC0;kernel下可以通过cat /proc/cmdline查看bootargs)

2、查看内核缓存打印buffer信息dmesg

    概念:linux系统中用于查看内核ringbuffer的信息(开机信息和printk信息都可以查看)

    用法:(1)直接输入dmesg,打印buffer中缓存所有信息,利用管道组合grep可以查找带XXX信息打印,dmesg | grep XXX ;(2)dmesg -C 显示的同时清空buffer数据,重新开始记录 (3)dmesg -n 打印级别,设置记录控制台输出最低级别

3、Android环境下日志控制logcat(用户态打印)

    概念:Android环境下可以通过logcat进行用户态的打印控制;

    用法:(1)直接输入logcat,默认持续打印所有信息(会阻塞输入),同样可以配合管道和grep指定输出打印,如logcat | grep XXX;(2)logcat -d,打印一次缓存信息到控制台输出(非阻塞);(3)logcat -c,清空打印缓存信息;(4)logcat 模块1:指定打印等级 模块2:指定打印等级,可以选择性的约束指定模块输出等级;

二、常见打印调试技巧

1、利用编译宏添加通用打印信息

    如 __LINE__行号、__FUNCTION__函数名

2、通过current指针打印进程上下文信息

在用户态或者内核态的进程上下问中利用current信息,打印当前进程号和进程名:printk("the process name=%s pid=%i\n",current->comm,current->pid);

3、打印函数调用堆栈信息

    (1)内核态直接在指定调用处添加dump_stack();

    (2)用户态C/C++用系统调用打印,参考实例如下(注意编译时带-rdynamic把符号带入动态链接标,否则只打印地址无 函数名称)

void mybacktrace()
{
    void *buffer[100];
    char **str=NULL;
    int i=0;
    int ptr_num = backtrace(buffer,100);//获取指定最大100个堆栈信息;
    str = backtrace_symbols(buffer,ptr_num);//转化为字符串
    if (NULL == str)return;
    for(i=0p;i<ptr_num;i++)printf("%s\n",str[i]);
    free(str);
}

    (3)用户态java代码添加,利用Exception对象

Exception e = new Exception("***dump stack****");
e.printStackTrace();

三、常见打印注意事项

1、注意打印会引入延时

特别是在中断等对延时敏感场景,打印信息更要简洁明了。。(调试一些耗时敏感的地方一定要注意打印注释掉后是否还符合预期)

2、频繁密集内核打印(如中断)可能挂死系统

不要一直在中断打印大量信息,在中断中过多打印信息会导致过长耗时引起系统响应缓慢、丢中断、甚至挂死系统等问题可以在状态变化时打印(可以加入计数信息),或者计数降频。

3、用户态和内核态打印是并发的

用户态和内核态打印是并发输出的,如果打印位置接近会出现A打印被B打印中断后继续输出甚至覆盖的的情况,在设计打印的时候要留意这一点。或者 使用过程中,直接把内核打印放到后台通过dmesg查看 并使用telnet循环抓取,用户态 直接串口输出记录(确定是无法体现两者同步关系)。

四、打印实现分析:

1、变长参数

     概念:C语言规定变长参数至少存在一个明确的参数,且变长参数必须处在所有参数最后,如:

        int printf(const char *format,...);--->尾部的...表示占位符

    实现参考(用man查看帮助)

        void va_start(va_list ap,last);在最开始变参处理前调用,ap指向最后一个明确参数,取得第一个变参起始地址

        [type] va_arg(va_list ap, [type]);每次调用返回ap指向type类型数据,并将ap指向下一个参数地址

        void va_end(va_list ap);结束变参访用调用(常为空)   

typedef char* va_list;
#define va_size(type)\
    (((sizeof(type)+sizeof(long)-1/sizeof(long)*sizeof(long)) //引入long为编译对齐处理
#define va_start(ap,last)\
((ap)=(va_list)&(last)+va_size(last))
#define va_arg(ap,type)\
(*(type*)((ap)+=va_size(type),(ap)-va_size(type)))
#define va_end(ap)

    变参用法

//使用va_arg逐个取出指定类型,打印n个int型变参
void test_print(int n,...)
{
   int i=0;
   va_list ap;
   va_start(ap,n);
   for (i=0;i<n;i++)
   {
       int tmp = va_arg(ap, int);
       printf("%d num=%d\n",i,tmp);
   }
   va_end(ap)
}
\\结合vsprintf获取字符串打印,制作自己的打印函数
void my_printf(char *fmt,...)
{
   char tmp[100];
   va_list ap;
   va_start(ap,fmt);
   vsprintf(tmp,fmt,ap);
   va_end(ap);
   printf("%s\n",tmp);
}

2、C库printf族函数分析

/*1.<stdio.h>相关打印函数*/
int printf(const *fmt,...);//定义变参,使用vsprintf将变参转化为字符串,再利用终端write向标准输出写数据打印
int fprintf(FILE *stream,const *fmt,...);//定义变参,使用vfprintf将变参输出到文件
int sprintf(char *str,const *fmt,...);//定义变参,使用svprintf将变参转化字符串到buffer中
int snprintf(char *str,size_t size ,const *fmt,...);//定义变参,使用svnprintf将变参转化字符串到buffer中
/*2.<stdarg.h>相关变参处理函数*/
int vprintf(const *fmt,va_list ap);//利用vsprintf将变参转化为字符串,再利用fwrite向标准输出写数据打印
int vfprintf(FILE *stream,const *fmt,va_list ap);//使用vsnprintf将变参转化字符串,并同fwrite将buffer写入文件
int vsprintf(char *str,const *fmt,va_list ap);//解析fmt格式信息和变参ap拼接生成字符串
int svnprintf(char *str,size_t size , const *fmt,...);//类似vsprintf,加入长度校验

 

 

 

 

 

 

 

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值