Skip to content

Commit 47655ee

Browse files
happy-dudesmira
authored andcommitted
feat: support PRINTK_CALLER kmsg logs
Closes #10 Add support for caller information in kmsg log entries. More examples available at #10 (comment) Also adds tests for a log entry with a Thread caller and a Cpu caller. Further references: * https://elixir.bootlin.com/linux/v6.6.22/source/kernel/printk/printk_ringbuffer.h#L15 * https://elixir.bootlin.com/linux/v6.6.22/source/kernel/printk/printk.c#L629 Signed-off-by: Stanley Chan <pocketgamer5000@gmail.com> Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent e358d13 commit 47655ee

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

message.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ const (
4040
Local7
4141
)
4242

43+
// CallerType is a single char byte caller type in kmsg logs with PRINTK_CALLER support.
44+
type CallerType byte
45+
46+
// Types of callers a kmsg can originate from.
47+
//
48+
// ref: https://elixir.bootlin.com/linux/v6.6.22/source/kernel/printk/printk.c#L629
49+
const (
50+
CPUCaller = 'C'
51+
ThreadCaller = 'T'
52+
)
53+
54+
// Caller struct containing the type and ID from kmsg logs with PRINTK_CALLER support.
55+
type Caller struct {
56+
Type CallerType
57+
ID uint32
58+
}
59+
4360
func (f Facility) String() string {
4461
return [...]string{
4562
"kern", "user", "mail", "daemon",
@@ -77,6 +94,7 @@ type Message struct {
7794
Priority Priority
7895
SequenceNumber int64
7996
Clock int64
97+
Caller Caller
8098
}
8199

82100
// ParseMessage parses internal kernel log format.
@@ -108,12 +126,38 @@ func ParseMessage(input []byte, bootTime time.Time) (Message, error) {
108126
return Message{}, fmt.Errorf("errors parsing clock from boot: %w", err)
109127
}
110128

129+
caller := Caller{}
130+
131+
if len(metadata) == 5 {
132+
// CONFIG_PRINTK_CALLER is enabled and kmsg has CPU or Thread caller info
133+
callParts := strings.Split(metadata[4], "=")
134+
135+
callStr := callParts[1]
136+
137+
switch callStr[0] {
138+
case ThreadCaller:
139+
caller.Type = ThreadCaller
140+
case CPUCaller:
141+
caller.Type = CPUCaller
142+
default:
143+
return Message{}, fmt.Errorf("errors parsing caller id type")
144+
}
145+
146+
id, err := strconv.ParseUint(callStr[1:], 10, 32)
147+
if err != nil {
148+
return Message{}, fmt.Errorf("errors parsing caller id value: %w", err)
149+
}
150+
151+
caller.ID = uint32(id)
152+
}
153+
111154
return Message{
112155
Priority: Priority(syslogPrefix & 7),
113156
Facility: Facility(syslogPrefix >> 3),
114157
SequenceNumber: sequence,
115158
Clock: clock,
116159
Timestamp: bootTime.Add(time.Duration(clock) * time.Microsecond),
160+
Caller: caller,
117161
Message: unescape(message),
118162
}, nil
119163
}

message_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,36 @@ func TestParseMessage(t *testing.T) {
6363
Message: "NET: Registered protocol family \\10\\ in µs",
6464
},
6565
},
66+
{
67+
input: `6,378,5140900,-,caller=T148;ata1: SATA max UDMA/133 abar m8192@0xf0820000 port 0xf0820100 irq 21`,
68+
expected: kmsg.Message{
69+
Facility: kmsg.Kern,
70+
Priority: kmsg.Info,
71+
SequenceNumber: 378,
72+
Clock: 5140900,
73+
Timestamp: mustParse("0001-01-01 00:00:05.1409 +0000 UTC"),
74+
Caller: kmsg.Caller{
75+
Type: kmsg.ThreadCaller,
76+
ID: 148,
77+
},
78+
Message: "ata1: SATA max UDMA/133 abar m8192@0xf0820000 port 0xf0820100 irq 21",
79+
},
80+
},
81+
{
82+
input: `0,380,5140900,-,caller=C10;watchdog: BUG: soft lockup - CPU#10 stuck for 216s! [softlockup_thre:529]`,
83+
expected: kmsg.Message{
84+
Facility: kmsg.Kern,
85+
Priority: kmsg.Emerg,
86+
SequenceNumber: 380,
87+
Clock: 5140900,
88+
Timestamp: mustParse("0001-01-01 00:00:05.1409 +0000 UTC"),
89+
Caller: kmsg.Caller{
90+
Type: kmsg.CPUCaller,
91+
ID: 10,
92+
},
93+
Message: "watchdog: BUG: soft lockup - CPU#10 stuck for 216s! [softlockup_thre:529]",
94+
},
95+
},
6696
} {
6797
message, err := kmsg.ParseMessage([]byte(testCase.input), time.Time{})
6898
require.NoError(t, err)

0 commit comments

Comments
 (0)