ログ出力

ログ出力とは

 本章のログ出力は、printfの様な関数を用いて、ログ領域に文字列を残す処理を示します。Linuxデバイスドライバでは、stdlib.hが使えない為、printf自体は使えません。標準出力自体が無い為、その他のputs等も使用できません。その代わりにLinuxデバイスドライバにおいて使用できるログ出力関数が用意されていますので、その関数について説明します。

使い方

 alsps_driver_addに出てくるALSPS_FUNやALSPS_PR_ERRは、alsps.hでpr_debugやpr_errに置き換えられているマクロ関数です。

#define ALSPS_FUN(f)			pr_debug(ALSPS_TAG"%s\n", __func__)
#define ALSPS_PR_ERR(fmt, args...)	pr_err(ALSPS_TAG"%s %d : "fmt,\
						__func__, __LINE__, ##args)

この、pr_debugやpr_err等がログを出力する為の関数になり、pr_err等は以下の通り定義されてます。

kernel/mediatek/4.4/include/linux/printk.h

#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__)

引数はfmt, ...なので、C言語標準のprintfとほぼ同じ方法で使用できます。このログはdmesgコマンドの実行や、/proc/kmsgを開く事で確認できます。

 ただし、このログには、レベルがあり一定以下のレベルは出力されないようになっています。マクロで置き換えられた先のprintk関数に指定されているKERN_ERR等がレベルとなり、以下の通り定義されています。

kernel/mediatek/4.4/include/linux/kern_levels.h

#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 */

これらのレベルを適切に選択する必要があります。

 pr_err等は単純にprintk関数に置き換えられていますが、pr_debugに関してはその限りではありません。CONFIG_DYNAMIC_DEBUGが有効の時に、他の関数に置き換えられます。

/* If you are writing a driver, please use dev_dbg instead */
#if 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(KLOG_MODNAME fmt, ##__VA_ARGS__)
#elif defined(DEBUG)
#define pr_debug(fmt, ...) \
	printk(KERN_DEBUG KLOG_MODNAME pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug(fmt, ...) \
	no_printk(KERN_DEBUG KLOG_MODNAME pr_fmt(fmt), ##__VA_ARGS__)
#endif

dynamicデバッグとは、その名の通り動的にログを出すか出さないか選択できる仕組みになります。
例えば、

echo 'file drivers/misc/mediatek/sensors-1.0/alsps/ltr578/alsps.c +p' > /sys/kernel/debug/dynamic_debug/control

と実行すると、alsps.c上のpr_debugがdmesg等に出力されるようになります。

最後に

 dynamicデバッグのような処理がある為、printkを直接使用するのでは無く、pr_debugやpr_errを使用することをお勧めします。
 C言語の範疇ですが、__func__は関数名に、__LINE__は行数にビルド時に置き換えられます。これらをpr_debugやpr_errの引数に取って、ログ位置を示す方法がドライバでは多用されています。

コメント

タイトルとURLをコピーしました