Files
android_kernel_samsung_sm87…/qcom/opensource/tools/linux-ramdump-parser-v2/dmesglib.py
2025-08-12 23:12:57 +02:00

288 lines
12 KiB
Python
Executable File

# Copyright (c) 2014-2015, 2020-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 and
# only version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import re
import string
import traceback
from parser_util import cleanupString
LOG_MAGIC = 0x5d7aefca
class DmesgLib(object):
def __init__(self, ramdump, outfile):
self.ramdump = ramdump
self.wrap_cnt = 0
self.outfile = outfile
if (self.ramdump.sizeof('struct printk_log') is None):
self.struct_name = 'struct log'
else:
self.struct_name = 'struct printk_log'
def log_from_idx(self, idx, logbuf):
len_offset = self.ramdump.field_offset(self.struct_name, 'len')
msg = logbuf + idx
msg_len = self.ramdump.read_u16(msg + len_offset)
if (msg_len == 0):
return logbuf
else:
return msg
def log_next(self, idx, logbuf):
len_offset = self.ramdump.field_offset(self.struct_name, 'len')
msg = idx
msg_len = self.ramdump.read_u16(msg + len_offset)
if (msg_len == 0):
self.wrap_cnt += 1
return logbuf
else:
return idx + msg_len
def verify_log_helper(self, msg, verbose):
# return early if CONFIG_LOG_BUF_MAGIC is not defined
log_align_addr = self.ramdump.address_of('__log_align')
if (log_align_addr is None):
return True
len_offset = self.ramdump.field_offset(self.struct_name, 'len')
text_offset = self.ramdump.field_offset(self.struct_name, 'text_len')
dict_offset = self.ramdump.field_offset(self.struct_name, 'dict_len')
magic_offset = self.ramdump.field_offset(self.struct_name, 'magic')
msg_len = self.ramdump.read_u16(msg + len_offset)
text_len = self.ramdump.read_u16(msg + text_offset)
dict_len = self.ramdump.read_u16(msg + dict_offset)
magic = self.ramdump.read_u32(msg + magic_offset)
log_size = self.ramdump.sizeof(self.struct_name)
log_align = self.ramdump.read_u32(log_align_addr)
is_logwrap_marker = not bool(text_len | msg_len | dict_len)
err = []
if (magic != LOG_MAGIC):
err.append('Bad Magic')
computed_msg_len = (text_len + dict_len + log_size + log_align - 1) & ~(log_align - 1)
if (not is_logwrap_marker and (msg_len != computed_msg_len)):
err.append('Bad length')
err = ' '.join(err)
if (err):
if (verbose):
f = '--------- Corrupted Dmesg {} for record @ {:x} ---------\n'.format(err, msg)
self.outfile.write(f)
f = self.ramdump.hexdump(msg - 0x40, 0xC0)
self.outfile.write(f)
return False
return True
def verify_log(self, msg, logbuf_addr, last_idx):
logbuf_size = self.ramdump.sizeof('__log_buf')
log_size = self.ramdump.sizeof(self.struct_name)
verbose = True
while msg != logbuf_addr + last_idx:
if (self.verify_log_helper(msg, verbose)):
return msg
verbose = False
msg = msg + 0x4
if (msg > logbuf_addr + logbuf_size - log_size):
msg = logbuf_addr
self.wrap_cnt += 1
return logbuf_addr + last_idx
def extract_dmesg_flat(self):
addr = self.ramdump.read_word(self.ramdump.address_of('log_buf'))
size = self.ramdump.read_word(self.ramdump.address_of('log_buf_len'))
dmesg = self.ramdump.read_physical(self.ramdump.virt_to_phys(addr), size)
self.outfile.write(cleanupString(dmesg.decode('ascii', 'ignore')) + '\n')
def extract_dmesg_binary(self):
first_idx_addr = self.ramdump.address_of('log_first_idx')
last_idx_addr = self.ramdump.address_of('log_next_idx')
logbuf_addr = self.ramdump.read_word(
self.ramdump.address_of('log_buf'))
time_offset = self.ramdump.field_offset(self.struct_name, 'ts_nsec')
len_offset = self.ramdump.field_offset(self.struct_name, 'len')
text_len_offset = self.ramdump.field_offset(self.struct_name, 'text_len')
log_size = self.ramdump.sizeof(self.struct_name)
first_idx = self.ramdump.read_u32(first_idx_addr)
if self.ramdump.is_config_defined('CONFIG_PRINTK_CALLER'):
callerid_off = self.ramdump.field_offset(self.struct_name, 'caller_id')
last_idx = self.ramdump.read_u32(last_idx_addr)
curr_idx = logbuf_addr + first_idx
while curr_idx != logbuf_addr + last_idx and self.wrap_cnt < 2:
timestamp = self.ramdump.read_dword(curr_idx + time_offset)
if self.ramdump.is_config_defined('CONFIG_PRINTK_CALLER'):
caller_data = self.ramdump.read_u32(curr_idx + callerid_off)
tid_info = "T"
if (caller_data & 0x80000000):
tid_info = "C"
caller_id_data = caller_data & ~0x80000000
caller_id_data = tid_info + str(caller_id_data)
text_len = self.ramdump.read_u16(curr_idx + text_len_offset)
text_str = self.ramdump.read_cstring(curr_idx + log_size, text_len)
if text_str is not None:
for partial in text_str.split('\n'):
if self.ramdump.is_config_defined('CONFIG_PRINTK_CALLER'):
f = '[{0:>5}.{1:0>6d}] [{caller_id_data:>6}] {2}\n'.format(
timestamp // 1000000000, (timestamp % 1000000000) // 1000, partial, caller_id_data=caller_id_data)
else:
f = '[{0:>5}.{1:0>6d}] {2}\n'.format(
timestamp // 1000000000, (timestamp % 1000000000) // 1000, partial)
self.outfile.write(f)
curr_idx = self.log_next(curr_idx, logbuf_addr)
curr_idx = self.verify_log(curr_idx, logbuf_addr, last_idx)
else:
self.outfile.write("[ Log wraps around ] at {0} \n".format(hex(curr_idx)))
curr_idx = logbuf_addr
self.wrap_cnt += 1
def extract_lockless_dmesg(self, write_to_file=True):
prb_addr = self.ramdump.read_pointer('prb')
off = self.ramdump.field_offset('struct printk_ringbuffer', 'desc_ring')
desc_ring_addr = prb_addr + off
off = self.ramdump.field_offset('struct prb_desc_ring', 'count_bits')
desc_ring_count = 1 << self.ramdump.read_u32(desc_ring_addr + off)
desc_sz = self.ramdump.sizeof('struct prb_desc')
off = self.ramdump.field_offset('struct prb_desc_ring', 'descs')
descs_addr = self.ramdump.read_ulong(desc_ring_addr + off)
info_sz = self.ramdump.sizeof('struct printk_info')
off = self.ramdump.field_offset('struct prb_desc_ring', 'infos')
infos_addr = self.ramdump.read_ulong(desc_ring_addr + off)
off = self.ramdump.field_offset(
'struct printk_ringbuffer', 'text_data_ring')
text_data_ring_addr = prb_addr + off
off = self.ramdump.field_offset('struct prb_data_ring', 'size_bits')
text_data_sz = 1 << self.ramdump.read_u32(text_data_ring_addr + off)
off = self.ramdump.field_offset('struct prb_data_ring', 'data')
data_addr = self.ramdump.read_ulong(text_data_ring_addr + off)
sv_off = self.ramdump.field_offset('struct prb_desc', 'state_var')
off = self.ramdump.field_offset('struct prb_desc','text_blk_lpos')
begin_off = off + self.ramdump.field_offset(
'struct prb_data_blk_lpos', 'begin')
next_off = off + self.ramdump.field_offset(
'struct prb_data_blk_lpos', 'next')
ts_off = self.ramdump.field_offset('struct printk_info', 'ts_nsec')
callerid_off = self.ramdump.field_offset('struct printk_info', 'caller_id')
len_off = self.ramdump.field_offset('struct printk_info', 'text_len')
desc_committed = 1
desc_finalized = 2
desc_sv_bits = self.ramdump.sizeof('long') * 8
desc_flags_shift = desc_sv_bits - 2
desc_flags_mask = 3 << desc_flags_shift
desc_id_mask = ~desc_flags_mask
off = self.ramdump.field_offset('struct prb_desc_ring','tail_id')
tail_id = self.ramdump.read_ulong(desc_ring_addr + off)
off = self.ramdump.field_offset('struct prb_desc_ring','head_id')
head_id = self.ramdump.read_ulong(desc_ring_addr + off)
did = tail_id
dmesg_list={}
while True:
ind = did % desc_ring_count
desc_off = desc_sz * ind
info_off = info_sz * ind
# skip non-committed record
state = 3 & (self.ramdump.read_ulong(descs_addr + desc_off +
sv_off) >> desc_flags_shift)
if state != desc_committed and state != desc_finalized:
if did == head_id:
break
did = (did + 1) & desc_id_mask
continue
begin = self.ramdump.read_ulong(descs_addr + desc_off +
begin_off) % text_data_sz
end = self.ramdump.read_ulong(descs_addr + desc_off +
next_off) % text_data_sz
if begin & 1 == 1:
text = ""
else:
if begin > end:
begin = 0
text_start = begin + self.ramdump.sizeof('long')
text_len = self.ramdump.read_u16(infos_addr +
info_off + len_off)
if end - text_start < text_len:
text_len = end - text_start
if text_len < 0:
text_len = 0
text = self.ramdump.read_cstring(data_addr +
text_start, text_len)
time_stamp = self.ramdump.read_u64(infos_addr +
info_off + ts_off)
caller_data = self.ramdump.read_u32(infos_addr +
info_off + callerid_off)
tid_info = "T"
if (caller_data & 0x80000000):
tid_info = "C"
caller_id_data = caller_data & ~0x80000000
pid = caller_id_data
caller_id_data = tid_info + str(caller_id_data)
for line in text.splitlines():
msg = u"[{time:12.6f}][{caller_id_data:>6}] {line}\n".format(
time=time_stamp / 1000000000.0,
line=line,caller_id_data=caller_id_data)
#print(msg, write_to_file)
if write_to_file:
self.outfile.write(msg)
else:
dmesg = []
dmesg.append(pid)
dmesg.append(line)
dmesg_list[time_stamp] = dmesg
if did == head_id:
break
did = (did + 1) & desc_id_mask
return dmesg_list
def extract_dmesg(self):
major, minor, patch = self.ramdump.kernel_version
if (major, minor) >= (5, 10):
return self.extract_lockless_dmesg()
if (major, minor) >= (3, 7):
self.extract_dmesg_binary()
return
self.extract_dmesg_flat()
def get_dmesg_as_dict(self):
major, minor, patch = self.ramdump.kernel_version
if (major, minor) >= (5, 10):
try:
return self.extract_lockless_dmesg(False)
except:
traceback.format_exc()
return {}