一、内核日志基本框架

1.内核日志通过printk函数实现的,它与用户空间对应的函数printf具有同样的作用,

内核会创建一个__log_buf环形缓冲区保存日志信息,定义在[kernel/printk/printk.c]

static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);

用户态可以通过Syslog相关的系统调用或者/proc文件以及/dev/kmsg设备节点来查看_log_buf的信息,这些操作都是通过do_syslog的系统调用接口来实现的。

2.printk()

2.1.Kernel API printk() 是整个Kernel Log机制的基础API,几乎所有的Log方式都是基于printk来实现的。

Printk的格式如下:int printk( const char * fmt, ... );

其中,fmt是一个定义文本和格式的字符串,其后面的参数列表是输出格式中需要输入的可变个数参数。

通过 printk 实现的日志是通过内核配置选项CONFIG_PRINTK激活的。虽然 CONFIG_PRINTK一般都是激活的,但是不包含这个选项的系统对内核的调用会返回一个ENOSYS 错误返回值。

2.2.printk()实现

流程大致如下图所示:

printk的流程大致可以分为两步:

a)将所有Log输出到内核的Log buffer,该Log Buffer是一个循环缓冲区,其地址可以在内核中用log_buf变量访问。

b)根据设定的Log级别决定是否将Log输出到Console。[接口:call_console_drivers]

所以我们打印的log会走向两个位置:

a)Log Buffer,该Buffer的内容可以在user space通过/proc/kmsg来访问。

b)Console,Console的实现可以有很多,目前我们用到的有UART Console和RAM Console。通向UART Console的log会在对应的UART端口打印出来。而RAM Console它可以记录重启前最后时刻的一段log,7KB~9KB的空间,可以通过/proc/last_kmsg来访问。【last_kmsg和ram console - bobfly1984 - 博客园】

对于console log,不可避免的对系统的性能有损失,尤其是像UART Log这种收到硬件传输效率影响的。所以对于console log设置了两道关卡。第一个是对Log级别进行过滤,只能输出高优先级的log;第二个是为UART Console设置单独的开关,在不必要的时候可以将其关闭以提高系统性能。

————————————————

2.3.日志级别

内核允许每一个消息根据日志级别(定义不同消息重要必的八种级别之一)来分类。这些级别可以用来判断系统是否不可用(紧急消息)、是否发现严重状况(严重消息)或者是否为简单报告消息。这个内核代码直接将日志级别定义消息的第一个参数。

例如下面的代码:

printk( KERN_CRIT "Error code %08x.\n", val );

如果调用者未将日志级别提供给printk,那么系统就会使用默认值KERN_WARNING(表示只有KERN_WARNING 级别以上的日志消息会被记录。)

系统默认的优先级可以通过读取/proc文件来得到,如下所示:

D:\>adb shell cat /proc/sys/kernel/printk

6       4       1       6

读取的这4个数字的含义分别是:

a)第一个参数表示console log level,即只有优先级大于这个级别的Log才可以打印到Console。

b)第二个参数表示默认Log级别,即打印Log未明确指定Log level的时候默认采用的Log级别。

c)第三个参数表示最小可用的Log Level,用于do_syslog()系统调用。

d)第四个参数表示默认的Console Log Level,未找到使用的地方。

Console Log 输出控制

我们还可以通过/proc/sys/kernel/printk设置console_loglevel来达到控制 console log 输出的目的,方法如下(修改这个参数需要有root权限):

D:\>adb shell "echo 8 > /proc/sys/kernel/printk"

D:\>adb shell cat /proc/sys/kernel/printk

8       4       1       6

通过echo命令将console_loglevel设置8,即所有级别的log(因为定义的最低优先级的Log Level是7)都可以输出到console。通过这个命令也可以禁止一些低优先级的log输出到console,只要将console_loglevel的值设置小一些即可。

UART Console 的控制

UART Console单独设置了开关,主要是因为其对系统性能的影响比较大,因为UART Console是同步的方式通过硬件以固定的传输速率印Log,如果Log量比较大的时候,会引起较多的Performance问题,如UI卡顿等。另外由于往UART Console印Log会关闭中断,严重的时候可能会引起系统无法响应或者重启。

也正因为此,我们在user版本上面默认是关闭UART Console Log的。

可以使用如下命令在runtime控制UART Console 的打开和关闭(执行该命令需要有root权限):

D:\>adb shell "echo 1 > /sys/module/printk/parameters/disable_uart"

D:\>adb shell "echo 0 > /sys/module/printk/parameters/disable_uart"

通过将disable_uart参数设置为1来关闭UART Console,反之通过将disable_uart参数设置为0来打开UART Console。

二、kernel调试方法

Linux内核调试方法总结 - FGQ的开源人生 - OSCHINA - 中文开源技术交流社区

Linux Kernel - Debug Guide (Linux内核调试指南 ) - zengkefu - 博客园

1.打印调试printk使用

1.1 在内核常用一些变形实现,例如:pr_xxx  ,dev_xxx

include\linux\printk.h定义pr_err,pr_warn ,pr_info

drivers\base\core.c 通过宏实现define_dev_printk_level

define_dev_printk_level(_dev_err, KERN_ERR);

define_dev_printk_level(_dev_warn, KERN_WARNING);

define_dev_printk_level(_dev_info, KERN_INFO);

1.2 常用打印标签

--printk("%s%d\n", __func__, __LINE__);     打印函数名,行号

%p可打印裸指针(raw pointer)

%pF可打印函数指针的函数名和偏移地址

%pf只打印函数指针的函数名,不打印偏移地址。

如:printk("%pf %pF\n", ptr, ptr) will print:

module_start module_start+0x0/0x62 [hello]

说明文档:

Documentation\core-api\printk-formats.rst

Documentation\translations\zh_CN\core-api\printk-formats.rst  中文

2.proc/kmsg ,dev/kmsg 读取实时log

3.dmesg

dmesg 命令也可用于打印和控制内核环缓冲区。这个命令使用 klogctl 系统调用来读取内核环缓冲区,并将它转发到标准输出(stdout)

获取开机log

Dmesg -w  实时监控并输出

搭配grep命令:

-i:在搜索的时候忽略大小写

-n:显示结果所在行号

-E:使用扩展正则表达式,而不是基本正则表达式,在使用”-E”选项时,相当于使用egrep

grep -iE "a|b"

4.使用/proc文件系统

通过在proc目录创建文件节点的形式获取我们需要输出的内核执行信息

接口:

example_dir=proc_mkdir("procfs_example",NULL);

Create_proc_entry("example_file",0666,example_dir);

5.OOPS & Panic

5.1 大部分错误都是因为对NULL指针取值或因为使用了其他不正确的指针值,这些错误通常会导致一个oops消息,这个消息会完整告诉了内核出错信息,出错的原因。,

panic的程度显然是高于oops的   内核panic后,就死机了,俗称内核崩溃。但是内核报oops,这个时候不见得会panic,它可能只是报个oops,杀死进程而已。oops不一定panic,中断上下文的oops会panic,如果panic_on_oops设置为1,一律panic。

下面例子是访问了一个NULL指针,出错的PC指针位置是globalfifo_read + 0x50/0x200(flobalfifo_read 最开始一条指令偏移0x50的位置,flobalfifo_read 整个代码段加起来是0x200) ,之后会列出完整的backtrace。

5.2 dump_stack()这个函数来追踪函数调用关系。可用来定位Kernel Panic和Oop的问题,配合objdump和addr2line可以定位到哪一行的哪句代码出现异常

BUG_ON()执行后抛出Oops

WARN_ON()执行后抛出栈回溯,不会panic

6.调试工具  prebuilts/gcc/linux-x86/arm/arm-linux-gnueabihf/bin

6.1 arm-linux-androideabi-addr2line

把程序地址转换为文件名和行号。在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出在给出的地址上是哪个文件以及行号。

应用场景:调试内核的时候,内核崩溃了,此时可以根据各种日志和打印中的偏移内存地址,然后根据这个地址定位到具体是哪个文件的哪一行

//通过arm-linux-gnu-addr2line --help可以查看该命令的用法

XXX$ arm-linux-gnu-addr2line -e output/build/vmlinux -C -f  8050e7bc

output/build/linux-20160205B/drivers/modules/dev_linux.c:900 可以知道是dev_linux.c的900行

6.2 arm-linux-androideabi-objdump     反汇编器,将可执行程序反汇编为汇编代码

arm-linux-gnueabihf-objdump -d -l -f -g -S drivers/usb/gadget/udc-core.ko > udc-core.log

arm-linux-gnueabihf-objdump -d -l -f -g -S vmlinux > vmlinux.log

或者通过start-address 和stop-address反汇编地址范围的内容

6.3 arm-linux-androideabi-gdb         调试工具,当应用程序发生核心错误(段错误)时,使用该工具分析生成的core文件可得到报错信息

arm-linux-androideabi-gdb vmlinux   加载符号表

输入list *函数名+函数偏移地址即可查看特定位置代码

可以正常使用gdb各种操作指令

6.4 arm-linux-androideabi-nm          列出目标文件中的符号

arm-linux-androideabi-nm vmlinux |grep 函数名

返回:函数首地址  T  函数名

通过首地址加偏移量得到异常地址,然后通过addr2line定位

6.5 Trace32集成化调试工具

可以加载符号表和coredump文件根据异常地址或者函数来分析汇编指令,同时可以添加对应源码

7.内核模块源码级调试

kgdb提供了一种使用 gdb调试 Linux 内核的机制。使用KGDB可以象调试普通的应用程序那样,在内核中进行设置断点、检查变量值、单步跟踪程序运行等操作。使用KGDB调试时需要两台机器,一台作为开发机(Development Machine),另一台作为目标机(Target Machine),两台机器之间通过串口或者以太网口相连

8.其他内核debug选项和功能

8.1 no_console_suspend

在suspend的时候console 不进行suspend,否则console suspend之后其他driver在suspend 过程中印的log都显示不出来,因此加这个参数一般用于调试suspend 和 resume。在bootargs中添加"no_console_suspend"参数。

8.2 SLUB_DEBUG   kmemleak  --->内存检测

Linux常见的内存访问错误有:越界访问(out of bounds)、访问已经释放的内存(use after free)、重复释放、内存泄露(memory leak)、栈溢出(stack overflow)等。

(a)、SLUB_DEBUG 内核中小块内存大量使用slab/slub分配器,slub_debug提供了:访问已经释放的内存、越界访问、重复释放内存等功能检测。

内核配置

重新配置kernel选项,打开如下选项即可。

CONFIG_SLUB=y

CONFIG_SLUB_DEBUG=y

CONFIG_SLUB_DEBUG_ON=y

8.3 kmemleak是内核提供的一种检测内存泄露工具,启动一个内核线程扫描内存,并打印发现新的未引用对象数量。

使用kmemlieak,需要打开如下内核选项。

CONFIG_HAVE_DEBUG_KMEMLEAK=y

CONFIG_DEBUG_KMEMLEAK=y

CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400

# CONFIG_DEBUG_KMEMLEAK_TEST is not set

CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y---------或者关闭此选项,则不需要在命令行添加kmemleak=on。

8.4 DEBUG_ATOMIC_SLEEP  -->检测在spinlock,中断,软中断中的sleep

Linux kernel log与调试相关推荐

  1. linux kernel内核、驱动日志调试方法(一)

    本文是对网络资料进行总结归纳,抄录其他博客资料,如有侵权,请告知,进行删除 一:通过打印调试printk printk是打印内核消息的函数,printk通过附加不同日志级别(loglevel)或者说消 ...

  2. linux 内核调试信息在哪里,Linux kernel debug技巧----开启DEBUG选项

    Linux kernel debug技巧----开启DEBUG选项 作者:wowo 发布于:2016-11-1 19:39 分类:Linux应用技巧 kernel的source code中有很多使用p ...

  3. Linux kernel 不输出log信息

    /********************************************************************************** Linux kernel 不输出 ...

  4. [linux kernel] 内核下ksz8081驱动调试

    系统版本:Ubuntu18.04-64 编译器版本:gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) uboot版本:2018.07 - ...

  5. Linux Kernel - Debug Guide (Linux内核调试指南 )

    linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 ...

  6. eclips调试linux内核,使用Eclipse调试Qemu及Linux Kernel

    1. 下载Qemu代码 1. 下载版本 下载qemu-2.6.0版本代码,下载地址 2. 解压tar jxvg qemu-2.6.0.tar.bz2 3. 配置 配置命令如下./configure – ...

  7. Linux Kernel and Android 休眠与唤醒(中文版)

    Linux Kernel and Android 休眠与唤醒(中文版) 四月 18th, 2010 0 Comments/1664 hits Table of Contents 简介 国际化 版本信息 ...

  8. Linux Kernel and Android休眠与唤醒

    版本信息 Linux Kernel: v2.6.28      Android: v2.0 对于休眠(suspend)的简单介绍 在Linux中,休眠主要分三个主要的步骤: 版本信息 Linux Ke ...

  9. android driver log,Android调试驱动抓log的方法

    转自:http://blog.csdn.NET/menghnhhuan/article/details/7470583 在程序开发过程中,LOG是广泛使用的用来记录程序执行过程的机制,它既可以用于程序 ...

最新文章

  1. hibernate中 query 的list方法 用法
  2. winform中的webbrowser里面操作html代码问题
  3. 键盘按下某键 停止运行java_实现按下一个键执行操作/松开一个键停止操作
  4. emacs python_Emacs之Python编程环境配置 - elpy
  5. 将EntityManager.refresh添加到所有Spring数据存储库
  6. uva861 Little Bishops
  7. Python在数字前方补0
  8. 【Linux】awk处理变量
  9. JSK-391 公约公倍【入门】
  10. PHP程序员7小时学会Kotlin 第二小时
  11. paip.c#.net未能找到任何资源
  12. Must specify unique android:id, android:tag, or have a parent with an id for XXX
  13. 产品经理的常见分类和术语
  14. C#操作word定位光标
  15. python中数字加引号和不加引号的区别_高考完小白自学Python,不太懂print语句中一个加引号,一个不加?...
  16. 1.1哈恩巴纳赫定理
  17. 用计算机0和1表达难舍之情,表达不舍之情的说说
  18. 找一个脚本大师做师傅,认真的
  19. 【Rust 日报】2022-03-27 Google对25名Rust开源贡献者做出奖励
  20. chorme-调试模式基本使用

热门文章

  1. MBA教不了的创富课
  2. YY游戏云平台在AngularJS上的实践总结
  3. 一文全搞定:应届生offer,三方,劳动合同区别与注意事项
  4. java爬虫之下载txt小说
  5. 计算机维修高级工考试员题库,职业技能鉴定国家题库统一试卷高级计算机维修工知识试题...
  6. HBuilderX 下载安装教程
  7. Apache Kafka 在 360 的深度实践
  8. 洛谷P1251 餐巾计划问题 无汇源最小费用流
  9. detach分片表非常慢的一个案例
  10. 宝华计算机维修,唐山市路北区宝华计算机维修服务