发布于   阅读: loading

通过钩子实时获取内核日志

在Linux运行环境,内核日志对我们排查内核态的故障非常重要,尤其是当内核发生panic或者Oops故障时,内核会通过日志输出堆栈信息,但是当发生此类问题时,设备往往会重启,重启后日志信息就会丢失,导致无法通过日志来定位问题。尤其是在嵌入式设备上,出现此类问题往往让人头疼。那么有没有什么办法能保存故障现场的日志呢?本文提供一种方法:在内核printk代码中添加日志处理钩子函数,通过此钩子你可以注册自己的日志接收函数,可以实时的把内核日志保存下来~

内核版本

  • linux-4.4

代码修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
diff --git a/include/linux/printk.h b/include/linux/printk.h
index e8aae29..2c1ec29 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -10,6 +10,9 @@
extern const char linux_banner[];
extern const char linux_proc_banner[];

+typedef int *printk_hook_t(char *text,size_t textlen);
+extern void printk_hook_register(printk_hook_t *hook);
+extern void printk_hook_unregister(void);
static inline int printk_get_level(const char *buffer)
{
if (buffer[0] == KERN_SOH_ASCII && buffer[1]) {
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 82e293a..eb45e81 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -62,6 +62,19 @@ int console_printk[4] = {
CONSOLE_LOGLEVEL_DEFAULT, /* default_console_loglevel */
};

+static printk_hook_t __rcu *printk_hook __read_mostly;
+void printk_hook_register(printk_hook_t *hook)
+{
+ rcu_assign_pointer(printk_hook, hook);
+}
+EXPORT_SYMBOL(printk_hook_register);
+void printk_hook_unregister(void)
+{
+ RCU_INIT_POINTER(printk_hook, NULL);
+ synchronize_rcu();
+}
+EXPORT_SYMBOL(printk_hook_unregister);
+
/*
* Low level drivers may need that to know if they can schedule in
* their unblank() callback or not. So let's export it.
@@ -1673,6 +1686,15 @@ asmlinkage int vprintk_emit(int facility, int level,
/* cpu currently holding logbuf_lock in this function */
static unsigned int logbuf_cpu = UINT_MAX;

+ printk_hook_t *pr_hook = rcu_dereference(printk_hook);
+ static bool first_time_pr_hook = true;
+ u64 hook_seq,pre_next_seq;
+ u32 hook_idx,pre_next_idx;
+ struct printk_log *hook_msg;
+ //static char textbuf[LOG_LINE_MAX];
+ size_t len;
+
+
if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
@@ -1707,6 +1729,9 @@ asmlinkage int vprintk_emit(int facility, int level,
lockdep_off();
raw_spin_lock(&logbuf_lock);
logbuf_cpu = this_cpu;
+
+ pre_next_idx = log_next_idx;
+ pre_next_seq = log_next_seq;

if (unlikely(recursion_bug)) {
static const char recursion_msg[] =
@@ -1800,6 +1825,28 @@ asmlinkage int vprintk_emit(int facility, int level,
printed_len += log_store(facility, level, lflags, 0,
dict, dictlen, text, text_len);
}
+
+ if(pr_hook){
+ if(first_time_pr_hook || (level == LOGLEVEL_EMERG)){
+ cont_flush(LOG_NEWLINE);
+ }
+ if(first_time_pr_hook){
+ hook_idx = log_first_idx;
+ hook_seq = log_first_seq;
+ first_time_pr_hook = false;
+ }else{
+ hook_idx = pre_next_idx;
+ hook_seq = pre_next_seq;
+ }
+
+ for(; hook_seq != log_next_seq;hook_seq ++){
+ hook_msg = log_from_idx(hook_idx);
+ len = sizeof(textbuf);
+ len = msg_print_text(hook_msg,0,false,textbuf,len);
+ pr_hook(textbuf,len);
+ hook_idx = log_next(hook_idx);
+ }
+ }

logbuf_cpu = UINT_MAX;
raw_spin_unlock(&logbuf_lock);

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 @Yake Pei 创建,使用 Stellar 作为主题。