ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

转载printk 调试

2022-01-27 21:02:28  阅读:242  来源: 互联网

标签:pr __ fmt dev printk 转载 调试 define


5. printk的格式字符

常见的数据类型对应的printk的格式字符如下:

    int %d or %x
    unsigned int %u, %x
    long %ld, %lx
    unsigned long %lu, %lx
    long long %lld, %llx
    unsigned long long %llu, %llx
    size_t %zu, %zx
    ssize_t %zd, %zx

 

5.1 打印指针

使用 %p 来打印指针, 但是在printk里面对此进行了扩展。

  • %pS, %pF 打印对应的符号名/函数名和偏移量
  • %ps, %pf 打印对应的符号名/函数名

5.2 打印受限的内核地址

使用 %pK来打印受限的内核地址

  • %pK 且 /proc/sys/kernel/kptr_restrict = 0 时可直接打印
  • %pK 且 /proc/sys/kernel/kptr_restrict = 1 时所有的地址打印为0(除非配置了CAP_SYSLOG)。
  • %pK 且 /proc/sys/kernel/kptr_restrict = 2 时所有的地址打印为0。

kptr_restrict = 0,所有用户都可以读取内核符号地址。

kptr_restrict = 1,普通用户无法读取内核符号地址, root用户可以查看。

kptr_restrict = 2,所有用户都无法读取内核符号地址。

5.3 打印结构资源

  • %pr 打印结构体资源
  • %pR 打印结构体资源,包含一个解码标记

打印物理地址

  • %pa

5.4 打印raw buffer(64字节以下,较大的buffer应使用print_hex_dump)

  • %*ph ,空格分割,如 00 01 02
  • %*phC, 冒号分割,如 00:01:02
  • %*phD ,横杠分割,如 00-01-02
  • %*phN ,无分割符,如 000102

5.5 打印MAC/FDDI

  • %pM,冒号分割, 如 00:01:02:03:04:05
  • %pMR ,冒号分割,反序, 如 05:04;03;02:01:00
  • %pMF, 横杠分割, 如 00-01-02-03-04-05
  • %pm ,无分割, 如 000102030405
  • %pmR ,无分割, 反序,如 0504030201

5.6 打印ipv4地址

  • %pI4, 1.2.3.4的形式
  • %pi4, 001.002.003.004的形式
  • [hnbl] 附加的’h’ ‘n’ ‘b’ ‘l’ 用于指定参数的字节序是主机字节序还是网络字节序, 或者是大端对齐还是小端对齐。 printk 默认地址是网络字节序的, 并且自动转换为主机字节序再打印, 不需要做额外的字节序转换

5.7 打印ipv6地址

  • %pI6 ,0001:0002:0003;0004;0005;0006;0007:0008 的形式
  • %pi6, 001002003004005006007008 的形式
  • %pI6C, 1;2;3:4;5;6:7:8 的形式

5.8 打印 UUID/GUID

  • %pUb 小写字母大端序
  • %pUB 大写字母大端序
  • %pUl 小写字母小端序
  • %pUL 大写字母小端序

5.9 打印结构体

  • %pV 打印结构的各个成员的名称和值

6. 基于 printk 的宏

每次使用printk都要指定log level太过于麻烦, 内核定义了一组宏。

    #ifndef pr_fmt
    #define pr_fmt(fmt) fmt
    #endif
    #define pr_emerg(fmt, ...) \
    printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_alert(fmt, ...) \
    printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_crit(fmt, ...) \
    printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_err(fmt, ...) \
    printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_warning(fmt, ...) \
    printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_warn pr_warning
    #define pr_notice(fmt, ...) \
    printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_info(fmt, ...) \
    printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_cont(fmt, ...) \
    printk(KERN_CONT fmt, ##__VA_ARGS__)

 

7. pr_debug, pr_devel

内核还定义了一组打印宏pr_devel在build debug版kernel时才会有效, 但是也可用DEBUG宏来开启。

    #ifdef DEBUG
    #define pr_devel(fmt, ...) \
    printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #else
    #define pr_devel(fmt, ...) \
    no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #endif

 

还有一组宏pr_debug, 与pr_devel相比,它还能用于dynamic debug。

    #if defined(DEBUG)
    #define pr_debug(fmt, ...) \
    printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #elif defined(CONFIG_DYNAMIC_DEBUG)
    /@@* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */
    #define pr_debug(fmt, ...) \
    dynamic_pr_debug(fmt, ##__VA_ARGS__)
    #else
    #define pr_debug(fmt, ...) \
    no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #endif

 

8. 防止printk刷屏

在频繁被执行的地方, 插入 printk 会导致输出大量消息, 刷掉其它的消息, 为了控制printk输出的次数,可以使用printk_ratelimited(…)。

需要 “#include ”

printk_ratelimited(…)会保证每一条在5秒钟内输出次数不大于10次, 如果需要更精细的频率控制, 可以设置 DEFINE_RATELIMIT_STATE 宏并使用__ratelimit函数。

“/proc/sys/kernel/printk_ratelimit” 定义了消息之间的最小时间间隔, “/proc/sys/kernel/printk_ratelimit_burst”定义了消息的数量,即在 printk_ratelimit 秒内最多打印 printk_ratelimit_burst 条消息。

每一个level的printk还有 “pr_err_ratelimited”, “pr_debug_ratelimited”, “pr_info_ratelimited”这些对应的宏。

如果只需要打印一次的话, 可以使用printk_once(…) 。

9. dev_xxx()

在开发设备驱动的过程中, 在输出消息时, 往往希望能够附带device相关的信息, 例如 device name等,可以使用 dev_printk()。

  1. int dev_printk(const char *level, const struct device *dev, const char *fmt, ...);

dev_printk() 能够额外输出设备的 driver name 或者 bus name, device name。如果对这些输出内容不满意的话, 可以基于__dev_printk()自己封装一个消息输出函数, dev_printk()正是基于该函数的封装。

如果希望在 DEBUG 版中才输出消息, 或者是dynamic debug, 则可以使用 dev_dbg()。

    #if defined(CONFIG_DYNAMIC_DEBUG)
    #define dev_dbg(dev, format, ...) \
    do { \
    dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
    } while (0)
    #elif defined(DEBUG)
    #define dev_dbg(dev, format, arg...) \
    dev_printk(KERN_DEBUG, dev, format, ##arg)
    #else
    #define dev_dbg(dev, format, arg...) \
    ({ \
    if (0) \
    dev_printk(KERN_DEBUG, dev, format, ##arg); \
    0; \
    })
    #endif

 

同样, 有限制输出频率的不同消息等级的 dev_xxx() 宏。

    #define dev_emerg_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_emerg, dev, fmt, ##__VA_ARGS__)
    #define dev_alert_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_alert, dev, fmt, ##__VA_ARGS__)
    #define dev_crit_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_crit, dev, fmt, ##__VA_ARGS__)
    #define dev_err_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_err, dev, fmt, ##__VA_ARGS__)
    #define dev_warn_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_warn, dev, fmt, ##__VA_ARGS__)
    #define dev_notice_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__)
    #define dev_info_ratelimited(dev, fmt, ...) \
    dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__)

 

DEBUG版 和 dynamic debug 版本也可以限制输出频率, dev_dbg_ratelimited()

    #if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
    #define dev_dbg_ratelimited(dev, fmt, ...) \
    do { \
    static DEFINE_RATELIMIT_STATE(_rs, \
    DEFAULT_RATELIMIT_INTERVAL, \
    DEFAULT_RATELIMIT_BURST); \
    DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
    if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \
    __ratelimit(&_rs)) \
    __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
    ##__VA_ARGS__); \
    } while (0)
    #else
    #define dev_dbg_ratelimited(dev, fmt, ...) \
    no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #endif

 

10. 在用户空间打印内核消息

“/dev/kmsg“ 设备提供了在用户空间输出内核消息的途径,可以使用 mknod -m 600 /dev/kmsg c 1 11 命令来创建一个。

 echo "Hello Kernel-World" > /dev/kmsg

 

或者还可以使用前置数字来指明消息等级。

# echo "2Writing critical printk messages from userspace" >/dev/kmsg

 

数字2等于 KERN_CRIT 的等级。

使用 dmesg -u 可以看到所有从用户空间打印的的内核消息。

11. 封装 printk 打造一个专属的打印函数

每次调试的时候都要加行号,加函数名调试,实在是太麻烦了,为什么不自己封装一个专属的 print log 的函数呢?

#define log_info(fmt, arg...) printk(KERN_INFO "[%s][%d] "fmt"", __func__, __LINE__, ##arg);

 

其中 _func_ _LINE_ 分别表示当前函数名和行号。

12. 查看printk输出的消息

在用户空间, 可以使用如下几种方式查看 内核消息

  • dmesg 命令
  • cat /proc/kmsg (不会返回, 会一直等待并输出新的内核消息, 再次之前的消息不会输出)
  • cat //var/log/syslog

dmesg是最常用的方式, 使用dmesg命令时可以加一些控制参数

  • -C 清空存放内核消息的环形缓冲区
  • -c 列出内核消息然后清空环形缓冲
  • -k 仅仅打印从内核中输出的内核消息
  • -u 仅仅打印从用户空间打印的内核消息
  • -n 调整将被打印到控制台的消息的等级
  • -s 设置环形缓冲区的大小

标签:pr,__,fmt,dev,printk,转载,调试,define
来源: https://www.cnblogs.com/codestack/p/15851215.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有