-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkern_error_monitor.bpf.c
More file actions
92 lines (80 loc) · 2.61 KB
/
kern_error_monitor.bpf.c
File metadata and controls
92 lines (80 loc) · 2.61 KB
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
// SPDX-License-Identifier: GPL-2.0
/*
* kern_error_monitor.bpf.c - BPF program to monitor KERN_ERR messages
*
* Attaches a kprobe to vprintk_emit() which is the common entry point for
* all printk paths including printk(KERN_ERR ...), pr_err(), and dev_err().
*
* Two cases are handled:
* 1. Explicit level parameter (dev_err, printk_emit, /dev/kmsg writes):
* vprintk_emit(facility, level=3, dev_info, fmt, args)
*
* 2. Level encoded in format string prefix (printk/pr_err):
* vprintk_emit(facility, level=-1, NULL, "\0013msg", args)
* where \001 is KERN_SOH and '3' is KERN_ERR level.
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "kern_error_monitor.h"
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
SEC("kprobe/vprintk_emit")
int BPF_KPROBE(trace_vprintk_emit, int facility, int level,
const void *dev_info, const char *fmt)
{
struct event *e;
char prefix[2] = {};
int is_kern_err = 0;
/*
* Case 1: Level explicitly set to KERN_ERR.
* This covers dev_err() -> dev_vprintk_emit() -> vprintk_emit()
* and /dev/kmsg writes -> printk_emit() -> vprintk_emit().
*/
if (level == KERN_ERR_LEVEL)
is_kern_err = 1;
/*
* Case 2: Level encoded in the format string prefix.
* printk(KERN_ERR "msg") expands to _printk("\001" "3" "msg")
* which calls vprintk() -> vprintk_emit(0, -1, NULL, "\0013msg", args).
* The level parameter is LOGLEVEL_DEFAULT (-1), but the real level
* is embedded as KERN_SOH (\001) followed by the level character.
*/
if (!is_kern_err) {
bpf_probe_read_kernel(prefix, sizeof(prefix), fmt);
if (prefix[0] == '\001' && prefix[1] == '3')
is_kern_err = 1;
}
if (!is_kern_err)
return 0;
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
u64 pid_tgid = bpf_get_current_pid_tgid();
e->pid = pid_tgid >> 32;
e->level = level;
e->facility = facility;
e->is_dev_err = 0;
e->ts_ns = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_probe_read_kernel_str(e->fmt, sizeof(e->fmt), fmt);
/*
* If dev_info is non-NULL, this came from dev_err() or similar.
* Read device subsystem and name from struct dev_printk_info.
*/
if (dev_info) {
struct dev_printk_info di = {};
e->is_dev_err = 1;
bpf_probe_read_kernel(&di, sizeof(di), dev_info);
__builtin_memcpy(e->dev_subsystem, di.subsystem,
sizeof(e->dev_subsystem));
__builtin_memcpy(e->dev_device, di.device,
sizeof(e->dev_device));
}
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";