linxu 内核调试(3) - 内核loglevel

  • 了解printk

1.Description

  Specify the initial console log level. Any log messages with levels less than this (that is, of higher priority) will be printed to the console, whereas any messages with levels equal to or greater than this will not be displayed.

  The console log level can also be changed by the klogd program, or by writing the specified level to the /proc/sys/kernel/printk file.

  The kernel log levels are:

  • 0 (KERN_EMERG)
    The system is unusable.

  • 1 (KERN_ALERT)
    Actions that must be taken care of immediately.

  • 2 (KERN_CRIT)
    Critical conditions.

  • 3 (KERN_ERR)
    Non-critical error conditions.

  • 4 (KERN_WARNING)
    Warning conditions that should be taken care of.

  • 5 (KERN_NOTICE)
    Normal, but significant events.

  • 6 (KERN_INFO)
    Informational messages that require no action.

  • 7 (KERN_DEBUG)
    Kernel debugging messages, output by the kernel if the developer enabled debugging at compile time.

  预定义的内核log等级:

// include/linux/kern_levels.h
#define KERN_SOH    "\001"      /* ASCII Start Of Header */
#define KERN_SOH_ASCII  '\001'
#define KERN_EMERG  KERN_SOH "0"    /* system is unusable */
#define KERN_ALERT  KERN_SOH "1"    /* action must be taken immediately */
#define KERN_CRIT   KERN_SOH "2"    /* critical conditions */
#define KERN_ERR    KERN_SOH "3"    /* error conditions */
#define KERN_WARNING    KERN_SOH "4"    /* warning conditions */
#define KERN_NOTICE KERN_SOH "5"    /* normal but significant condition */
#define KERN_INFO   KERN_SOH "6"    /* informational */
#define KERN_DEBUG  KERN_SOH "7"    /* debug-level messages */
#define KERN_DEFAULT    KERN_SOH "d"    /* the default kernel loglevel */

在这里插入图片描述
  The pr_* macros (with exception of pr_debug) are simple shorthand definitions in include/linux/printk.h for their respective printk call and should probably be used in newer drivers.

  pr_devel and pr_debug are replaced with printk(KERN_DEBUG … if the kernel was compiled with DEBUG, otherwise replaced with an empty statement.

  For drivers the pr_debug should not be used anymore (use dev_dbg instead).

  If you don’t specify a log level in your message it defaults to MESSAGE_LOGLEVEL_DEFAULT (usually “4”=KERN_WARNING) which can be set via the CONFIG_MESSAGE_LOGLEVEL_DEFAULT kernel config option (make menuconfig-> Kernel Hacking -> Default message log level)

  函数宏定义如下:

 include/linux/printk.h:
  297 #define pr_emerg(fmt, ...) \
  298     printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
  299 #define pr_alert(fmt, ...) \
  300     printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
  301 #define pr_crit(fmt, ...) \
  302     printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
  303 #define pr_err(fmt, ...) \                                                                             
  304     printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
  305 #define pr_warning(fmt, ...) \
  306     printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
  307 #define pr_warn pr_warning
  308 #define pr_notice(fmt, ...) \
  309     printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
  310 #define pr_info(fmt, ...) \
  311     printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

2.Linux kernel ring buffer

  Set the size of the kernel’s internal log buffer. n must be a power of 2, if not, it will be rounded up to be a power of two. This value can also be changed by the CONFIG_LOG_BUF_SHIFT kernel configuration value.

  Kernel log 默认大小是128KB,即17,修改该值,然后重新编译内核即可.

 kernel/init/Kconfig
 741 config LOG_BUF_SHIFT
 742     int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"                                                            
 743     range 12 21
 744     default 17
 745     help
 746       Select kernel log buffer size as a power of 2.
 747       Examples:
 748              17 => 128 KB
 749              16 => 64 KB
 750                  15 => 32 KB
 751                  14 => 16 KB
 752              13 =>  8 KB
 753              12 =>  4 KB
  • 编译后,查看android/out/target/product/msm8952_64/obj/KERNEL_OBJ/.config:

CONFIG_LOG_BUF_SHIFT=19

  • To change it, there are 3 possibles ways:

    Modify CONFIG_LOG_BUF_SHIFT value in defconfig file
    or use the Linux kernel menuconfig update
    Location:
    -> General setup
    -> Kernel log buffer size (16 => 64KB, 17 => 128KB)

    Or modify kernel arguments in kernel command-line (via bootargs value in device tree, or directly in extlinux uboot config file)
    bootargs = “root=/dev/mmcblk0p5 rootwait rw console=ttySTM0,115200 log_buf_len=65536”;

3.printk

3.1.printk的log等级控制

// linux/include/printk.h
/* printk's without a loglevel use this.. */
#define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT
/* We show everything that is MORE important than this.. */
#define CONSOLE_LOGLEVEL_DEFAULT 7 
#define CONSOLE_LOGLEVEL_MIN     1 
#define CONSOLE_LOGLEVEL_DEFAULT 7 

-------------------------------------------------------------------------
// kernel/printk.c
int console_printk[4] = {
    CONSOLE_LOGLEVEL_DEFAULT,   /* console_loglevel */
    MESSAGE_LOGLEVEL_DEFAULT,   /* default_message_loglevel */
    CONSOLE_LOGLEVEL_MIN,       /* minimum_console_loglevel */
    CONSOLE_LOGLEVEL_DEFAULT,   /* default_console_loglevel */
};
-------------------------------------------------------------------------
// linux/include/printk.h
extern int console_printk[];
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
  • console_loglevel
    只有当printk打印消息的log优先级高于console_loglevel时,才能输出到终端上

  • default_message_loglevel
    printk打印消息时默认的log等级

  • minimum_console_loglevel
    console_loglevel可以被设置的最小值

  • default_console_loglevel
    console_loglevel的缺省值

  只有当printk打印信息时的loglevel小于console loglevel的值(即:优先级高于console loglevel),这些信息才会被打印到console上。

  未指定日志级别的printk() 采用的默认级别是 default_message_loglevel。

3.2.改变console loglevel方法

  • 启动时Kernel boot option:loglevel=level
  • 运行时Runtime: dmesg -n level
    (注意:demsg -n level 改变的是console上的loglevel,dmesg命令仍然会打印出所有级别的系统信息。)
  • 运行时Runtime: echo $level > /proc/sys/kernel/printk
  • 运行时Runtime:写程序使用syslog系统调用(可以man syslog)

3.3.通过procfs改变log等级 [案例参考]

/proc/sys/kernel/printk:
              The  four values in this file are console_loglevel, default_mes-
              sage_loglevel,    minimum_console_level     and     default_con-
              sole_loglevel.   These  values  influence printk() behavior when
              printing or logging error messages.   Messages with a higher priority
              than console_loglevel will be printed to the console.   Messages
              without  an  explicit  priority  will  be  printed with priority
              default_message_level.  minimum_console_loglevel is the  minimum
              (highest)   value   to   which   console_loglevel  can  be  set.
              default_console_loglevel  is  the   default   value   for   con-
              sole_loglevel.

1.查看当前printk的log等级

# cat /proc/sys/kernel/printk
# 7 4 1 7

  “7 4 1 7” 分别对应console_loglevel、default_message_loglevel、minimum_c onsole_loglevel、default_console_loglevel,意味着只有优先级高于KERN_DEBUG(7)的打印消息才能输出到终端。

2.改变console_loglevel

# echo 8 4 1 7 > /proc/sys/kernel/printk

  输入“8 4 1 7”改变console_loglevel值,使得所有的打印消息都能输出到终端

3.4.修改内核配置

lib/Kconfig.debug:
CONFIG_PRINTK_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
  38 config CONSOLE_LOGLEVEL_DEFAULT
  39     int "Default console loglevel (1-15)"
  40     range 1 15
  41     default "7"
  42     help
  43       Default loglevel to determine what will be printed on the console.
  44 
  45       Setting a default here is equivalent to passing in loglevel=<x> in
  46       the kernel bootargs. loglevel=<x> continues to override whatever
  47       value is specified here as well.
  48 
  49       Note: This does not affect the log level of un-prefixed printk()
  50       usage in the kernel. That is controlled by the MESSAGE_LOGLEVEL_DEFAULT
  51       option.    
  -----------------------------------------------------------------------------------
  64 config MESSAGE_LOGLEVEL_DEFAULT
  65     int "Default message log level (1-7)"
  66     range 1 7
  67     default "4"
  68     help
  69       Default log level for printk statements with no specified priority.
  70 
  71       This was hard-coded to KERN_WARNING since at least 2.6.10 but folks
  72       that are auditing their logs closely may want to set it to a lower
  73       priority.
  74 
  75       Note: This does not affect what message level gets printed on the console                         
  76       by default. To change that, use loglevel=<x> in the kernel bootargs,
  77       or pick a different CONSOLE_LOGLEVEL_DEFAULT configuration value.

3.5.ignore_loglevel [Documentation/kernel-parameters.txt]

  如果dtsi 加上该属性bootargs=“ignore_loglevel”,则修改如下属性,不再起任何作用,去掉之后,才起作用。

CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4

动态调试:/sys/module/printk/parameters/ignore_loglevel.

3.6. loglevel

  dtsi 加上该属性bootargs=“loglevel=6”,设置loglevel.

4.dmesg

  dmesg是从kernel的ring buffer(环缓冲区)中读取信息的.

dmesg is used to examine or control the kernel ring buffer.The program helps users to print out their bootup messages. Instead of copying the messages by hand, the user need only: dmesg > dmesg.log

  ring buffer:

  在LINUX中,所有的系统信息(包括内核信息)都会传送到ring buffer中。而内核产生的信息由printk()打印出来。系统启动时所看到的信息都是由该函数打印到屏幕中。printk()打出的信息往往以 <0>…<2>… 这的数字表明消息的重要级别。高于一定的优先级别(当前的console loglevel)就会打印到console上,否则只会保留在系统的缓冲区中(ring buffer)。

4.1.Not all kernel messages are displayed by dmesg

  The printk-times feature adds a number of bytes at the beginning of each printk message. The default kernel message buffer size may not be sufficient to hold all the messages with this additional overhead. You can increase the kernel message buffer size when compiling the kernel, by adjusting the “Kernel Log buffer size” (found on the “General Setup” menu). Note that you must also specify a larger buffer read size with “dmesg”, with the ‘-s’ option.

ex: dmesg -s 128000 >/tmp/bootup_printks

5.Dynamic debug

  dynamic debug (dyndbg)是内核提供的一个调试功能,允许动态的开关内核打印输出,包含的API:pr_debug()、dev_dbg() 、print_hex_dump_debug()、rint_hex_dump_bytes()和netdev_dbg等。 dynamic debug通过设置/dynamic_debug/control文件来控制内核输出,有多种匹配的条件:文件名,函数名,行号,模块名和输出字符的格式; control文件的语法格式(Documentation/admin-guide/dynamic-debug-howto.rst):

command ::= match-spec* flags-spec \ 如:echo -n ‘file svcsock.c line 1603 +p’ > /dynamic_debug/control

其中match-spec的关键词是:

match-spec ::= 'func' string |
               'file' string |
               'module' string |
               'format' string |
               'line' line-range

  可以通过设置不同的flag输出函数名,行号,模块名和线程ID,这些信息可以帮助我们调试。

p enables the pr_debug() callsite.
f Include the function name in the printed message
l Include line number in the printed message
m Include module name in the printed message
t Include thread ID in messages not generated from interrupt context
_ No flags are set. (Or'd with others on input)

5.1.Enable debugfs in kernel dynamically

  Kernel developer has provided a functionality known as “debugfs” to debug the kernel, enabling the log at the run time. It is very useful for the modules which have a lots of logs, and enabling the log statically will make the system very slow. So Kernel developers came up with this idea to enabling the logs only for the time when user wants to check a particular scenario.

So here is the process to enable it :

  • Enable the debugfs, which can be done by following command :
    Enabled the CONFIG_DYNAMIC_DEBUG=y flag in kernel config file,than mount -o rw,remount -t debugfs none /sys/kernel/debug
//check your debugfs mount sistuation
adb root
adb shell
mount
  • Now if you want to see what are the logs defined you can do the same using following command : cat /sys/kernel/debug/dynamic_debug/control
    [Here /sys/kernel/debug/ is the directory we mounted in previous command, you can change it if you want]

  • Now enable the debug log for the files or paricular function you want to see in the logs:
    echo ‘file mdp.c +p’ > /sys/kernel/debug/dynamic_debug/control [It will enable all debugfs logs in mdp.c]

  • Now you can check the logs in demsg or cat /proc/kmsg.

5.2.代码分析

函数定义:

  • pr_debug(include/linux/printk.h)
    pr_debug定义在文件include/linux/printk.h,从pr_debug的源码注释建议:如果写驱动,请用dev_dbg。
/* If you are writing a driver, please use dev_dbg instead */
#if defined(CONFIG_DYNAMIC_DEBUG)
#include <linux/dynamic_debug.h>

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

#define dynamic_pr_debug(fmt, ...)				\
do {								\
	DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);		\
	if (DYNAMIC_DEBUG_BRANCH(descriptor))			\
		__dynamic_pr_debug(&descriptor, pr_fmt(fmt),	\
				   ##__VA_ARGS__);		\
} while (0)
  • dev_dbg(include/linux/device.h)
    dev_debug的定义在文件include/linux/device.h
#if defined(CONFIG_DYNAMIC_DEBUG)
#define dev_dbg(dev, fmt, ...)	\
 dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
#elif defined(DEBUG)
#define dev_dbg(dev, fmt, ...)	\
 dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#else
#define dev_dbg(dev, fmt, ...)	\
({	\
 if (0)	\
  dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
})
#endif

#define dynamic_dev_dbg(dev, fmt, ...)				\
do {								\
	DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);		\
	if (DYNAMIC_DEBUG_BRANCH(descriptor))			\
		__dynamic_dev_dbg(&descriptor, dev, fmt,	\
				  ##__VA_ARGS__);		\
} while (0)

  根据CONFIG_DYNAMIC_DEBUG和DEBUG的配置,pr_debug/dev_dbg的输出分为以下情况:
在这里插入图片描述

实现机制:

  • dynamic_pr_debug宏展开情况(CONFIG_DYNAMIC_DEBUG=y):
#define dynamic_pr_debug(fmt, ...)				\
do {								\
	DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);		\
	if (DYNAMIC_DEBUG_BRANCH(descriptor))			\
		__dynamic_pr_debug(&descriptor, pr_fmt(fmt),	\
				   ##__VA_ARGS__);		\
} while (0)

展开:
#define DEFINE_DYNAMIC_DEBUG_METADATA_KEY(name, fmt, key, init)	\
	static struct _ddebug  __aligned(8)			\
	__attribute__((section("__verbose"))) name = {		\
		.modname = KBUILD_MODNAME,			\
		.function = __func__,				\
		.filename = __FILE__,				\
		.format = (fmt),				\
		.lineno = __LINE__,				\
		.flags = _DPRINTK_FLAGS_DEFAULT,		\
		dd_key_init(key, init)				\
	}

static struct _ddebug __aligned(8)	\
 __attribute__((section("__verbose"))) descriptor = {	\
	.modname = KBUILD_MODNAME,	\
	.function = __func__,	\
	.filename = __FILE__,	\
	.format = ("hello"),	\
	.lineno = __LINE__,	\
	.flags = _DPRINTK_FLAGS_DEFAULT,	\
	dd_key_init(key, init)	\
 }
 
 if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))	\
__dynamic_pr_debug(&descriptor, pr_fmt(fmt),	\
     ##__VA_ARGS__);	\

  在调用pr_debug的源文件定义一个存放在kerenl的__verbose段的名为descriptor,类型为struct _ddebug变量;然后判断descriptor.flags & _DPRINTK_FLAGS_PRINT为true的话,就输出pr_debug内容。在定义descriptor时,将其flag成员赋值为_DPRINTK_FLAGS_DEFAULT,看这个宏的定义:

#if defined DEBUG
#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT
#else
#define _DPRINTK_FLAGS_DEFAULT 0
#endif

  如果源文件定义了DEBUG宏,_DPRINTK_FLAGS_DEFAULT 等于_DPRINTK_FLAGS_PRINT,所以默认就是可以打印的;如果没有定义DEBUG宏,那么_DPRINTK_FLAGS_DEFAULT 就是0,上面的条件就不成立,就不会打印出来。通过写/sys/kernel/debug/dynamic_debug/control节点来动态修改descriptor.flags为_DPRINTK_FLAGS_PRINT来使条件成立。control属性节点在源码<lib/dynamic_debug.c>中实现。 接着看__dynamic_pr_debug函数:


void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
{
	va_list args;
	struct va_format vaf;
	char buf[PREFIX_SIZE];

	BUG_ON(!descriptor);
	BUG_ON(!fmt);

	va_start(args, fmt);

	vaf.fmt = fmt;
	vaf.va = &args;

	printk(KERN_DEBUG "%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);

	va_end(args);
}
EXPORT_SYMBOL(__dynamic_pr_debug);

refer to

  • kernel/printk/printk.c
  • https://www.cnblogs.com/mylinux/p/4028787.html
  • https://www.cnblogs.com/pengdonglin137/p/5808373.html
  • https://www.cnblogs.com/pengdonglin137/p/4621576.html
  • https://linuxconfig.org/introduction-to-the-linux-kernel-log-levels
  • https://wiki.st.com/stm32mpu/wiki/How_to_use_the_kernel_dynamic_debug
  • https://wiki.stmicroelectronics.cn/stm32mpu/wiki/Dmesg_and_Linux_kernel_log
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Electron是一个用于构建跨平台桌面应用程序的开源框架,而Vue是一个前端框架,用于构建用户界面。在Vue中使用Electron时,可以通过引入electron-log来处理日志。 electron-log是一个基于Electron的日志记录库,它提供了一种简单易用的方式来记录和管理应用程序的日志。它可以在Electron主进程和渲染进程中使用。 使用electron-log,我们可以轻松地在Vue中记录日志。首先,我们需要在项目中安装electron-log包。可以通过npm或yarn进行安装:npm install electron-log。 然后,在Vue项目的主进程文件(main.js)中引入electron-log: import log from 'electron-log' 接下来,你可以在Vue的任何组件中使用log对象来记录日志。例如,在某个方法中记录日志: methods: { exampleMethod() { log.info('This is an info log') log.warn('This is a warning log') log.error('This is an error log') } } 这样,当exampleMethod被调用时,相应的日志信息将被记录下来。 此外,electron-log还提供了自定义日志文件的功能,可以配置日志文件的保存路径、日志格式、日期格式等。 综上所述,使用electron-log可以很方便地在Vue中记录和管理日志,为应用程序的开发和调试提供了便利。 ### 回答2: 在Vue中使用electron-log,可以帮助我们在Electron应用程序中管理和记录日志。 首先,我们需要在Vue项目中安装electron-log。可以使用npm或yarn进行安装。在项目的根目录下打开终端,然后运行以下命令进行安装: ``` npm install electron-log ``` 或 ``` yarn add electron-log ``` 安装完成后,我们可以在main.js(Electron主进程)文件中导入electron-log: ```javascript import log from 'electron-log' ``` 接下来,我们可以在Vue应用的主文件(通常是main.js或App.vue)中设置全局日志对象,以便在整个应用程序中使用它: ```javascript Vue.prototype.$log = log ``` 现在,我们可以在Vue组件中使用`this.$log`来访问electron-log的各种功能。 例如,我们可以使用以下函数来记录一些日志信息: ```javascript this.$log.info('This is an info log.') this.$log.error('This is an error log.') this.$log.warn('This is a warning log.') ``` 我们还可以设置日志级别,以决定日志应该显示哪些级别的信息。例如,我们可以在`createWindow`函数中设置日志级别: ```javascript function createWindow() { // ... log.transports.console.level = 'silly' log.transports.file.level = 'silly' // ... } ``` 在上述示例中,我们将日志级别设置为'silly',这将允许所有级别的日志信息显示在控制台和日志文件中。 通过在Vue中使用electron-log,我们可以更好地管理和记录Electron应用程序的日志,以便进行调试和错误追踪。 ### 回答3: 在Vue中使用Electron和electron-log,可以方便地记录和显示程序的日志信息。 首先,要在Vue项目中安装`electron-log`,可以通过命令行运行`npm install electron-log`来进行安装。 接下来,在Vue的主进程文件(通常是`main.js`)中,引入并配置`electron-log`。可以在文件的开头添加以下代码: ```javascript const log = require('electron-log'); const { app } = require('electron'); // 配置electron-log log.transports.console.level = 'info'; log.transports.file.level = 'info'; log.transports.file.file = `${app.getPath('userData')}/log.log`; log.transports.file.format = '{h}:{i}:{s} {level}: {text}'; log.transports.file.maxSize = 5 * 1024 * 1024; // 限制日志文件大小为5MB // 在Renderer进程中通过global.log访问electron-log global.log = log; ``` 上述代码中,我们将`electron-log`的输出级别设置为`info`,并将日志保存在用户数据目录下的`log.log`文件中。可以根据需要修改输出级别和文件路径。 在Vue组件中,可以直接通过`global.log`来调用`electron-log`的各种方法,例如记录日志、显示弹窗等。下面是一个示例: ```javascript export default { name: 'MyComponent', methods: { logInfo() { global.log.info('This is an information log.'); }, logError() { global.log.error('This is an error log.'); }, showMessageBox() { global.log.info('Displaying a message box.'); global.log.info('I am a message box!'); }, }, }; ``` 在上述示例中,我们分别定义了`logInfo`、`logError`和`showMessageBox`三个方法,分别记录了信息日志、错误日志和显示一个带有自定义文本的弹窗。 通过以上步骤,我们就可以在Vue中方便地使用`electron-log`来记录和显示日志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值