ログ出力とは
本章のログ出力は、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の引数に取って、ログ位置を示す方法がドライバでは多用されています。
コメント