replace common qcom sources with samsung ones

This commit is contained in:
SaschaNes
2025-08-12 22:13:00 +02:00
parent ba24dcded9
commit 6f7753de11
5682 changed files with 2450203 additions and 103634 deletions

View File

@@ -0,0 +1,92 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
import os
from parser_util import register_parser, RamParser
from dmesglib import DmesgLib
from print_out import print_out_str
import linux_list as llist
import parsers.linux_devices as ldevices
from struct_print import struct_print_class
def ARM_SMMU_GR0_S2CR(n):
return (0xc00 + ((n) << 2))
@register_parser('--arm_smmu_device', 'arm_smmu_device')
class arm_smmu_device(RamParser):
def get_arm_smmu_device_info(self):
ramdump = self.ramdump
f_path = os.path.join(self.ramdump.outdir, "arm_smmu_devices.txt")
fout = open(f_path, "w")
arm_smmu_devices_sid = os.path.join(self.ramdump.outdir, "arm_smmu_devices_sid.txt")
arm_smmu_devices_sid_fout = open(arm_smmu_devices_sid, "w")
device = ldevices.DevicesList(self.ramdump)
device_lists = device.get_device_list(None)
for item in device_lists:
name = item[1]
device = item[0]
if name == None:
continue
if 'apps-smmu' in name:
drvdata = self.ramdump.read_structure_field(device, 'struct device', 'driver_data')
if drvdata != 0:
try:
print ("v.v (struct device)0x%x %-64s %-32s 0x%-32x 0x%x" % (item[0], item[1], item[2], item[3], item[4]), file = fout)
print (" v.v (struct arm_smmu_device *)0x%x " % (drvdata), file = fout)
arm_smmu_device = drvdata
num_mapping_groups = self.ramdump.read_structure_field(arm_smmu_device, 'struct arm_smmu_device', 'num_mapping_groups')
print("v.v (struct arm_smmu_device *)0x%x num_mapping_groups %d"%(arm_smmu_device, num_mapping_groups), file = fout)
arm_smmu_device_datatype = ramdump.read_datatype(arm_smmu_device, 'struct arm_smmu_device')
s2crs = arm_smmu_device_datatype.s2crs
#s2crs = ramdump.read_structure_field(arm_smmu_device, 'struct arm_smmu_device', 's2crs')
ptr_size = ramdump.sizeof('struct arm_smmu_s2cr')
smrs = arm_smmu_device_datatype.smrs
ptr_size_smrs = ramdump.sizeof('struct arm_smmu_smr')
except Exception as e: print_out_str(str(e))
for i in range(0, num_mapping_groups):
try:
s2crs += i * ptr_size
s2crs_data = struct_print_class(ramdump, 'arm_smmu_s2cr', s2crs, fout)
s2crs_data.append('group', 'ptr')
s2crs_data.append('count', 'u32')
s2crs_data.append('type', 'u32')
s2crs_data.append('privcfg', 'u32')
s2crs_data.append('cbndx', 'u8')
s2crs_data.append('pinned', 'u8')
s2crs_data.process()
cbndx = s2crs_data.get_val('cbndx')
reg_addr = ARM_SMMU_GR0_S2CR(cbndx)
print("=================================================================0x%x index %x"%(reg_addr, i), file = fout)
print("----------------------------- %x"%(s2crs), file = fout)
s2crs_data.print_struct()
smrs += i * ptr_size_smrs
print("----------------------------- %x"%(smrs), file = fout)
smrs_data = struct_print_class(ramdump, 'arm_smmu_smr', smrs, fout)
smrs_data.append('mask', 'u16')
smrs_data.append('id', 'u16')
smrs_data.append('valid', 'u8')
smrs_data.append('pinned', 'u8')
smrs_data.process()
smrs_data.print_struct()
except Exception as e: print_out_str(e)
iommu = self.ramdump.read_structure_field(device, 'struct device', 'iommu')
ids = 0
ARM_SMMU_SMR_ID = 0xaa
ARM_SMMU_SMR_MASK = 0xaa
try:
if iommu != None and iommu != 0:
fwspec = self.ramdump.read_structure_field(iommu, 'struct dev_iommu', ' fwspec')
if fwspec != None and fwspec != 0:
ids_offset = self.ramdump.field_offset('struct iommu_fwspec', 'ids')
ids = self.ramdump.read_u32(ids_offset + fwspec)
ARM_SMMU_SMR_ID = ids & 0xffff
ARM_SMMU_SMR_MASK = (ids >> 16) & 0xffff
print("name = %-64s SID = 0x%-8x" %(name, ARM_SMMU_SMR_ID), file = arm_smmu_devices_sid_fout)
except Exception as e: print_out_str(str(e))
arm_smmu_devices_sid_fout.close()
fout.close()
def parse(self):
self.get_arm_smmu_device_info()

View File

@@ -0,0 +1,231 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
import linux_list
import rb_tree
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
@register_parser('--binder-dump', 'Extract binder information in binder driver', optional=True)
class BinderParser(RamParser) :
def __init__(self, *args):
super(BinderParser, self).__init__(*args)
self.SEPARATOR = "|=================================================================================================|"
self.TITLE_PROC = "| pid | task | binder_proc | requested | started | dead | todo |"
self.SEPARATOR_SUB1 = "|--------|------------------|------------------|---------------------|-------|--------------------|"
self.TITLE_THREAD = "| | | binder_thread | transaction_stack | pid | |"
self.SEPARATOR_SUB2 = "| | |------------------|---------------------|-------|--------------------|"
self.TITLE_NODE = "| | | binder_node |has_async_transaction| | async_todo |"
self.TITLE_REF = "| | | binder_ref | remote binder_node | | |"
self.PROC_OUTPUT_FMT = "| {0:^6} | {1:^16s} |0x{2:^16X}| {3:^2} | {4:^2} | {5:^1} | |"
self.THREAD_OUTPUT_FMT1 = "| | |0x{0:^16X}|" + " NULL " + "| {1:^6}| |"
self.THREAD_OUTPUT_FMT2 = "| | |0x{0:^16X}|" + " 0x{1:^16X} " + "| {2:^6}| |"
self.NODE_OUTPUT_FMT = "| | |0x{0:^16X}| {1:^1} | | |"
self.REF_OUTPUT_FMT = "| | |0x{0:^16X}| 0x{1:^16X} | desc {2:^8}| |"
self.TRANS_FMT = "| | | | | |"
self.TRANS_FMT1 = self.TRANS_FMT + " NULL |"
self.TRANS_FMT2 = self.TRANS_FMT + " 0x{0:^16X} |"
self.binder_node_offset = self.ramdump.field_offset('struct binder_node', 'rb_node')
self.binder_ref_offset = self.ramdump.field_offset('struct binder_ref', 'rb_node_desc')
self.work_entry_offset = self.ramdump.field_offset('struct binder_work', 'entry')
def transactions_walker(self, work):
if work == self.work_head:
return
transaction_work_offset = self.ramdump.field_offset('struct binder_transaction', 'work')
trans = work - transaction_work_offset
print (self.TRANS_FMT2.format(trans), file=self.outfd)
# Parse binder_thread node one by one.
def binder_threads_walker(self, node, extra):
thread_node_offset = self.ramdump.field_offset('struct binder_thread', 'rb_node')
thread = node - thread_node_offset
thread_pid_offset = self.ramdump.field_offset('struct binder_thread', 'pid')
thread_stack_offset = self.ramdump.field_offset('struct binder_thread', 'transaction_stack')
thread_todo_offset = self.ramdump.field_offset('struct binder_thread', 'todo')
thread_pid = self.ramdump.read_s32(thread + thread_pid_offset)
thread_stack = self.ramdump.read_word(thread + thread_stack_offset)
print (self.SEPARATOR_SUB2, file=self.outfd)
if thread_stack == 0:
print (self.THREAD_OUTPUT_FMT1.format(thread, thread_pid), file=self.outfd)
else:
print (self.THREAD_OUTPUT_FMT2.format(thread, thread_stack, thread_pid), file=self.outfd)
# Walk binder_thread.todo list.
todo_head = thread + thread_todo_offset
fist_node = self.ramdump.read_word(todo_head)
works_walker = linux_list.ListWalker(self.ramdump, fist_node, self.work_entry_offset)
if works_walker.is_empty():
print (self.TRANS_FMT1, file=self.outfd)
else:
self.work_head = todo_head - self.work_entry_offset
works_walker.walk(fist_node, self.transactions_walker)
# Parse binder_thread node one by one.
def binder_nodes_walker(self, node, extra):
bnode_offset = self.ramdump.field_offset('struct binder_node', 'rb_node')
bnode = node - bnode_offset
bnode_async_offset = self.ramdump.field_offset('struct binder_node', 'has_async_transaction')
bnode_todo_offset = self.ramdump.field_offset('struct binder_node', 'async_todo')
has_async = self.ramdump.read_u32(bnode + bnode_async_offset)
has_async = (has_async >> 4) & 1
print (self.SEPARATOR_SUB2, file=self.outfd)
print (self.NODE_OUTPUT_FMT.format(bnode, has_async), file=self.outfd)
# Walk binder_node.async_todo list.
todo_head = bnode + bnode_todo_offset
fist_node = self.ramdump.read_word(todo_head)
works_walker = linux_list.ListWalker(self.ramdump, fist_node, self.work_entry_offset)
if works_walker.is_empty():
print (self.TRANS_FMT1, file=self.outfd)
else:
self.work_head = todo_head - self.work_entry_offset
works_walker.walk(fist_node, self.transactions_walker)
# Parse binder_ref node one by one.
def binder_refs_walker(self, node, extra):
bref_offset = self.ramdump.field_offset('struct binder_ref', 'rb_node_desc')
bref = node - bref_offset
bref_node_offset = self.ramdump.field_offset('struct binder_ref', 'node')
bref_node = self.ramdump.read_word(bref + bref_node_offset)
data_offset = self.ramdump.field_offset('struct binder_ref', 'data')
desc_offset = self.ramdump.field_offset('struct binder_ref_data', 'desc')
desc_addr = bref + desc_offset + data_offset
desc = self.ramdump.read_u32(desc_addr)
print (self.SEPARATOR_SUB2, file=self.outfd)
print (self.REF_OUTPUT_FMT.format(bref, bref_node, desc), file=self.outfd)
def binder_alloc_allocated_buffers_walker(self, node, extra):
rb_node_offset = self.ramdump.field_offset('struct binder_buffer', 'rb_node')
binder_buffer = node - rb_node_offset
print("v.v %s (binder_buffer)0x%-32x" % ("%h", binder_buffer), file=self.outfd, end = ' ')
user_data = self.ramdump.read_structure_field(binder_buffer, 'struct binder_buffer', 'user_data')
free = self.ramdump.read_structure_field(binder_buffer, 'struct binder_buffer', 'free')
if free == None:
free = 2
else:
free = free & 0x1
if user_data == None:
user_data = 0xaa55
print(" user_data 0x%-32x free = %-16d" % (user_data, free), file=self.outfd)
return
def parse_binder_procs(self):
hlist_first_offset = self.ramdump.field_offset('struct hlist_head', 'first')
hlist_next_offset = self.ramdump.field_offset('struct hlist_node', 'next')
proc_node_offset = self.ramdump.field_offset('struct binder_proc', 'proc_node')
proc_pid_offset = self.ramdump.field_offset('struct binder_proc', 'pid')
proc_task_offset = self.ramdump.field_offset('struct binder_proc', 'tsk')
task_comm_offset = self.ramdump.field_offset('struct task_struct', 'comm')
requested_threads_offset = self.ramdump.field_offset('struct binder_proc', 'requested_threads')
started_threads_offset = self.ramdump.field_offset('struct binder_proc', 'requested_threads_started')
is_dead_offset = self.ramdump.field_offset('struct binder_proc', 'is_dead')
proc_todo_offset = self.ramdump.field_offset('struct binder_proc', 'todo')
alloc_offset = self.ramdump.field_offset('struct binder_proc', 'alloc')
binder_procs_addr = self.ramdump.address_of('binder_procs')
first_proc_node = self.ramdump.read_word(binder_procs_addr + hlist_first_offset)
proc_node = first_proc_node
while proc_node != 0:
proc = proc_node - proc_node_offset
# Get binder_proc fields.
proc_pid = self.ramdump.read_s32(proc + proc_pid_offset)
task = self.ramdump.read_word(proc + proc_task_offset)
task_name = cleanupString(self.ramdump.read_cstring(task + task_comm_offset, 16))
requested = self.ramdump.read_s32(proc + requested_threads_offset)
requested_started = self.ramdump.read_s32(proc + started_threads_offset)
is_dead = self.ramdump.read_s32(proc + is_dead_offset)
alloc = proc + alloc_offset
print("v.v %s (binder_proc)0x%x" % ("%h", proc), file=self.outfd)
print("v.v %s (binder_alloc)0x%x" %("%h", alloc), file=self.outfd)
print(" allocated_buffers", file=self.outfd)
allocated_buffers_offset = self.ramdump.field_offset('struct binder_alloc', 'allocated_buffers')
allocated_buffers_node = allocated_buffers_offset + alloc
allocated_buffers_node = self.ramdump.read_pointer(allocated_buffers_node)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(allocated_buffers_node, self.binder_alloc_allocated_buffers_walker)
print(" free_buffers", file=self.outfd)
allocated_buffers_offset = self.ramdump.field_offset('struct binder_alloc', 'free_buffers')
allocated_buffers_node = allocated_buffers_offset + alloc
allocated_buffers_node = self.ramdump.read_pointer(allocated_buffers_node)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(allocated_buffers_node, self.binder_alloc_allocated_buffers_walker)
print (self.SEPARATOR, file=self.outfd)
print (self.TITLE_PROC, file=self.outfd)
print (self.SEPARATOR_SUB1, file=self.outfd)
print (self.PROC_OUTPUT_FMT.format(proc_pid, task_name, proc, requested, requested_started, is_dead), file=self.outfd)
# Walk binder_proc.todo list.
todo_head = proc + proc_todo_offset
fist_node = self.ramdump.read_word(todo_head)
works_walker = linux_list.ListWalker(self.ramdump, fist_node, self.work_entry_offset)
if works_walker.is_empty():
print (self.TRANS_FMT1, file=self.outfd)
else:
self.work_head = todo_head - self.work_entry_offset
works_walker.walk(fist_node, self.transactions_walker)
print (self.SEPARATOR_SUB2, file=self.outfd)
print (self.TITLE_THREAD, file=self.outfd)
# Walk binder_proc.threads rb_tree.
proc_threads_offset = self.ramdump.field_offset('struct binder_proc', 'threads')
proc_threads_node = self.ramdump.read_word(proc + proc_threads_offset)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(proc_threads_node, self.binder_threads_walker)
print (self.SEPARATOR_SUB2, file=self.outfd)
print (self.TITLE_NODE, file=self.outfd)
# Walk binder_proc.nodes rb_tree.
proc_nodes_offset = self.ramdump.field_offset('struct binder_proc', 'nodes')
proc_nodes_node = self.ramdump.read_word(proc + proc_nodes_offset)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(proc_nodes_node, self.binder_nodes_walker)
print (self.SEPARATOR_SUB2, file=self.outfd)
print (self.TITLE_REF, file=self.outfd)
# Walk binder_proc.refs_by_desc rb_tree.
proc_refs_offset = self.ramdump.field_offset('struct binder_proc', 'refs_by_desc')
proc_refs_node = self.ramdump.read_word(proc + proc_refs_offset)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(proc_refs_node, self.binder_refs_walker)
# Get the next binder_node to parse.
proc_node = self.ramdump.read_word(proc_node + hlist_next_offset)
print (self.SEPARATOR, file=self.outfd)
def parse(self):
self.outfd = open(self.ramdump.outdir + "/binder_output.txt", "w")
self.parse_binder_procs()
self.outfd.close()
print_out_str("--- Wrote the output to binder_output.txt")

View File

@@ -0,0 +1,124 @@
# Copyright (c) 2012-2015, 2020 The Linux Foundation. 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 struct
from parser_util import register_parser, RamParser
from print_out import print_out_str
# assuming cache way size of 8, fix this for badger probably
cache_way = 8
def save_l1_dump(ram_dump, cache_base, size):
with ram_dump.open_file('l1_cache_dump.bin', 'wb') as cache_file:
for i in range(0, size):
val = ram_dump.read_byte(cache_base + i, False)
cache_file.write(struct.pack('<B', val))
print_out_str('--- Wrote cache dump to l1_cache_dump.bin')
def parse_cache_dump(ram_dump, cache_base):
magic_num_offset = ram_dump.field_offset(
'struct l2_cache_dump', 'magic_number')
version_offset = ram_dump.field_offset('struct l2_cache_dump', 'version')
line_size_offset = ram_dump.field_offset(
'struct l2_cache_dump', 'line_size')
total_lines_offset = ram_dump.field_offset(
'struct l2_cache_dump', 'total_lines')
cache_offset_struct = ram_dump.field_offset(
'struct l2_cache_dump', 'cache')
l2dcrtr0_offset_struct = ram_dump.field_offset(
'struct l2_cache_line_dump', 'l2dcrtr0_val')
l2dcrtr1_offset_struct = ram_dump.field_offset(
'struct l2_cache_line_dump', 'l2dcrtr1_val')
cache_line_data_offset_struct = ram_dump.field_offset(
'struct l2_cache_line_dump', 'cache_line_data')
cache_line_struct_size = ram_dump.sizeof('struct l2_cache_line_dump')
magic = ram_dump.read_word(cache_base + magic_num_offset, False)
version = ram_dump.read_word(cache_base + version_offset, False)
line_size = ram_dump.read_word(cache_base + line_size_offset, False)
total_lines = ram_dump.read_word(cache_base + total_lines_offset, False)
cache = ram_dump.read_word(cache_base + cache_offset_struct, False)
cache_file = ram_dump.open_file('l2_cache_dump.txt')
cache_file.write('Magic = {0:x}\n'.format(magic))
cache_file.write('version = {0:x}\n'.format(version))
cache_file.write('line size = {0:x}\n'.format(line_size))
select = 0
lines = total_lines // cache_way
header_str = '({0:4},{1:1}) {2:5} {3:8} '.format(
'Set', 'Way', 'valid', 'Address')
# currently assumes 32 bit word like everything else...
for i in range(0, 32):
header_str = header_str + '{0:8} '.format('Word{0}'.format(i))
header_str = header_str + '{0:8} {1:8}\n'.format('L2DCRTR0', 'L2DCRTR0')
cache_ptr = cache_base + cache_offset_struct
for i in range(0, lines):
cache_file.write(header_str)
for j in range(0, cache_way):
cache_line_ptr = cache_ptr + (i * cache_way + j) * line_size
l2dcrtr0_val = ram_dump.read_word(
cache_line_ptr + l2dcrtr0_offset_struct, False)
l2dcrtr1_val = ram_dump.read_word(
cache_line_ptr + l2dcrtr1_offset_struct, False)
# this is valid for krait, will probably need to be more generic
addr = l2dcrtr1_val & 0xFFFE0000
addr = addr | (select & 0x0001ff80)
valid = (l2dcrtr0_val >> 14) & 0x3
out_str = '({0:4},{1:1}) {2:5} {3:8x} '.format(i, j, valid, addr)
cache_line_data_ptr = cache_line_ptr + \
cache_line_data_offset_struct
for k in range(0, 32):
out_str = out_str + \
'{0:0=8x} '.format(
ram_dump.read_word(cache_line_data_ptr + 4 * k, False))
out_str = out_str + \
'{0:0=8x} {1:0=8x}\n'.format(l2dcrtr0_val, l2dcrtr1_val)
cache_file.write(out_str)
select = select + 0x10
cache_file.close()
print_out_str('--- Wrote cache dump to l2_cache_dump.txt')
@register_parser('--print-cache-dump', 'Print L2 cache dump', optional=True)
class CacheDump(RamParser):
def parse(self):
if not self.ramdump.is_config_defined('CONFIG_MSM_CACHE_DUMP'):
print_out_str(
'!!! Cache dumping was not enabled. No cache will be dumped')
return
cache_base_addr = self.ramdump.address_of('l2_dump')
cache_base = self.ramdump.read_word(cache_base_addr)
parse_cache_dump(self.ramdump, cache_base)

View File

@@ -0,0 +1,76 @@
# Copyright (c) 2018-2019, 2021 The Linux Foundation. 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.
from parser_util import cleanupString
from parser_util import register_parser, RamParser
from print_out import print_out_str
from re import finditer
# This is hardcoded since coreboot console is always at below physical address
#addr1 = 0xfffde000
#len1 = 0x20000
CBMC_CURSOR_MASK = ((1 << 28) - 1)
CBMC_OVERFLOW = (1 << 31)
COREBOOT_BOOTBLOCK = b'coreboot-[^\n]* bootblock starting.*\\.\\.\\.\n'
@register_parser('--cbmem', 'Print the coreboot console log', shortopt='-z')
class CBMEM(RamParser):
def print_cbmem(self):
#get starting address of cbmem_console
#get the size of console
cbmem_console_addr = self.ramdump.read_u64('cbmem_console')
cbmem_console_size = self.ramdump.read_u32('cbmem_console_size')
if (cbmem_console_addr is None) or (cbmem_console_size is None):
print_out_str('cbmem_console stucture not found')
return
#read the valie of cbmem_console->cursor. getting offset and reading u32
cursor_offset = self.ramdump.field_offset('struct cbmem_cons', 'cursor')
cbmem_console_cusor = self.ramdump.read_u32(cbmem_console_addr+cursor_offset)
#convert the console address to Phy and read full untill size
addr = self.ramdump.virt_to_phys(cbmem_console_addr)
size = self.ramdump.sizeof('struct cbmem_console')
cursor = cbmem_console_cusor & CBMC_CURSOR_MASK
if (not(cbmem_console_cusor & CBMC_OVERFLOW) and cursor < cbmem_console_size):
size = cursor
else:
size = cbmem_console_size
if (cbmem_console_cusor & CBMC_OVERFLOW):
if (cursor >= size):
print_out_str("cbmem: ERROR: CBMEM console struct is illegal, "
"output may be corrupt or out of order!\n\n")
cursor = 0
cbmem = self.ramdump.read_physical(addr+cursor, size-cursor)
cbmemPart1 = self.ramdump.read_physical(addr, cursor)
cbmem = cbmem + cbmemPart1
else:
cbmem = self.ramdump.read_physical(addr, size)
cbmem_console_out = self.ramdump.open_file('cbmem_console.txt')
cbmem_console_out.write(cleanupString(cbmem.decode('ascii', 'ignore')) + '\n')
cbmem_console_out.close()
m = 0
for match in finditer(COREBOOT_BOOTBLOCK, cbmem):
m = match.start()
cbmem = cbmem[m:]
cbmem_out = self.ramdump.open_file('cbmem.txt')
cbmem_out.write(cleanupString(cbmem.decode('ascii', 'ignore')) + '\n')
cbmem_out.close()
print_out_str('Wrote Coreboot console log to cbmem.txt')
def parse(self):
self.print_cbmem()

View File

@@ -0,0 +1,379 @@
# Copyright (c) 2015,2017-2018 The Linux Foundation. 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 linux_list
from print_out import print_out_str
from parser_util import register_parser, RamParser
@register_parser('--clock-dump', 'Dump all the clocks in the system')
class ClockDumps(RamParser):
def __init__(self, *args):
super(ClockDumps, self).__init__(*args)
self.enabled_clocks = []
self.disabled_clocks = []
self.prepared_clocks = []
self.head = ''
def print_header(self, type, title):
if type == 'CLK_PROVIDERS':
self.output_file.write("--------------------------------------------\n")
self.output_file.write("{0} from of_clk_providers list\n".format(title))
self.output_file.write("--------------------------------------------\n")
str = " {0:40} {1:21} {2:25} {3:10} {4:45} {5:40}\n"
self.output_file.write(str.format('CLOCK NAME',
'COUNT/PREPARE_COUNT',
'RATE', 'CUR_LEVEL',
'CLOCK STRUCTURE', 'CLOCK_OPS'))
elif type == 'CLOCKS':
self.output_file.write("----------------------------------\n")
self.output_file.write("{0} from clocks list\n".format(title))
self.output_file.write("----------------------------------\n")
self.output_file.write(" {0:40} {1:25} {2:20} {3:21} {4:25} {5:20} {6:40}\n".format('CLOCK NAME', 'DEVID', 'CONID', 'COUNT/PREPARE_COUNT', 'RATE', 'CUR_LEVEL', 'CLOCK STRUCTURE'))
def printclocks(self, type):
if len(self.disabled_clocks):
self.print_header(type, "Disabled Clocks")
for clocks in self.disabled_clocks:
self.output_file.write('D ' + clocks)
if len(self.enabled_clocks):
self.output_file.write("\n")
self.print_header(type, "Enabled Clocks")
for clocks in self.enabled_clocks:
self.output_file.write('E ' + clocks)
if len(self.prepared_clocks):
self.output_file.write("\n")
self.print_header(type, "Prepared Clocks")
for clocks in self.prepared_clocks:
self.output_file.write('P ' + clocks)
def get_clocks(self):
clocks = self.ramdump.address_of('clocks')
if clocks is None:
self.output_file.write("NOTE: 'clocks' list not found to extract the clocks information")
return
head = self.ramdump.read_word(clocks, True)
self.head = clocks
node_offset = self.ramdump.field_offset('struct clk_lookup', 'node')
clocks_walker = linux_list.ListWalker(self.ramdump, head, node_offset)
clocks_walker.walk(head, self.clocks_walker)
def clocks_walker(self, node):
if node == self.head:
return
devid_address = node + self.ramdump.field_offset('struct clk_lookup', 'dev_id')
devid = self.ramdump.read_cstring(self.ramdump.read_word(devid_address, True), 48)
conid_address = node + self.ramdump.field_offset('struct clk_lookup', 'con_id')
conid = self.ramdump.read_cstring(self.ramdump.read_word(conid_address, True), 48)
clock_address = node + self.ramdump.field_offset('struct clk_lookup', 'clk')
clk = self.ramdump.read_word(clock_address, True)
dbg_name_address = clk + self.ramdump.field_offset('struct clk', 'dbg_name')
dbg_name = self.ramdump.read_cstring(self.ramdump.read_word(dbg_name_address, True), 48)
rate_address = clk + self.ramdump.field_offset('struct clk', 'rate')
rate = self.ramdump.read_word(rate_address, True)
count_address = clk + self.ramdump.field_offset('struct clk', 'count')
count = self.ramdump.read_u32(count_address, True)
prepare_count_address = clk + self.ramdump.field_offset('struct clk', 'prepare_count')
prepare_count = self.ramdump.read_u32(prepare_count_address, True)
vdd_class_address = clk + self.ramdump.field_offset('struct clk', 'vdd_class')
vdd_class = self.ramdump.read_word(vdd_class_address, True)
if vdd_class != 0:
cur_level_address = vdd_class + self.ramdump.field_offset('struct clk_vdd_class', 'cur_level')
cur_level = self.ramdump.read_word(cur_level_address, True)
else:
cur_level = "NULL"
output = "{0:40} {1:<25} {2:20} {3:<2}/ {4:<17} {5:<25} {6:<10} v.v (struct clk *)0x{7:<20x}\n".format(
dbg_name, devid, conid, count, prepare_count, rate, cur_level, clk)
if count > 0:
self.enabled_clocks.append(output)
elif prepare_count > 0:
self.prepared_clocks.append(output)
else:
self.disabled_clocks.append(output)
def get_clk_providers(self):
clocks = self.ramdump.address_of('of_clk_providers')
if clocks is None:
self.output_file.write("NOTE: 'of_clk_providers' list not found to extract the clocks information")
return
self.enabled_clocks = []
self.disabled_clocks = []
self.prepared_clocks = []
self.head = clocks
head = self.ramdump.read_word(clocks, True)
node_offset = self.ramdump.field_offset('struct clk_lookup', 'node')
clk_providers_walker = linux_list.ListWalker(self.ramdump, head, node_offset)
clk_providers_walker.walk(head, self.clk_providers_walker)
def print_clk_of_msm_provider_data(self, data):
table_address = data + self.ramdump.field_offset('struct of_msm_provider_data', 'table')
size_address = data + self.ramdump.field_offset('struct of_msm_provider_data', 'size')
table = self.ramdump.read_word(table_address, True)
size = self.ramdump.read_word(size_address, True)
counter = 0
while counter < size:
clock_address = table + self.ramdump.field_offset('struct clk_lookup', 'clk')
clk = self.ramdump.read_word(clock_address, True)
dbg_name_address = clk + self.ramdump.field_offset('struct clk', 'dbg_name')
dbg_name = self.ramdump.read_cstring(self.ramdump.read_word(dbg_name_address, True), 48)
rate_address = clk + self.ramdump.field_offset('struct clk', 'rate')
rate = self.ramdump.read_word(rate_address, True)
count_address = clk + self.ramdump.field_offset('struct clk', 'count')
count = self.ramdump.read_u32(count_address, True)
prepare_count_address = clk + self.ramdump.field_offset('struct clk', 'prepare_count')
prepare_count = self.ramdump.read_u32(prepare_count_address, True)
vdd_class_address = clk + self.ramdump.field_offset('struct clk', 'vdd_class')
vdd_class = self.ramdump.read_word(vdd_class_address, True)
if vdd_class != 0:
cur_level_address = vdd_class + self.ramdump.field_offset('struct clk_vdd_class', 'cur_level')
cur_level = self.ramdump.read_word(cur_level_address, True)
else:
cur_level = "NULL"
output = "{0:40} {1:<2}/ {2:<17} {3:<25} {4:<10} v.v (struct clk *)0x{5:<20x}\n".format(dbg_name, count, prepare_count, rate, cur_level, clk)
if count > 0:
self.enabled_clocks.append(output)
elif prepare_count > 0:
self.prepared_clocks.append(output)
else:
self.disabled_clocks.append(output)
counter = counter + 1
table = table + self.ramdump.sizeof('struct clk_lookup')
def dump_clock(self,clk_core,clk_name):
offset_vdd_cur_level = self.ramdump.field_offset(
'struct clk_vdd_class', 'cur_level')
clk_prepare_count = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'prepare_count')
clk_enable_count = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'enable_count')
clk_rate = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'rate')
vdd_class = self.ramdump.read_structure_field(
clk_core,'struct clk_core','vdd_class')
clk_ops = self.ramdump.read_structure_field(
clk_core,'struct clk_core','ops')
clk_ops = self.ramdump.unwind_lookup(clk_ops)
if clk_ops is None:
clk_ops = ["dynamic module"]
cur_level = 0
if vdd_class != 0 and vdd_class is not None:
cur_level_address = (vdd_class + offset_vdd_cur_level)
cur_level = self.ramdump.read_word(cur_level_address, True)
formatStr = "{0:40} {1:<2}/ {2:<17} {3:<25} {4:<10} " \
"v.v (struct clk_core *)0x{5:<20x} {6:<40}\n"
output = formatStr.format(
clk_name,
clk_enable_count,
clk_prepare_count,
clk_rate, cur_level,
clk_core,clk_ops[0])
if clk_enable_count > 0:
self.enabled_clocks.append(output)
elif clk_prepare_count > 0:
self.prepared_clocks.append(output)
else:
self.disabled_clocks.append(output)
def print_clk_onecell_data(self, data):
offset_clk_onecell_data_clks = (
self.ramdump.field_offset('struct clk_onecell_data', 'clks'))
offset_clk_onecell_data_clknum = (
self.ramdump.field_offset(
'struct clk_onecell_data', 'clk_num'))
clks = self.ramdump.read_word(data + offset_clk_onecell_data_clks)
if (clks == 0 or clks == None):
return
size = self.ramdump.read_int(data + offset_clk_onecell_data_clknum)
sizeof_clk_pointer = self.ramdump.sizeof('struct clk *')
offset_vdd_cur_level = self.ramdump.field_offset(
'struct clk_vdd_class', 'cur_level')
counter = 0
if size > 10000:
return
while counter < size:
clk = self.ramdump.read_word(clks + (sizeof_clk_pointer * counter))
if clk == 0 or clk is None:
counter = counter + 1
continue
clk_core = self.ramdump.read_structure_field(
clk, 'struct clk', 'core')
if clk_core == 0 or clk_core is None:
counter = counter + 1
continue
clk_name_addr = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'name')
clk_name = self.ramdump.read_cstring(clk_name_addr, 48)
if (clk_name == 0 or clk_name == None):
break
self.dump_clock(clk_core,clk_name)
counter = counter + 1
# qcom_cc_clk_hw_get clk is added in kernel 4.9
def print_clk_qcom_cc_data(self, data):
size = self.ramdump.read_structure_field(
data,'struct qcom_cc','num_rclks')
clks = self.ramdump.read_structure_field(
data,'struct qcom_cc','rclks')
sizeof_clk_regmap = self.ramdump.sizeof('struct clk_regmap *')
offset_vdd_cur_level = self.ramdump.field_offset(
'struct clk_vdd_class', 'cur_level')
counter = 0
while counter < size:
clk = self.ramdump.read_word(clks +
(sizeof_clk_regmap * counter))
clk_core = self.ramdump.read_structure_field(
clk,'struct clk_regmap','hw.core')
if clk_core == 0 or clk_core is None:
counter = counter + 1
continue
clk_name_addr = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'name')
clk_name = self.ramdump.read_cstring(clk_name_addr, 48)
if (clk_name == 0 or clk_name == None):
break
self.dump_clock(clk_core,clk_name)
counter = counter + 1
# spmi_pmic_div_clk_hw_get clk is added kernel 4.14
def print_clk_spmi_pmic_data(self, data):
size = self.ramdump.read_structure_field(
data,'struct spmi_pmic_div_clk_cc','nclks')
clks = self.ramdump.field_offset(
'struct spmi_pmic_div_clk_cc','clks')
clks = data + clks
sizeof_clk_regmap = self.ramdump.sizeof('struct clkdiv')
offset_vdd_cur_level = self.ramdump.field_offset(
'struct clk_vdd_class', 'cur_level')
counter = 0
while counter < size:
clk = clks + (sizeof_clk_regmap * counter)
clk_core = self.ramdump.read_structure_field(
clk,'struct clkdiv','hw.core')
if clk_core == 0 or clk_core is None:
counter = counter + 1
continue
clk_name_addr = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'name')
clk_name = self.ramdump.read_cstring(clk_name_addr, 48)
if (clk_name == 0 or clk_name == None):
break
self.dump_clock(clk_core,clk_name)
counter = counter + 1
# of_clk_src_simple_get was added for quite a long time, but
# hasn't been used until clk-dummy.ko is enabled by
# CONFIG_COMMON_CLK_QCOM on Hypervisor based platforms
def print_clk_simple(self, data):
clk = data
clk_core = self.ramdump.read_structure_field(
clk, 'struct clk', 'core')
clk_name_addr = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'name')
clk_name = self.ramdump.read_cstring(clk_name_addr, 48)
if clk_name == 0 or clk_name is None:
return
self.dump_clock(clk_core, clk_name)
# of_clk_hw_virtio_get clk was added since kernel 5.4 but
# only on Hypervisor based platforms
def print_clk_virtio(self, data):
size = self.ramdump.read_structure_field(
data, 'struct virtio_clk', 'num_clks')
clks = self.ramdump.read_structure_field(
data, 'struct virtio_clk', 'clks')
sizeof_clk_virtio = self.ramdump.sizeof('struct clk_virtio')
for counter in range(size):
clk = clks + (sizeof_clk_virtio * counter)
clk_core = self.ramdump.read_structure_field(
clk, 'struct clk_virtio', 'hw.core')
if clk_core == 0 or clk_core is None:
continue
clk_name_addr = self.ramdump.read_structure_field(
clk_core, 'struct clk_core', 'name')
clk_name = self.ramdump.read_cstring(clk_name_addr, 48)
if clk_name == 0 or clk_name is None:
break
self.dump_clock(clk_core, clk_name)
def clk_providers_walker(self, node):
if node == self.head:
return
data_address = node + self.ramdump.field_offset(
'struct of_clk_provider', 'data')
data = self.ramdump.read_word(data_address, True)
getfunc = self.ramdump.read_structure_field(
node,'struct of_clk_provider','get')
if getfunc == 0:
getfunchw = self.ramdump.read_structure_field(
node,'struct of_clk_provider','get_hw')
getfunchw = self.ramdump.unwind_lookup(getfunchw)
if "spmi_pmic_div_clk_hw_get" in getfunchw[0]:
self.print_clk_spmi_pmic_data(data)
return
elif "qcom_cc_clk_hw_get" in getfunchw[0]:
self.print_clk_qcom_cc_data(data)
return
elif "of_clk_hw_virtio_get" in getfunchw[0]:
self.print_clk_virtio(data)
return
else:
return
getfunc = self.ramdump.unwind_lookup(getfunc)
if "of_clk_src_simple_get" in getfunc[0]:
self.print_clk_simple(data)
elif self.ramdump.is_config_defined('CONFIG_COMMON_CLK_MSM'):
self.print_clk_of_msm_provider_data(data)
else:
self.print_clk_onecell_data(data)
def print_a7_cpu_frequency(self):
cpu_clks_hws = self. ramdump.read_u32('cpu_clks_hws')
if cpu_clks_hws is not None:
clk_offset = self.ramdump.read_u32(cpu_clks_hws + 0x4)
core_offset = self.ramdump.read_u32(clk_offset)
clk_rate_offset = self.ramdump.field_offset('struct clk_core',
'rate')
dbg_name_address = core_offset + self.ramdump.field_offset('struct clk_core', 'name')
dbg_name = self.ramdump.read_cstring(self.ramdump.read_word(dbg_name_address, True), 48)
cpu_frequency = self.ramdump.read_u32(core_offset + clk_rate_offset)
self.output_file.write("{0} = {1} \n".format(dbg_name, cpu_frequency))
def parse(self):
self.output_file = self.ramdump.open_file('ClockDumps.txt')
if (self.ramdump.kernel_version < (4, 9, 0)):
self.get_clocks()
self.printclocks('CLOCKS')
self.get_clk_providers()
self.printclocks('CLK_PROVIDERS')
self.print_a7_cpu_frequency()
self.output_file.close()
print_out_str("--- Wrote the output to ClockDumps.txt")

View File

@@ -0,0 +1,95 @@
# Copyright (c) 2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022 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 sys
import re
import os
import struct
import string
import glob
from parser_util import register_parser, RamParser
@register_parser('--print-svm-vcpu-ctx', 'Print svm vcpu context')
class svm_vcpu_context_parse(RamParser):
def svm_vcpu_context_parse(self,fop):
opfile = fop
for core in range(0,2):
input_file = "corevcpu{0}_vm_*.cmm".format(core)
file_path_list = glob.glob(os.path.join(self.ramdump.outdir + "\..", input_file))
for each_file in file_path_list:
input_file_cmm = each_file
if "svm" in self.ramdump.hw_id and "vm_45" not in each_file:
continue
if "oemvm" in self.ramdump.hw_id and "vm_49" not in each_file:
continue
if os.path.exists(input_file_cmm):
fd = open(input_file_cmm, "r")
else:
print("not exisit")
continue
fp = 0
sp = 0
lr = 0
pc = 0
pc_flag = False
lr_flag = False
sp_flag = False
fp_flag = False
for line in fd:
columns = line.split()
if "r.s pc" in line and pc_flag is False:
pc_flag = True
pc = int(columns[-1], 16)
if "r.s x30" in line and lr_flag is False:
lr_flag = True
lr = int(columns[-1], 16)
if "r.s x29" in line and fp_flag is False:
fp_flag = True
fp = int(columns[-1], 16)
if "r.s sp_el1" in line and sp_flag is False:
sp_flag = True
sp = int(columns[-1], 16)
opfile.write("Core {0} context\n".format(core))
a = self.ramdump.unwind_lookup(pc)
if a is not None:
symname, offset = a
else:
symname = 'UNKNOWN'
offset = 0
opfile.write(
'Core {3} PC: {0}+{1:x} <0x{2:x}>'.format(symname, offset,
pc, core))
opfile.write("\n")
a = self.ramdump.unwind_lookup(lr)
if a is not None:
symname, offset = a
else:
symname = 'UNKNOWN'
offset = 0
opfile.write(
'Core {3} LR: {0}+{1:x} <0x{2:x}>'.format(symname, offset,
lr, core))
opfile.write("\n")
opfile.write('')
self.ramdump.unwind.unwind_backtrace(sp, fp, pc, lr, '',opfile)
opfile.write('')
opfile.write("\n")
opfile.close()
def parse(self):
with self.ramdump.open_file('vm_vcpu_context.txt') as fop:
self.svm_vcpu_context_parse(fop)
return

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,630 @@
# Copyright (c) 2015-2017, 2020 The Linux Foundation. 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 linux_list
from print_out import print_out_str
from parser_util import register_parser, RamParser
from collections import defaultdict
CPRH_CTRL_TYPE = 2
@register_parser('--cpr3-info', 'Print CPR3 information')
class CPR3Info(RamParser):
def __init__(self, *args):
super(CPR3Info, self).__init__(*args)
self.head = ''
self.cprinfo_fields = ['speed_bin', 'cpr_fuse_revision',
'cpr_fuse_map_match', 'num_fuse_corners',
'num_corners', 'corner']
self.voltages = ['ceiling_volt', 'open_loop_volt',
'last_volt', 'floor_volt']
self.corner_info = ['cpr_fuse_target_quot',
'quot_adjust', 'corner_map']
self.value_list = defaultdict(list)
self.attr_list = defaultdict(list)
self.output = []
self.consumer_head = ''
def get_cpr(self):
# Return if the cpr3_regulator_list is not available
cpr = self.ramdump.address_of('cpr3_controller_list')
if cpr is None:
self.output_file.write(
"NOTE: 'cpr3_regulator_list' list " +
"not found to extract cpr information")
return
head = self.ramdump.read_word(cpr)
self.head = cpr
node_offset = self.ramdump.field_offset('struct cpr3_controller',
'list')
c_w = linux_list.ListWalker(self.ramdump, head, node_offset)
c_w.walk(head, self.cpr_walker)
def get_kryo(self):
kryo_addr = self.ramdump.address_of('kryo_regulator_list')
if kryo_addr is None:
print_out_str(
"NOTE: 'kryo_regulator_list' list " +
"not found to extract kryo_addr information")
return
head = self.ramdump.read_word(kryo_addr)
self.head = kryo_addr
node_offset = self.ramdump.field_offset('struct kryo_regulator',
'link')
k_w = linux_list.ListWalker(self.ramdump, head, node_offset)
tmp = "=" * 80 + "\n"
tmp += "Kryo Regulator (LDO/BHS management)\n"
tmp += "=" * 80 + "\n"
self.output.append(tmp)
k_w.walk(head, self.kryo_walker)
def dump_cpr3_regulator_voltages(self, vreg_addr):
corner_count = self.ramdump.read_int(
vreg_addr +
self.ramdump.field_offset(
'struct cpr3_regulator',
'corner_count'))
tmp = 'CPR voltages(uV) and Target Quotients\n'
tmp += "%-7s%-3s%-10s%-10s%-10s%-21s%-40s\n" % (
"Corner", "-", "Floor", "Open-Loop", "Ceiling",
"Cached closed-loop",
"Target Quotients")
self.output.append(tmp)
base_addr = self.ramdump.read_word(
vreg_addr +
self.ramdump.field_offset('struct cpr3_regulator', 'corner'))
size = self.ramdump.sizeof('struct cpr3_corner')
for i in range(corner_count):
corner_addr = base_addr + size * i
try:
self.dump_cpr3_corner_info(corner_addr, i + 1, 1, 1)
except:
self.output.append(
"Note: Failed to dump cpr3 regulator voltage\n")
return
def dump_cpr3_corner_info(self, aggr_corner_addr, corner_num,
print_quots, list_format):
if aggr_corner_addr is None:
return
ceiling = self.ramdump.read_int(
aggr_corner_addr +
self.ramdump.field_offset(
'struct cpr3_corner', 'ceiling_volt'))
open_loop = self.ramdump.read_int(
aggr_corner_addr +
self.ramdump.field_offset(
'struct cpr3_corner', 'open_loop_volt'))
last = self.ramdump.read_int(
aggr_corner_addr +
self.ramdump.field_offset(
'struct cpr3_corner', 'last_volt'))
floor = self.ramdump.read_int(
aggr_corner_addr +
self.ramdump.field_offset(
'struct cpr3_corner', 'floor_volt'))
quots = ""
if print_quots == 1:
t0 = aggr_corner_addr + self.ramdump.field_offset(
'struct cpr3_corner', 'target_quot')
size = self.ramdump.sizeof('u32')
num = self.ramdump.sizeof('((struct cpr3_corner*)0)->target_quot') \
// size
for i in range(num):
quot = self.ramdump.read_u32(t0 + i * size)
quots = quots + " " + str(quot)
if list_format == 0:
tmp = '%-30s = %d uV\n' % ("Ceiling volt", ceiling)
tmp += '%-30s = %d uV\n' % ("Open-loop", open_loop)
tmp += '%-30s = %d uV\n' % ("Cached closed-loop", last)
tmp += '%-30s = %d uV\n' % ("Floor", floor)
self.output.append(tmp)
if print_quots == 1:
tmp = "\n%-30s = %s\n" % ("Target quotients", quots)
self.output.append(tmp)
self.output.append("\n")
else:
tmp = "%-7d%-3s%-10d%-10d%-10d%-20d" % (
corner_num, "-", floor, open_loop, ceiling, last)
tmp += quots
tmp += "\n"
self.output.append(tmp)
def dump_vdd_regulator(self, ctrl_addr):
tmp = ""
vdd_reg_addr = self.ramdump.read_word(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'vdd_regulator'))
if vdd_reg_addr is None:
return
rdev_addr = self.ramdump.read_word(
vdd_reg_addr +
self.ramdump.field_offset(
'struct regulator',
'rdev'))
reg_data_addr = self.ramdump.read_word(
rdev_addr +
self.ramdump.field_offset('struct regulator_dev', 'reg_data'))
desc_addr = self.ramdump.read_word(
rdev_addr +
self.ramdump.field_offset('struct regulator_dev', 'desc'))
desc_ops_addr = self.ramdump.read_word(
desc_addr +
self.ramdump.field_offset('struct regulator_desc', 'ops'))
name_addr = self.ramdump.read_word(
desc_addr +
self.ramdump.field_offset('struct regulator_desc', 'name'))
name = self.ramdump.read_cstring(name_addr, 48)
tmp += '\n%-30s = %s\n' % ("PMIC supply", name)
duple = self.ramdump.unwind_lookup(desc_ops_addr)
function_name = duple[0]
if "qpnp" in function_name:
# QPNP-regulator
set_points_addr = self.ramdump.read_word(
reg_data_addr + self.ramdump.field_offset(
'struct qpnp_regulator',
'set_points'))
range_addr = self.ramdump.read_word(
set_points_addr +
self.ramdump.field_offset(
'struct qpnp_voltage_set_points', 'range'))
step_uV = self.ramdump.read_int(
range_addr +
self.ramdump.field_offset(
'struct qpnp_voltage_range',
'step_uV'))
min_uV = self.ramdump.read_int(
range_addr +
self.ramdump.field_offset(
'struct qpnp_voltage_range', 'min_uV'))
volt_sel = self.ramdump.read_byte(
reg_data_addr + 1 +
self.ramdump.field_offset(
'struct qpnp_regulator', 'ctrl_reg'))
volt = (volt_sel * step_uV) + min_uV
tmp += "%-30s = %d uV\n" % ("PMIC voltage", volt)
if "spm" in function_name:
last_set_volt = self.ramdump.read_int(
reg_data_addr + self.ramdump.field_offset(
'struct spm_vreg', 'last_set_uV'))
tmp += "%-30s = %d uV\n" % ("PMIC last set voltage",
last_set_volt)
self.output.append(tmp)
def get_apm_threshold(self, addr_ctrl):
apm_addr = self.ramdump.read_word(
addr_ctrl + self.ramdump.field_offset('struct cpr3_controller',
'apm'))
if apm_addr is None:
return
apm_thresh_volt = self.ramdump.read_int(
addr_ctrl + self.ramdump.field_offset(
'struct cpr3_controller', 'apm_threshold_volt'))
if apm_thresh_volt == 0:
return
tmp = '%-30s = %d uV\n' % ("APM threshold", apm_thresh_volt)
if apm_addr == 0:
self.output.append(tmp)
return
apm_supply = self.ramdump.read_int(
apm_addr + self.ramdump.field_offset('struct msm_apm_ctrl_dev',
'supply'))
if apm_supply is None:
print_out_str("could not read APM supply")
elif apm_supply == 0:
tmp += '%-30s = %s\n' % ("APM supply", "APCC")
elif apm_supply == 1:
tmp += '%-30s = %s\n' % ("APM supply", "MX")
self.output.append(tmp)
def get_aging_info(self, ctrl_addr):
test = self.ramdump.field_offset(
'struct cpr3_controller', 'aging_required')
if test is None:
return
aging_required = self.ramdump.read_bool(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'aging_required'))
aging_succeeded = self.ramdump.read_bool(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'aging_succeeded'))
aging_failed = self.ramdump.read_bool(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'aging_failed'))
tmp = ""
if (aging_required or aging_succeeded or aging_failed):
if aging_succeeded:
aging_ref_adjust_volt = self.ramdump.read_int(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'aging_ref_adjust_volt'))
tmp += '%-30s = %s\n' % ("Aging measurement", "succeeded")
tmp += '%-30s = %d uV\n' % ("Aging adjustment voltage",
aging_ref_adjust_volt)
elif aging_failed:
tmp += '%-30s = %s\n' % ("Aging measurement",
"failed")
else:
tmp += '%-30s = %s\n' % ("Aging measurement",
"not yet executed")
self.output.append(tmp)
def dump_cpr3_regulator_state(self, vreg_addr, ctrl_type):
tmp = ""
if vreg_addr is None:
return
name_addr = self.ramdump.read_word(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'name'))
name = self.ramdump.read_cstring(name_addr, 48)
tmp += "-" * 80 + "\n"
tmp += "Regulator: %s\n" % name
tmp += "-" * 80 + "\n"
vreg_enabled = self.ramdump.read_bool(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator',
'vreg_enabled'))
current_corner = self.ramdump.read_int(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'current_corner'))
corner_count = self.ramdump.read_int(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator',
'corner_count'))
ldo_regulator_addr = self.ramdump.read_word(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'ldo_regulator'))
ldo_mode_allowed = self.ramdump.read_bool(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'ldo_mode_allowed'))
cpr_rev_fuse = self.ramdump.read_int(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'cpr_rev_fuse'))
if ldo_regulator_addr != 0:
ldo_mode_bool = self.ramdump.read_bool(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator',
'ldo_regulator_bypass'))
if ldo_mode_bool:
ldo_mode = "LDO"
else:
ldo_mode = "BHS"
tmp += "%-30s = %d\n" % ("CPR fuse revision", cpr_rev_fuse)
fuse_combo = self.ramdump.read_int(
vreg_addr +
self.ramdump.field_offset(
'struct cpr3_regulator', 'fuse_combo'))
tmp += "%-30s = %d\n" % ("CPR fuse combo", fuse_combo)
speed_bin_fuse = self.ramdump.read_int(
vreg_addr +
self.ramdump.field_offset(
'struct cpr3_regulator',
'speed_bin_fuse'))
tmp += "%-30s = %d\n" % ("Speed-bin fuse", speed_bin_fuse)
if ctrl_type != CPRH_CTRL_TYPE:
tmp += "\n%-30s = %d/%d\n" % ("CPR corner", current_corner + 1,
corner_count)
if vreg_enabled is True:
vreg_enabled = 1
else:
vreg_enabled = 0
tmp += "%-30s = %d\n" % ("Enabled", vreg_enabled)
if ldo_regulator_addr != 0:
if ldo_mode_allowed is True:
ldo_mode_allowed = 1
else:
ldo_mode_allowed = 0
tmp += "\n%-30s = %d\n" % ("LDO mode allowed", ldo_mode_allowed)
tmp += "%-30s = %s\n" % ("LDO/BHS mode", ldo_mode)
tmp += "\nCurrent CPR voltages:\n"
self.output.append(tmp)
tmp = ""
corner_addr = self.ramdump.read_word(
vreg_addr + self.ramdump.field_offset(
'struct cpr3_regulator', 'corner'))
size = self.ramdump.sizeof("struct cpr3_corner")
if ctrl_type != CPRH_CTRL_TYPE:
corner_addr = corner_addr + current_corner * size
self.dump_cpr3_corner_info(corner_addr, 0, 1, 0)
self.dump_cpr3_regulator_voltages(vreg_addr)
rdev_addr = self.ramdump.read_word(
vreg_addr + self.ramdump.field_offset('struct cpr3_regulator',
'rdev'))
offset = self.ramdump.field_offset('struct regulator_dev',
'consumer_list')
if ctrl_type != CPRH_CTRL_TYPE:
self.dump_consumer(rdev_addr + offset)
def dump_cpr3_thread_state(self, thread_addr, ctrl_type):
tmp = ""
thread_id = self.ramdump.read_u32(
thread_addr + self.ramdump.field_offset(
'struct cpr3_thread', 'thread_id'))
aggr_corner_addr = thread_addr + self.ramdump.field_offset(
'struct cpr3_thread', 'aggr_corner')
tmp += "-" * 80 + "\n"
tmp += "Thread: %d\n" % thread_id
tmp += "-" * 80 + "\n"
if ctrl_type != CPRH_CTRL_TYPE:
tmp += "CPR aggregated voltages:\n"
self.output.append(tmp)
if ctrl_type != CPRH_CTRL_TYPE:
self.dump_cpr3_corner_info(aggr_corner_addr, 0, 1, 0)
vreg_addr = self.ramdump.read_word(
thread_addr +
self.ramdump.field_offset('struct cpr3_thread', 'vreg'))
vreg_count = self.ramdump.read_int(
thread_addr +
self.ramdump.field_offset(
'struct cpr3_thread', 'vreg_count'))
size_reg = self.ramdump.sizeof('struct cpr3_regulator')
for i in range(vreg_count):
self.dump_cpr3_regulator_state(vreg_addr + i * size_reg, ctrl_type)
def cpr_walker(self, ctrl_addr):
if ctrl_addr == self.head:
return
cpr_controller_name_addr = self.ramdump.read_word(
ctrl_addr + self.ramdump.field_offset('struct cpr3_controller',
'name'))
cpr_controller_name = self.ramdump.read_cstring(
cpr_controller_name_addr, 48)
supports_hw_closed_loop = self.ramdump.read_bool(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller',
'supports_hw_closed_loop'))
use_hw_closed_loop = self.ramdump.read_bool(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller',
'use_hw_closed_loop'))
cpr_allowed_sw = self.ramdump.read_bool(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller',
'cpr_allowed_sw'))
cpr_allowed_hw = self.ramdump.read_bool(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller',
'cpr_allowed_hw'))
cpr_enabled = self.ramdump.read_bool(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller', 'cpr_enabled'))
ctrl_type = self.ramdump.read_int(
ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller',
'ctrl_type'))
if cpr_allowed_sw == 0 or cpr_allowed_hw == 0:
cpr_mode = "open-loop"
elif supports_hw_closed_loop == 1 and ctrl_type != CPRH_CTRL_TYPE:
if use_hw_closed_loop == 0:
cpr_mode = "SW closed-loop"
else:
cpr_mode = "HW closed-loop"
elif supports_hw_closed_loop == 1 and ctrl_type == CPRH_CTRL_TYPE:
if use_hw_closed_loop == 0:
cpr_mode = "open-loop"
else:
cpr_mode = "full HW closed-loop"
else:
cpr_mode = "closed-loop"
thread_addr = self.ramdump.read_word(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'thread'))
if thread_addr is None:
return
thread_ctrl_addr = self.ramdump.read_word(
thread_addr +
self.ramdump.field_offset('struct cpr3_thread', 'ctrl'))
if cpr_controller_name is None or thread_ctrl_addr != ctrl_addr:
return
tmp = ""
tmp += "=" * 80 + "\n"
tmp += 'CPR3 controller state: %s\n' % cpr_controller_name
tmp += "=" * 80 + "\n"
tmp += '%-30s = %s\n' % ("CPR mode", cpr_mode)
tmp += '%-30s = %d\n' % ("CPR loop currently operating",
cpr_enabled)
self.output.append(tmp)
tmp = ""
self.get_apm_threshold(ctrl_addr)
self.get_aging_info(ctrl_addr)
if ctrl_type != CPRH_CTRL_TYPE:
self.dump_vdd_regulator(ctrl_addr)
if cpr_allowed_sw == 1 and use_hw_closed_loop == 1 and ctrl_type != CPRH_CTRL_TYPE:
tmp = "* The actual voltage at the PMIC may be anywhere " \
"between the aggregated ceiling and floor voltage when"\
" using CPR HW closed-loop mode.\n"
elif ctrl_type == CPRH_CTRL_TYPE:
tmp = "* With full HW closed-loop operation, the expected PMIC " \
"voltage can be checked via the CPRH_STATUS and " \
"L2_SAW4_PMIC_STS registers in the DCC register dump.\n"
self.output.append(tmp)
tmp = ""
if ctrl_type != CPRH_CTRL_TYPE:
aggr_corner_addr = ctrl_addr + self.ramdump.field_offset(
'struct cpr3_controller', 'aggr_corner')
self.output.append("\nCPR aggregated voltages:\n")
self.dump_cpr3_corner_info(aggr_corner_addr, 0, 0, 0)
thread_count = self.ramdump.read_int(
ctrl_addr +
self.ramdump.field_offset(
'struct cpr3_controller', 'thread_count'))
size_thr = self.ramdump.sizeof('struct cpr3_thread')
for i in range(thread_count):
self.dump_cpr3_thread_state(thread_addr + i * size_thr, ctrl_type)
# print new line for each regulator struct
tmp += '\n'
self.output.append(tmp)
def kryo_walker(self, kryo_addr):
if kryo_addr == self.head:
return
retention_mode = self.ramdump.read_int(
kryo_addr +
self.ramdump.field_offset(
'struct kryo_regulator', 'retention_mode'))
mode = self.ramdump.read_int(
kryo_addr + self.ramdump.field_offset('struct kryo_regulator',
'mode'))
if mode == 0:
mode = "BHS"
else:
mode = "LDO"
if retention_mode == 0:
retention_mode = "BHS"
else:
retention_mode = "LDO"
volt = self.ramdump.read_int(kryo_addr + self.ramdump.field_offset(
'struct kryo_regulator', 'volt'))
retention_volt = self.ramdump.read_int(
kryo_addr +
self.ramdump.field_offset(
'struct kryo_regulator',
'retention_volt'))
vreg_en = self.ramdump.read_bool(
kryo_addr +
self.ramdump.field_offset(
'struct kryo_regulator',
'vreg_en'))
vref_func_step_volt = self.ramdump.read_int(
kryo_addr +
self.ramdump.field_offset(
'struct kryo_regulator',
'vref_func_step_volt'))
vref_func_min_volt = self.ramdump.read_int(
kryo_addr +
self.ramdump.field_offset(
'struct kryo_regulator',
'vref_func_min_volt'))
vref_func_max_volt = self.ramdump.read_int(
kryo_addr + self.ramdump.field_offset(
'struct kryo_regulator', 'vref_func_max_volt'))
vref_ret_step_volt = self.ramdump.read_int(
kryo_addr + self.ramdump.field_offset(
'struct kryo_regulator', 'vref_ret_step_volt'))
vref_ret_min_volt = self.ramdump.read_int(
kryo_addr + self.ramdump.field_offset(
'struct kryo_regulator', 'vref_ret_min_volt'))
vref_ret_max_volt = self.ramdump.read_int(
kryo_addr + self.ramdump.field_offset(
'struct kryo_regulator', 'vref_ret_max_volt'))
name_addr = self.ramdump.read_word(
kryo_addr +
self.ramdump.field_offset('struct kryo_regulator', 'name'))
name = self.ramdump.read_cstring(name_addr, 48)
tmp = ""
tmp += "-" * 80 + "\n"
tmp += "Regulator: %s\n" % name
tmp += "-" * 80 + "\n"
tmp += "%-30s = %d\n" % ("Enabled", vreg_en)
tmp += "%-30s = %s\n" % ("Mode", mode)
tmp += "%-30s = %d uV\n" % ("Voltage", volt)
tmp += "%-30s = %s\n" % ("Retention Mode", retention_mode)
tmp += "%-30s = %d uV\n" % ("Retention Voltage", retention_volt)
tmp += "%-30s = %d uV\n" % ("Vref Functional Step Voltage",
vref_func_step_volt)
tmp += "%-30s = %d uV\n" % ("Vref Functional Min Voltage",
vref_func_min_volt)
tmp += "%-30s = %d uV\n" % ("Vref Functional Max Voltage",
vref_func_max_volt)
tmp += "%-30s = %d uV\n" % ("Vref Retention Step Voltage",
vref_ret_step_volt)
tmp += "%-30s = %d uV\n" % ("Vref Retention Min Voltage",
vref_ret_min_volt)
tmp += "%-30s = %d uV\n" % ("Vref Retention Max Voltage",
vref_ret_max_volt)
self.output.append(tmp)
rdev_addr = self.ramdump.read_word(
kryo_addr + self.ramdump.field_offset('struct kryo_regulator',
'rdev'))
offset = self.ramdump.field_offset('struct regulator_dev',
'consumer_list')
self.dump_consumer(rdev_addr + offset)
def dump_consumer(self, consumer_head):
tmp = ""
tmp += "\nConsumers:\n"
tmp += "%-48s%-10s%-10s%-10s\n" % ("Device-Supply", "EN", "Min_Uv",
"Max_Uv")
self.output.append(tmp)
node_offset = self.ramdump.field_offset('struct regulator', 'list')
self.consumer_head = consumer_head
c_w = linux_list.ListWalker(self.ramdump, consumer_head, node_offset)
c_w.walk(consumer_head, self.consumer_walker)
self.output.append("\n")
def consumer_walker(self, reg_addr):
if reg_addr + self.ramdump.field_offset('struct regulator', 'list') \
== self.consumer_head:
return
min_uV = self.ramdump.read_int(
reg_addr +
self.ramdump.field_offset('struct regulator', 'min_uV'))
max_uV = self.ramdump.read_int(
reg_addr +
self.ramdump.field_offset('struct regulator', 'max_uV'))
enabled = self.ramdump.read_int(
reg_addr + self.ramdump.field_offset('struct regulator',
'enabled'))
if enabled == 1:
enabled = 'Y'
else:
enabled = 'N'
name_addr = self.ramdump.read_word(
reg_addr +
self.ramdump.field_offset('struct regulator', 'supply_name'))
name = self.ramdump.read_cstring(name_addr, 64)
tmp = "%-48s%-10s%-10d%-10d\n" % (name, enabled, min_uV, max_uV)
self.output.append(tmp)
def parse(self):
self.output_file = self.ramdump.open_file('cpr3_info.txt')
self.get_cpr()
self.get_kryo()
for i in self.output:
self.output_file.write(i)
print_out_str("--- Wrote the output to cpr3_info.txt")
self.output_file.close()

View File

@@ -0,0 +1,145 @@
# Copyright (c) 2014-2015, The Linux Foundation. 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 linux_list
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
from collections import defaultdict
@register_parser('--cpr-info', 'Print CPR information')
class CPRInfo(RamParser):
def __init__(self, *args):
super(CPRInfo, self).__init__(*args)
self.head = ''
self.cprinfo_fields = ['speed_bin', 'cpr_fuse_revision', 'cpr_fuse_map_match', 'num_fuse_corners', 'num_corners', 'corner']
self.voltages = ['ceiling_volt', 'open_loop_volt', 'last_volt', 'floor_volt']
self.corner_info = ['cpr_fuse_target_quot', 'quot_adjust', 'corner_map']
self.value_list = defaultdict(list)
self.attr_list = defaultdict(list)
self.output = []
def print_cpr_target_quot(self):
tmp = '{0:20}'.format('Target quotient')
for i in range(self.attr_list['num_corners']):
a = self.value_list['corner_map'][i]
b = self.value_list['cpr_fuse_target_quot'][a-1] - self.value_list['quot_adjust'][i]
tmp += '{0:10} '.format(b)
tmp += '\n'
self.output.append(tmp)
def print_cpr_info(self):
tmp = ''
# Print RO_SEL value
num_fuse_corn = self.attr_list['num_fuse_corners']
if num_fuse_corn is not None:
self.output.append('{:40}{:10d}\n'.format('ro_sel', self.value_list['cpr_fuse_ro_sel'][num_fuse_corn-1]))
# Print all available RO_SEL values
tmp += '{:40}'.format('cpr_fuse_ro_sel')
for ro_sel in self.value_list['cpr_fuse_ro_sel']:
tmp += '{:10} '.format(ro_sel)
tmp += '\n\n'
self.output.append(tmp)
tmp = ''
self.output.append('{:20}'.format('Corner'))
for i in range(self.attr_list['num_corners']):
tmp += '{:10} '.format(i + 1)
tmp += '\n'
for volt in self.voltages:
tmp += '{:20}'.format(volt)
for i in self.value_list[volt]:
tmp += '{:10} '.format(i)
tmp += '\n'
self.output.append(tmp)
def get_cpr(self):
# Return if the cpr_regulator_list is not available
cpr = self.ramdump.address_of('cpr_regulator_list')
if cpr is None:
self.output_file.write("NOTE: 'cpr_regulator_list' list not found to extract cpr information")
return
head = self.ramdump.read_word(cpr)
self.head = cpr
node_offset = self.ramdump.field_offset('struct cpr_regulator', 'list')
cpr_walker = linux_list.ListWalker(self.ramdump, head, node_offset)
cpr_walker.walk(head, self.cpr_walker)
def get_cpr_fuse_ro_sel(self, node):
entry_offset = self.ramdump.sibling_field_addr(node, 'struct cpr_regulator', 'list', 'cpr_fuse_ro_sel')
entry_addr = self.ramdump.read_word(entry_offset)
i = 1
while i <= self.attr_list['num_fuse_corners']:
value = self.ramdump.read_int(self.ramdump.array_index(entry_addr, "int", i))
self.value_list['cpr_fuse_ro_sel'].append(value)
i += 1
def get_cpr_volts(self, node, listing):
i = 1
num_corn = self.attr_list['num_corners']
while i <= num_corn:
i += 1
for entry in listing:
entry_offset = self.ramdump.sibling_field_addr(node, 'struct cpr_regulator', 'list', entry)
entry_addr = self.ramdump.read_word(entry_offset)
i = 1
while i <= num_corn:
value = self.ramdump.read_int(self.ramdump.array_index(entry_addr, "int", i))
self.value_list[entry].append(value)
i += 1
def get_cpr_attrs(self, node):
for attr in self.cprinfo_fields:
attr_offset = self.ramdump.field_offset('struct cpr_regulator', attr)
if attr_offset is not None:
value = self.ramdump.read_s32(node + attr_offset)
self.attr_list[attr] = value
tmp = '{:40}{:10}\n'.format(attr, value)
self.output.append(tmp)
attr_offset = self.ramdump.field_offset('struct cpr_regulator', 'cpr_fuse_redundant')
if attr_offset is not None:
value = self.ramdump.read_bool(node + attr_offset)
# add an extra line here as this is the last attribute before the corner table
tmp = '{:40} {:10}\n'.format('cpr_fuse_redundant', int(value))
self.output.append(tmp)
def cpr_walker(self, node):
if node == self.head:
return
rdesc_addr = self.ramdump.sibling_field_addr(node, 'struct cpr_regulator', 'list', 'rdesc')
rdesc_ptr = self.ramdump.read_word(rdesc_addr + self.ramdump.field_offset('struct regulator_desc', 'name'))
cpr_name = self.ramdump.read_cstring(rdesc_ptr, 48)
cpr_enable = self.ramdump.read_u32(node + self.ramdump.field_offset('struct cpr_regulator', 'enable'))
vdd_apc_addr = self.ramdump.read_word(self.ramdump.sibling_field_addr(node, 'struct cpr_regulator', 'list', 'vdd_apc'))
vdd_apc_uv = self.ramdump.read_u32(vdd_apc_addr + self.ramdump.field_offset('struct regulator', 'min_uV'))
self.output.append("{:40}{:10s}\n".format('CPR Regulator', cpr_name))
self.output.append("{:40}{:10}\n".format('CPR Enabled', cpr_enable))
self.output.append("{:40}{:10d}\n".format('Current Voltage', vdd_apc_uv))
self.get_cpr_attrs(node)
self.get_cpr_volts(node, self.voltages)
self.get_cpr_volts(node, self.corner_info)
self.get_cpr_fuse_ro_sel(node)
self.print_cpr_info()
self.print_cpr_target_quot()
# print new line for each regulator struct
self.output.append('\n')
self.attr_list.clear()
self.value_list.clear()
def parse(self):
self.output_file = self.ramdump.open_file('cprinfo.txt')
self.get_cpr()
for i in self.output:
self.output_file.write(i)
self.output_file.close()

View File

@@ -0,0 +1,65 @@
# Copyright (c) 2013-2015, The Linux Foundation. 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.
from itertools import cycle
from parser_util import register_parser, RamParser
from print_out import print_out_str
@register_parser('--cpu-state', "Reads register values of non-panic'ing CPUs")
class CpuState(RamParser):
def parse(self):
regs_before_stop_addr = self.ramdump.address_of('regs_before_stop')
if regs_before_stop_addr is None:
print_out_str('regs_before_stop not found. Nothing to do.')
return
# see pt_regs and associated #defines in
# arch/arm/include/asm/ptrace.h
regs = (
'r0',
'r1',
'r2',
'r3',
'r4',
'r5',
'r6',
'r7',
'r8',
'r9',
'r10',
'fp',
'ip',
'sp',
'lr',
'pc',
'cpsr',
)
max_len = max([len(s) for s in regs])
for cpu in self.ramdump.iter_cpus():
print_out_str('CPU %d' % cpu)
lines = []
for index, reg in enumerate(regs):
reg_addr = self.ramdump.array_index(
regs_before_stop_addr, 'unsigned long', index)
reg_val = self.ramdump.read_word(reg_addr, cpu=cpu)
lines.append(
' {0:{width}} = 0x{1:x}'.format(reg, reg_val, width=max_len))
c = cycle([', ', ', ', ', ', '\n'])
output = ''
for line in lines:
output += line + next(c)
print_out_str(output)

View File

@@ -0,0 +1,175 @@
# Copyright (c) 2014-2015, 2017, 2019-2020, The Linux Foundation. All rights reserved.
# Copyright (c) 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 os
import struct
import re
import sys
import subprocess
from print_out import print_out_str
from parser_util import register_parser, RamParser
@register_parser('--ddr-compare', 'Sanity check the DDR data to find possible corruptions')
class DDRCompare(RamParser) :
def compare_magic(self):
self.output_file.write("----------------------------------------------------------------------------------------\n")
self.output_file.write("Comparing statically initialized lock values from vmlinux and ramdumps\n")
self.output_file.write("----------------------------------------------------------------------------------------\n")
if not self.ramdump.is_config_defined('CONFIG_DEBUG_SPINLOCK'):
self.output_file.write('Kernel Configuration for debug spinlocks is not enabled, cannot comapre the magic values!!\n\n')
return
elif self.ramdump.objdump_path is None:
self.output_file.write("!!! Objdump path is not set, please use --objdump-path option to specify the path\n\n")
return
cmdarr = [self.ramdump.objdump_path, "-D", "-j.data", self.ramdump.vmlinux]
p = subprocess.Popen(cmdarr, stdout=subprocess.PIPE, universal_newlines=True)
output = p.communicate()[0]
foundcorruption = 0;
for line in output.split('\n'):
m = re.search("^(.*?):\s+(dead4ead|deaf1eed?)\s+\.word\s+(0x\\2?)", line)
if m:
virtual = m.group(1)
virtual = int(m.group(1), 16)
bitcheck = virtual & 0x3
if bitcheck:
virtual = virtual - bitcheck
physical = self.ramdump.virt_to_phys(virtual + self.ramdump.get_kaslr_offset())
magic = hex(self.ramdump.read_u32(physical, False)).rstrip("L").lstrip("0x").zfill(8)
if (m.group(2) != magic):
foundcorruption = 1;
self.output_file.write("Magic didn't match for virtual address {0}\n".format("0x"+m.group(1)))
for i in range(2):
physical = physical - 4
dumpvalue = hex(self.ramdump.read_u32(physical, False)).rstrip("L").lstrip("0x").zfill(8)
self.output_file.write("{0}\n".format(dumpvalue))
physical = physical + 8
self.output_file.write("{0}\n".format(magic))
for i in range(2):
physical = physical + 4
dumpvalue = hex(self.ramdump.read_u32(physical, False)).rstrip("L").lstrip("0x").zfill(8)
self.output_file.write("{0}\n".format(dumpvalue))
if (foundcorruption == 0):
self.output_file.write("No Corruption found in the lock values\n\n")
def validate_sched_class(self, address):
sc_stop = self.ramdump.address_of('stop_sched_class')
sc_rt = self.ramdump.address_of('rt_sched_class')
sc_idle = self.ramdump.address_of('idle_sched_class')
sc_fair = self.ramdump.address_of('fair_sched_class')
sched_class_offset = address + self.ramdump.field_offset('struct task_struct', 'sched_class');
sched_class_pointer = self.ramdump.read_word(sched_class_offset, True)
if not ((sched_class_pointer == sc_stop) or (sched_class_pointer == sc_rt) or (sched_class_pointer == sc_idle) or (sched_class_pointer == sc_fair)):
self.output_file.write(hex(address) + " seems to be corrupted! sched_class doesn't match with the defined ones\n")
return -1;
def validate_task_struct(self, address):
thread_info_address = self.ramdump.get_thread_info_addr(address)
if self.ramdump.is_thread_info_in_task():
#Task is no longer found in thread_info
task_struct = address
else:
task_address = thread_info_address + self.ramdump.field_offset('struct thread_info', 'task');
task_struct = self.ramdump.read_word(task_address, True)
cpu_number = self.ramdump.get_task_cpu(task_struct, thread_info_address)
if((address != task_struct) or (thread_info_address == 0x0)):
self.output_file.write(hex(address) + " seems to be corrupted! Please check task_struct and thread_info to find corruptions\n")
return -1
if((cpu_number < 0) or (cpu_number >= self.ramdump.get_num_cpus())):
self.output_file.write(hex(address) + " seems to be corrupted! CPU number " + str(int(cpu_number)) + " seems to be corrupted\n")
return -1
def check_thread_group(self, address, comm_offset_index):
output_str = ""
threads_count = 0
for task_addr in self.ramdump.for_each_thread(address):
threads_count += 1
if(task_addr <= 0x0):
output_str += "task_struct " + hex(task_addr) + " was corrupted\n"
break
comm_offset = task_addr + comm_offset_index
comm = self.ramdump.read_cstring(comm_offset, 16, True)
output_str += "Next = {0} ({1})\n".format(hex(task_addr).rstrip("L"), comm)
if threads_count > 1:
self.output_file.write("-----------------------------------\n")
self.output_file.write("Threads of 0x{0:x}\n".format(address))
self.output_file.write("-----------------------------------\n")
self.output_file.write(output_str)
def corruptionchecker(self):
self.output_file.write("----------------------------------------------------------------------------------------\n")
self.output_file.write("Checking for task list corruption.\n")
self.output_file.write("----------------------------------------------------------------------------------------\n")
init_task = self.ramdump.address_of('init_task')
self.output_file.write("Init Task Address = {0}\n".format(hex(init_task)))
tasks_offset = self.ramdump.field_offset('struct task_struct', 'tasks')
self.output_file.write("Task Offset {0}\n".format(hex(tasks_offset).rstrip("L")))
comm_offset = self.ramdump.field_offset('struct task_struct', 'comm')
self.output_file.write("Comm Offset {0}\n\n".format(hex(comm_offset).rstrip("L")))
seen_task = []
next = init_task;
found_corruption = 0
while 1:
tasks_pointer = self.ramdump.read_word(next + tasks_offset, True)
if(tasks_pointer == 0x0):
found_corruption = 1
break
task_struct = tasks_pointer - tasks_offset
comm = self.ramdump.read_cstring(task_struct + comm_offset, 16, True)
if (self.validate_task_struct(task_struct) == -1):
found_corruption = 1
break
if (self.validate_sched_class(task_struct) == -1):
found_corruption = 1
break
if (self.check_thread_group(task_struct, comm_offset) == -1):
found_corruption = 1
break
if task_struct in seen_task:
self.output_file.write("!!!! Cycle in task group! The list is corrupt!\n")
break
self.output_file.write("Next = {0} ({1})\n".format(hex(task_struct).rstrip("L"), comm))
seen_task.append(task_struct)
next = task_struct;
if (next == init_task):
break
if(found_corruption):
self.output_file.write("----------------------------------------\n")
self.output_file.write("RESULT: Corruption found in the task list\n")
self.output_file.write("----------------------------------------\n")
else:
self.output_file.write("----------------------------------------\n")
self.output_file.write("RESULT: No issues found in the task list\n")
self.output_file.write("----------------------------------------\n")
def parse(self):
self.output_file = self.ramdump.open_file('DDRCacheCompare.txt')
self.compare_magic()
self.corruptionchecker()
self.output_file.close()
print_out_str("--- Wrote the output to DDRCacheCompare.txt")

View File

@@ -0,0 +1,211 @@
# Copyright (c) 2012-2015, 2020 The Linux Foundation. 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 struct
from parser_util import register_parser, RamParser
from print_out import print_out_str
from qdss import QDSSDump
from parsers.cachedump import save_l1_dump, parse_cache_dump
from parsers.watchdog import TZRegDump
from debug_image_v2 import DebugImage_v2
QDSS_MAGIC = 0x5D1DB1Bf
print_table = {
'MSM_CPU_CTXT': 'parse_cpu_ctx',
'MSM_L1_CACHE': 'parse_l1_cache',
'MSM_L2_CACHE': 'parse_l2_cache',
'MSM_OCMEM': 'parse_ocmem',
'MSM_TMC0_REG': 'parse_qdss_common',
'MSM_TMC_ETFETB': 'parse_qdss_common',
'MSM_TMC1_REG': 'parse_qdss_common',
'MSM_ETM0_REG': 'parse_qdss_common',
'MSM_ETM1_REG': 'parse_qdss_common',
'MSM_ETM2_REG': 'parse_qdss_common',
'MSM_ETM3_REG': 'parse_qdss_common',
}
tag_to_field_name = {
'MSM_TMC0_REG': 'tmc_etr_start',
'MSM_TMC_ETFETB': 'etf_start',
'MSM_TMC1_REG': 'tmc_etf_start',
'MSM_ETM0_REG': 'etm_regs0',
'MSM_ETM1_REG': 'etm_regs1',
'MSM_ETM2_REG': 'etm_regs2',
'MSM_ETM3_REG': 'etm_regs3',
}
@register_parser('--parse-debug-image', 'Parse the debug image and associated information')
class DebugImage(RamParser):
def __init__(self, *args):
super(DebugImage, self).__init__(*args)
self.qdss = QDSSDump()
self.name_lookup_table = []
def parse_cpu_ctx(self, start, end, tag):
print_out_str(
'Parsing CPU context start {0:x} end {1:x}'.format(start, end))
# For historical reasons, we can't rely on the magic number to indicate if there
# is context dumped. Check the magic number here instead
magic = self.ramdump.read_word(start, False)
if magic is None:
print_out_str(
"!!! Address {0:x} is bogus! Can't parse!".format(start))
return
if magic != 0x44434151:
print_out_str(
"!!! Magic {0:x} doesn't match! No context was dumped!".format(magic))
return
regs = TZRegDump(self.ramdump)
regs.init_regs(start)
for i in range(regs.ncores):
regs.dump_core_pc(i)
regs.dump_all_regs()
def parse_l2_cache(self, start, end, tag):
print_out_str(
'Parsing L2 cache context start {0:x} end {1:x}'.format(start, end))
magic = self.ramdump.read_word(start, False)
if magic is None:
print_out_str(
"!!! Address {0:x} is bogus! Can't parse!".format(start))
return
if magic != 0xcac1ecac:
print_out_str(
"!!! Magic {0:x} doesn't match! No cache was dumped!".format(magic))
return
parse_cache_dump(self.ramdump, start)
def parse_l1_cache(self, start, end, tag):
print_out_str(
'Parsing L1 cache context start {0:x} end {1:x}'.format(start, end))
magic = self.ramdump.read_word(start, False)
if magic is None:
print_out_str(
"!!! Address {0:x} is bogus! Can't parse!".format(start))
return
if magic != 0x314C4151:
print_out_str(
"!!! Magic {0:X} doesn't match! No cache was dumped!".format(magic))
return
print_out_str('Saving L1 cache')
save_l1_dump(self.ramdump, start, end - start)
def parse_ocmem(self, start, end, tag):
print_out_str(
'[!!!] Parsing not implemented yet start {0:x} end {1:x}'.format(start, end))
def parse_qdss_common(self, start, end, tag):
print_out_str(
'Parsing {0} context start {1:x} end {2:x}'.format(tag, start, end))
magic = self.ramdump.read_word(start, False)
if magic is None:
print_out_str(
"!!! Address {0:x} is bogus! Can't parse!".format(start))
return
if magic != QDSS_MAGIC:
print_out_str(
"!!! Magic {0:X} doesn't match! Tracing was not dumped!".format(magic))
return
setattr(self.qdss, tag_to_field_name[tag], start + 4096)
def parse_dump(self):
out_dir = self.ramdump.outdir
self.name_lookup_table = self.ramdump.gdbmi.get_enum_lookup_table(
'dump_client_type', 32)
dump_table_ptr_offset = self.ramdump.field_offset(
'struct msm_memory_dump', 'dump_table_ptr')
version_offset = self.ramdump.field_offset(
'struct msm_dump_table', 'version')
num_entries_offset = self.ramdump.field_offset(
'struct msm_dump_table', 'num_entries')
client_entries_offset = self.ramdump.field_offset(
'struct msm_dump_table', 'client_entries')
id_offset = self.ramdump.field_offset('struct msm_client_dump', 'id')
start_addr_offset = self.ramdump.field_offset(
'struct msm_client_dump', 'start_addr')
end_addr_offset = self.ramdump.field_offset(
'struct msm_client_dump', 'end_addr')
client_dump_entry_size = self.ramdump.sizeof('struct msm_client_dump')
mem_dump_data = self.ramdump.address_of('mem_dump_data')
dump_table = self.ramdump.read_word(
mem_dump_data + dump_table_ptr_offset)
version = self.ramdump.read_word(dump_table + version_offset)
if version is None:
print_out_str('Version is bogus! Can\'t parse debug image')
return
num_entries = self.ramdump.read_word(dump_table + num_entries_offset)
if num_entries is None or num_entries > 100:
print_out_str('num_entries is bogus! Can\'t parse debug image')
return
print_out_str('\nDebug image version: {0}.{1} Number of entries {2}'.format(
version >> 20, version & 0xFFFFF, num_entries))
print_out_str('--------')
for i in range(0, num_entries):
this_client = dump_table + client_entries_offset + \
i * client_dump_entry_size
client_id = self.ramdump.read_word(this_client + id_offset)
client_start = self.ramdump.read_word(
this_client + start_addr_offset)
client_end = self.ramdump.read_word(this_client + end_addr_offset)
if client_id < 0 or client_id > len(self.name_lookup_table):
print_out_str(
'!!! Invalid client id found {0:x}'.format(client_id))
continue
client_name = self.name_lookup_table[client_id]
if client_name not in print_table:
print_out_str(
'!!! {0} Does not have an associated function. The parser needs to be updated!'.format(client_name))
else:
print_out_str(
'Parsing debug information for {0}'.format(client_name))
func = print_table[client_name]
getattr(DebugImage, func)(self, client_start,
client_end, client_name)
print_out_str('--------')
self.qdss.dump_standard(self.ramdump)
if not self.ramdump.skip_qdss_bin:
self.qdss.save_etf_bin(self.ramdump)
self.qdss.save_etr_bin(self.ramdump)
def parse(self):
# use the mem_dump_data variable to detect if debug image feature was compiled in,
# and memdump data variable for debug image v2 feature, rather than relying on
# configuration option.
if self.ramdump.address_of('mem_dump_data'):
self.parse_dump()
elif self.ramdump.address_of('memdump'):
regs = DebugImage_v2(self.ramdump)
regs.parse_dump_v2(self.ramdump)
else:
print_out_str(
'!!! Debug image was not enabled. No debug dump will be provided')
return

View File

@@ -0,0 +1,20 @@
# Copyright (c) 2013-2014, The Linux Foundation. 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.
from parser_util import register_parser, RamParser
import print_out
import dmesglib
@register_parser('--dmesg', 'Print the dmesg', shortopt='-d')
class Dmesg(RamParser):
def parse(self):
dmesglib.DmesgLib(self.ramdump, print_out.out_file).extract_dmesg()

View File

@@ -0,0 +1,54 @@
"""
Copyright (c) 2020 The Linux Foundation. All rights reserved.
SPDX-License-Identifier: GPL-2.0-only
Copyright (c) 2024 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 os,sys
import struct
import subprocess
from print_out import print_out_str
from parser_util import register_parser, RamParser
import local_settings
@register_parser('--dtb', 'Dump the devicetree blob information')
class dtb_parsing(RamParser):
def __init__(self, *args):
super(dtb_parsing, self).__init__(*args)
def dtb_parse(self, ram_dump):
initial_boot_params_addr = ram_dump.address_of('initial_boot_params')
if initial_boot_params_addr:
initial_boot_params = ram_dump.read_u64(initial_boot_params_addr)
magic = ram_dump.read_u32(initial_boot_params)
if (magic == 0xEDFE0DD0):
db_size = ram_dump.read_u64(initial_boot_params + 0x4)
dtbsize=((db_size&0xFF)<<24)|((db_size&0xFF00)<<8)|((db_size&0xFF0000)>>8)|((db_size&0xFF000000)>>24)
dtb_file = "devicetree.dtb"
dtb_path = os.path.join(ram_dump.outdir,dtb_file)
dtb_fd = open(dtb_path,'wb')
dtb_data = ram_dump.read_physical(ram_dump.virt_to_phys(initial_boot_params),dtbsize)
dtb_fd.write(dtb_data)
dtb_fd.close()
def parse(self):
self.dtb_parse(self.ramdump)
dts_output_file = "{0}/{1}".format(self.ramdump.outdir, "dts.txt")
with open(dts_output_file, 'w') as dts_out:
devicetree_dtb = os.path.join(self.ramdump.outdir, "devicetree.dtb")
if os.path.exists(devicetree_dtb):
try:
retcode = subprocess.Popen([local_settings.dtc_path, '-f', '-I', 'dtb', '-O', 'dts', devicetree_dtb], stdout=dts_out, stderr=dts_out, shell=False)
except OSError as e:
print_out_str("exception is {0} dtc used {1}".format(str(e), local_settings.dtc_path))

View File

@@ -0,0 +1,462 @@
# Copyright (c) 2021 The Linux Foundation. All rights reserved.
# Copyright (c) 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.
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
from struct_print import struct_print_class
def parse_mountpoint(ramdump, dentry):
d_iname_offset = ramdump.field_offset('struct dentry', 'd_iname')
d_iname_last = cleanupString(ramdump.read_cstring(dentry + d_iname_offset, 40))
d_parent = ramdump.read_structure_field(dentry, 'struct dentry', 'd_parent')
d_parent_prev = 0
while d_parent != d_parent_prev:
d_iname = cleanupString(ramdump.read_cstring(d_parent + d_iname_offset, 40))
if d_iname != "/":
d_iname_last = d_iname + "/" + d_iname_last
d_parent_prev = d_parent
d_parent = ramdump.read_structure_field(d_parent, 'struct dentry', 'd_parent')
mount_point_path = "/" + d_iname_last
return mount_point_path
def print_f2fs_data(ramdump):
output_file = ramdump.open_file("f2fs_info.txt")
init_nsproxy_addr = ramdump.address_of('init_nsproxy')
mnt_ns = ramdump.read_structure_field(init_nsproxy_addr, 'struct nsproxy', 'mnt_ns')
mnt_ns_list_offset = ramdump.field_offset('struct mnt_namespace', 'list')
mnt_ns_list_head = mnt_ns + mnt_ns_list_offset
next_offset = ramdump.field_offset('struct list_head', 'next')
mnt_ns_entry = ramdump.read_word(mnt_ns_list_head + next_offset)
mount_list_offset = ramdump.field_offset('struct mount', 'mnt_list')
mount_mnt_o = ramdump.field_offset('struct mount', 'mnt')
mount_mnt_rcu_o = ramdump.field_offset('struct mount', 'mnt_rcu')
vfsmount_size = ramdump.sizeof('struct vfsmount')
mnt_sb_o = ramdump.field_offset('struct vfsmount', 'mnt_sb')
mnt_o = ramdump.field_offset('struct mount', 'mnt')
s_writers_o = ramdump.field_offset('struct super_block', 's_writers')
frozen_o = ramdump.field_offset('struct sb_writers', 'frozen')
s_umount_o = ramdump.field_offset('struct super_block', 's_umount')
count_o = ramdump.field_offset('struct rw_semaphore', 'count')
s_id_o = ramdump.field_offset('struct super_block', 's_id')
d_iname_offset = ramdump.field_offset('struct dentry', 'd_iname')
dentry_d_child_o = ramdump.field_offset('struct dentry', 'd_child')
output_file.write("(struct mount *) (struct super_block *) [FS_TYPE] \t\t[DEVNAME] [DIRNAME] [SB_ID] [umount_RW_LOCK_count] [READ_ONLY] [writer FROZEN (1=YES,generally)]\n")
while mnt_ns_entry != mnt_ns_list_head:
root = mnt_ns_entry - mount_list_offset
root_mnt_devname = ramdump.read_structure_cstring(root, 'struct mount', 'mnt_devname', 40)
root_mnt_sb = ramdump.read_word(root + mnt_o + mnt_sb_o)
sb_s_type = ramdump.read_structure_field(root_mnt_sb, 'struct super_block', 's_type')
s_type_name = ramdump.read_structure_cstring(sb_s_type, 'struct file_system_type', 'name', 40)
sb_s_id = cleanupString(ramdump.read_cstring(root_mnt_sb + s_id_o, 40))
sb_s_flags = ramdump.read_structure_field(root_mnt_sb, 'struct super_block', 's_flags')
sb_s_flags = sb_s_flags & 0x1 #MS_RDONLY=0x1
sb_s_writers_frozen = ramdump.read_int(root_mnt_sb + s_writers_o + frozen_o)
if sb_s_writers_frozen == 0:
sb_s_writers_frozen = 0x1
sb_s_umount_count = ramdump.read_word(root_mnt_sb + s_umount_o + count_o)
root_mnt_mountpoint = ramdump.read_structure_field(root, 'struct mount', 'mnt_mountpoint')
mountpoint = parse_mountpoint(ramdump, root_mnt_mountpoint)
if s_type_name == "f2fs" and mountpoint == "/data":
output_file.write("0x{0:16X}\t 0x{1:16X}\t\t{2:12}\t{3:30}\t\t\t{4:30}\t{5:15}\t{6:016X}\t\t{7:3}\t\t\t\t\t{8:2}\n".format(root, root_mnt_sb,
s_type_name, root_mnt_devname, mountpoint, sb_s_id, sb_s_umount_count, sb_s_flags, sb_s_writers_frozen))
f2fs_sb_info = ramdump.read_structure_field(root_mnt_sb, 'struct super_block', 's_fs_info')
f2fs_sb = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'sb')
nm_info = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'nm_info')
sm_info = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'sm_info')
ckpt_info = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'ckpt')
sbi = struct_print_class(ramdump, 'f2fs_sb_info', f2fs_sb_info, output_file)
sbi.append('sb_lock', 'rw_semaphore')
sbi.append('valid_super_block', 'u32')
sbi.append('s_flag', 'u32')
sbi.append('writepages', 'mutex')
sbi.append('io_order_lock', 'rw_semaphore')
sbi.append('cur_cp_pack', 'u32')
sbi.append('cp_global_sem', 'rw_semaphore')
sbi.append('cp_rwsem', 'rw_semaphore')
sbi.append('node_write', 'rw_semaphore')
sbi.append('node_change', 'rw_semaphore')
sbi.append('fsync_seg_id', 'u32')
sbi.append('fsync_node_num', 'u32')
sbi.append('max_orphans', 'u32')
sbi.append('flush_lock', 'mutex')
if (ramdump.kernel_version <= (5, 4, 0)):
sbi.append('extent_tree_lock', 'mutex')
#basic filesystem units
sbi.append('log_sectors_per_block', 'u32')
sbi.append('log_blocksize', 'u32')
sbi.append('blocksize', 'u32')
sbi.append('root_ino_num', 'u32')
sbi.append('node_ino_num', 'u32')
sbi.append('meta_ino_num', 'u32')
sbi.append('log_blocks_per_seg', 'u32')
sbi.append('blocks_per_seg', 'u32')
sbi.append('segs_per_sec', 'u32')
sbi.append('secs_per_zone', 'u32')
sbi.append('total_sections', 'u32')
sbi.append('total_node_count', 'u32')
sbi.append('total_valid_node_count', 'u32')
if (ramdump.kernel_version <= (5, 4, 0)):
sbi.append('max_file_blocks', 'u32')
sbi.append('dir_level', 'u32')
sbi.append('readdir_ra', 'u32')
sbi.append('user_block_count', 'u32')
sbi.append('total_valid_block_count', 'u32')
sbi.append('discard_blks', 'u32')
sbi.append('last_valid_block_count', 'u32')
sbi.append('reserved_blocks', 'u32')
sbi.append('current_reserved_blocks', 'u32')
sbi.append('unusable_block_count', 'u32')
sbi.append('nquota_files', 'u32')
sbi.append('quota_sem', 'rw_semaphore')
sbi.append('nr_pages[0]', 'u32')
sbi.append('nr_pages[1]', 'u32')
sbi.append('nr_pages[2]', 'u32')
sbi.append('nr_pages[3]', 'u32')
sbi.append('nr_pages[4]', 'u32')
sbi.append('nr_pages[5]', 'u32')
sbi.append('nr_pages[6]', 'u32')
sbi.append('nr_pages[7]', 'u32')
sbi.append('nr_pages[8]', 'u32')
sbi.append('nr_pages[9]', 'u32')
sbi.append('nr_pages[10]', 'u32')
sbi.append('nr_pages[11]', 'u32')
sbi.append('nr_pages[12]', 'u32')
sbi.append('nr_pages[13]', 'u32')
sbi.append('wb_sync_req[0]', 'u32')
sbi.append('wb_sync_req[1]', 'u32')
sbi.append('gc_lock', 'rw_semaphore')
sbi.append('cur_victim_sec', 'u32')
sbi.append('gc_mode', 'u32')
sbi.append('next_victim_seg[0]', 'u32')
sbi.append('next_victim_seg[1]', 'u32')
sbi.append('atomic_files', 'u32')
if (ramdump.kernel_version <= (5, 4, 0)):
sbi.append('skipped_atomic_files[0]', 'u64')
sbi.append('skipped_atomic_files[1]', 'u64')
sbi.append('skipped_gc_rwsem', 'u64')
sbi.append('gc_pin_file_threshold', 'u64')
sbi.append('pin_sem', 'rw_semaphore')
sbi.append('max_victim_search', 'u32')
sbi.append('migration_granularity', 'u32')
if (ramdump.is_config_defined('CONFIG_F2FS_STAT_FS')):
sbi.append('meta_count', 'u32')
sbi.append('segment_count[0]', 'u32')
sbi.append('segment_count[1]', 'u32')
sbi.append('block_count[0]', 'u32')
sbi.append('block_count[1]', 'u32')
sbi.append('inplace_count', 'u32')
sbi.append('total_hit_ext', 'u64')
sbi.append('read_hit_rbtree', 'u64')
sbi.append('read_hit_largest', 'u64')
sbi.append('read_hit_cached', 'u64')
sbi.append('inline_xattr', 'u32')
sbi.append('inline_inode', 'u32')
sbi.append('inline_dir', 'u32')
sbi.append('compr_inode', 'u32')
sbi.append('compr_blocks', 'u32')
if (ramdump.kernel_version <= (5, 4, 0)):
sbi.append('vw_cnt', 'u32')
sbi.append('max_vw_cnt', 'u32')
sbi.append('max_aw_cnt', 'u32')
sbi.append('io_skip_bggc', 'u32')
sbi.append('other_skip_bggc', 'u32')
sbi.append('ndirty_inode[0]', 'u32')
sbi.append('ndirty_inode[1]', 'u32')
sbi.append('ndirty_inode[2]', 'u32')
sbi.append('ndirty_inode[3]', 'u32')
if (ramdump.kernel_version <= (5, 4, 0)):
sbi.append('rw_iostat', 'u64')
sbi.append('prev_rw_iostat', 'u64')
sbi.append('iostat_enable', 'u8')
sbi.append('iostat_next_period', 'u64')
sbi.append('iostat_period_ms', 'u32')
sbi.append('data_io_flag', 'u32')
sbi.append('node_io_flag', 'u32')
sbi.append('s_ndevs', 'u32')
sbi.append('dirty_device', 'u32')
sbi.append('umount_mutex', 'mutex')
sbi.append('shrinker_run_no', 'u32')
sbi.append('sectors_written_start', 'u64')
sbi.append('kbytes_written', 'u64')
sbi.process()
sbi.print_struct()
sb = struct_print_class(ramdump, 'super_block', f2fs_sb, output_file)
sb.append('s_flags', 'u64')
sb.append('s_iflags', 'u64')
sb.append('s_magic', 'u64')
sb.append('s_umount', 'rw_semaphore')
sb.append('s_count', 'u32')
sb.append('s_active', 'u32')
sb.process()
sb.print_struct()
f2fs_raw_super = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'raw_super')
rawsb = struct_print_class(ramdump, 'f2fs_super_block', f2fs_raw_super, output_file)
rawsb.append('magic', 'u32')
rawsb.append('major_ver', 'u16')
rawsb.append('minor_ver', 'u16')
rawsb.append('log_sectorsize', 'u32')
rawsb.append('log_sectors_per_block', 'u32')
rawsb.append('log_blocksize', 'u32')
rawsb.append('log_blocks_per_seg', 'u32')
rawsb.append('segs_per_sec', 'u32')
rawsb.append('secs_per_zone', 'u32')
rawsb.append('checksum_offset', 'u32')
rawsb.append('block_count', 'u64')
rawsb.append('section_count', 'u32')
rawsb.append('segment_count', 'u32')
rawsb.append('segment_count_ckpt', 'u32')
rawsb.append('segment_count_sit', 'u32')
rawsb.append('segment_count_nat', 'u32')
rawsb.append('segment_count_ssa', 'u32')
rawsb.append('segment_count_main', 'u32')
rawsb.append('extension_count', 'u32')
rawsb.append('cp_payload', 'u32')
rawsb.append('feature', 'u32')
rawsb.append('encryption_level', 'u8')
rawsb.process()
rawsb.print_struct()
#Dump F2FS node manager sbi->nm_info
nmi = struct_print_class(ramdump, 'f2fs_nm_info', nm_info, output_file)
nmi.append('max_nid', 'u32')
nmi.append('available_nids', 'u32')
nmi.append('next_scan_nid', 'u32')
nmi.append('ram_thresh', 'u32')
nmi.append('ra_nid_pages', 'u32')
nmi.append('dirty_nats_ratio', 'u32')
nmi.append('nat_tree_lock', 'rw_semaphore')
nmi.append('nat_cnt', 'u32')
if (ramdump.kernel_version < (5, 4, 0)):
nmi.append('dirty_nat_cnt', 'u32')
nmi.append('nat_blocks', 'u32')
nmi.append('nid_cnt[0]', 'u32')
nmi.append('nid_cnt[1]', 'u32')
nmi.append('build_lock', 'mutex')
nmi.append('nat_bits_blocks', 'u32')
nmi.append('bitmap_size', 'u32')
nmi.process()
nmi.print_struct()
#F2FS segment manager sbi->sm_info
smi = struct_print_class(ramdump, 'f2fs_sm_info', sm_info, output_file)
smi.append('curseg_lock', 'rw_semaphore')
smi.append('segment_count', 'u32')
smi.append('main_segments', 'u32')
smi.append('reserved_segments', 'u32')
smi.append('ovp_segments', 'u32')
smi.append('rec_prefree_segments', 'u32')
if (ramdump.kernel_version <= (5, 4, 0)):
smi.append('trim_sections', 'u32')
smi.append('ipu_policy', 'u32')
smi.append('min_ipu_util', 'u32')
smi.append('min_fsync_blocks', 'u32')
smi.append('min_seq_blocks', 'u32')
smi.append('min_hot_blocks', 'u32')
smi.append('min_ssr_sections', 'u32')
smi.process()
smi.print_struct()
sit_info = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'sit_info')
sit = struct_print_class(ramdump, 'sit_info', sit_info, output_file)
sit.append('sit_blocks', 'u32')
sit.append('written_valid_blocks', 'u32')
sit.append('bitmap_size', 'u32')
sit.append('dirty_sentries', 'u32')
sit.append('sents_per_block', 'u32')
sit.append('sentry_lock', 'rw_semaphore')
sit.append('elapsed_time', 'u64')
sit.append('mounted_time', 'u64')
sit.append('min_mtime', 'u64')
sit.append('max_mtime', 'u64')
sit.append('last_victim[0]', 'u32')
sit.append('last_victim[1]', 'u32')
sit.append('last_victim[2]', 'u32')
sit.append('last_victim[3]', 'u32')
sit.process()
sit.print_struct()
free_info = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'free_info')
freeinfo = struct_print_class(ramdump, 'free_segmap_info', free_info, output_file)
freeinfo.append('start_segno', 'u32')
freeinfo.append('free_segments', 'u32')
freeinfo.append('free_sections', 'u32')
freeinfo.process()
freeinfo.print_struct()
dirty_info = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'dirty_info')
dirtyinfo = struct_print_class(ramdump, 'dirty_seglist_info', dirty_info, output_file)
dirtyinfo.append('seglist_lock', 'mutex')
dirtyinfo.append('nr_dirty[0]', 'u32')
dirtyinfo.append('nr_dirty[1]', 'u32')
dirtyinfo.append('nr_dirty[2]', 'u32')
dirtyinfo.append('nr_dirty[3]', 'u32')
dirtyinfo.append('nr_dirty[4]', 'u32')
dirtyinfo.append('nr_dirty[5]', 'u32')
dirtyinfo.append('nr_dirty[6]', 'u32')
dirtyinfo.append('nr_dirty[7]', 'u32')
dirtyinfo.process()
dirtyinfo.print_struct()
curseg_array = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'curseg_array')
curseginfo = struct_print_class(ramdump, 'curseg_info', curseg_array, output_file)
curseginfo.append('curseg_mutex', 'mutex')
curseginfo.append('journal_rwsem', 'rw_semaphore')
curseginfo.append('segno', 'u32')
curseginfo.append('next_blkoff', 'u16')
curseginfo.append('zone', 'u32')
curseginfo.append('next_segno', 'u32')
curseginfo.process()
curseginfo.print_struct()
fcc_info = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'fcc_info')
fccinfo = struct_print_class(ramdump, 'flush_cmd_control', fcc_info, output_file)
fccinfo.append('issued_flush', 'u32')
fccinfo.append('queued_flush', 'u32')
fccinfo.process()
fccinfo.print_struct()
dcc_info = ramdump.read_structure_field(sm_info, 'struct f2fs_sm_info', 'dcc_info')
dccinfo = struct_print_class(ramdump, 'discard_cmd_control', dcc_info, output_file)
dccinfo.append('discard_wake', 'u32')
dccinfo.append('cmd_lock', 'mutex')
dccinfo.append('nr_discards', 'u32')
dccinfo.append('max_discards', 'u32')
dccinfo.append('discard_granularity', 'u32')
dccinfo.append('undiscard_blks', 'u32')
dccinfo.append('next_pos', 'u32')
dccinfo.append('issued_discard', 'u32')
dccinfo.append('queued_discard', 'u32')
dccinfo.append('discard_cmd_cnt', 'u32')
dccinfo.process()
dccinfo.print_struct()
writeio_0 = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'write_io[0]')
writeio_1 = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'write_io[1]')
writeio_2 = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'write_io[2]')
writeio_list = [writeio_0, writeio_1, writeio_2]
for writeio in writeio_list:
bio = ramdump.read_structure_field(writeio, 'struct f2fs_bio_info', 'bio')
w_io = struct_print_class(ramdump, 'f2fs_bio_info', writeio, output_file)
w_io.append('bio', 'ptr')
if bio:
w_io.append('io_rwsem', 'rw_semaphore')
w_io.append('bio_list_lock', 'rw_semaphore')
w_io.process()
w_io.print_struct()
ckpt = struct_print_class(ramdump, 'f2fs_checkpoint', ckpt_info, output_file)
ckpt.append('checkpoint_ver', 'u64')
ckpt.append('user_block_count', 'u64')
ckpt.append('valid_block_count', 'u64')
ckpt.append('rsvd_segment_count', 'u32')
ckpt.append('overprov_segment_count', 'u32')
ckpt.append('free_segment_count', 'u32')
ckpt.append('cur_node_segno[0]', 'u32')
ckpt.append('cur_node_segno[1]', 'u32')
ckpt.append('cur_node_segno[2]', 'u32')
ckpt.append('cur_node_segno[3]', 'u32')
ckpt.append('cur_node_segno[4]', 'u32')
ckpt.append('cur_node_segno[5]', 'u32')
ckpt.append('cur_node_segno[6]', 'u32')
ckpt.append('cur_node_segno[7]', 'u32')
ckpt.append('cur_node_blkoff[0]', 'u16')
ckpt.append('cur_node_blkoff[1]', 'u16')
ckpt.append('cur_node_blkoff[2]', 'u16')
ckpt.append('cur_node_blkoff[3]', 'u16')
ckpt.append('cur_node_blkoff[4]', 'u16')
ckpt.append('cur_node_blkoff[5]', 'u16')
ckpt.append('cur_node_blkoff[6]', 'u16')
ckpt.append('cur_node_blkoff[7]', 'u16')
ckpt.append('cur_data_segno[0]', 'u32')
ckpt.append('cur_data_segno[1]', 'u32')
ckpt.append('cur_data_segno[2]', 'u32')
ckpt.append('cur_data_segno[3]', 'u32')
ckpt.append('cur_data_segno[4]', 'u32')
ckpt.append('cur_data_segno[5]', 'u32')
ckpt.append('cur_data_segno[6]', 'u32')
ckpt.append('cur_data_segno[7]', 'u32')
ckpt.append('cur_data_blkoff[0]', 'u16')
ckpt.append('cur_data_blkoff[1]', 'u16')
ckpt.append('cur_data_blkoff[2]', 'u16')
ckpt.append('cur_data_blkoff[3]', 'u16')
ckpt.append('cur_data_blkoff[4]', 'u16')
ckpt.append('cur_data_blkoff[5]', 'u16')
ckpt.append('cur_data_blkoff[6]', 'u16')
ckpt.append('cur_data_blkoff[7]', 'u16')
ckpt.append('ckpt_flags', 'u32')
ckpt.append('cp_pack_total_block_count', 'u32')
ckpt.append('cp_pack_start_sum', 'u32')
ckpt.append('valid_node_count', 'u32')
ckpt.append('valid_inode_count', 'u32')
ckpt.append('next_free_nid', 'u32')
ckpt.append('sit_ver_bitmap_bytesize', 'u32')
ckpt.append('nat_ver_bitmap_bytesize', 'u32')
ckpt.append('checksum_offset', 'u32')
ckpt.append('elapsed_time', 'u64')
ckpt.append('alloc_type[0]', 'u8')
ckpt.append('alloc_type[1]', 'u8')
ckpt.append('alloc_type[2]', 'u8')
ckpt.append('alloc_type[3]', 'u8')
ckpt.append('alloc_type[4]', 'u8')
ckpt.append('alloc_type[5]', 'u8')
ckpt.append('alloc_type[6]', 'u8')
ckpt.append('alloc_type[7]', 'u8')
ckpt.append('alloc_type[8]', 'u8')
ckpt.append('alloc_type[9]', 'u8')
ckpt.append('alloc_type[10]', 'u8')
ckpt.append('alloc_type[11]', 'u8')
ckpt.append('alloc_type[12]', 'u8')
ckpt.append('alloc_type[13]', 'u8')
ckpt.append('alloc_type[14]', 'u8')
ckpt.append('alloc_type[15]', 'u8')
ckpt.process()
ckpt.print_struct()
mount_opt = ramdump.struct_field_addr(f2fs_sb_info, 'struct f2fs_sb_info', 'mount_opt')
mntop = struct_print_class(ramdump, 'f2fs_mount_info', mount_opt, output_file)
mntop.append('opt', 'u32')
mntop.append('write_io_size_bits', 'u32')
mntop.append('root_reserved_blocks', 'u32')
mntop.append('s_resuid', 'u32')
mntop.append('s_resgid', 'u32')
mntop.append('active_logs', 'u32')
mntop.append('inline_xattr_size', 'u32')
if (ramdump.is_config_defined('CONFIG_QUOTA')):
mntop.append('s_jquota_fmt', 'u8')
if (ramdump.kernel_version <= (5, 4, 0)):
mntop.append('whint_mode', 'u32')
mntop.append('alloc_mode', 'u32')
mntop.append('fsync_mode', 'u32')
mntop.append('fs_mode', 'u32')
mntop.append('bggc_mode', 'u32')
if (ramdump.is_config_defined('CONFIG_FS_ENCRYPTION') and ramdump.kernel_version <= (5, 4, 0)):
mntop.append('inlinecrypt', 'u8')
mntop.append('unusable_cap_perc', 'u32')
mntop.append('unusable_cap', 'u32')
mntop.process()
mntop.print_struct()
gc_thread = ramdump.read_structure_field(f2fs_sb_info, 'struct f2fs_sb_info', 'gc_thread')
gc = struct_print_class(ramdump, 'f2fs_gc_kthread', gc_thread, output_file)
gc.append('urgent_sleep_time', 'u32')
gc.append('min_sleep_time', 'u32')
gc.append('max_sleep_time', 'u32')
gc.append('no_gc_sleep_time', 'u32')
gc.append('gc_wake', 'u32')
gc.process()
gc.print_struct()
break
mnt_ns_entry = ramdump.read_structure_field(mnt_ns_entry, 'struct list_head', 'next')
output_file.close()
@register_parser('--print-f2fs', 'Extract F2FS super_block info')
class FileSystem(RamParser):
def parse(self):
if self.ramdump.kernel_version < (4, 9):
print_out_str("Linux version lower than 4.9 is not supported!!")
return
else:
print_f2fs_data(self.ramdump)

View File

@@ -0,0 +1,171 @@
# Copyright (c) 2012,2014-2015,2017-2020 The Linux Foundation. All rights reserved.
# Copyright (c) 2023-2024 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
from mm import pfn_to_page, page_buddy, page_count, for_each_pfn
from mm import page_to_pfn
import sys
files = {
'' : {
'filename' : 'unknown',
'filepath' : 'unknown',
'a_ops' : 'unknown',
'addr_space' : 'unknown',
'total_pages' : 0,
'total_size' : 0,
}
}
@register_parser('--print-filetracking', 'print file tracking information (if available)')
class FileTracking(RamParser):
def get_filepath(self, path, name, parent):
cycle_flag = 0
cycle_detection = 12
while True:
if name == '/' or name == '' or name is None:
return path, cycle_flag
path = '/' + name + path
parent = self.ramdump.read_structure_field(parent, 'struct dentry', 'd_parent')
if parent is not None:
d_name_ptr = (parent + self.ramdump.field_offset('struct dentry ', 'd_name')) + \
self.ramdump.field_offset('struct qstr', 'name')
test_name = self.ramdump.read_cstring(self.ramdump.read_pointer(d_name_ptr), 100)
if test_name == name and name.find('/') == -1 and test_name.find('/') == -1:
cycle_flag = 2
break
else:
if name.find('/') != -1 and test_name.find('/') != -1:
break
name = test_name
else:
break
cycle_detection -= 1
if cycle_detection < 1:
cycle_flag = 1
break
if path[1] == '/':
path = path[2:]
return path, cycle_flag
def get_file_metadata(self, page):
addr_space = inode = dentry_list = dentry = a_ops = None
name = path = ''
if page is not None:
addr_space = self.ramdump.read_structure_field(page, 'struct page', 'mapping')
if addr_space is not None:
inode = self.ramdump.read_structure_field(addr_space, 'struct address_space', 'host')
a_ops = self.ramdump.read_structure_field(addr_space, 'struct address_space ', 'a_ops')
else:
return '', '', None, None, 0
if inode is not None:
dentry_list = self.ramdump.read_structure_field(inode, 'struct inode', 'i_dentry')
if dentry_list is not None:
dentry = self.ramdump.container_of(dentry_list, 'struct dentry', 'd_u')
if dentry is not None:
d_name_ptr = (dentry + self.ramdump.field_offset('struct dentry ', 'd_name')) + \
self.ramdump.field_offset('struct qstr', 'name')
name = self.ramdump.read_cstring(self.ramdump.read_pointer(d_name_ptr), 100)
else:
return '', '', None, None, 0
if name is None:
return '', '', None, None, 0
path, cycle_flag = self.get_filepath(path, name, dentry)
return name, path, a_ops, addr_space, cycle_flag
def update_file_list(self, name, path, a_ops, addr_space, cycle_flag):
if addr_space not in files:
files[addr_space] = {}
files[addr_space]['total_pages'] = 0
files[addr_space]['total_size'] = 0
if 'filename' not in files[addr_space]:
files[addr_space]['filename'] = name
if cycle_flag == 1:
files[addr_space]['filepath'] = 'PATH CYCLE DETECTED: ' + path
elif cycle_flag == 2:
files[addr_space]['filepath'] = 'PARENT == CURRENT FILE: ' + path
else:
files[addr_space]['filepath'] = path
a_ops_symbol = self.ramdump.unwind_lookup(a_ops)
files[addr_space]['a_ops'] = a_ops_symbol[0] if a_ops_symbol is not None else a_ops_symbol
files[addr_space]['addr_space'] = addr_space
files[addr_space]['total_pages'] += 1
files[addr_space]['total_size'] += 4
def parse(self):
ranges = None
g_optimization = False
for arg in sys.argv:
if "ranges=" in arg:
g_optimization = True
k, ranges = arg.split("=")
start, end = ranges.split('-')
start_pfn = int(start, 16) >> self.ramdump.page_shift
end_pfn = int(end, 16) >> self.ramdump.page_shift
break
elif "page=" in arg:
g_optimization = True
k, page = arg.split('=')
page = int(page, 16)
start_pfn = page_to_pfn(self.ramdump, page)
end_pfn = start_pfn + 1
break
out_tracking = self.ramdump.open_file('file_tracking.txt')
if g_optimization:
ranges = range(start_pfn, end_pfn)
else:
ranges = for_each_pfn(self.ramdump)
for pfn in ranges:
page = pfn_to_page(self.ramdump, pfn)
if (page_buddy(self.ramdump, page) or \
page_count(self.ramdump, page) == 0):
continue
name, path, a_ops, addr_space, cycle_flag = self.get_file_metadata(page)
if a_ops is None:
continue
self.update_file_list(name, path, a_ops, addr_space, cycle_flag)
total_sizes = {}
file_list = sorted(files, key=lambda x: (files[x]['total_size']), reverse=True)
for file in range(len(file_list)):
if files[file_list[file]]['filename'] == 'unknown':
continue
name = files[file_list[file]]['filename']
path = files[file_list[file]]['filepath']
a_ops = files[file_list[file]]['a_ops']
addr_space = files[file_list[file]]['addr_space']
pages = files[file_list[file]]['total_pages']
size = files[file_list[file]]['total_size']
out_str = 'File : {0}\nPath : {1}\na_ops : {2}\nAddr Space : 0x{3:x}\nNo. Pages : {4}\nSize (KB) : {5}\n\n\n'
out_tracking.write(out_str.format(name, path, a_ops, addr_space, pages, size))
if a_ops not in total_sizes:
total_sizes[a_ops] = 0
total_sizes[a_ops] += size
out_tracking.write('-----------= Total Sizes (KB) =-----------\n\n')
total_sizes_list = sorted(total_sizes, key=lambda x: (total_sizes[x]), reverse=True)
for aops in range(len(total_sizes_list)):
out_str = '{0} : {1}\n'
out_tracking.write(out_str.format(total_sizes_list[aops], total_sizes[total_sizes_list[aops]]))
out_tracking.close()
print_out_str('---wrote file tracking information to file_tracking.txt')

View File

@@ -0,0 +1,116 @@
# Copyright (c) 2021, The Linux Foundation. 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 os
import ctypes
import pdb
from parser_util import register_parser, RamParser
from print_out import print_out_str
from linux_list import ListWalker
from ramdump import Struct
from struct_print import struct_print_class
@register_parser('--fs-parser', 'FS report', optional=True)
class fs_parser_class(RamParser):
def __init__(self, *args):
super(fs_parser_class, self).__init__(*args)
self.sb_vaddr_list = []
self.inode_vaddr_list = []
def output(self, str_val):
self.output_file.write(str_val)
def parse(self):
self.output_file = self.ramdump.open_file('fs-report.txt')
print_out_str("fs-parser:start")
s1 = self.ramdump.get_kernel_version()
s2 = 'Kernel version : [{0:d}.{0:d}.{0:d}]\n'.format(s1[0], s1[1], s1[2])
self.output(s2)
vaddr = self.ramdump.read_word('file_systems')
self.file_systems_struct(vaddr)
self.iterate_sb()
#self.iterate_inode()
print_out_str("fs-parser:end")
self.output_file.close()
return
def file_systems_struct(self, fs_vaddr):
if fs_vaddr == 0:
return
shc = struct_print_class(self.ramdump, 'file_system_type', fs_vaddr, self.output_file)
shc.append('name', 'char *')
shc.append('fs_flags', 'u32')
node = shc.append('fs_supers', 'hlist_head', list_struct_name='super_block', list_field_name='s_instances')
shc.append('next', 'ptr')
shc.process()
shc.print_struct()
name_ptr = shc.get_val('name')
name = self.ramdump.read_cstring(name_ptr)
if name not in ['proc', 'sysfs', 'tmpfs', 'debugfs', 'tracefs']:
fs_supers = shc.get_val('fs_supers')
s_instance_offset = self.ramdump.field_offset('struct super_block', 's_instances')
list_walker = ListWalker(self.ramdump, fs_supers, s_instance_offset)
list_walker.walk(fs_supers, self.fs_sb_list_func)
next_ptr = shc.get_val('next')
self.file_systems_struct(next_ptr)
def fs_sb_list_func(self, sb_vaddr):
self.sb_vaddr_list.append(sb_vaddr)
def iterate_sb(self):
for sb_vaddr in self.sb_vaddr_list:
self.print_sb(sb_vaddr)
def print_sb(self, sb_vaddr):
shc = struct_print_class(self.ramdump, 'super_block', sb_vaddr, self.output_file)
shc.append('s_flags', 'u32')
shc.append('s_iflags', 'u32')
shc.append('s_magic', 'u32')
shc.append('s_id', 'char[32]')
shc.append('s_inodes', 'list_head', list_struct_name='inode', list_field_name='i_sb_list')
shc.append('s_inodes_wb', 'list_head', list_struct_name='inode', list_field_name='i_wb_list')
shc.process()
shc.print_struct()
s_inodes = shc.get_val('s_inodes')
s_inodes_offset = self.ramdump.field_offset('struct inode', 'i_sb_list')
list_walker = ListWalker(self.ramdump, s_inodes, s_inodes_offset)
list_walker.walk(s_inodes, self.fs_inode_list_func)
s_inodes_wb = shc.get_val('s_inodes_wb')
s_inodes_wb_offset = self.ramdump.field_offset('struct inode', 'i_wb_list')
list_walker = ListWalker(self.ramdump, s_inodes_wb, s_inodes_wb_offset)
list_walker.walk(s_inodes_wb, self.fs_inode_list_func)
def fs_inode_list_func(self, inode_vaddr):
self.inode_vaddr_list.append(inode_vaddr)
def iterate_inode(self):
for inode_vaddr in self.inode_vaddr_list:
self.print_inode(inode_vaddr)
def print_inode(self, inode_vaddr):
shc = struct_print_class(self.ramdump, 'inode', inode_vaddr, self.output_file)
shc.append('i_opflags', 'u16')
shc.append('i_flags', 'u32')
shc.append('i_ino', 'u64')
shc.append('i_size', 'u64')
shc.append('i_bytes', 'u16')
shc.append('i_state', 'u64')
node = shc.append('i_lru', 'list_head', list_struct_name='inode', list_field_name='i_lru')
shc.process()
shc.print_struct()
if __name__ == '__main__':
print('nothing to do yet\n')

View File

@@ -0,0 +1,370 @@
# Copyright (c) 2017-2022, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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
from collections import OrderedDict
from parser_util import register_parser, RamParser
from print_out import print_out_str
from tempfile import NamedTemporaryFile
from .ftrace_event_list import FtraceParser_Event_List
from .ftrace_event import FtraceParser_Event, BufferedWrite
import linux_list as llist
#import time
@register_parser('--dump-ftrace', 'extract ftrace by iterate the ring buffer page',optional=True)
class FtraceParser(RamParser):
def __init__(self, *args):
super(FtraceParser, self).__init__(*args)
self.format_event_map = OrderedDict()
self.format_event_field_map = OrderedDict()
self.event_call = 'struct trace_event_call'
self.event_class = 'struct trace_event_class'
self.trace_names = ["binder", "bootreceiver", "clock_reg", "kgsl-fence",
"memory", "mmc", "rproc_qcom", "suspend", "ufs",
"usb", "wifi", "rwmmio"]
self.whitelisted_trace_names =[]
self.ftrace_buffer_size_kb = None
self.per_cpu_buffer_pages = None
self.savedcmd = self.ramdump.read_pdatatype('savedcmd')
if len(self.ramdump.ftrace_args):
self.whitelisted_trace_names = self.ramdump.ftrace_args
if self.ramdump.ftrace_max_size:
self.per_cpu_buffer_pages = self.ramdump.ftrace_max_size / 4
def ftrace_field_func(self, common_list, ram_dump):
name_offset = ram_dump.field_offset('struct ftrace_event_field', 'name')
type_offset = ram_dump.field_offset('struct ftrace_event_field', 'type')
filter_type_offset = ram_dump.field_offset('struct ftrace_event_field', 'filter_type')
field_offset = ram_dump.field_offset('struct ftrace_event_field', 'offset')
size_offset = ram_dump.field_offset('struct ftrace_event_field', 'size')
signed_offset = ram_dump.field_offset('struct ftrace_event_field', 'is_signed')
name = ram_dump.read_word(common_list + name_offset)
field_name = ram_dump.read_cstring(name, 256)
type_name = ram_dump.read_word(common_list + type_offset)
type_str = ram_dump.read_cstring(type_name, 256)
offset = ram_dump.read_u32(common_list + field_offset)
size = ram_dump.read_u32(common_list + size_offset)
signed = ram_dump.read_u32(common_list + signed_offset)
if re.match('(.*)\[(.*)', type_str) and not (re.match('__data_loc', type_str)):
s = re.split('\[', type_str)
s[1] = '[' + s[1]
self.formats_out.write(
"\tfield:{0} {1}{2};\toffset:{3};\tsize:{4};\tsigned:{5};\n".format(s[0], field_name, s[1], offset,
size, signed))
if "common_type" == field_name or "common_flags" == field_name or "common_preempt_count" == field_name or "common_pid" == field_name:
temp = 0
else:
format_list = []
format_list.append(type_str)
format_list.append(offset)
format_list.append(size)
self.format_event_field_map[field_name] = format_list
else:
self.formats_out.write(
"\tfield:{0} {1};\toffset:{2};\tsize:{3};\tsigned:{4};\n".format(type_str, field_name, offset, size,
signed))
#self.format_event_field_map = {}
if "common_type" == field_name or "common_flags" == field_name or "common_preempt_count" == field_name or "common_pid" == field_name:
temp = 0
else:
format_list = []
format_list.append(type_str)
format_list.append(offset)
format_list.append(size)
self.format_event_field_map[field_name] = format_list
def ftrace_events_func(self, ftrace_list, ram_dump):
event_offset = ram_dump.field_offset(self.event_call, 'event')
fmt_offset = ram_dump.field_offset(self.event_call, 'print_fmt')
class_offset = ram_dump.field_offset(self.event_call, 'class')
flags_offset = ram_dump.field_offset(self.event_call, 'flags')
flags = ram_dump.read_word(ftrace_list + flags_offset)
if ram_dump.kernel_version >= (4, 14):
TRACE_EVENT_FL_TRACEPOINT = 0x10
elif ram_dump.kernel_version >= (4, 9):
TRACE_EVENT_FL_TRACEPOINT = 0x20
else:
TRACE_EVENT_FL_TRACEPOINT = 0x40
if (ram_dump.kernel_version >= (3, 18) and (flags & TRACE_EVENT_FL_TRACEPOINT)):
tp_offset = ram_dump.field_offset(self.event_call, 'tp')
tp_name_offset = ram_dump.field_offset('struct tracepoint', 'name')
tp = ram_dump.read_word(ftrace_list + tp_offset)
name = ram_dump.read_word(tp + tp_name_offset)
else:
name_offset = ram_dump.field_offset(self.event_call, 'name')
name = ram_dump.read_word(ftrace_list + name_offset)
type_offset = ram_dump.field_offset('struct trace_event', 'type')
fields_offset = ram_dump.field_offset(self.event_class, 'fields')
common_field_list = ram_dump.address_of('ftrace_common_fields')
field_next_offset = ram_dump.field_offset('struct ftrace_event_field', 'link')
name_str = ram_dump.read_cstring(name, 512)
event_id = ram_dump.read_word(ftrace_list + event_offset + type_offset)
fmt = ram_dump.read_word(ftrace_list + fmt_offset)
fmt_str = ram_dump.read_cstring(fmt, 2048)
self.formats_out.write("name: {0}\n".format(name_str))
self.formats_out.write("ID: {0}\n".format(event_id))
self.formats_out.write("format:\n")
#self.format_event_map[name_str] = format_event_field_map
list_walker = llist.ListWalker(ram_dump, common_field_list, field_next_offset)
list_walker.walk_prev(common_field_list, self.ftrace_field_func, ram_dump)
self.formats_out.write("\n")
event_class = ram_dump.read_word(ftrace_list + class_offset)
field_list = event_class + fields_offset
list_walker = llist.ListWalker(ram_dump, field_list, field_next_offset)
list_walker.walk_prev(field_list, self.ftrace_field_func, ram_dump)
self.formats_out.write("\n")
self.formats_out.write("print fmt: {0}\n".format(fmt_str))
fmt_list = []
fmt_list.append(self.format_event_field_map)
fmt_list.append(fmt_str)
self.format_event_map[name_str] = fmt_list
self.format_event_field_map = OrderedDict()
def ftrace_get_format(self):
self.formats_out = self.ramdump.open_file('formats.txt')
fevent_list = FtraceParser_Event_List(self.ramdump)
#print(fevent_list.ftrace_raw_struct_type)
ftrace_events_list = self.ramdump.address_of('ftrace_events')
next_offset = self.ramdump.field_offset(self.event_call, 'list')
list_walker = llist.ListWalker(self.ramdump, ftrace_events_list, next_offset)
list_walker.walk_prev(ftrace_events_list, self.ftrace_events_func, self.ramdump)
self.formats_out.close()
return fevent_list
def ftrace_extract(self):
#ftrace_event_time = 0
#post_ftrace_event_time = 0
#taskdump_time = 0
#parse_trace_entry_time = 0
global_trace_data_org = self.ramdump.address_of('ftrace_trace_arrays')
global_trace_data_offset = self.ramdump.field_offset(
'struct list_head ', 'next')
global_trace_data_next = self.ramdump.read_pointer(global_trace_data_org + global_trace_data_offset)
if self.ramdump.kernel_version >= (5, 10):
trace_buffer_offset = self.ramdump.field_offset(
'struct trace_array', 'array_buffer')
else:
trace_buffer_offset = self.ramdump.field_offset(
'struct trace_array', 'trace_buffer')
trace_buffer_name_offset = self.ramdump.field_offset(
'struct trace_array', 'name')
if self.ramdump.kernel_version >= (5, 10):
ring_trace_buffer_ptr = self.ramdump.field_offset(
'struct array_buffer', 'buffer')
else:
ring_trace_buffer_ptr = self.ramdump.field_offset(
'struct trace_buffer', 'buffer')
if self.ramdump.kernel_version >= (5, 10):
ring_trace_buffer_cpus_ptr = self.ramdump.field_offset(
'struct trace_buffer', 'cpus')
ring_trace_buffer_base_addr = self.ramdump.field_offset(
'struct trace_buffer', 'buffers')
else:
ring_trace_buffer_cpus_ptr = self.ramdump.frame_field_offset(
'rb_wake_up_waiters','struct ring_buffer', 'cpus')
if ring_trace_buffer_cpus_ptr is None:
ring_trace_buffer_cpus_ptr = 0x4
ring_trace_buffer_base_addr = self.ramdump.frame_field_offset(
'rb_wake_up_waiters','struct ring_buffer', 'buffers')
if ring_trace_buffer_base_addr is None:
ring_trace_buffer_base_addr = self.ramdump.field_offset(
'struct ring_buffer', 'buffers')
if ring_trace_buffer_base_addr is None:
if self.ramdump.arm64:
ring_trace_buffer_base_addr = 0x58
else:
ring_trace_buffer_base_addr = 0x38
ring_trace_buffer_nr_pages = self.ramdump.field_offset(
'struct ring_buffer_per_cpu', 'nr_pages')
log_pattern = re.compile(r'\s*(.*)-(\d+)\s*\[(\d+)\]\s*.*')
fevent_list = self.ftrace_get_format();
while(global_trace_data_org != global_trace_data_next):
trace_array = global_trace_data_next
#print("v.v (struct trace_array)0x%x" %(trace_array))
trace_buffer_name = self.ramdump.read_word(trace_array + trace_buffer_name_offset)
if not (trace_buffer_name):
trace_name = None
else:
trace_name = self.ramdump.read_cstring(trace_buffer_name, 256)
trace_buffer_ptr_data = self.ramdump.read_pointer(trace_array + trace_buffer_offset)
ring_trace_buffer_data = trace_buffer_ptr_data + trace_buffer_offset
ring_trace_buffer_base_data = self.ramdump.read_pointer(ring_trace_buffer_data + ring_trace_buffer_ptr)
ring_trace_buffer_base_data1 = self.ramdump.read_pointer(ring_trace_buffer_base_data + ring_trace_buffer_base_addr)
if trace_name is None or trace_name == 0x0 or trace_name == "0x0" or trace_name == "None" or trace_name == "null" or len(trace_name) < 1:
#ftrace_out = self.ramdump.open_file('ftrace.txt','w')
fout = self.ramdump.open_file('ftrace.txt','w')
ftrace_out = BufferedWrite(fout)
header_data = "# tracer: nop \n" \
"#\n" \
"# entries-in-buffer/entries-written: 315882/1727030 #P:8\n" \
"#\n" \
"# _-----=> irqs-off\n" \
"# / _----=> need-resched\n" \
"# | / _---=> hardirq/softirq\n" \
"# || / _--=> preempt-depth\n" \
"# ||| / delay\n" \
"# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n" \
"# | | | |||| | |\n"
ftrace_out.write(header_data)
else:
if trace_name in self.whitelisted_trace_names or self.whitelisted_trace_names == ["all"]:
#ftrace_out = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '.txt','w')
fout = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '.txt','w')
ftrace_out = BufferedWrite(fout)
else:
global_trace_data_next = self.ramdump.read_pointer(global_trace_data_next)
continue
# ftrace_out = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '.txt','w')
ftrace_time_data = {}
nr_total_buffer_pages = 0
rb_per_cpu = []
nr_pages_per_buffer = []
#taskdump.do_dump_stacks(self.ramdump, 0)
for cpu_idx in range(0,8):
#array_ptr = self.ramdump.read_u64(ring_trace_buffer_base_data1 + self.ramdump.sizeof('void *') * cpu_idx)
array_ptr = (ring_trace_buffer_base_data1 + self.ramdump.sizeof('void *') * cpu_idx)
b = self.ramdump.read_pointer(array_ptr)
if b is None or b == 0x0:
continue
if self.ramdump.arm64:
nr_pages = self.ramdump.read_u64(
b + ring_trace_buffer_nr_pages)
else:
nr_pages = self.ramdump.read_u32(
b + ring_trace_buffer_nr_pages)
if nr_pages is None:
continue
if self.per_cpu_buffer_pages and self.per_cpu_buffer_pages < nr_pages:
nr_pages = self.per_cpu_buffer_pages
nr_total_buffer_pages = nr_total_buffer_pages + nr_pages
nr_pages_per_buffer.append(nr_pages)
rb_per_cpu.append(b)
#print "ring_trace_buffer_cpus nr_pages = %d" % nr_pages
#print "cpu_buffer = {0}".format(hex(b))
print("\nTotal pages across cpu trace buffers = {}".format(round(nr_total_buffer_pages)))
#start = time.time()
for cpu_idx in range(0,len(rb_per_cpu)):
nr_pages_per_buffer_item = nr_pages_per_buffer[cpu_idx]
per_cpu_buffer = rb_per_cpu[cpu_idx]
if per_cpu_buffer is not None:
evt = FtraceParser_Event(self.ramdump,ftrace_out,cpu_idx,fevent_list.ftrace_event_type,fevent_list.ftrace_raw_struct_type,ftrace_time_data,self.format_event_map,self.savedcmd)
evt.ring_buffer_per_cpu_parsing(per_cpu_buffer)
#parse_trace_entry_time += evt.parse_trace_entry_time
#ftrace_event_time += (time.time()-start)
global_trace_data_next = self.ramdump.read_pointer(global_trace_data_next)
switch_map = {}
ftrace_file_map = {}
if trace_name is None or trace_name == 0x0 or trace_name == "0x0" or trace_name == "None" or trace_name == "null" or len(trace_name) < 1:
ftrace_core0_fd = self.ramdump.open_file('ftrace_core0.txt', 'w')
ftrace_core1_fd = self.ramdump.open_file('ftrace_core1.txt', 'w')
ftrace_core2_fd = self.ramdump.open_file('ftrace_core2.txt', 'w')
ftrace_core3_fd = self.ramdump.open_file('ftrace_core3.txt', 'w')
ftrace_core4_fd = self.ramdump.open_file('ftrace_core4.txt', 'w')
ftrace_core5_fd = self.ramdump.open_file('ftrace_core5.txt', 'w')
ftrace_core6_fd = self.ramdump.open_file('ftrace_core6.txt', 'w')
ftrace_core7_fd = self.ramdump.open_file('ftrace_core7.txt', 'w')
else:
if trace_name in self.whitelisted_trace_names or self.whitelisted_trace_names == ["all"]:
ftrace_core0_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core0.txt','w')
ftrace_core1_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core1.txt','w')
ftrace_core2_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core2.txt','w')
ftrace_core3_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core3.txt','w')
ftrace_core4_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core4.txt','w')
ftrace_core5_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core5.txt','w')
ftrace_core6_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core6.txt','w')
ftrace_core7_fd = self.ramdump.open_file('ftrace_parser/' + 'ftrace_' + trace_name + '_core7.txt','w')
else:
continue
ftrace_file_map["000"] = BufferedWrite(ftrace_core0_fd)
ftrace_file_map["001"] = BufferedWrite(ftrace_core1_fd)
ftrace_file_map["002"] = BufferedWrite(ftrace_core2_fd)
ftrace_file_map["003"] = BufferedWrite(ftrace_core3_fd)
ftrace_file_map["004"] = BufferedWrite(ftrace_core4_fd)
ftrace_file_map["005"] = BufferedWrite(ftrace_core5_fd)
ftrace_file_map["006"] = BufferedWrite(ftrace_core6_fd)
ftrace_file_map["007"] = BufferedWrite(ftrace_core7_fd)
#start = time.time()
sorted_dict = {k: ftrace_time_data[k] for k in sorted(ftrace_time_data)}
for key in sorted(sorted_dict.keys()):
for i in range(0,len(ftrace_time_data[key])):
line = str(ftrace_time_data[key][i])
replaced_line = line
trace_log = log_pattern.match(line)
bestguess_pid = None
bestguess_comm = None
if bool(trace_log):
cpu_number = trace_log.group(3)
entry_pid = trace_log.group(2)
else:
cpu_number = None
entry_pid = None
if "sched_switch:" in line:
prev_comm = line.split("prev_comm=")[1].split(" ")[0]
prev_pid = line.split("prev_pid=")[1].split(" ")[0]
curr_comm = line.split("next_comm=")[1].split(" ")[0]
curr_pid = line.split("next_pid=")[1].split(" ")[0]
if cpu_number not in switch_map:
switch_map[cpu_number] = {}
switch_map[cpu_number]["comm"] = curr_comm
switch_map[cpu_number]["pid"] = curr_pid
bestguess_pid = prev_pid
bestguess_comm = prev_comm
elif "<TBD>" in line and cpu_number in switch_map:
bestguess_comm = switch_map[cpu_number]["comm"]
bestguess_pid = switch_map[cpu_number]["pid"]
if "<TBD>" in line:
if entry_pid is not None and bestguess_pid is not None and int(entry_pid) == int(bestguess_pid):
replaced_line = line.replace("<TBD>", bestguess_comm)
else:
replaced_line = line.replace("<TBD>", "<...>")
ftrace_out.write(replaced_line)
ftrace_file_map[str(cpu_number)].write(replaced_line)
#post_ftrace_event_time += (time.time()-start)
#print("Ftrace Event Parsing took {} secs".format(ftrace_event_time))
#print("Post Ftrace Event Sorting and Write took {} secs".format(post_ftrace_event_time))
#print("Parse Ftrace Entry function took {} secs".format(parse_trace_entry_time))
def parse(self):
if self.ramdump.ftrace_limit_time == 0:
self.ftrace_extract()
else:
from func_timeout import func_timeout
print_out_str("Limit ftrace parser running time to {}s".format(self.ramdump.ftrace_limit_time))
func_timeout(self.ramdump.ftrace_limit_time, self.ftrace_extract)

View File

@@ -0,0 +1,969 @@
# Copyright (c) 2020-2022 The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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.
from collections import OrderedDict
import re
from parser_util import register_parser, RamParser
from print_out import print_out_str
from tempfile import NamedTemporaryFile
from struct_print import struct_print_class
comm_pid_dict = {}
softirq_action_list = {}
softirq_action_list[0] = "HI_SOFTIRQ"
softirq_action_list[1] = "TIMER_SOFTIRQ"
softirq_action_list[2] = "NET_TX_SOFTIRQ"
softirq_action_list[3] = "NET_RX_SOFTIRQ"
softirq_action_list[4] = "BLOCK_IOPOLL_SOFTIRQ"
softirq_action_list[5] = "TASKLET_SOFTIRQ"
softirq_action_list[6] = "TASKLET_SOFTIRQ"
softirq_action_list[7] = "SCHED_SOFTIRQ"
softirq_action_list[8] = "HRTIMER_SOFTIRQ"
softirq_action_list[9] = "RCU_SOFTIRQ"
softirq_action_list[10] = "NR_SOFTIRQS"
TRACE_FLAG_IRQS_OFF = 0x01
TRACE_FLAG_IRQS_NOSUPPORT = 0x02
TRACE_FLAG_NEED_RESCHED = 0x04
TRACE_FLAG_HARDIRQ = 0x08
TRACE_FLAG_SOFTIRQ = 0x10
TRACE_FLAG_PREEMPT_RESCHED = 0x20
TRACE_FLAG_NMI = 0x40
TRACE_FLAG_BH_OFF = 0x80
class BufferedWrite(object):
"""
Helper class to facilitate batch
writes into the files.
Default batch size is 8000
"""
def __init__(self, fdesc):
self.buffer = []
self.fdesc = fdesc
self.batchsize = 8000
self.count = 0
def write(self, data):
self.buffer.append(data)
self.count += 1
if self.count >= 8000:
self.flush()
self.count = 0
def flush(self):
if len(self.buffer):
self.fdesc.write("".join(self.buffer))
self.buffer = []
def __del__(self):
if self.fdesc:
self.flush()
class FtraceParser_Event(object):
def __init__(self,ramdump,ftrace_out,cpu, trace_event_type,ftrace_raw_struct_type,ftrace_time_data,fromat_event_map,savedcmd):
self.cpu = "[{:03d}]".format(cpu)
self.ramdump = ramdump
self.ftrace_out = ftrace_out
#self.ftrace_out = BufferedWrite(ftrace_out)
self.nr_ftrace_events = 0
self.ftrace_event_type = trace_event_type
self.ftrace_raw_struct_type = ftrace_raw_struct_type
self.ftrace_time_data = ftrace_time_data
self.fromat_event_map = fromat_event_map
#self.parse_trace_entry_time = 0
self.buffer_page_real_end_offset = self.ramdump.field_offset(
'struct buffer_page ', 'real_end')
self.buffer_page_data_page_offset = self.ramdump.field_offset(
'struct buffer_page ', 'page')
self.buffer_data_page_commit_offset = self.ramdump.field_offset(
'struct buffer_data_page ', 'commit')
self.buffer_data_page_time_stamp_offset = self.ramdump.field_offset(
'struct buffer_data_page ', 'time_stamp')
self.buffer_data_page_data_offset = self.ramdump.field_offset(
'struct buffer_data_page ', 'data')
self.rb_event_array_offset = self.ramdump.field_offset(
'struct ring_buffer_event', 'array')
self.rb_event_timedelta_offset = self.ramdump.field_offset(
'struct ring_buffer_event', 'time_delta')
self.rb_event_typelen_offset = self.ramdump.field_offset(
'struct ring_buffer_event', 'type_len')
self.trace_entry_type_offset = self.ramdump.field_offset('struct trace_entry ', 'type')
self.pid_max = self.ramdump.read_int("pid_max")
self.map_cmdline_to_pid_offset = self.ramdump.field_offset(
'struct saved_cmdlines_buffer', 'map_cmdline_to_pid')
self.saved_cmdlines_offset = self.ramdump.field_offset(
'struct saved_cmdlines_buffer', 'saved_cmdlines')
self.pid_offset = self.ramdump.field_offset("struct trace_entry" , "pid")
self.preempt_count_offset = self.ramdump.field_offset("struct trace_entry", "preempt_count")
self.flags_offset = self.ramdump.field_offset("struct trace_entry", "flags")
self.comm_pid_dict = comm_pid_dict
self.savedcmd = savedcmd
def get_event_length(self, rb_event, rb_event_type, time_delta, buffer_data_page_end):
type_len = rb_event_type
if(type_len == 0):
length = self.ramdump.read_u32(rb_event + self.rb_event_array_offset)
return length
elif(type_len <= 28):
return (type_len << 2)
elif(type_len == 29):
if(time_delta == 1):
length = self.ramdump.read_u32(rb_event + self.rb_event_array_offset)
return length
else:
if rb_event > buffer_data_page_end:
print_out_str("rb_event({}) is bigger than buffer_data_page_end({})".format(hex(rb_event), hex(buffer_data_page_end)))
return -1
return buffer_data_page_end - rb_event #Padding till end of page
elif(type_len == 30):
# Accounts for header size + one u32 array entry
return 8
elif(type_len == 31):
return 8
else:
print_out_str("Unknown type_len {}".format(type_len))
return -1
def parse_buffer_page_entry(self, buffer_page_entry):
buffer_data_page = None
buffer_data_page_end = None
#buffer_data_page_data_offset = None
rb_event = None
rb_event_timestamp = 0
time_delta = 0
record_length = 0
#rb_event_array_offset = 0
tr_entry = None
tr_event_type = None
commit = 0
'''
struct buffer_page {
[0x0] struct list_head list;
[0x10] local_t write;
[0x18] unsigned int read;
[0x20] local_t entries;
[0x28] unsigned long real_end;
[0x30] struct buffer_data_page *page;
}
'''
buffer_data_page = self.ramdump.read_pointer(buffer_page_entry + self.buffer_page_data_page_offset)
'''
struct buffer_data_page {
[0x0] u64 time_stamp;
[0x8] local_t commit;
[0x10] unsigned char data[];
}
'''
commit = 0
if self.ramdump.arm64:
commit = self.ramdump.read_u64(
buffer_data_page + self.buffer_data_page_commit_offset)
else:
commit = self.ramdump.read_u32(
buffer_data_page + self.buffer_data_page_commit_offset)
if commit and commit > 0:
buffer_data_page_end = buffer_data_page + commit
time_stamp = self.ramdump.read_u64(
buffer_data_page + self.buffer_data_page_time_stamp_offset)
rb_event = buffer_data_page + self.buffer_data_page_data_offset
total_read = 0
while (total_read < commit):
time_delta = self.ramdump.read_u32(rb_event + self.rb_event_timedelta_offset)
time_delta = time_delta >> 5
# print_out_str("time_delta after = {0} ".format(time_delta))
rb_event_timestamp = rb_event_timestamp + time_delta
rb_event_length_old = self.ramdump.read_u32(rb_event + self.rb_event_typelen_offset)
rb_event_type = (((1 << 5) - 1) & rb_event_length_old);
record_length = self.get_event_length(rb_event, rb_event_type, time_delta, buffer_data_page_end)
if record_length == -1:
break
#print("rb_event_type is ", rb_event_type)
if rb_event_type == 0:
# This could be that type_len * 4 > 112
# so type_len is set to 0 and 32 bit array filed holds length
# while payload starts afterwards at array[1]
tr_entry = rb_event + self.rb_event_array_offset + 0x4
tr_event_type = self.ramdump.read_u16( tr_entry + self.trace_entry_type_offset)
if tr_event_type < self.nr_ftrace_events:
#self.ftrace_out.write("unknown event \n")
pass
else:
self.parse_trace_entry(tr_entry, tr_event_type, time_stamp + rb_event_timestamp)
record_length = record_length + 0x4 #Header Size
elif rb_event_type <= 28: #Data Events
tr_entry = rb_event + self.rb_event_array_offset
tr_event_type = self.ramdump.read_u16(tr_entry + self.trace_entry_type_offset)
if tr_event_type < self.nr_ftrace_events:
#self.ftrace_out.write("unknown event \n")
pass
else:
self.parse_trace_entry(tr_entry, tr_event_type, time_stamp + rb_event_timestamp)
record_length = record_length + 0x4
elif rb_event_type == 29:
"""
Padding event or discarded event
time_delta here can be 0 or 1
time delta is set 0 when event is bigger than minimum size (8 bytes)
in this case we consider rest of the page as padding
time delta is set to 1 for discarded event
Here the size is stored in array[0]
"""
record_length = record_length + 0x4
pass
elif rb_event_type == 30:
# This is a time extend event so we need to use the 32 bit field from array[0](28..59)
# if time delta actually exceeds 2^27 nanoseconds which is > what 27 bit field can hold
# We are accounting for a complete time stamp stored in this field (59 bits)
rb_event_timestamp = rb_event_timestamp + (self.ramdump.read_u32(rb_event + self.rb_event_array_offset) << 27)
elif rb_event_type == 31:
# Accounts for an absolute timestamp
rb_event_timestamp = 0
rb_event = rb_event + record_length
total_read += record_length
#alignment = 4 - (rb_event % 4)
#rb_event += alignment
def remaing_space(self,count,text_count):
r = count - text_count
temp = " "
for idx in range(r):
temp = temp + " "
return temp
def find_cmdline(self, pid):
comm = "<TBD>"
if self.savedcmd is not None:
if pid == 0:
comm = "<idle>"
else:
tpid = pid & (self.pid_max - 1)
cmdline_map = self.savedcmd.map_pid_to_cmdline[tpid]
if cmdline_map != -1 and cmdline_map != None:
map_cmdline_to_pid = self.savedcmd.map_cmdline_to_pid
cmdline_tpid = self.ramdump.read_int(map_cmdline_to_pid + cmdline_map * 4)
if cmdline_tpid == pid:
saved_cmdlines = self.savedcmd.saved_cmdlines
comm = self.ramdump.read_cstring(saved_cmdlines + cmdline_map * 16, 16) #TASK_COMM_LEN
comm = "{}-{}".format(comm, pid)
self.comm_pid_dict[pid] = comm
return comm
def get_lat_fmt(self, flags, preempt_count):
lat_fmt = ''
nmi = flags & TRACE_FLAG_NMI
hardirq = flags & TRACE_FLAG_HARDIRQ
softirq = flags & TRACE_FLAG_SOFTIRQ
bh_off = flags & TRACE_FLAG_BH_OFF
if (flags & TRACE_FLAG_IRQS_OFF) and bh_off:
irqs_off = 'D'
elif (flags & TRACE_FLAG_IRQS_OFF):
irqs_off = 'd'
elif bh_off:
irqs_off = 'b'
elif (flags & TRACE_FLAG_IRQS_NOSUPPORT):
irqs_off = 'X'
else:
irqs_off = '.'
resched = flags & (TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED)
if (resched == (TRACE_FLAG_NEED_RESCHED | TRACE_FLAG_PREEMPT_RESCHED)):
need_resched = 'N'
elif (resched == TRACE_FLAG_NEED_RESCHED):
need_resched = 'n'
elif (resched == TRACE_FLAG_PREEMPT_RESCHED):
need_resched = 'p'
else:
need_resched = '.'
if nmi and hardirq:
hardsoft_irq = 'Z'
elif nmi:
hardsoft_irq = 'z'
elif hardirq and softirq:
hardsoft_irq = 'H'
elif hardirq:
hardsoft_irq = 'h'
elif softirq:
hardsoft_irq = 's'
else:
hardsoft_irq = '.'
lat_fmt += irqs_off
lat_fmt += need_resched
lat_fmt += hardsoft_irq
if (preempt_count & 0xf):
lat_fmt += '{0:x}'.format((preempt_count & 0xf))
else:
lat_fmt += '.'
if (preempt_count & 0xf0):
lat_fmt += '{0:x}'.format((preempt_count >> 4))
else:
lat_fmt += '.'
return lat_fmt
def parse_trace_entry(self, entry, type, time):
ftrace_raw_entry = None
event_name = ""
local_timestamp = None
pid = 0
preempt_count = 0
struct_type = None
next_comm = None
next_pid = 0
next_prio = 0
work = None
print_ip = None
print_buffer = None
vector = None
space_count = 25
local_timestamp = time / 1000000000.0
if not (local_timestamp in self.ftrace_time_data):
self.ftrace_time_data[local_timestamp] = []
#print("type = {0}".format(type))
if str(type) not in self.ftrace_event_type:
#print_out_str("unknown event type = {0}".format(str(type)))
return
event_name = str(self.ftrace_event_type[str(type)])
#print("event_name {0}".format(event_name))
if event_name is None or event_name == 'None' or 'None' in event_name or len(event_name) <= 1:
return
ftrace_raw_entry = entry
struct_type = self.ftrace_raw_struct_type[str(type)]
pid = self.ramdump.read_u32(ftrace_raw_entry + self.pid_offset)
if pid > self.pid_max:
return
preempt_count = self.ramdump.read_u16(ftrace_raw_entry + self.preempt_count_offset) & 0xFF
flags = self.ramdump.read_u16(ftrace_raw_entry + self.flags_offset) & 0xFF
DEBUG_ENABLE = 0
if pid in self.comm_pid_dict.keys():
comm = self.comm_pid_dict[pid]
else:
comm = self.find_cmdline(pid)
curr_comm = "{0: >25}".format(comm)
lat_fmt = self.get_lat_fmt(flags, preempt_count)
if event_name == "scm_call_start":
#print("ftrace_raw_entry of scm_call_start = {0}".format(hex(ftrace_raw_entry)))
trace_event_raw_offset = self.ramdump.field_offset('struct ' + struct_type, "x0")
trace_event_raw_next_comm = self.ramdump.field_offset('struct ' + struct_type, "arginfo")
trace_event_raw_next_pid = self.ramdump.field_offset('struct ' + struct_type, "args")
trace_event_raw_next_prio = self.ramdump.field_offset('struct ' + struct_type, "x5")
"""prev_state = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_offset)
if ( prev_state == 0) or ( prev_state == 0x400):
prev_state = "RUNNING";
elif ( prev_state == 1):
prev_state = "S";
elif ( prev_state == 2):
prev_state = "T";
else:
prev_state = "OTHERS";"""
if self.ramdump.arm64:
x0 = self.ramdump.read_u64(ftrace_raw_entry + trace_event_raw_offset)
else:
x0 = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_offset)
arginfo = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_next_comm)
args = (ftrace_raw_entry + trace_event_raw_next_pid)
if self.ramdump.arm64:
x5 = self.ramdump.read_u64(ftrace_raw_entry + trace_event_raw_next_prio)
else:
x5 = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_next_prio)
#print("x0 = {0}".format(hex(x0)))
#print("x5 = {0}".format(hex(x5)))
#print("arginfo = {0}".format(hex(arginfo)))
arr = []
ptr_size = self.ramdump.sizeof('void *')
#print("ptr_size = {0}".format(ptr_size))
for i in range(1, 9):
if self.ramdump.arm64:
ptr = self.ramdump.read_u64(args + (i*ptr_size))
else:
ptr = self.ramdump.read_u32(args + (i*ptr_size))
arr.append(hex(ptr))
space_data = self.remaing_space(space_count,len("scm_call_start:"))
if DEBUG_ENABLE == 1:
self.ftrace_out.write(
" <TBD>-{9} {0} {10} {1:.6f}: scm_call_start:{2}func id={3}:(args:{4}, {5}, {6} ,{7} ,{8})\n".format(self.cpu, round(local_timestamp,6),
space_data,x0,arginfo,arr[0],arr[1],arr[2],x5,pid,lat_fmt))
temp_data = " {9} {0} {10} {1:.6f}: scm_call_start:{2}func id={3}:(args:{4}, {5}, {6} ,{7} ,{8})\n".format(self.cpu, round(local_timestamp,6),
space_data,hex(x0),hex(arginfo),arr[0],arr[1],arr[2],hex(x5),curr_comm,lat_fmt)
#print("temp_data = {0}".format(temp_data))
self.ftrace_time_data[local_timestamp].append(temp_data)
elif event_name == "scm_call_end":
#print("ftrace_raw_entry of scm_call_start = {0}".format(hex(ftrace_raw_entry)))
trace_event_raw_offset = self.ramdump.field_offset('struct ' + struct_type, "ret")
"""prev_state = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_offset)
if ( prev_state == 0) or ( prev_state == 0x400):
prev_state = "RUNNING";
elif ( prev_state == 1):
prev_state = "S";
elif ( prev_state == 2):
prev_state = "T";
else:
prev_state = "OTHERS";"""
if self.ramdump.arm64:
rets = self.ramdump.read_u64(ftrace_raw_entry + trace_event_raw_offset)
else:
rets = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_offset)
#print("x0 = {0}".format(hex(x0)))
#print("x5 = {0}".format(hex(x5)))
#print("arginfo = {0}".format(hex(arginfo)))
arr = []
ptr_size = self.ramdump.sizeof('void *')
#print("ptr_size = {0}".format(ptr_size))
for i in range(1, 4):
if self.ramdump.arm64:
ptr = self.ramdump.read_u64(rets + (i*ptr_size))
else:
ptr = self.ramdump.read_u32(rets + (i*ptr_size))
arr.append(ptr)
space_data = self.remaing_space(space_count,len("scm_call_end:"))
if DEBUG_ENABLE == 1:
self.ftrace_out.write(
" <TBD>-{6} {0} {7} {1:.6f}: scm_call_end:{2}ret:{3}, {4}, {5}\n".format(self.cpu, round(local_timestamp,6),
space_data,arr[0],arr[1],arr[2],pid,lat_fmt))
temp_data = " {6} {0} {7} {1:.6f}: scm_call_end:{2}ret:{3}, {4}, {5}\n)\n".format(self.cpu, round(local_timestamp,6),
space_data,arr[0],arr[1],arr[2],curr_comm,lat_fmt)
#print("temp_data = {0}".format(temp_data))
self.ftrace_time_data[local_timestamp].append(temp_data)
elif event_name == "sched_switch":
trace_event_raw_offset = self.ramdump.field_offset('struct ' + struct_type, "prev_state")
trace_event_raw_next_comm = self.ramdump.field_offset('struct ' + struct_type, "next_comm")
trace_event_raw_next_pid = self.ramdump.field_offset('struct ' + struct_type, "next_pid")
trace_event_raw_next_prio = self.ramdump.field_offset('struct ' + struct_type, "next_prio")
trace_event_raw_prev_comm = self.ramdump.field_offset('struct ' + struct_type, "prev_comm")
trace_event_raw_prev_pid = self.ramdump.field_offset('struct ' + struct_type, "prev_pid")
trace_event_raw_prev_prio = self.ramdump.field_offset('struct ' + struct_type, "prev_prio")
trace_event_raw_prev_state = self.ramdump.field_offset('struct ' + struct_type, "prev_state")
next_comm = self.ramdump.read_cstring(ftrace_raw_entry + trace_event_raw_next_comm)
next_pid = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_next_pid)
next_prio = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_next_prio)
prev_comm = self.ramdump.read_cstring(ftrace_raw_entry + trace_event_raw_prev_comm)
prev_pid = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_prev_pid)
prev_prio = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_prev_prio)
prev_state1 = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_prev_state)
'''if ( prev_state1 == 0) or ( prev_state1 == 0x400):
prev_state1 = "R";
elif ( prev_state1 == 1):
prev_state1 = "S";
elif ( prev_state1 == 2):
prev_state1 = "D";
else:
prev_state1 = "T";'''
prev_state_info = (prev_state1 & ((((0x0000 | 0x0001 | 0x0002 | 0x0004 | 0x0008 | 0x0010 | 0x0020 | 0x0040) + 1) << 1) - 1))
if ( prev_state_info == 0):
prev_state_info = "R"
elif ( prev_state_info == 1):
prev_state_info = "S"
elif ( prev_state_info == 2):
prev_state_info = "D"
elif ( prev_state_info == 4):
prev_state_info = "T"
elif ( prev_state_info == 8):
prev_state_info = "t"
elif ( prev_state_info == 16):
prev_state_info = "X"
elif ( prev_state_info == 32):
prev_state_info = "Z"
elif ( prev_state_info == 64):
prev_state_info = "P"
elif ( prev_state_info == 128):
prev_state_info = "I"
prev_state_info2 = ""
if prev_state_info:
prev_state_info2 = "+"
space_data = self.remaing_space(space_count,len("sched_switch:"))
if DEBUG_ENABLE == 1:
self.ftrace_out.write(
" <TBD>-{10} {0} {11} {1:.6f}: sched_switch:{2}{3}:{4} [{5}] {6} ==> {7}:{8} [{9}]\n".format(self.cpu, round(local_timestamp,6),
space_data,prev_comm,prev_pid,prev_prio,prev_state_info,next_comm,next_pid,next_prio,pid,lat_fmt))
temp_data = " {10} {0} {11} {1:.6f}: sched_switch:{2}{3}:{4} [{5}] {6} ==> {7}:{8} [{9}]\n".format(self.cpu, round(local_timestamp,6),
space_data,prev_comm,prev_pid,prev_prio,prev_state_info,next_comm,next_pid,next_prio,curr_comm,lat_fmt)
temp_data1 = " {9} {0} {10} {1:.6f}: sched_switch: prev_comm={2} prev_pid={3} prev_prio={4} prev_state={5} ==> next_comm={6} next_pid={7} next_prio={8}\n".format(self.cpu, round(local_timestamp,6),
prev_comm,prev_pid,prev_prio,prev_state_info,next_comm,next_pid,next_prio,curr_comm,lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data1)
elif event_name == "softirq_raise":
trace_event_softirq_vec_offset = self.ramdump.field_offset('struct ' + 'trace_event_raw_softirq', "vec")
if trace_event_softirq_vec_offset:
vector = self.ramdump.read_u32(ftrace_raw_entry + trace_event_softirq_vec_offset)
if DEBUG_ENABLE == 1:
self.ftrace_out.write(
" <TBD>-{4} {0} {5} {1:.6f}: softirq_entry: vec={2} [action={3}]\n".format(self.cpu, local_timestamp, vector,softirq_action_list[vector],pid,lat_fmt))
try:
temp_data = " {4} {0} {5} {1:.6f}: softirq_raise: vec={2} [action={3}]\n".format(self.cpu, local_timestamp, vector,softirq_action_list[vector],curr_comm,lat_fmt)
except Exception as err:
print_out_str("failed to find a softirq action = {0}".format(vector))
temp_data = " {4} {0} {5} {1:.6f}: softirq_raise: vec={2} [action={3}]\n".format(self.cpu, local_timestamp, vector,"softirq unknown vector",curr_comm,lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
elif event_name == "workqueue_activate_work":
trace_event_raw_work_offset = self.ramdump.field_offset('struct ' + 'trace_event_raw_workqueue_execute_start', "work")
function_offset = self.ramdump.field_offset(
'struct ' + 'work_struct', "func")
if trace_event_raw_work_offset:
space_data = self.remaing_space(space_count,len("workqueue_activate_work:"))
if self.ramdump.arm64:
work = self.ramdump.read_u64(ftrace_raw_entry + trace_event_raw_work_offset)
function = self.ramdump.read_u64(work + function_offset)
else:
work = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_work_offset)
function = self.ramdump.read_u32(work + function_offset)
if function != None:
function_name = self.ramdump.unwind_lookup(function)
if function_name == None:
function_name = 'na'
else:
function = 0
function_name = 'na'
temp_data = " {4} {0} {7} {1:.6f}: workqueue_activate_work:{2}work struct {3} function 0x{5:x} {6}\n".format(self.cpu,
local_timestamp,space_data,
str(hex(work)).replace("L",""), curr_comm, function, function_name, lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
elif event_name == "workqueue_execute_start" or event_name == "workqueue_execute_end" or event_name == "workqueue_queue_work":
trace_event_raw_work_offset = 0
function_offset = 0
if event_name == "workqueue_execute_start":
function_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_execute_start', "function")
trace_event_raw_work_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_execute_start', "work")
elif event_name == "workqueue_execute_end":
function_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_queue_work', "function")
trace_event_raw_work_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_queue_work', "work")
elif event_name == "workqueue_queue_work":
function_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_execute_end', "function")
trace_event_raw_work_offset = self.ramdump.field_offset(
'struct ' + 'trace_event_raw_workqueue_execute_end', "work")
function = 0
if function_offset:
if self.ramdump.arm64:
function = self.ramdump.read_u64(ftrace_raw_entry + function_offset)
else:
function = self.ramdump.read_u32(ftrace_raw_entry + function_offset)
function_name = 'na'
if function != 0:
function_name = self.ramdump.unwind_lookup(function)
if function_name == None:
function_name = 'na'
if trace_event_raw_work_offset:
if self.ramdump.arm64:
work = self.ramdump.read_u64(ftrace_raw_entry + trace_event_raw_work_offset)
else:
work = self.ramdump.read_u32(ftrace_raw_entry + trace_event_raw_work_offset)
temp_data = " {4} {0} {7} {1:.6f}: {2} work_struct {3} function 0x{5:x} {6}\n".format(self.cpu,
local_timestamp, event_name,
str(hex(work)).replace("L",""), curr_comm, function, function_name, lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
elif event_name == "bprint":
MAX_LEN = 1000
print_entry_ip_offset = self.ramdump.field_offset('struct bprint_entry' , "ip")
print_entry_buf_offset = self.ramdump.field_offset('struct bprint_entry', "buf")
print_entry_fmt_offset = self.ramdump.field_offset('struct bprint_entry', "fmt")
print_ip = self.ramdump.read_word(ftrace_raw_entry + print_entry_ip_offset)
if self.ramdump.arm64:
print_entry_fmt = self.ramdump.read_u64(ftrace_raw_entry + print_entry_fmt_offset)
else:
print_entry_fmt = self.ramdump.read_u32(ftrace_raw_entry + print_entry_fmt_offset)
print_entry_fmt_data = self.ramdump.read_cstring(print_entry_fmt, MAX_LEN)
"""
['%px', '%llx', '%ps', '%p']
Supported :
d for integers
f for floating-point numbers
b for binary numbers
o for octal numbers
x for octal hexadecimal numbers
s for string
e for floating-point in an exponent format
"""
regex = re.compile('%[\*]*[a-zA-Z]+')
length = 0
print_buffer = []
print_buffer_offset = ftrace_raw_entry + print_entry_buf_offset
if print_entry_fmt_data:
function = self.ramdump.get_symbol_info1(print_ip)
prev_match = None
unaligned_print_buffer_offset = None
for match in regex.finditer(print_entry_fmt_data):
replacement = match.group()
if 'c' in match.group():
replacement = '%s'
print_buffer.append(self.ramdump.read_byte(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('char')
elif "%*pbl" in match.group():
replacement = "%s"
print_entry_fmt_data = print_entry_fmt_data.replace(match.group(), replacement)
align = self.ramdump.sizeof("int") - 1
#Read precision/width
#print_buffer.append(self.ramdump.read_int(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('unsigned int')
print_buffer_offset = (print_buffer_offset + (align)) & (~align)
#Read bitmask
nr_cpu_ids = self.ramdump.address_of("nr_cpu_ids")
nr_cpu_ids = self.ramdump.read_u32(nr_cpu_ids)
#single element of long is enough to accomodate all cpus
cpu_bits = self.ramdump.read_u64(print_buffer_offset)
# Trim bits to valid mask only
def getValidBits(num,k,p):
binary = bin(num)
binary = binary[2:]
end = len(binary) - p
start = end - k
return binary[start : end+1]
cpu_bits = getValidBits(cpu_bits, nr_cpu_ids, 0)
#print_buffer.append("{:b}".format(cpu_bits))
print_buffer.append(cpu_bits)
print_buffer_offset += self.ramdump.sizeof('unsigned long')
print_buffer_offset = (print_buffer_offset + (align)) & (~align)
continue
elif '%ps' in match.group():
replacement = "%s%x"
if self.ramdump.arm64:
addr = self.ramdump.read_u64(print_buffer_offset)
wname = self.ramdump.unwind_lookup(addr)
if wname is None:
wname = 'na'
print_buffer.append(wname)
print_buffer.append(addr)
print_buffer_offset += 8
else:
addr = self.ramdump.read_u32(print_buffer_offset)
wname = self.ramdump.unwind_lookup(addr)
if wname is None:
wname = 'na'
print_buffer.append(wname)
print_buffer.append(addr)
print_buffer_offset += 4
elif '%pS' in match.group():
replacement = "%s(%x)"
if self.ramdump.arm64:
addr = self.ramdump.read_u64(print_buffer_offset)
wname = self.ramdump.unwind_lookup(addr)
if wname is None:
wname = 'na'
else:
wname = '{}+{}'.format(wname[0], hex(wname[1]))
print_buffer.append(wname)
print_buffer.append(addr)
print_buffer_offset += 8
else:
addr = self.ramdump.read_u32(print_buffer_offset)
wname = self.ramdump.unwind_lookup(addr)
if wname is None:
wname = 'na'
else:
wname = '{}+{}'.format(wname[0], hex(wname[1]))
print_buffer.append(wname)
print_buffer.append(addr)
print_buffer_offset += 4
elif '%p' in match.group() and '%ps' not in match.group() and '%pS' not in match.group():
replacement = "%x"
if self.ramdump.arm64:
print_buffer.append(self.ramdump.read_u64(print_buffer_offset))
print_buffer_offset += 8
else:
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += 4
elif 'x' in match.group():
replacement = "%x"
if self.ramdump.arm64:
print_buffer.append(self.ramdump.read_u64(print_buffer_offset))
print_buffer_offset += 8
else:
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += 4
elif 's' in match.group():
replacement = "%s"
if prev_match is not None and '%s' in prev_match:
print_buffer_offset = unaligned_print_buffer_offset
sdata = self.ramdump.read_cstring(print_buffer_offset)
print_buffer.append(sdata)
print_buffer_offset = print_buffer_offset + len(sdata) + 1
elif 'll' in match.group() or 'l' in match.group():
replacement = "%d"
if self.ramdump.arm64:
print_buffer.append(self.ramdump.read_u64(print_buffer_offset))
print_buffer_offset += 8
else:
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += 4
elif 'h' in match.group():
print_buffer.append(self.ramdump.read_u16(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('short')
elif 'd' in match.group():
replacement = "%d"
if self.ramdump.arm64:
print_buffer.append(self.ramdump.read_int(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('int')
elif 'u' in match.group():
replacement = "%d"
if 'll' in match.group() or 'l' in match.group():
if self.ramdump.arm64:
print_buffer.append(self.ramdump.read_u64(print_buffer_offset))
print_buffer_offset += 8
else:
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += 4
else:
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('unsigned int')
elif 'f' in match.group():
replacement = "%f"
print_buffer.append(self.ramdump.read_u32(print_buffer_offset))
print_buffer_offset += self.ramdump.sizeof('float')
if replacement != match.group():
print_entry_fmt_data = print_entry_fmt_data.replace(match.group(), replacement)
length += 1
prev_match = match.group()
unaligned_print_buffer_offset = print_buffer_offset
align = self.ramdump.sizeof("int") - 1
print_buffer_offset = (print_buffer_offset + (align)) & (~align)
try:
temp_data = " {4} {0} {5} {1:.6f}: bprint: {2} {3}\n".format(self.cpu,
local_timestamp,
function,print_entry_fmt_data% (
tuple(print_buffer)),
curr_comm,lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
except Exception as err:
temp_data = "Error parsing bprint event entry"
return
elif event_name == "print":
#print("ftrace_raw_entry = {0}".format(hex(ftrace_raw_entry)))
print_entry_ip_offset = self.ramdump.field_offset('struct print_entry' , "ip")
print_entry_buf_offset = self.ramdump.field_offset('struct print_entry', "buf")
#print_entry_fmt_offset = self.ramdump.field_offset('struct print_entry', "fmt")
print_ip = self.ramdump.read_word(ftrace_raw_entry + print_entry_ip_offset)
print_buffer = self.ramdump.read_cstring(ftrace_raw_entry + print_entry_buf_offset)
#print_entry_fmt = self.ramdump.read_u64(ftrace_raw_entry + print_entry_fmt_offset)
#print_entry_fmt_data = self.ramdump.read_cstring(print_entry_fmt)
#print_ip_func = self.ramdump.read_cstring(print_ip)
function = self.ramdump.get_symbol_info1(print_ip)
temp_data = " {4} {0} {5} {1:.6f}: print: {2} {3}\n".format(self.cpu, local_timestamp , function, print_buffer, curr_comm, lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
else:
event_data = self.fromat_event_map[event_name]
fmt_str = event_data[1]
if "rpmh" in event_name:
fmt_str = fmt_str.replace('send-msg:','send-msg')
fmt_str = fmt_str.replace(': ','')
elif "workqueue" in event_name:
fmt_str = fmt_str.replace('work struct','work_struct')
offset_data = event_data[0]
fmt_name_value_map = OrderedDict()
try:
d = str(fmt_str.split('",')[1].replace("'", ''))
pr = str(fmt_str.split('",')[0].replace("'", ''))
pr = str(pr.split('",')[0].replace('"', ''))
pr = str(pr.split('",')[0].replace('[', ''))
pr = str(pr.split('",')[0].replace(']', ''))
if "cpuhp_latency" == event_name:
pr = pr.replace("USEC ret: %d","USEC_ret:%d")
if "thermal_device_update" == event_name:
pr = pr.replace("received event","received_event")
temp_a = []
for ii in d.split(","):
ii = str(ii).replace("'","").replace(" ","")
temp_a.append(ii)
j = 0
temp_a = []
pr_f = []
if "workqueue_execute" in event_name:
for ki in pr.split(": "):
pr_f.append(str(ki))
else:
if ", " in pr and event_name != 'user_fault':
for ki in pr.split(", "):
if len(ki) >= 1:
pr_f.append(str(ki).replace(" ",""))
else:
for ki in pr.split(" "):
if len(ki) >= 1:
pr_f.append(str(ki).replace(" ",""))
for item,item_list in offset_data.items():
type_str,offset,size = item_list
if 'unsigned long' in type_str or 'u64' in type_str or '*' in type_str:
if self.ramdump.arm64:
v = self.ramdump.read_u64(ftrace_raw_entry + offset)
else:
v = self.ramdump.read_u32(ftrace_raw_entry + offset)
if "rwmmio" in event_name and "addr" in item:
phys = self.ramdump.virt_to_phys(v)
fmt_name_value_map[item] = "{}({})".format(hex(int(v)), hex(phys))
elif "func" not in item:
fmt_name_value_map[item] = hex(int(v))
else:
fmt_name_value_map[item] = v
elif 'long' in type_str or 'int' in type_str or 'u32' in type_str or 'bool' in type_str or 'pid_t' in type_str:
v = self.ramdump.read_u32(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
elif 'u8' in type_str:
v = self.ramdump.read_byte(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
elif 'const' in type_str and 'char *' in type_str:
v = self.ramdump.read_pointer(ftrace_raw_entry + offset)
v = self.ramdump.read_cstring(v)
fmt_name_value_map[item] = v
elif type_str.startswith('__data_loc') and type_str.endswith('char[]'):
v = self.ramdump.read_u32(ftrace_raw_entry + offset)
v = self.ramdump.read_cstring(ftrace_raw_entry + (v & 0xffff), (v >> 16))
if isinstance(v, bytes):
v = self.ramdump.read_cstring(ftrace_raw_entry + (offset*4))
fmt_name_value_map[item] = v
elif 'char[' in type_str:
length = re.match(r'(?:unsigned )?char\[(\d+)\]', type_str)
if length:
length = int(length.group(1))
else:
if "[TASK_COMM_LEN]" in type_str:
length = 16
else:
print_out_str("ftrace: unknown length for {} ({})".format(item, type_str))
length = 12 # Chosen arbitrarily
v = self.ramdump.read_cstring(ftrace_raw_entry + offset, max_length=length)
fmt_name_value_map[item] = v
elif 'char' in type_str:
v = self.ramdump.read_byte(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
elif 'unsigned short' in type_str or 'u16' in type_str:
v = self.ramdump.read_u16(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
elif 'short' in type_str or 'signed short' in type_str or 's16' in type_str:
v = self.ramdump.read_s32(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
elif 's64' in type_str:
v = self.ramdump.read_s64(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
else:
v = self.ramdump.read_u32(ftrace_raw_entry + offset)
fmt_name_value_map[item] = v
if "softirq" in event_name:
if v > len(softirq_action_list) -1:
action = v
else:
action = softirq_action_list[v]
fmt_name_value_map['action'] = action
if "rwmmio" in event_name and "caller" in item:
symbol = self.ramdump.read_word(ftrace_raw_entry + offset)
if symbol is not None:
fmt_name_value_map[item] = self.ramdump.get_symbol_info1(symbol)
temp_a.append(v)
j = j + 1
temp = ""
try:
for keyinfo in fmt_name_value_map:
if "function" == keyinfo and isinstance(fmt_name_value_map[keyinfo], int):
wq_function1 = self.ramdump.get_symbol_info1(fmt_name_value_map[keyinfo])
tt = keyinfo + "=" + wq_function1
if "func" in keyinfo and isinstance(fmt_name_value_map[keyinfo], int):
wq_function1 = self.ramdump.get_symbol_info1(fmt_name_value_map[keyinfo])
if wq_function1 and len(wq_function1) > 1 and wq_function1 != 'No':
tt = keyinfo + "=" + wq_function1
else:
tt = keyinfo + "=" + str(hex(fmt_name_value_map[keyinfo]))
else:
tt = keyinfo + "=" + str(fmt_name_value_map[keyinfo])
temp = temp + tt + " "
except Exception as err:
#print_out_str("missing event = {0} err = {1}".format(event_name,str(err)))
pass
try:
temp = temp + "\n"
temp_data = " {4} {0} {5} {1:.6f}: {2} {3}".format(self.cpu, round(local_timestamp, 6),event_name,temp,curr_comm,lat_fmt)
self.ftrace_time_data[local_timestamp].append(temp_data)
temp = ""
except Exception as err:
#print_out_str("missing event = {0} err = {1}".format(event_name,str(err)))
pass
except Exception as err:
#print_out_str("missing event = {0} err = {1}".format(event_name,str(err)))
pass
def ring_buffer_per_cpu_parsing(self, ring_trace_buffer_cpu):
page_index = 0
buffer_page_list_offset = self.ramdump.field_offset(
'struct buffer_page ', 'list')
buffer_page_list_prev_offset = self.ramdump.field_offset(
'struct list_head ', 'prev')
trace_ring_buffer_per_cpu_data = struct_print_class(self.ramdump, 'ring_buffer_per_cpu', ring_trace_buffer_cpu, None)
'''
crash> struct ring_buffer_per_cpu -x -o
struct ring_buffer_per_cpu {
[0x0] int cpu;
[0x4] atomic_t record_disabled;
[0x8] atomic_t resize_disabled;
[0x10] struct trace_buffer *buffer;
[0x80] unsigned long nr_pages;
[0x88] unsigned int current_context;
[0x90] struct list_head *pages;
[0x98] struct buffer_page *head_page;
[0xa0] struct buffer_page *tail_page; // parser this
[0xa8] struct buffer_page *commit_page;
[0xb0] struct buffer_page *reader_page;
'''
if self.ramdump.arm64:
trace_ring_buffer_per_cpu_data.append('nr_pages', 'u64')
else:
trace_ring_buffer_per_cpu_data.append('nr_pages', 'u32')
trace_ring_buffer_per_cpu_data.append('tail_page', 'ptr')
trace_ring_buffer_per_cpu_data.process()
nr_pages = trace_ring_buffer_per_cpu_data.get_val('nr_pages')
buffer_page_entry = trace_ring_buffer_per_cpu_data.get_val('tail_page')
while page_index < nr_pages:
if buffer_page_entry:
self.parse_buffer_page_entry(buffer_page_entry)
buffer_page_entry_list = buffer_page_entry + buffer_page_list_offset
buffer_page_entry = self.ramdump.read_pointer(buffer_page_entry_list + buffer_page_list_prev_offset)
page_index = page_index + 1
self.ftrace_out.flush()

View File

@@ -0,0 +1,69 @@
# Copyright (c) 2020, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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.
from parser_util import register_parser, RamParser
from print_out import print_out_str
class FtraceParser_Event_List(object):
def __init__(self, ramdump):
self.ramdump = ramdump
ftrace_event_call_list_offset = self.ramdump.field_offset("struct trace_event_call" , "list")
#print ("self.ftrace_event_call_list_offset = {0}".format(hex(self.ftrace_event_call_list_offset)))
ftrace_events_head = self.ramdump.address_of("ftrace_events")
ftrace_events_entry_offset = self.ramdump.field_offset("struct list_head", "next")
ftrace_events_entry = self.ramdump.read_pointer(ftrace_events_head + ftrace_events_entry_offset)
#print ("self.ftrace_events_entry = {0}".format(hex(self.ftrace_events_entry)))
ftrace_event_call_offset = self.ramdump.field_offset("struct trace_event_call" , "event")
tp_offset = self.ramdump.field_offset("struct trace_event_call", "tp")
ftrace_event_call_name_offset = self.ramdump.field_offset("struct tracepoint", "name")
ftrace_event_offset = self.ramdump.field_offset("struct trace_event", "type")
self.ftrace_event_type = {}
self.ftrace_raw_struct_type = {}
while ftrace_events_entry != ftrace_events_head:
ftrace_event = ftrace_events_entry - ftrace_event_call_list_offset
#ftrace_event_data = self.ramdump.read_u64(ftrace_event + ftrace_event_call_offset)
ftrace_event_data = ftrace_event + ftrace_event_call_offset
tp_data = ftrace_event + tp_offset
if ftrace_event_data:
#print ("ftrace_event_data +++ {0}".format(hex(ftrace_event_data)))
event_type = self.ramdump.read_u16(ftrace_event_data + ftrace_event_offset)
if self.ramdump.arm64:
event_name = self.ramdump.read_u64(tp_data)
#print ("event_name +++ {0}".format((hex(event_name))))
event_name_value = self.ramdump.read_u64(event_name + ftrace_event_call_name_offset)
else:
event_name = self.ramdump.read_u32(tp_data)
#print ("event_name +++ {0}".format((hex(event_name))))
event_name_value = self.ramdump.read_u32(event_name + ftrace_event_call_name_offset)
#print ("event_name_value +++ {0}".format((event_name_value)))
event_name1 = self.ramdump.read_cstring(event_name_value)
event_name2 = self.ramdump.read_cstring(event_name)
if "6" == str(event_type):
print_out_str("ftrace_event_data => {0} ftrace_event >> {1} tp_data >> {2} event_name >> {3} event_name_value >> {4} event_name2 {5} event_type {6}".format(hex(ftrace_event_data), hex(ftrace_event),hex(tp_data),hex(event_name),hex(event_name_value),event_name2,event_type))
self.ftrace_event_type[str(event_type)] = "bprint"
self.ftrace_raw_struct_type[str(event_type)] = "bprint"
elif "5" == str(event_type):
print_out_str("ftrace_event_data => {0} ftrace_event >> {1} tp_data >> {2} event_name >> {3} event_name_value >> {4} event_name2 {5} event_type {6}".format(hex(ftrace_event_data), hex(ftrace_event),hex(tp_data),hex(event_name),hex(event_name_value),event_name2,event_type))
self.ftrace_event_type[str(event_type)] = "print"
self.ftrace_raw_struct_type[str(event_type)] = "print"
else:
self.ftrace_event_type[str(event_type)] = str(event_name1)
self.ftrace_raw_struct_type[str(event_type)] = "trace_event_raw_" + str(event_name1)
ftrace_events_entry = self.ramdump.read_pointer(ftrace_events_entry + ftrace_events_entry_offset)
else:
break

View File

@@ -0,0 +1,10 @@
# Copyright (c) 2024 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.

View File

@@ -0,0 +1,271 @@
# Copyright (c) 2022 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 os
import re
config_sim_t32 = """; Printer settings
PRINTER=WINDOWS
; Environment variables
OS=
ID=T32
TMP=C:\\Temp
;SYS=C:\\T32
PBI=SIM
SCREEN=
VFULL
FONT=MEDIUM
"""
launch_t32_gmu = """set PWD=%~dp0
start C:\\t32\\bin\\windows64\\t32marm.exe -c %PWD%/configsim.t32, %PWD%\\snapshot\\gmu_t32\\gmu_startup_script.cmm %PWD%\\snapshot\\gmu_t32
"""
gmu_startup_script_header = """title "GMU snapshot"
ENTRY &DumpDir &AxfSymbol
WinCLEAR
AREA
AREA.CLEAR
sys.cpu CORTEXM3
sys.up
PRINT "DumpDir is &DumpDir"
cd &DumpDir
PRINT "using symbol from &AxfSymbol"
"""
gmu_startup_script_footer = """
; enable single-stepping
PRINT "Enabling single-stepping..."
SYStem.Option IMASKASM ON
SYStem.Option IMASKHLL ON
SNOOP.PC ON
; reset the processor registers
Register.Init
; load the saved fault info register values
&r0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r0))
Register.Set R0 (&r0)
&r1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r1))
Register.Set R1 (&r1)
&r2=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r2))
Register.Set R2 (&r2)
&r3=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r3))
Register.Set R3 (&r3)
&r12=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r12))
Register.Set R12 (&r12)
&lr=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.stackedLR))
Register.Set LR (&lr)
&pc=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.stackedPC))
Register.Set PC (&pc)
&xpsr=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.stackedxPSR))
Register.Set XPSR (&xpsr)
&sp=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.faultStack))
Register.Set SP (&sp)
; load the other core register values
&r4=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[4]))
Register.Set R4 (&r4)
&r5=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[5]))
Register.Set R5 (&r5)
&r6=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[6]))
Register.Set R6 (&r6)
&r7=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[7]))
Register.Set R7 (&r7)
&r8=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[8]))
Register.Set R8 (&r8)
&r9=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[9]))
Register.Set R9 (&r9)
&r10=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[10]))
Register.Set R10 (&r10)
&r11=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[11]))
Register.Set R11 (&r11)
; load the saved fault info register values
&r0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[0]))
Register.Set R0 (&r0)
&r1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[1]))
Register.Set R1 (&r1)
&r2=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[2]))
Register.Set R2 (&r2)
&r3=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[3]))
Register.Set R3 (&r3)
&r12=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[12]))
Register.Set R12 (&r12)
&r13=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[13]))
Register.Set R13 (&r13)
&r14=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[14]))
Register.Set R14 (&r14)
&r15=Data.Long(Var.ADDRESS(gs_cm3Snapshot.coreRegs.r[15]))
Register.Set R15 (&r15)
; load the special register values
&msp=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.MSP))
Register.Set MSP (&msp)
&psp=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.PSP))
Register.Set PSP (&psp)
&basepri=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.BASEPRI))
Register.Set BASEPRI (&basepri)
&primask=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.PRIMASK))
Register.Set PRIMASK (&primask)
&faultmask=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.FAULTMASK))
Register.Set FAULTMASK (&faultmask)
&control=Data.Long(Var.ADDRESS(gs_cm3Snapshot.specialRegs.CONTROL))
Register.Set CONTROL (&control)
; load the nvic register values
&iser0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ISER[0]))
Data.Set SD:0xE000E100 %LE %Long &iser0
&iser1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ISER[1]))
Data.Set SD:0xE000E104 %LE %Long &iser1
&icer0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ICER[0]))
Data.Set SD:0xE000E180 %LE %Long &icer0
&icer1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ICER[1]))
Data.Set SD:0xE000E184 %LE %Long &icer1
&ispr0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ISPR[0]))
Data.Set SD:0xE000E200 %LE %Long &ispr0
&ispr1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ISPR[1]))
Data.Set SD:0xE000E204 %LE %Long &ispr1
&icpr0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ICPR[0]))
Data.Set SD:0xE000E280 %LE %Long &icpr0
&icpr1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.ICPR[1]))
Data.Set SD:0xE000E284 %LE %Long &icpr1
&iabr0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IABR[0]))
Data.Set SD:0xE000E300 %LE %Long &iabr0
&iabr1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IABR[1]))
Data.Set SD:0xE000E304 %LE %Long &iabr1
; load the interrupt priority register values
&ipr0=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[0]))
Data.Set SD:0xE000E400 %LE %Long &ipr0
&ipr1=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[1]))
Data.Set SD:0xE000E404 %LE %Long &ipr1
&ipr2=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[2]))
Data.Set SD:0xE000E408 %LE %Long &ipr2
&ipr3=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[3]))
Data.Set SD:0xE000E40C %LE %Long &ipr3
&ipr4=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[4]))
Data.Set SD:0xE000E410 %LE %Long &ipr4
&ipr5=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[5]))
Data.Set SD:0xE000E414 %LE %Long &ipr5
&ipr6=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[6]))
Data.Set SD:0xE000E418 %LE %Long &ipr6
&ipr7=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[7]))
Data.Set SD:0xE000E41c %LE %Long &ipr7
&ipr8=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[8]))
Data.Set SD:0xE000E420 %LE %Long &ipr8
&ipr9=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[9]))
Data.Set SD:0xE000E424 %LE %Long &ipr9
&ipr10=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[10]))
Data.Set SD:0xE000E428 %LE %Long &ipr10
&ipr11=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[11]))
Data.Set SD:0xE000E42C %LE %Long &ipr11
&ipr12=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[12]))
Data.Set SD:0xE000E430 %LE %Long &ipr12
&ipr13=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[13]))
Data.Set SD:0xE000E434 %LE %Long &ipr13
&ipr14=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[14]))
Data.Set SD:0xE000E438 %LE %Long &ipr14
&ipr15=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.IPR[15]))
Data.Set SD:0xE000E43C %LE %Long &ipr15
; load the software trigger interrupt register value
&stir=Data.Long(Var.ADDRESS(gs_cm3Snapshot.nvicRegs.swTriggerIntrReg))
Data.Set SD:0xE000EF00 %LE %Long &stir
; load the fault status register values
&cfsr=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultStatus.cfsr))
Data.Set SD:0xE000ED28 %LE %Long &cfsr
&hfsr=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultStatus.hfsr))
Data.Set SD:0xE000ED2C %LE %Long &hfsr
&mmfar=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultStatus.mmfar))
Data.Set SD:0xE000ED34 %LE %Long &mmfar
&bfar=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultStatus.bfar))
Data.Set SD:0xE000ED38 %LE %Long &bfar
&exec_return=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.excReturn))
PRINT &exec_return
IF (&exec_return&0x4)==0
(
PRINT "MSP in use"
)
ELSE
(
PRINT "PSP in use"
)
&SP=Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.faultStack))
&SP=&SP+0x20
Register.Set R0 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r0))
Register.Set R1 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r1))
Register.Set R2 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r2))
Register.Set R3 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r3))
Register.Set R12 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.r12))
Register.Set R13 &SP
Register.Set R14 Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.stackedLR))
Register.Set PC Data.Long(Var.ADDRESS(gs_cm3Snapshot.faultInfo.stackedPC))
v.v gs_cm3Snapshot
b::Register
v.f
PRINT "Success!"
"""
gmu_startup_script_symbol = 'data.load.elf "code_compile_mem.axf" /nocode'
gmu_t32_path = "gpu_parser/snapshot/gmu_t32/"
def prepare_bin_load_command(file):
address = re.split(r"-|\.", file)[3]
return ('data.load.binary "' + str(file) + '" ' + address + '\n')
def generate_cmm_script(dump):
gmu_startup_script = gmu_startup_script_header
for root, dirs, files in os.walk(gmu_t32_path):
bin_load_commands = [prepare_bin_load_command(file) for file in files
if file.endswith('.bin')]
gmu_startup_script += "".join(bin_load_commands)
gmu_startup_script += "\n"
gmu_startup_script += gmu_startup_script_symbol
gmu_startup_script += gmu_startup_script_footer
file = dump.open_file(gmu_t32_path + "gmu_startup_script.cmm", "w")
file.write(gmu_startup_script)
file.close()
def generate_gmu_t32_files(dump):
generate_cmm_script(dump)
cfg_file = dump.open_file("gpu_parser/configsim.t32", "w")
cfg_file.write(config_sim_t32)
cfg_file.close()
launcht32_bat = dump.open_file("gpu_parser/launch_t32_gmu.bat", "w")
launcht32_bat.write(launch_t32_gmu)
launcht32_bat.close()

View File

@@ -0,0 +1,233 @@
# Copyright (c) 2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2024 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.
from ctypes import c_uint32, c_uint64, c_char, sizeof, Structure
MAGIC = 0xabbaabba
LOG_SKIP = 1
LOG_FIRE_EVENT = 2
LOG_CMDBATCH_SUBMITTED_EVENT = 3
LOG_CMDBATCH_RETIRED_EVENT = 4
LOG_SYNCPOINT_FENCE_EVENT = 5
LOG_SYNCPOINT_FENCE_EXPIRE_EVENT = 6
LOG_TIMELINE_FENCE_ALLOC_EVENT = 7
LOG_TIMELINE_FENCE_RELEASE_EVENT = 8
KGSL_EVENT_RETIRED = 1
KGSL_EVENT_CANCELLED = 2
NANO_TO_SEC = 1000000000
class fire_event (Structure):
_fields_ = [('id', c_uint32),
('ts', c_uint32),
('type', c_uint32),
('age', c_uint32)]
class cmdbatch_submitted (Structure):
_fields_ = [('id', c_uint32),
('ts', c_uint32),
('prio', c_uint32),
('flags', c_uint64)]
class cmdbatch_retired (Structure):
_fields_ = [('id', c_uint32),
('ts', c_uint32),
('prio', c_uint32),
('flag', c_uint64),
('start', c_uint64),
('retire', c_uint64)]
class syncpoint_fence (Structure):
_fields_ = [('id', c_uint32),
('name', c_char*74)]
class timeline_fence (Structure):
_fields_ = [('id', c_uint32),
('seqno', c_uint64)]
def get_event_id(event):
event_id_mask = (1 << 16) - 1
return event & event_id_mask
def fire_event_write(func_writeln, dump, kgsl_eventlog_buffer, header):
if header["payload_size"] != sizeof(fire_event):
return True
id = dump.read_u32(kgsl_eventlog_buffer + fire_event.id.offset)
ts = dump.read_u32(kgsl_eventlog_buffer + fire_event.ts.offset)
type = dump.read_u32(kgsl_eventlog_buffer + fire_event.type.offset)
age = dump.read_u32(kgsl_eventlog_buffer + fire_event.age.offset)
if type == KGSL_EVENT_RETIRED:
type = "retired"
elif type == KGSL_EVENT_CANCELLED:
type = "cancelled"
format_str = "pid:{0:<12}{1:<16.6f}" \
"kgsl_fire_event: ctx={2} ts={3} type={4} age={5}"
func_writeln(format_str.format(header["pid"], header["time"],
id, ts, type, age))
return False
def cmdbatch_submitted_event_write(func_writeln, dump, kgsl_eventlog_buffer,
header):
if header["payload_size"] != sizeof(cmdbatch_submitted):
return True
id = dump.read_u32(kgsl_eventlog_buffer + cmdbatch_submitted.id.offset)
ts = dump.read_u32(kgsl_eventlog_buffer + cmdbatch_submitted.ts.offset)
prio = dump.read_u32(kgsl_eventlog_buffer +
cmdbatch_submitted.prio.offset)
flags = dump.read_u64(kgsl_eventlog_buffer +
cmdbatch_submitted.flags.offset)
format_str = "pid:{0:<12}{1:<16.6f}" \
"adreno_cmdbatch_submitted: ctx={2} ctx_prio={3} " \
"ts={4} flags={5}"
func_writeln(format_str.format(header["pid"], header["time"],
id, prio, ts, flags))
return False
def cmdbatch_retired_event_write(func_writeln, dump, kgsl_eventlog_buffer,
header):
if header["payload_size"] != sizeof(cmdbatch_retired):
return True
id = dump.read_u32(kgsl_eventlog_buffer + cmdbatch_retired.id.offset)
ts = dump.read_u32(kgsl_eventlog_buffer + cmdbatch_retired.ts.offset)
prio = dump.read_u32(kgsl_eventlog_buffer + cmdbatch_retired.prio.offset)
flag = dump.read_u64(kgsl_eventlog_buffer + cmdbatch_retired.flag.offset)
start = dump.read_u64(kgsl_eventlog_buffer +
cmdbatch_retired.start.offset)
retire = dump.read_u64(kgsl_eventlog_buffer +
cmdbatch_retired.retire.offset)
format_str = "pid:{0:<12}{1:<16.6f}" \
"adreno_cmdbatch_retired: ctx={2} ctx_prio={3} " \
"ts={4} flags={5} start={6} retire={7}"
func_writeln(format_str.format(header["pid"], header["time"], id,
prio, ts, flag, start, retire))
return False
def syncpoint_fence_event_write(func_writeln, dump, kgsl_eventlog_buffer,
header):
if header["payload_size"] != sizeof(syncpoint_fence):
return True
id = dump.read_u32(kgsl_eventlog_buffer + syncpoint_fence.id.offset)
name = dump.read_cstring(kgsl_eventlog_buffer +
syncpoint_fence.name.offset, 74)
if header["event_id"] == LOG_SYNCPOINT_FENCE_EVENT:
event = "syncpoint_fence"
elif header["event_id"] == LOG_SYNCPOINT_FENCE_EXPIRE_EVENT:
event = "syncpoint_fence_expire"
format_str = "pid:{0:<12}{1:<16.6f}" \
"{2}: ctx={3} name={4}"
func_writeln(format_str.format(header["pid"], header["time"],
event, id, name))
return False
def timeline_fence_event_write(func_writeln, dump, kgsl_eventlog_buffer,
header):
if header["payload_size"] != sizeof(timeline_fence):
return True
id = dump.read_u32(kgsl_eventlog_buffer + timeline_fence.id.offset)
seqno = dump.read_u64(kgsl_eventlog_buffer + timeline_fence.seqno.offset)
if header["event_id"] == LOG_TIMELINE_FENCE_ALLOC_EVENT:
event = "kgsl_timeline_fence_alloc"
elif header["event_id"] == LOG_TIMELINE_FENCE_RELEASE_EVENT:
event = "kgsl_timeline_fence_release"
format_str = "pid:{0:<12}{1:<16.6f}" \
"{2}: timeline={3} seqno={4}"
func_writeln(format_str.format(header["pid"], header["time"], event,
id, seqno))
return False
def parse_eventlog_buffer(func_writeln, dump):
kgsl_eventlog_buffer = dump.read('kgsl_eventlog')
format_str = '{0:<15} {1:<16}{2}'
func_writeln(format_str.format("PID", "Timestamp", "Function"))
min_timestamp = None
offset = 0
ret = True
header = {}
while offset < 8192:
header["magic_num"] = dump.read_structure_field(
kgsl_eventlog_buffer, 'struct kgsl_log_header',
'magic')
header["event_id"] = dump.read_structure_field(
kgsl_eventlog_buffer,
'struct kgsl_log_header', 'eventid')
if header["event_id"] is None:
event = dump.read_structure_field(
kgsl_eventlog_buffer,
'struct kgsl_log_header', 'event')
header["event_id"] = get_event_id(event)
header["time"] = dump.read_structure_field(
kgsl_eventlog_buffer,
'struct kgsl_log_header', 'time') / NANO_TO_SEC
header["pid"] = dump.read_structure_field(
kgsl_eventlog_buffer,
'struct kgsl_log_header', 'pid')
header["payload_size"] = dump.read_structure_field(
kgsl_eventlog_buffer,
'struct kgsl_log_header', 'size')
if (header["magic_num"] != MAGIC) or (header["event_id"] > 8) or \
(header["event_id"] < 1):
offset += 1
kgsl_eventlog_buffer += 1
continue
if min_timestamp is None:
min_timestamp = header["time"]
kgsl_eventlog_buffer += dump.sizeof('struct kgsl_log_header')
if min_timestamp > header["time"]:
func_writeln("End of logging".center(90, '-') + '\n')
min_timestamp = header["time"]
if header["event_id"] == LOG_SKIP:
break
elif header["event_id"] == LOG_FIRE_EVENT:
ret = fire_event_write(func_writeln, dump, kgsl_eventlog_buffer,
header)
elif header["event_id"] == LOG_CMDBATCH_SUBMITTED_EVENT:
ret = cmdbatch_submitted_event_write(func_writeln, dump,
kgsl_eventlog_buffer, header)
elif header["event_id"] == LOG_CMDBATCH_RETIRED_EVENT:
ret = cmdbatch_retired_event_write(func_writeln, dump,
kgsl_eventlog_buffer, header)
elif (header["event_id"] == LOG_SYNCPOINT_FENCE_EVENT) or \
(header["event_id"] == LOG_SYNCPOINT_FENCE_EXPIRE_EVENT):
ret = syncpoint_fence_event_write(func_writeln, dump,
kgsl_eventlog_buffer, header)
elif (header["event_id"] == LOG_TIMELINE_FENCE_ALLOC_EVENT) or \
(header["event_id"] == LOG_TIMELINE_FENCE_RELEASE_EVENT):
ret = timeline_fence_event_write(func_writeln, dump,
kgsl_eventlog_buffer, header)
if ret:
offset += 1
kgsl_eventlog_buffer += 1
continue
offset += dump.sizeof('struct kgsl_log_header') + \
header["payload_size"]
kgsl_eventlog_buffer += header["payload_size"]

View File

@@ -0,0 +1,380 @@
# Copyright (c) 2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022 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.
from ctypes import c_int, c_int64, c_uint, c_ushort, sizeof, Structure
# High word is static, low word is snapshot version ID
SNAPSHOT_MAGIC = 0x504D0002
# Section header
SNAPSHOT_SECTION_MAGIC = 0xABCD
# Snapshot Sections
KGSL_SNAPSHOT_SECTION_OS = 0x0101
KGSL_SNAPSHOT_SECTION_END = 0xFFFF
KGSL_SNAPSHOT_SECTION_RB_V2 = 0x0302
KGSL_SNAPSHOT_SECTION_GPU_OBJECT_V2 = 0x0B02
KGSL_SNAPSHOT_SECTION_MEMLIST_V2 = 0x0E02
KGSL_SNAPSHOT_SECTION_MEM_HISTORY = 0x1202
KGSL_SNAPSHOT_SECTION_IB_V2 = 0x0402
KGSL_SNAPSHOT_SECTION_REGS = 0x0201
KGSL_SNAPSHOT_SECTION_MVC = 0x1501
KGSL_SNAPSHOT_SECTION_INDEXED_REGS = 0x0501
KGSL_SNAPSHOT_SECTION_DEBUG = 0x0901
KGSL_SNAPSHOT_SECTION_DEBUGBUS = 0x0A01
KGSL_SNAPSHOT_SECTION_SHADER = 0x1201
KGSL_SNAPSHOT_SECTION_GMU = 0x1601
KGSL_SNAPSHOT_SECTION_GMU_MEMORY = 0x1701
# Ringbuffer
KGSL_RB_SIZE = (32 * 1024)
KGSL_RB_DWORDS = (KGSL_RB_SIZE >> 2)
# GMU Sections
GMU_SECTION_TYPE_OTHER = 0
GMU_SECTION_TYPE_HFIMEM = 1
GMU_SECTION_TYPE_LOG = 2
GMU_SECTION_TYPE_BWMEM = 3
GMU_SECTION_TYPE_DEBUG = 4
GMU_SECTION_TYPE_DCACHE = 5
GMU_SECTION_TYPE_ICACHE = 6
GMU_SECTION_TYPE_UNCACHE = 7
GMU_SECTION_TYPE_REASON = 8
GMU_SECTION_TYPE_VERSION = 9
# GMU Memory Sections
SNAPSHOT_GMU_MEM_UNKNOWN = 0
SNAPSHOT_GMU_MEM_HFI = 1
SNAPSHOT_GMU_MEM_LOG = 2
SNAPSHOT_GMU_MEM_BWTABLE = 3
SNAPSHOT_GMU_MEM_DEBUG = 4
SNAPSHOT_GMU_MEM_BIN_BLOCK = 5
SNAPSHOT_GMU_MEM_CTXT_QUEUE = 6
# KGSL structures
class kgsl_snapshot_header(Structure):
_pack_ = 1
_fields_ = [('magic', c_uint),
('gpuid', c_uint),
('chipid', c_uint)]
class kgsl_snapshot_section_header(Structure):
_pack_ = 1
_fields_ = [('magic', c_ushort),
('id', c_ushort),
('size', c_uint)]
class kgsl_snapshot_rb_v2(Structure):
_pack_ = 1
_fields_ = [('start', c_int),
('end', c_int),
('rbsize', c_int),
('wptr', c_int),
('rptr', c_int),
('count', c_int),
('timestamp_queued', c_uint),
('timestamp_retired', c_uint),
('gpuaddr', c_int64),
('id', c_uint)]
class kgsl_snapshot_gmu_mem(Structure):
_pack_ = 1
_fields_ = [('type', c_int),
('hostaddr', c_int64),
('gmuaddr', c_int64),
('gpuaddr', c_int64)]
class kgsl_snapshot_gmu_mem_header(Structure):
_pack_ = 1
_fields_ = [('type', c_int),
('hostaddr_lower', c_uint),
('hostaddr_upper', c_uint),
('gmuaddr_lower', c_uint),
('gmuaddr_upper', c_uint),
('gpuaddr_lower', c_uint),
('gpuaddr_upper', c_uint)]
def gmu_log(devp, dump, gpurev):
if gpurev >= 0x80000:
gmu_dev = dump.sibling_field_addr(devp, 'struct gen8_device',
'adreno_dev', 'gmu')
gmu_logs = dump.read_structure_field(gmu_dev,
'struct gen8_gmu_device',
'gmu_log')
elif gpurev >= 0x70000:
gmu_dev = dump.sibling_field_addr(devp, 'struct gen7_device',
'adreno_dev', 'gmu')
gmu_logs = dump.read_structure_field(gmu_dev,
'struct gen7_gmu_device',
'gmu_log')
else:
gmu_dev = dump.sibling_field_addr(devp, 'struct a6xx_device',
'adreno_dev', 'gmu')
gmu_logs = dump.read_structure_field(gmu_dev,
'struct a6xx_gmu_device',
'gmu_log')
if dump.kernel_version >= (5, 10, 0):
gmu_log_hostptr = dump.read_structure_field(gmu_logs,
'struct kgsl_memdesc',
'hostptr')
gmu_log_size = dump.read_structure_field(gmu_logs,
'struct kgsl_memdesc', 'size')
gmu_log_gpuaddr = dump.read_structure_field(gmu_logs,
'struct kgsl_memdesc',
'gpuaddr')
# Set gmuaddr to 0 since it is not present in kgsl_memdesc
gmu_log_gmuaddr = 0
else:
gmu_log_hostptr = dump.read_structure_field(gmu_logs,
'struct gmu_memdesc',
'hostptr')
gmu_log_size = dump.read_structure_field(gmu_logs,
'struct gmu_memdesc', 'size')
gmu_log_gmuaddr = dump.read_structure_field(gmu_logs,
'struct gmu_memdesc',
'gmuaddr')
gmu_log_gpuaddr = 0
return (gmu_log_hostptr, gmu_log_size, gmu_log_gmuaddr, gmu_log_gpuaddr)
def hfi_mem(devp, dump, gpurev):
if gpurev >= 0x80000:
gmu_dev = dump.sibling_field_addr(devp, 'struct gen8_device',
'adreno_dev', 'gmu')
hfi = dump.struct_field_addr(gmu_dev, 'struct gen8_gmu_device',
'hfi')
hfi_mem = dump.read_structure_field(hfi, 'struct gen8_hfi',
'hfi_mem')
elif gpurev >= 0x70000:
gmu_dev = dump.sibling_field_addr(devp, 'struct gen7_device',
'adreno_dev', 'gmu')
hfi = dump.struct_field_addr(gmu_dev, 'struct gen7_gmu_device',
'hfi')
hfi_mem = dump.read_structure_field(hfi, 'struct gen7_hfi',
'hfi_mem')
else:
gmu_dev = dump.sibling_field_addr(devp, 'struct a6xx_device',
'adreno_dev', 'gmu')
hfi = dump.struct_field_addr(gmu_dev, 'struct a6xx_gmu_device',
'hfi')
hfi_mem = dump.read_structure_field(hfi, 'struct a6xx_hfi',
'hfi_mem')
if dump.kernel_version >= (5, 10, 0):
hfi_mem_hostptr = dump.read_structure_field(hfi_mem,
'struct kgsl_memdesc',
'hostptr')
hfi_mem_size = dump.read_structure_field(hfi_mem,
'struct kgsl_memdesc', 'size')
hfi_mem_gpuaddr = dump.read_structure_field(hfi_mem,
'struct kgsl_memdesc',
'gpuaddr')
# Set gmuaddr to 0 since it is not present in kgsl_memdesc
hfi_mem_gmuaddr = 0
else:
hfi_mem_hostptr = dump.read_structure_field(hfi_mem,
'struct gmu_memdesc',
'hostptr')
hfi_mem_size = dump.read_structure_field(hfi_mem,
'struct gmu_memdesc', 'size')
hfi_mem_gmuaddr = dump.read_structure_field(hfi_mem,
'struct gmu_memdesc',
'gmuaddr')
hfi_mem_gpuaddr = 0
return (hfi_mem_hostptr, hfi_mem_size, hfi_mem_gmuaddr, hfi_mem_gpuaddr)
def snapshot_gmu_mem_section(devp, dump, gpurev, file, hdr_type):
if hdr_type == SNAPSHOT_GMU_MEM_HFI:
(gmu_mem_hostptr, gmu_mem_size, gmu_mem_gmuaddr, gmu_mem_gpuaddr) = \
hfi_mem(devp, dump, gpurev)
elif hdr_type == SNAPSHOT_GMU_MEM_LOG:
(gmu_mem_hostptr, gmu_mem_size, gmu_mem_gmuaddr, gmu_mem_gpuaddr) = \
gmu_log(devp, dump, gpurev)
else:
return
section_header = kgsl_snapshot_section_header()
section_header.magic = SNAPSHOT_SECTION_MAGIC
section_header.id = KGSL_SNAPSHOT_SECTION_GMU_MEMORY
section_header.size = (gmu_mem_size + sizeof(kgsl_snapshot_gmu_mem) +
sizeof(kgsl_snapshot_section_header))
file.write(section_header)
mem_hdr = kgsl_snapshot_gmu_mem()
mem_hdr.type = hdr_type
mem_hdr.hostaddr = gmu_mem_hostptr
mem_hdr.gmuaddr = gmu_mem_gmuaddr
mem_hdr.gpuaddr = gmu_mem_gpuaddr
file.write(mem_hdr)
data = dump.read_binarystring(gmu_mem_hostptr, gmu_mem_size)
file.write(data)
def snapshot_rb_section(devp, dump, file, rb_type):
# Scratch buffer information
scratch_obj = dump.read_structure_field(devp,
'struct kgsl_device',
'scratch')
scratch_hostptr = dump.read_structure_field(scratch_obj,
'struct kgsl_memdesc',
'hostptr')
# Memstore information
memstore_obj = dump.read_structure_field(devp,
'struct kgsl_device',
'memstore')
memstore_hostptr = dump.read_structure_field(memstore_obj,
'struct kgsl_memdesc',
'hostptr')
# RB information
rb = dump.read_structure_field(devp,
'struct adreno_device', rb_type)
if (not rb):
return
rb_id = dump.read_structure_field(rb,
'struct adreno_ringbuffer',
'id')
rb_wptr = dump.read_structure_field(rb,
'struct adreno_ringbuffer',
'wptr')
rb_rptr = dump.read_s32(scratch_hostptr + rb_id * 4)
rb_queued_ts = dump.read_structure_field(rb,
'struct adreno_ringbuffer',
'timestamp')
rb_buffer_desc = dump.read_structure_field(rb,
'struct adreno_ringbuffer',
'buffer_desc')
rb_gpuaddr = dump.read_structure_field(rb_buffer_desc,
'struct kgsl_memdesc',
'gpuaddr')
rb_hostptr = dump.read_structure_field(rb_buffer_desc,
'struct kgsl_memdesc',
'hostptr')
rb_size = dump.read_structure_field(rb_buffer_desc,
'struct kgsl_memdesc', 'size')
rb_retired_ts = dump.read_s32(memstore_hostptr +
((rb_id + 0x32E) * 0x28 + 0x8))
# RB section
section_header = kgsl_snapshot_section_header()
section_header.magic = SNAPSHOT_SECTION_MAGIC
section_header.id = KGSL_SNAPSHOT_SECTION_RB_V2
section_header.size = (KGSL_RB_SIZE + sizeof(kgsl_snapshot_rb_v2) +
sizeof(kgsl_snapshot_section_header))
file.write(section_header)
rb_header = kgsl_snapshot_rb_v2()
rb_header.start = 0
rb_header.end = KGSL_RB_DWORDS
rb_header.wptr = rb_wptr
rb_header.rptr = rb_rptr
rb_header.rbsize = KGSL_RB_DWORDS
rb_header.count = KGSL_RB_DWORDS
rb_header.timestamp_queued = rb_queued_ts
rb_header.timestamp_retired = rb_retired_ts
rb_header.gpuaddr = rb_gpuaddr
rb_header.id = rb_id
file.write(rb_header)
data = dump.read_binarystring(rb_hostptr, rb_size)
file.write(data)
def create_snapshot_from_ramdump(devp, dump):
# GPU revision
gpucore = dump.read_structure_field(devp,
'struct adreno_device', 'gpucore')
gpurev = dump.read_structure_field(gpucore,
'struct adreno_gpu_core', 'gpurev')
# Gpu chip id
chipid = dump.read_structure_field(devp, 'struct adreno_device', 'chipid')
file_name = 'mini_snapshot.bpmd'
file = dump.open_file('gpu_parser/' + file_name, 'wb')
# Dump snapshot header
header = kgsl_snapshot_header()
header.magic = SNAPSHOT_MAGIC
header.gpuid = (0x0003 << 16) | gpurev
header.chipid = chipid
file.write(header)
# Dump RBs
snapshot_rb_section(devp, dump, file, 'cur_rb')
snapshot_rb_section(devp, dump, file, 'prev_rb')
# Check & dump GMU info
gmu_core = dump.struct_field_addr(devp, 'struct kgsl_device', 'gmu_core')
gmu_on = dump.read_structure_field(gmu_core,
'struct gmu_core_device', 'flags')
if ((gmu_on >> 4) & 1):
snapshot_gmu_mem_section(devp,
dump, gpurev, file, SNAPSHOT_GMU_MEM_HFI)
snapshot_gmu_mem_section(devp,
dump, gpurev, file, SNAPSHOT_GMU_MEM_LOG)
# Dump last section
last_section = kgsl_snapshot_section_header()
last_section.magic = SNAPSHOT_SECTION_MAGIC
last_section.id = KGSL_SNAPSHOT_SECTION_END
last_section.size = sizeof(kgsl_snapshot_section_header)
file.write(last_section)
file.close()
def extract_gmu_mem_from_snapshot(dump, snapshot_path):
file = dump.open_file(snapshot_path, 'rb')
header = kgsl_snapshot_header()
file.readinto(header)
while True:
current_pos = file.tell()
section_header = kgsl_snapshot_section_header()
file.readinto(section_header)
if section_header.id == KGSL_SNAPSHOT_SECTION_GMU_MEMORY:
gmu_mem_header = kgsl_snapshot_gmu_mem_header()
file.readinto(gmu_mem_header)
gmu_memory_sz = section_header.size - \
sizeof(kgsl_snapshot_gmu_mem_header) - \
sizeof(kgsl_snapshot_section_header)
data = file.read(gmu_memory_sz)
bin_filename = "gmu-section-" + str(gmu_mem_header.type) + "-" + \
str(hex(gmu_mem_header.gmuaddr_lower)) + ".snap.bin"
gmu_bin_file = dump.open_file("gpu_parser/snapshot/gmu_t32/" +
bin_filename, mode='wb')
gmu_bin_file.write(data)
gmu_bin_file.close()
elif section_header.id == 0 or section_header.size == 0:
print('Invalid id & size:', section_header.id, section_header.size)
print('Total size:', file.tell())
break
elif section_header.id == KGSL_SNAPSHOT_SECTION_END:
break
file.seek(current_pos + section_header.size, 0)
file.close()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
# Copyright (c) 2013-2015, 2020-2021 The Linux Foundation. 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.
from parser_util import register_parser, RamParser
from parsers.gpu.gpuinfo_54 import GpuParser_54
from parsers.gpu.gpuinfo_510 import GpuParser_510
from print_out import print_out_str
@register_parser('--print-gpuinfo',
'print gpu driver related info', optional=True)
class GpuParser(RamParser):
def __init__(self, dump):
super(GpuParser, self).__init__(dump)
def parse(self):
if not (self.ramdump.is_config_defined('CONFIG_QCOM_KGSL') or
'msm_kgsl' in self.ramdump.ko_file_names):
print_out_str(
"No GPU support detected... Skipping GPU parser.")
return
if (self.ramdump.kernel_version == (0, 0, 0) or
self.ramdump.kernel_version >= (5, 10, 0)):
self.parser = GpuParser_510(self.ramdump)
elif self.ramdump.kernel_version >= (4, 9, 0):
self.parser = GpuParser_54(self.ramdump)
else:
print_out_str(
"No GPU support detected for specified kernel version..."
+ " Skipping GPU parser.")
return
self.parser.parse()
def write(self, string):
self.out.write(string)
def writeln(self, string=""):
self.out.write(string + '\n')

View File

@@ -0,0 +1,98 @@
"""
Copyright (c) 2020 The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os,sys
import struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
ipa_log = "hotplug.txt"
def setup_out_file(path, self):
global out_file
try:
out_file = self.ramdump.open_file(path, 'wb')
return out_file
except:
print_out_str("could not open path {0}".format(path))
print_out_str("Do you have write/read permissions on the path?")
def print_out_ip(string):
out_file.write((string + '\n').encode('ascii', 'ignore'))
@register_parser('--hotplug', 'print hotplug notifier information')
class hp_logging(RamParser):
def __init__(self, *args):
super(hp_logging, self).__init__(*args)
def hp_parse(self, ram_dump):
curr_chan=0
ep_idx=0
cpuhp_hp_states_addr = ram_dump.address_of('cpuhp_hp_states')
if cpuhp_hp_states_addr:
idx = 0
cpuhp_sz = ram_dump.sizeof('struct cpuhp_step')
hp_states_sz = ram_dump.sizeof('cpuhp_hp_states')
max_idx = hp_states_sz/cpuhp_sz
name_offset = ram_dump.field_offset('struct cpuhp_step','name')
startup_soffset = ram_dump.field_offset('struct cpuhp_step','startup')
teardown_soffset = ram_dump.field_offset('struct cpuhp_step','teardown')
can_stop_offset = ram_dump.field_offset('struct cpuhp_step','cant_stop')
multi_instance_offset = ram_dump.field_offset('struct cpuhp_step','multi_instance')
while (idx < max_idx):
name_off = ram_dump.read_u64(cpuhp_hp_states_addr + name_offset)
name = ram_dump.read_cstring(name_off)
startup_soffset_addr = ram_dump.read_u64(cpuhp_hp_states_addr + startup_soffset)
if startup_soffset_addr != 0:
startup_soffset_addr = ram_dump.get_symbol_info1(startup_soffset_addr)
teardown_soffset_addr = ram_dump.read_u64(cpuhp_hp_states_addr + teardown_soffset)
if teardown_soffset_addr != 0:
teardown_soffset_addr = ram_dump.get_symbol_info1(teardown_soffset_addr)
can_stop = ram_dump.read_bool(cpuhp_hp_states_addr + can_stop_offset)
multi_instance = ram_dump.read_bool(cpuhp_hp_states_addr + multi_instance_offset)
if (name_off == 0 or name_off is None):
idx = idx + 1
cpuhp_hp_states_addr = cpuhp_hp_states_addr + cpuhp_sz
continue
temp = name + " startup = (single = {0} = , multi = {1} = ),teardown = ( single = {2} = , multi = {3} = ),cant_stop = {4}, multi_instance = {5}".format(startup_soffset_addr,startup_soffset_addr,teardown_soffset_addr,teardown_soffset_addr,can_stop,multi_instance)
print_out_ip(temp)
idx = idx + 1
cpuhp_hp_states_addr = cpuhp_hp_states_addr + cpuhp_sz
def parse(self):
setup_out_file(ipa_log, self)
self.hp_parse(self.ramdump)

View File

@@ -0,0 +1,29 @@
# Copyright (c) 2021 The Linux Foundation. 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 print_out
from parser_util import RamParser, cleanupString, register_parser
@register_parser('--hyp-log', 'Dump hypervisor log')
class HypLog(RamParser):
def parse(self):
if self.ramdump.hyp_diag_addr is None:
raise Exception('Cannot find hyp_diag_addr!!!')
hyp_diag_addr = self.ramdump.read_u32(self.ramdump.hyp_diag_addr, False)
hyp_log_addr = self.ramdump.read_u32(hyp_diag_addr + 0x78, False)
hyp_log_size = self.ramdump.read_u32(hyp_diag_addr + 0x10, False)
hyp_log = self.ramdump.read_physical(hyp_log_addr, hyp_log_size)
if hyp_log is None:
raise Exception('!!!Could not read hyp_log from address {0:x}'.format(addr))
hyp_log_output = self.ramdump.open_file('hyp_log.txt')
hyp_log_output.write(cleanupString(hyp_log.decode('ascii', 'ignore')))
hyp_log_output.close()
print_out.print_out_str('Hypervisor log successfully extracted!')

View File

@@ -0,0 +1,112 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
from parser_util import register_parser, RamParser
@register_parser('--icc-summary', 'script to dump the interconnect summary to get BW information')
class IccSummary(RamParser):
def __init__(self, *args):
super(IccSummary, self).__init__(*args)
self.fop = self.ramdump.open_file("icc_summary.txt")
self.fop_provider = self.ramdump.open_file("icc_providers.txt")
self.external_link_data = []
def __del__(self):
self.fop.close()
self.fop_provider.close()
def get_internal_link(self, node):
numlinks = node.num_links
if node.links == 0x0:
return
for i in range(numlinks):
dnode_addr = node.links + i * 8
dnode = self.ramdump.read_pdatatype(dnode_addr, 'struct icc_node', attr_list=['name', 'id', 'provider'])
did = dnode.id
dname = self.ramdump.read_cstring(dnode.name)
sname = self.ramdump.read_cstring(node.name)
sid = node.id
if node.provider == dnode.provider:
self.fop_provider.write(" | {0:5}:{1:30} -> {2:5}:{3:30}\n".format(sid, sname, did, dname))
else:
data = "+ {0:5}:{1:30} -> {2:5}:{3:30}\n".format(sid, sname, did, dname)
self.external_link_data.append(data)
def extract_icc_summary(self):
icc_providers_addr = self.ramdump.address_of("icc_providers")
next_offset = self.ramdump.field_offset('struct list_head', 'next')
icc_providers = self.ramdump.read_pointer(icc_providers_addr + next_offset)
self.fop_provider.write("[providers]\n")
self.fop.write("{0:65} {1:12} {2:12} {3:12}\n".format(" node", "tag", "avg", "peak"))
line = "-" * 150 + "\n"
formatProvider = " | {0:5}:{1:40}\t\t\t [label={0:5}:{1:40}| avg_bw={2:<10} kBps\t| peak_bw={3:<10} kBps ]\n"
format_req_str = " | {0:90} \t\t\t\t\t\t\t {1:<12}\t\t\t\t {2:<12}\n"
formatStr = "+ {0:5}:{1:40} \t\t\t\t{2:<12} {3:<12}\n"
reqStr = "\t| {0:35} \t\t\t {1:<12} {2:<12} {3:<12} \n"
self.fop.write(line)
provider = self.ramdump.read_linkedlist('struct icc_provider',
"provider_list.next",
icc_providers)
no_of_provider = len(provider)
for i in range(no_of_provider):
self.fop_provider.write("\n\n+ subgraph cluster {}\n".format(i))
label = self.ramdump.read_datatype(provider[i].dev,
"struct device",
attr_list=['kobj.name'])
label_name = self.ramdump.read_cstring(label.kobj.name)
self.fop_provider.write(" + label = {}\n".format(label_name))
node = provider[i].nodes.next - 0x28
nodes = self.ramdump.read_linkedlist('struct icc_node',
'node_list.next', node)
no_of_nodes = len(nodes)
for j in range(no_of_nodes):
icc_node_name = self.ramdump.read_cstring(nodes[j].name)
avg_bw = nodes[j].avg_bw
peak_bw = nodes[j].peak_bw
node_id = nodes[j].id
self.fop_provider.write(
formatProvider.format(node_id, icc_node_name, avg_bw,
peak_bw))
self.fop.write(
formatStr.format(node_id, icc_node_name, avg_bw, peak_bw))
req_list = nodes[j].req_list.first
req = self.ramdump.read_linkedlist('struct icc_req',
'req_node.next', req_list,
attr_list=['req_node',
'avg_bw',
'peak_bw', 'tag',
'dev'])
no_of_req = len(req)
print_seq = 0
for k in range(no_of_req):
dev = self.ramdump.read_datatype(req[k].dev,
'struct device',
attr_list=['kobj.name'])
dev_name = self.ramdump.read_cstring(dev.kobj.name)
req_avg = req[k].avg_bw
req_peak = req[k].peak_bw
req_tag = req[k].tag
if req_avg or req_peak:
if print_seq == 0:
self.fop_provider.write(
" + reqs (active only)\n")
self.fop_provider.write(
format_req_str.format(dev_name, req_avg, req_peak))
print_seq = 1
self.fop.write(
reqStr.format(dev_name, req_tag, req_avg, req_peak))
self.fop.write("\n\n")
self.fop_provider.write("\n")
self.fop_provider.write(' + intenral links\n')
for j in range(no_of_nodes):
self.get_internal_link(nodes[j])
if self.external_link_data:
self.fop_provider.write("\n\n[external links]\n")
for i in range(len(self.external_link_data)):
self.fop_provider.write(self.external_link_data[i])
def parse(self):
self.extract_icc_summary()

View File

@@ -0,0 +1,352 @@
# Copyright (c) 2013-2016, 2020 The Linux Foundation. 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 math
from print_out import print_out_str
from parser_util import register_parser, RamParser
from sizes import SZ_4K, SZ_64K, SZ_1M, SZ_16M, get_order, order_size_strings
from iommulib import IommuLib, MSM_SMMU_DOMAIN, MSM_SMMU_AARCH64_DOMAIN, ARM_SMMU_DOMAIN
from lpaeiommulib import parse_long_form_tables
from aarch64iommulib import parse_aarch64_tables
@register_parser('--print-iommu-pg-tables', 'Print IOMMU page tables')
class IOMMU(RamParser):
class FlatMapping(object):
def __init__(self, virt, phys=-1, type='[]', size=SZ_4K, mapped=False):
self.virt = virt
self.phys = phys
self.mapping_type = type
self.mapping_size = size
self.mapped = mapped
class CollapsedMapping(object):
def __init__(self, virt_start, virt_end, phys_start=-1, phys_end=-1, type='[]', size=SZ_4K, mapped=False):
self.virt_start = virt_start
self.virt_end = virt_end - 1
self.phys_start = phys_start
self.phys_end = phys_end - 1
self.mapping_type = type
self.mapping_size = size
self.mapped = mapped
def phys_size(self):
return (self.phys_end - self.phys_start + 1)
def virt_size(self):
return (self.virt_end - self.virt_start + 1)
def __init__(self, *args):
super(IOMMU, self).__init__(*args)
self.out_file = None
self.NUM_FL_PTE = 4096
self.NUM_SL_PTE = 256
self.FL_BASE_MASK = 0xFFFFFC00
self.FL_TYPE_TABLE = (1 << 0)
self.FL_TYPE_SECT = (1 << 1)
self.FL_SUPERSECTION = (1 << 18)
self.FL_AP0 = (1 << 10)
self.FL_AP1 = (1 << 11)
self.FL_AP2 = (1 << 15)
self.FL_SHARED = (1 << 16)
self.FL_BUFFERABLE = (1 << 2)
self.FL_CACHEABLE = (1 << 3)
self.FL_TEX0 = (1 << 12)
self.FL_NG = (1 << 17)
self.SL_BASE_MASK_LARGE = 0xFFFF0000
self.SL_BASE_MASK_SMALL = 0xFFFFF000
self.SL_TYPE_LARGE = (1 << 0)
self.SL_TYPE_SMALL = (2 << 0)
self.SL_AP0 = (1 << 4)
self.SL_AP1 = (2 << 4)
self.SL_AP2 = (1 << 9)
self.SL_SHARED = (1 << 10)
self.SL_BUFFERABLE = (1 << 2)
self.SL_CACHEABLE = (1 << 3)
self.SL_TEX0 = (1 << 6)
self.SL_NG = (1 << 11)
self.node_offset = self.ramdump.field_offset(
'struct msm_iova_data', 'node')
def fl_offset(va):
return (((va) & 0xFFF00000) >> 20)
def sl_offset(va):
return (((va) & 0xFF000) >> 12)
def print_sl_page_table(self, pg_table):
sl_pte = pg_table
for i in range(0, self.NUM_SL_PTE):
phy_addr = self.ramdump.read_u32(sl_pte, False)
if phy_addr is not None: # and phy_addr & self.SL_TYPE_SMALL:
read_write = '[R/W]'
if phy_addr & self.SL_AP2:
read_write = '[R]'
if phy_addr & self.SL_TYPE_SMALL:
self.out_file.write('SL_PTE[%d] = %x %s\n' %
(i, phy_addr & self.SL_BASE_MASK_SMALL, read_write))
elif phy_addr & self.SL_TYPE_LARGE:
self.out_file.write('SL_PTE[%d] = %x %s\n' %
(i, phy_addr & self.SL_BASE_MASK_LARGE, read_write))
elif phy_addr != 0:
self.out_file.write(
'SL_PTE[%d] = %x NOTE: ERROR [Do not understand page table bits]\n' % (i, phy_addr))
sl_pte += 4
def print_page_table(self, pg_table):
fl_pte = pg_table
for i in range(0, self.NUM_FL_PTE):
# for i in range(0,5):
sl_pg_table_phy_addr = self.ramdump.read_u32(fl_pte)
if sl_pg_table_phy_addr is not None:
if sl_pg_table_phy_addr & self.FL_TYPE_TABLE:
self.out_file.write('FL_PTE[%d] = %x [4K/64K]\n' %
(i, sl_pg_table_phy_addr & self.FL_BASE_MASK))
self.print_sl_page_table(
sl_pg_table_phy_addr & self.FL_BASE_MASK)
elif sl_pg_table_phy_addr & self.FL_SUPERSECTION:
self.out_file.write('FL_PTE[%d] = %x [16M]\n' %
(i, sl_pg_table_phy_addr & 0xFF000000))
elif sl_pg_table_phy_addr & self.FL_TYPE_SECT:
self.out_file.write('FL_PTE[%d] = %x [1M]\n' %
(i, sl_pg_table_phy_addr & 0xFFF00000))
elif sl_pg_table_phy_addr != 0:
self.out_file.write(
'FL_PTE[%d] = %x NOTE: ERROR [Cannot understand first level page table entry]\n' % (i, sl_pg_table_phy_addr))
else:
self.out_file.write(
'FL_PTE[%d] NOTE: ERROR [Cannot understand first level page table entry]\n' % (i))
fl_pte += 4
def get_mapping_info(self, pg_table, index):
sl_pte = pg_table + (index * 4)
phy_addr = self.ramdump.read_u32(sl_pte, False)
current_phy_addr = -1
current_page_size = SZ_4K
current_map_type = 0
status = True
if phy_addr is not None:
if phy_addr & self.SL_AP2:
current_map_type = self.SL_AP2
if phy_addr & self.SL_TYPE_SMALL:
current_phy_addr = phy_addr & self.SL_BASE_MASK_SMALL
current_page_size = SZ_4K
elif phy_addr & self.SL_TYPE_LARGE:
current_phy_addr = phy_addr & self.SL_BASE_MASK_LARGE
current_page_size = SZ_64K
elif phy_addr != 0:
current_phy_addr = phy_addr
status = False
return (current_phy_addr, current_page_size, current_map_type, status)
def get_sect_mapping_info(self, addr):
current_phy_addr = -1
current_page_size = SZ_4K
current_map_type = 0
status = True
if addr is not None:
if addr & self.SL_AP2:
current_map_type = self.SL_AP2
if addr & self.FL_SUPERSECTION:
current_phy_addr = addr & 0xFF000000
current_page_size = SZ_16M
elif addr & self.FL_TYPE_SECT:
current_phy_addr = addr & 0xFFF00000
current_page_size = SZ_1M
elif addr != 0:
current_phy_addr = addr
status = False
return (current_phy_addr, current_page_size, current_map_type, status)
def add_flat_mapping(self, mappings, fl_idx, sl_idx, phy_adr, map_type, page_size, mapped):
virt = (fl_idx << 20) | (sl_idx << 12)
map_type_str = '[R/W]'
if map_type == self.SL_AP2:
map_type_str = '[R]'
map = self.FlatMapping(virt, phy_adr, map_type_str, page_size, mapped)
if virt not in mappings:
mappings[virt] = map
else:
self.out_file.write(
'[!] WARNING: FL_PTE[%d] SL_PTE[%d] ERROR [Duplicate mapping?]\n' % (fl_idx, sl_idx))
return mappings
def add_collapsed_mapping(self, mappings, virt_start, virt_end, phys_start, phys_end, map_type, page_size, mapped):
map = self.CollapsedMapping(
virt_start, virt_end, phys_start, phys_end, map_type, page_size, mapped)
if virt_start not in mappings:
mappings[virt_start] = map
else:
self.out_file.write(
'[!] WARNING: ERROR [Duplicate mapping at virtual address 0x%08x?]\n' % (virt_start))
return mappings
def create_flat_mapping(self, pg_table):
tmp_mapping = {}
fl_pte = pg_table
for fl_index in range(0, self.NUM_FL_PTE):
fl_pg_table_entry = self.ramdump.read_u32(fl_pte)
if fl_pg_table_entry is not None:
if fl_pg_table_entry & self.FL_TYPE_SECT:
(phy_addr, page_size, map_type,
status) = self.get_sect_mapping_info(fl_pg_table_entry)
if status:
if phy_addr != -1:
tmp_mapping = self.add_flat_mapping(
tmp_mapping, fl_index, 0, phy_addr, map_type, page_size, True)
else:
# no mapping
tmp_mapping = self.add_flat_mapping(
tmp_mapping, fl_index, 0, -1, 0, 0, False)
elif fl_pg_table_entry & self.FL_TYPE_TABLE:
sl_pte = fl_pg_table_entry & self.FL_BASE_MASK
for sl_index in range(0, self.NUM_SL_PTE):
(phy_addr, page_size, map_type,
status) = self.get_mapping_info(sl_pte, sl_index)
if status:
if phy_addr != -1:
tmp_mapping = self.add_flat_mapping(
tmp_mapping, fl_index, sl_index, phy_addr, map_type, page_size, True)
else:
# no mapping
tmp_mapping = self.add_flat_mapping(
tmp_mapping, fl_index, sl_index, -1, 0, 0, False)
else:
self.out_file.write(
'[!] WARNING: FL_PTE[%d] SL_PTE[%d] ERROR [Unknown error]\n' % (fl_index, sl_index))
elif fl_pg_table_entry != 0:
self.out_file.write(
'[!] WARNING: FL_PTE[%d] = %x NOTE: ERROR [Cannot understand first level page table entry]\n' %
(fl_index, fl_pg_table_entry))
else:
tmp_mapping = self.add_flat_mapping(
tmp_mapping, fl_index, 0, -1, 0, 0, False)
else:
self.out_file.write(
'[!] WARNING: FL_PTE[%d] NOTE: ERROR [Cannot understand first level page table entry]\n' % (fl_index))
fl_pte += 4
return tmp_mapping
def create_collapsed_mapping(self, flat_mapping):
collapsed_mapping = {}
if len(flat_mapping.keys()) > 0:
virt_addrs = sorted(flat_mapping.keys())
start_map = prev_map = flat_mapping[virt_addrs[0]]
last_mapping = False
for virt in virt_addrs[1:]:
map = flat_mapping[virt]
new_mapping = False
if map.mapping_size == prev_map.mapping_size and map.mapping_type == prev_map.mapping_type and map.mapped == prev_map.mapped:
if prev_map.mapping_size == SZ_4K:
if (map.phys - SZ_4K) != prev_map.phys and map.phys != prev_map.phys:
new_mapping = True
elif prev_map.mapping_size == SZ_64K:
if (map.phys - SZ_64K) != prev_map.phys and map.phys != prev_map.phys:
new_mapping = True
elif prev_map.mapping_size == SZ_1M:
if (map.phys - SZ_1M) != prev_map.phys and map.phys != prev_map.phys:
new_mapping = True
elif prev_map.mapping_size == SZ_16M:
if (map.phys - SZ_16M) != prev_map.phys and map.phys != prev_map.phys:
new_mapping = True
elif virt == virt_addrs[-1]:
# Last one
last_mapping = True
else:
new_mapping = True
if new_mapping:
collapsed_mapping = self.add_collapsed_mapping(
collapsed_mapping, start_map.virt, map.virt,
start_map.phys, prev_map.phys +
prev_map.mapping_size,
prev_map.mapping_type, prev_map.mapping_size, prev_map.mapped)
start_map = map
elif last_mapping:
collapsed_mapping = self.add_collapsed_mapping(
collapsed_mapping, start_map.virt, 0xFFFFFFFF + 1,
start_map.phys, prev_map.phys +
prev_map.mapping_size,
prev_map.mapping_type, prev_map.mapping_size, prev_map.mapped)
prev_map = map
return collapsed_mapping
def print_page_table_pretty(self, pg_table):
flat_mapping = self.create_flat_mapping(pg_table)
collapsed_mapping = self.create_collapsed_mapping(flat_mapping)
for virt in sorted(collapsed_mapping.keys()):
mapping = collapsed_mapping[virt]
if mapping.mapped:
self.out_file.write(
'0x%08x--0x%08x [0x%08x] A:0x%08x--0x%08x [0x%08x] %s[%s]\n' % (mapping.virt_start, mapping.virt_end, mapping.virt_size(),
mapping.phys_start, mapping.phys_end,
mapping.phys_size(), mapping.mapping_type, order_size_strings[get_order(mapping.mapping_size)]))
else:
self.out_file.write('0x%08x--0x%08x [0x%08x] [UNMAPPED]\n' %
(mapping.virt_start, mapping.virt_end, mapping.virt_size()))
def parse_short_form_tables(self, d, domain_num):
self.out_file = self.ramdump.open_file(
'msm_iommu_domain_%02d_0x%12X.txt' % (domain_num, d.pg_table))
redirect = 'OFF'
if d.redirect is None:
redirect = 'UNKNOWN'
elif d.redirect > 0:
redirect = 'ON'
iommu_context = 'None attached'
if len(d.ctx_list) > 0:
iommu_context = ''
for (num, name) in d.ctx_list:
iommu_context += '%s (%d) ' % (name, num)
iommu_context = iommu_context.strip()
self.out_file.write('IOMMU Context: %s. Domain: %s'
'[L2 cache redirect for page tables is %s]\n' % (
iommu_context, d.client_name, redirect))
self.out_file.write(
'[VA Start -- VA End ] [Size ] [PA Start -- PA End ] [Size ] [Read/Write][Page Table Entry Size]\n')
if d.pg_table == 0:
self.out_file.write(
'No Page Table Found. (Probably a secure domain)\n')
else:
self.print_page_table_pretty(d.pg_table)
self.out_file.write('\n-------------\nRAW Dump\n')
self.print_page_table(d.pg_table)
self.out_file.close()
def parse(self):
ilib = IommuLib(self.ramdump)
self.domain_list = ilib.domain_list
if self.domain_list is None:
print_out_str(
'[!] WARNING: IOMMU domains was not found in this build. No IOMMU page tables will be generated')
return
for (domain_num, d) in enumerate(self.domain_list):
if self.ramdump.is_config_defined('CONFIG_IOMMU_LPAE'):
parse_long_form_tables(self.ramdump, d, domain_num)
elif (d.domain_type == MSM_SMMU_DOMAIN):
self.parse_short_form_tables(d, domain_num)
elif ((d.domain_type == ARM_SMMU_DOMAIN) or
(d.domain_type == MSM_SMMU_AARCH64_DOMAIN)):
parse_aarch64_tables(self.ramdump, d, domain_num)

View File

@@ -0,0 +1,603 @@
"""
Copyright (c) 2016, 2018, 2020-2021 The Linux Foundation. All rights reserved.
Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Changes from Qualcomm Innovation Center are provided under the following license:
Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause-Clear
"""
from parser_util import register_parser, RamParser
from print_out import print_out_str
from rb_tree import RbTree
import logging
import os
RB_PARENT_COLOR_MASK = 0xFFFFFFFFFFFFFFFC
grand_total = 0
TASK_NAME_LENGTH = 16
ion_heap_buffers = []
dmabuf_heap_names = ["qcom_dma_heaps"]
def bytes_to_KB(bytes):
kb_val = 0
if bytes != 0:
kb_val = bytes // 1024
return kb_val
def bytes_to_mb(bytes):
val = 0
if bytes != 0:
val = (bytes // 1024) // 1024
return val
def get_dmabuf_heap_names(self, ramdump, ion_info):
heap_list = ramdump.address_of('heap_list')
if heap_list is None:
ion_info.write("NOTE: 'heap_list' list not found to extract the "
"dmabuf heap type list")
return False
list_offset = ramdump.field_offset('struct dma_heap', 'list')
name_offset = ramdump.field_offset('struct dma_heap', 'name')
next_offset = ramdump.field_offset('struct list_head', 'next')
prev_offset = ramdump.field_offset('struct list_head', 'prev')
head = ramdump.read_word(heap_list + next_offset)
while (head != heap_list):
dma_heap_addr = head - list_offset
dma_heap_name_addr = ramdump.read_word(dma_heap_addr + name_offset)
dma_heap_name = ramdump.read_cstring(dma_heap_name_addr, 48)
dmabuf_heap_names.append(dma_heap_name)
head = ramdump.read_word(head + next_offset)
prev = ramdump.read_word(head + prev_offset)
if head == 0 or prev == 0:
ion_info.write("NOTE: 'dmabuf heap_list' is corrupted")
return False
return True
def ion_buffer_info(self, ramdump, ion_info):
ion_info = ramdump.open_file('ionbuffer.txt')
db_list = ramdump.address_of('db_list')
if db_list is None:
ion_info.write("NOTE: 'db_list' list not found to extract the ion "
"buffer information")
return
total_dma_heap = 0
total_dma_info = ramdump.open_file('total_dma_heap.txt')
ion_info.write("*****Parsing dma buf info for ion leak debugging*****\n\n")
head_offset = ramdump.field_offset('struct dma_buf_list', 'head')
head = ramdump.read_word(db_list + head_offset)
next_offset = ramdump.field_offset('struct list_head', 'next')
prev_offset = ramdump.field_offset('struct list_head', 'prev')
list_node_offset = ramdump.field_offset('struct dma_buf', 'list_node')
size_offset = ramdump.field_offset('struct dma_buf', 'size')
ops_offset = ramdump.field_offset('struct dma_buf', 'ops')
file_offset = ramdump.field_offset('struct dma_buf', 'file')
f_count_offset = ramdump.field_offset('struct file', 'f_count')
name_offset = ramdump.field_offset('struct dma_buf', 'buf_name')
if name_offset is None:
name_offset = ramdump.field_offset('struct dma_buf', 'name')
exp_name_offset = ramdump.field_offset('struct dma_buf', 'exp_name')
print("File_addr dma_buf REF Name Size Exp Heap Size in KB dma_heap->ops dma_buf->ops \n", file = ion_info)
dma_buf_info = []
if (ramdump.kernel_version >= (5, 10)):
if get_dmabuf_heap_names(self, ramdump, ion_info) is False:
return
while (head != db_list):
dma_buf_addr = head - list_node_offset
size = ramdump.read_word(dma_buf_addr + size_offset)
total_dma_heap = total_dma_heap + size
file = ramdump.read_word(dma_buf_addr + file_offset)
f_count = ramdump.read_u64(file + f_count_offset)
exp_name = ramdump.read_word(dma_buf_addr + exp_name_offset)
exp_name = ramdump.read_cstring(exp_name, 48)
dma_buf_ops = ''
dma_buf_ops_addr = ramdump.read_word(dma_buf_addr + ops_offset)
look = ramdump.unwind_lookup(dma_buf_ops_addr)
if look != None:
fop, offset = look
dma_buf_ops = fop
ionheap_name = None
ops = ''
if (ramdump.kernel_version >= (5, 10)):
if exp_name in dmabuf_heap_names:
ion_buffer = ramdump.read_structure_field(dma_buf_addr, 'struct dma_buf', 'priv')
ion_heap = ramdump.read_structure_field(ion_buffer, 'struct qcom_sg_buffer', 'heap')
ionheap_name_addr = ramdump.read_structure_field(ion_heap, 'struct dma_heap', 'name')
ionheap_name = ramdump.read_cstring(ionheap_name_addr, 48)
dma_heap_ops = ramdump.read_structure_field(ion_heap, 'struct dma_heap', 'ops')
look = ramdump.unwind_lookup(dma_heap_ops)
if look !=None:
fop, offset = look
ops = fop
else:
if exp_name == 'ion':
ion_buffer = ramdump.read_structure_field(dma_buf_addr, 'struct dma_buf', 'priv')
ion_heap = ramdump.read_structure_field(ion_buffer, 'struct ion_buffer', 'heap')
ionheap_name_addr = ramdump.read_structure_field(ion_heap, 'struct ion_heap', 'name')
ionheap_name = ramdump.read_cstring(ionheap_name_addr, 48)
if ionheap_name is None:
ionheap_name = "None"
name = ramdump.read_word(dma_buf_addr + name_offset)
if not name:
name = "None"
else:
name = ramdump.read_cstring(name, 48)
dma_buf_info.append([dma_buf_addr, f_count, name, size, exp_name,
ionheap_name, size, ops, dma_buf_ops, file])
head = ramdump.read_word(head)
dma_buf_info = sorted(dma_buf_info, key=lambda l: l[6], reverse=True)
total_dma_heap_mb = bytes_to_mb(total_dma_heap)
total_dma_heap_mb = str(total_dma_heap_mb) + "MB"
total_dma_info.write("Total dma memory: {0}".format(total_dma_heap_mb))
for item in dma_buf_info:
print("v.v (struct file *)0x%x v.v (struct dma_buf*)0x%x %2d %8s 0x%-8x %-24s %-24s %16dKB %-32s %-32s"
%(item[9], item[0], item[1], item[2], item[3], item[4], item[5], item[6]/1024, item[7], item[8]), file = ion_info)
def get_bufs(self, task, bufs, ion_info, ramdump):
t_size = 0
dma_buf_fops = ramdump.address_of('dma_buf_fops')
if dma_buf_fops is None:
ion_info.write("NOTE: 'dma_buf_fops' not found for file information\n")
return 0
if task is None:
return 0
files = ramdump.read_pointer(task + self.files_offset)
if files is None:
return 0
fdt = ramdump.read_pointer(files + self.fdt_offset)
if fdt is None:
return 0
fd = ramdump.read_pointer(fdt + self.fd_offset)
max_fds = ramdump.read_halfword(fdt + self.max_fds_offset)
stime = ramdump.read_word(self.timekeeper + self.stime_offset)
ctime_offset = ramdump.field_offset('struct dma_buf', 'ktime')
if ctime_offset is not None:
ctime_offset += ramdump.field_offset('struct timespec', 'tv_sec')
for i in range(max_fds):
file = ramdump.read_pointer(fd + i*8)
if (file == 0):
continue
f_op = ramdump.read_pointer(file + self.f_op_offset)
if (f_op != dma_buf_fops):
continue
dmabuf = ramdump.read_pointer(file + self.private_data_offset)
size = ramdump.read_word(dmabuf + self.size_offset)
time = 0
if ctime_offset is not None:
ctime = ramdump.read_word(dmabuf + ctime_offset)
ctime = ctime // 1000000000
time = stime - ctime
name = ramdump.read_word(dmabuf + self.exp_name_offset)
if not name:
name = "None"
else:
name = ramdump.read_cstring(name, 48)
item = [name, hex(size), bytes_to_KB(size), str(time), file, dmabuf]
if item not in bufs:
t_size = t_size + size
bufs.append(item)
bufs.sort(key=lambda item: -item[2])
return t_size
def get_proc_bufs(self, task, bufs, ion_info, ramdump):
size = 0
for curr in ramdump.for_each_thread(task):
size += get_bufs(self, curr, bufs, ion_info, ramdump)
return size
def ion_proc_info(self, ramdump):
ionproc_file = ramdump.open_file('ionproc.txt')
ionproc_file.write("*****Parsing dma proc info for ion leak debugging*****\n")
pid_offset = ramdump.field_offset('struct task_struct', 'tgid')
comm_offset = ramdump.field_offset('struct task_struct', 'comm')
dma_procs = []
for task in ramdump.for_each_process():
bufs = []
size = get_proc_bufs(self, task, bufs, ionproc_file, ramdump)
if (size == 0):
continue
comm = ramdump.read_cstring(task + comm_offset)
pid = ramdump.read_int(task + pid_offset)
dma_procs.append([comm, pid, bytes_to_KB(size), bufs])
dma_procs.sort(key=lambda item: -item[2])
for proc in dma_procs:
str = "\n{0} (PID {1}) size (KB): {2}\n"\
.format(proc[0], proc[1], proc[2])
ionproc_file.write(str)
ionproc_file.write("{0:15} {1:15} {2:10} {3:20} {4:20} {5:20}\n".format(
'Name', 'Size', 'Size in KB', 'Time Alive(sec)', 'file', 'dma_buf'))
for item in proc[3]:
print("%-32s %8s %8s %8s v.v (struct file*)0x%x v.v (struct dma_buf)0x%x" %(
item[0], item[1], item[2], item[3], item[4], item[5]), file = ionproc_file)
def do_dump_ionbuff_info(self, ramdump, ion_info):
addressspace = 8
heap_addr_array = []
ionbuffer_file = ramdump.open_file('ionbuffer.txt')
# read num of heaps
number_of_heaps = ramdump.read_word('num_heaps')
ion_info.write('Number of heaps:{0} \n'.format(number_of_heaps))
# get heap starting address
heap_addr = ramdump.read_pointer('heaps')
if self.ramdump.arm64:
addressspace = 8
else:
addressspace = 4
# get address of all heaps
nIndex = 0
for nIndex in range(0, number_of_heaps):
heap_addr_array.append(heap_addr + (nIndex*addressspace))
# parse a heap
nIndex = 0
for nIndex in range(0, number_of_heaps):
str = "\n\n parsing {0:0} of {1:0} heap Heap: 0x{2:x}"
ionbuffer_file.write(str.format(
nIndex + 1,
number_of_heaps,
ramdump.read_word(
heap_addr_array[nIndex])))
parse_heap(self, ramdump, heap_addr_array[nIndex], ionbuffer_file)
ionbuffer_file.write(
'\n Total ION buffer size: {0:1} KB'.format(
bytes_to_KB(grand_total)))
def parse_heap(self, ramdump, heap_addr, ionbuffer_file):
global grand_total
nr_clients = 0
total_orphan_buffer_size = 0
ion_heap = ramdump.read_word(heap_addr)
ionheap_id = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'id')
ionheap_name_addr = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'name')
ionheap_name = ramdump.read_cstring(ionheap_name_addr, TASK_NAME_LENGTH)
ionheap_type = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'type')
ionheap_total_allocated = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'total_allocated.counter')
ionheap_total_handles = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'total_handles.counter')
self.ion_handle_node_offset = ramdump.field_offset(
'struct ion_handle', 'node')
ionbuffer_file.write("\n*********************************************")
str = "\n Heap ID : {0} Heap Type: {1} Heap Name : {2}\n"
ionbuffer_file.write(str.format(ionheap_id, ionheap_type, ionheap_name))
ionbuffer_file.write('\n Total allocated : {0:1} KB'.format(
bytes_to_KB(ionheap_total_allocated)))
ionbuffer_file.write('\n Total Handles : {0:1} KB'.format(
bytes_to_KB(ionheap_total_handles)))
orphan = bytes_to_KB(ionheap_total_allocated - ionheap_total_handles)
ionbuffer_file.write('\n Orphan : {0:1} KB'.format(orphan))
ionbuffer_file.write("\n*********************************************")
ion_dev = ramdump.read_structure_field(
ion_heap, 'struct ion_heap', 'dev')
clients_rb_root = ion_dev + ramdump.field_offset('struct ion_device', 'clients')
if ionheap_total_allocated != 0:
nr_clients = show_ion_dev_client(
self, ramdump,
clients_rb_root,
ionheap_id, ionbuffer_file)
str = "\n \nTotal number of clients: {0:1}"
ionbuffer_file.write(str.format(nr_clients))
ionbuffer_file.write("\n ----------------------------------")
str = "\n orphaned allocations (info is from last known client):\n"
ionbuffer_file.write(str)
total_orphan_buffer_size, total_buffer_size = \
parse_orphan_buffers(self, ramdump, ion_dev, ionheap_id, ionbuffer_file)
ionbuffer_file.write("\n ----------------------------------")
ionbuffer_file.write(
'\n total orphan size: {0} KB'.format(
bytes_to_KB(total_orphan_buffer_size)))
ionbuffer_file.write(
'\n total buffer size: {0} KB'.format(
bytes_to_KB(total_buffer_size)))
ionbuffer_file.write("\n ----------------------------------")
grand_total = grand_total + total_buffer_size
def parse_orphan_buffers(self, ramdump, ion_dev, heap_id, ionbuffer_file):
orphan_buffer_size = 0
total_buffer_size = 0
rbtree = RbTree(ramdump, ion_dev + ramdump.field_offset('struct ion_device', 'buffers'),
logger = self.logger, debug = True)
ion_buffer_rb_node_offset = ramdump.field_offset(
'struct ion_buffer', 'node')
ion_buffer_task_comm_offset = ramdump.field_offset(
'struct ion_buffer', 'task_comm')
ion_buffer_ref_offset = ramdump.field_offset(
'struct ion_buffer', 'ref')
str = "\n buffer: 0x{0:x}, Buffer size: {1} KB "
str = str + "comm: {2} PID: {3} kmap count: {4} ref_count : {5}"
for rb_node in rbtree:
ion_buffer = rb_node - ion_buffer_rb_node_offset
ion_buffer_ref_add = ion_buffer + ion_buffer_ref_offset
ion_buffer_heap = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'heap')
ion_heap_id = ramdump.read_structure_field(
ion_buffer_heap, 'struct ion_heap', 'id')
ion_buffer_size = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'size')
ion_buffer_handlecount = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'handle_count')
ref_counter = ramdump.read_structure_field(
ion_buffer_ref_add, 'struct kref', 'refcount.counter')
if heap_id == ion_heap_id:
total_buffer_size = total_buffer_size + ion_buffer_size
# if orphaned allocation
if ion_buffer_handlecount == 0:
ion_buffer_pid = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'pid')
ion_buffer_kmap_count = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'kmap_cnt')
client_name = ramdump.read_cstring(
(ion_buffer + ion_buffer_task_comm_offset),
TASK_NAME_LENGTH)
ionbuffer_file.write(str.format(
ion_buffer,
bytes_to_KB(ion_buffer_size),
client_name,
ion_buffer_pid,
ion_buffer_kmap_count,
ref_counter))
orphan_buffer_size = orphan_buffer_size + ion_buffer_size
return orphan_buffer_size, total_buffer_size
def show_ion_dev_client(
self,
ramdump,
rb_root,
ionheap_id, ionbuffer_file):
global ion_heap_buffers
nr_clients = 0
client_name = 0
rbtree = RbTree(ramdump, rb_root, logger = self.logger, debug = True)
ion_client_node_offset = ramdump.field_offset(
'struct ion_client', 'node')
task_comm_offset = ramdump.field_offset(
'struct task_struct', 'comm')
tempstr = "\n\n CLIENT: (struct ion_client *)0x{0:x} , "
str = tempstr + "task : {1} / ion_client : {2} / PID: {3} / Size : {4} KB"
str1 = tempstr + "ion_client : {1} / PID: {2} / Size : {3} KB"
if True:
for rb_node in rbtree:
ion_client = rb_node - ion_client_node_offset
heap_size = traverse_ion_heap_buffer(
self,
ramdump,
ion_client,
ionheap_id,
ionbuffer_file)
if heap_size > 0:
nr_clients = nr_clients + 1
ion_client_task = ramdump.read_structure_field(
ion_client, 'struct ion_client', 'task')
task_comm = ion_client_task + task_comm_offset
client_name = ramdump.read_cstring(
task_comm, TASK_NAME_LENGTH)
ion_client_name = ramdump.read_structure_field(
ion_client,
'struct ion_client',
'display_name')
ion_client_name = ramdump.read_cstring(
ion_client_name,
TASK_NAME_LENGTH)
client_PID = ramdump.read_structure_field(
ion_client, 'struct ion_client', 'pid')
if ion_client_task != 0:
ionbuffer_file.write(str.format(
ion_client, client_name, ion_client_name,
client_PID, bytes_to_KB(heap_size)))
else:
ionbuffer_file.write(str1.format(
ion_client, ion_client_name,
client_PID, bytes_to_KB(heap_size)))
for heap_buffer in ion_heap_buffers:
ionbuffer_file.write(heap_buffer)
return nr_clients
def traverse_ion_heap_buffer(self, ramdump, ion_client, ionheap_id, ionbuffer_file):
global ion_heap_buffers
ion_handle_root_offset = ramdump.field_offset(
'struct ion_client', 'handles')
ion_handle_root_address = ion_client + ion_handle_root_offset
ion_buffer_heap_size = 0
ion_heap_buffers = []
str = "\n (+) ion_buffer: 0x{0:x} size: {1:0} KB Handle Count: {2:0}"
rbtree = RbTree(ramdump, ion_handle_root_address,
logger=self.logger, debug = True)
for ion_handle_rb_node in rbtree:
ion_handle = ion_handle_rb_node - self.ion_handle_node_offset
ion_buffer = ramdump.read_structure_field(
ion_handle, 'struct ion_handle', 'buffer')
ion_buffer_size = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'size')
ion_buffer_heap = ramdump.read_structure_field(
ion_buffer, 'struct ion_buffer', 'heap')
ion_heap_id = ramdump.read_structure_field(
ion_buffer_heap, 'struct ion_heap', 'id')
if ionheap_id == ion_heap_id:
ion_buffer_heap_size = ion_buffer_heap_size + ion_buffer_size
ion_buffer_handlecount = ramdump.read_structure_field(
ion_buffer,
'struct ion_buffer', 'handle_count')
temp = str.format(
ion_buffer,
bytes_to_KB(ion_buffer_size),
ion_buffer_handlecount)
ion_heap_buffers.append(temp)
return ion_buffer_heap_size
def parser(self, arg, ramdump, node, ion_info):
rb_root = 0
last_node = 0
self.orphan_size = 0
rbnode_left_offset = ramdump.field_offset('struct rb_node', 'rb_left')
temp = ramdump.read_word(node)
if temp == 0:
return 0
if arg == 1:
rb_root = find_rb_root(self, ramdump, node, ion_info)
last_node = find_rb_first(
self, ramdump, rb_root, rbnode_left_offset, ion_info)
if arg == 2:
last_node = find_rb_next(
self, arg, ramdump, node, rbnode_left_offset, ion_info)
return last_node
def find_rb_next(self, arg, ramdump, node, rbnode_left_offset, ion_info):
parent = cal_rb_parent(self, ramdump, node, ion_info)
tmp_node = 0
if parent == node:
ion_info.write("RETURNING NULL")
return 0
rbnode_right_offset = ramdump.field_offset('struct rb_node', 'rb_right')
rb_right = ramdump.read_word(node + rbnode_right_offset)
if rb_right != 0: # right node exist
next_rb_node = find_rb_first(
self, ramdump, rb_right, rbnode_left_offset, ion_info)
return next_rb_node
else: # no right node, parse left node
flag = 1
while flag:
if parent == 0 or None:
tmp_node = 0
parent = 0
else:
parent = cal_rb_parent(self, ramdump, node, ion_info)
tmp_node = ramdump.read_word(parent + rbnode_right_offset)
if tmp_node == node:
node = parent
continue
else:
return parent
return 0
def find_rb_first(self, ramdump, node, rbnode_left_offset, ion_info):
last_node = node
while node != 0:
last_node = node
node = ramdump.read_word(node + rbnode_left_offset)
return last_node
def cal_rb_parent(self, ramdump, ion_dev_rb_root, ion_info):
rbnode_color_offset = ramdump.field_offset(
'struct rb_node', '__rb_parent_color')
color = ramdump.read_word(ion_dev_rb_root + rbnode_color_offset)
color = color & RB_PARENT_COLOR_MASK
return color
def find_rb_root(self, ramdump, ion_dev_rb_root, ion_info):
parent = ion_dev_rb_root
rbnode_color_offset = ramdump.field_offset(
'struct rb_node', '__rb_parent_color')
color = ramdump.read_word(ion_dev_rb_root + rbnode_color_offset)
while color != 1:
parent = cal_rb_parent(self, ramdump, parent, ion_info)
color = ramdump.read_word(parent + rbnode_color_offset)
return parent
@register_parser('--print-ionbuffer', 'Print ion buffer', optional=True)
class DumpIonBuffer(RamParser):
def __init__(self, *args):
super(DumpIonBuffer, self).__init__(*args)
self.timekeeper = self.ramdump.address_of('shadow_timekeeper')
self.files_offset = self.ramdump.field_offset(
'struct task_struct', 'files')
self.fdt_offset = self.ramdump.field_offset(
'struct files_struct', 'fdt')
self.fd_offset = self.ramdump.field_offset('struct fdtable', 'fd')
self.max_fds_offset = self.ramdump.field_offset(
'struct fdtable', 'max_fds')
self.f_op_offset = self.ramdump.field_offset('struct file', 'f_op')
self.private_data_offset = self.ramdump.field_offset('struct file',
'private_data')
self.size_offset = self.ramdump.field_offset('struct dma_buf', 'size')
self.name_offset = self.ramdump.field_offset('struct dma_buf', 'buf_name')
if self.name_offset is None:
self.name_offset = self.ramdump.field_offset('struct dma_buf', 'name')
self.exp_name_offset = self.ramdump.field_offset('struct dma_buf', 'exp_name')
self.stime_offset = self.ramdump.field_offset('struct timekeeper',
'ktime_sec')
def parse(self):
with self.ramdump.open_file('ionbuffer.txt') as ionbuffer_file:
if (self.ramdump.kernel_version < (3, 18, 0)):
ionbuffer_file.write('Kernel version 3.18 \
and above are supported, current version {0}.\
{1}'.format(self.ramdump.kernel_version[0],
self.ramdump.kernel_version[1]))
return
self.logger = logging.getLogger(__name__)
path = os.path.join(self.ramdump.outdir, 'print-ionbuffer.stderr')
self.logger.addHandler(logging.FileHandler(path, mode='w'))
self.logger.setLevel(logging.INFO)
self.logger.info("Starting --print-ionbuffer")
if (self.ramdump.kernel_version >= (4, 14)):
ion_buffer_info(self, self.ramdump, ionbuffer_file)
ion_proc_info(self, self.ramdump)
else:
do_dump_ionbuff_info(self, self.ramdump, ionbuffer_file)

View File

@@ -0,0 +1,133 @@
#SPDX-License-Identifier: GPL-2.0-only
#Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
import linux_list as llist
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
@register_parser('--print-iostat', 'Print iostat info and block device list')
class iostat(RamParser):
def __init__(self, *args):
super(iostat, self).__init__(*args)
self.disk_name = []
def io_per_cpu_offset(self, cpu):
per_cpu_offset_addr = self.ramdump.address_of('__per_cpu_offset')
if per_cpu_offset_addr is None:
return 0
per_cpu_offset_addr_indexed = self.ramdump.array_index(
per_cpu_offset_addr, 'unsigned long', cpu)
#instead of read_slong, bd_stats + u64
return self.ramdump.read_u64(per_cpu_offset_addr_indexed)
def klist_node_to_dev(self, klist_node):
dev = self.ramdump.container_of(klist_node, 'struct device', 'knode_class')
if dev is None:
dev_private = self.ramdump.container_of(klist_node, 'struct device_private',
'knode_class')
if dev_private is None:
return None
dev = self.ramdump.read_structure_field(dev_private, 'struct device_private',
'device')
if dev is None:
return None
return dev
def block_class_init_walker(self, klist_node):
dev = self.klist_node_to_dev(klist_node)
if dev is None:
return
block_device = self.ramdump.container_of(dev, 'struct block_device', 'bd_device')
bd_disk = self.ramdump.read_structure_field(block_device, 'struct block_device', 'bd_disk')
disk_name_addr = self.ramdump.struct_field_addr(bd_disk, 'struct gendisk', 'disk_name')
disk_name = self.ramdump.read_cstring(disk_name_addr)
major = self.ramdump.read_structure_field(bd_disk, 'struct gendisk', 'major')
queue = self.ramdump.read_structure_field(bd_disk, 'struct gendisk', 'queue')
part0 = self.ramdump.read_structure_field(bd_disk, 'struct gendisk', 'part0')
bd_stats = self.ramdump.read_structure_field(part0, 'struct block_device', 'bd_stats')
count0 = 0
count1 = 0
if disk_name == None or disk_name in self.disk_name:
return
else:
self.disk_name.append(disk_name)
for i in self.ramdump.iter_cpus():
disk_stats = bd_stats + self.io_per_cpu_offset(i)
in_flight = self.ramdump.struct_field_addr(disk_stats, 'struct disk_stats', 'in_flight')
if self.ramdump.arm64:
in_flight0 = self.ramdump.read_s64(in_flight)
in_flight1 = self.ramdump.read_s64(in_flight + 8)
else:
in_flight0 = self.ramdump.read_s32(in_flight)
in_flight1 = self.ramdump.read_s32(in_flight + 4)
count0 += in_flight0
count1 += in_flight1
self.list_ouput.append([major, bd_disk, disk_name, queue, count0, count1, count0 + count1])
def init_block_class(self):
p = None
block_class = self.ramdump.address_of('block_class')
if block_class is None:
self.output.write("ERROR: 'block_class' not found\n")
return
if self.ramdump.field_offset('struct class', 'p') is None:
classkset_ptr = self.ramdump.read_pointer('class_kset')
classkset_list_ptr = self.ramdump.read_structure_field(classkset_ptr, 'struct kset', 'list.next')
kobj_offset = self.ramdump.field_offset('struct kobject', 'entry')
sp_buf = []
class_subsys_walker = llist.ListWalker(self.ramdump, classkset_list_ptr, kobj_offset)
class_subsys_walker.walk(classkset_list_ptr, self.get_class_to_subsys, block_class, sp_buf)
if bool(sp_buf):
p = sp_buf[0]
else:
p = self.ramdump.read_structure_field(block_class, 'struct class', 'p')
list_head = (p + self.ramdump.field_offset('struct subsys_private', 'klist_devices')
+ self.ramdump.field_offset('struct klist', 'k_list'))
list_offset = self.ramdump.field_offset('struct klist_node', 'n_node')
init_list_walker = llist.ListWalker(self.ramdump, list_head, list_offset)
if not init_list_walker.is_empty():
init_list_walker.walk(init_list_walker.next() + list_offset,
self.block_class_init_walker)
sorted_list = sorted(self.list_ouput, key=lambda l: l[6], reverse=True)
print("MAJOR gendisk NAME request_queue TOTAL ASYNC SYNC \n", file = self.f)
for item in sorted_list:
major = item[0]
bd_disk = item[1]
fops_offset = self.ramdump.field_offset('struct gendisk', 'fops')
fops = self.ramdump.read_pointer(bd_disk + fops_offset)
a_ops_name = self.ramdump.unwind_lookup(fops)
a_ops_string = ''
if a_ops_name is not None:
name, a = a_ops_name
a_ops_string = '[{0}]'.format(name)
else:
a_ops_string = "xxxxxxxxxx"
disk_name = item[2]
queue = item[3]
count0 = item[4]
count1 = item[5]
print("%-6d 0x%x %-8s 0x%x %-8d %-8d %-8d %-16s "
% (major, bd_disk, disk_name, queue, count0 + count1, count0, count1, a_ops_string), file=self.f)
def get_class_to_subsys(self, kobj_addr, block_class, found_sp:list):
if not bool(found_sp):
kset_addr = self.ramdump.container_of(kobj_addr, 'struct kset', 'kobj')
sp_addr = self.ramdump.container_of(kset_addr, 'struct subsys_private', 'subsys')
sp_class = self.ramdump.read_structure_field(sp_addr, 'struct subsys_private', 'class')
if block_class == sp_class:
found_sp.append(sp_addr)
return
def parse(self):
if (self.ramdump.kernel_version) < (5, 15, 0):
print_out_str('\n kernel 5.15 or above supported \n')
return
self.list_ouput = []
self.f = open(self.ramdump.outdir + "/iostat.txt", "w")
self.init_block_class()
self.f.close()

View File

@@ -0,0 +1,353 @@
"""
Copyright (c) 2020 The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os
import struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
ipa_log = "ipalog.txt"
def setup_out_file(path, self):
global out_file
try:
out_file = self.ramdump.open_file(path, 'wb')
return out_file
except:
print_out_str("could not open path {0}".format(path))
print_out_str("Do you have write/read permissions on the path?")
def print_out_ip(string):
out_file.write((string + '\n').encode('ascii', 'ignore'))
@register_parser('--ipa', 'print ipa_logging information')
class ipa_logging(RamParser):
def __init__(self, *args):
super(ipa_logging, self).__init__(*args)
def ipa_parse(self, ram_dump):
curr_chan=0
ep_idx=0
ipa3_ctx_addr_offset = ram_dump.address_of('ipa3_ctx')
ipa3_ctx_addr = ram_dump.read_u64(ipa3_ctx_addr_offset)
ep_offset = ram_dump.field_offset('struct ipa3_context','ep')
ipa_num_pipes_offset = ram_dump.field_offset('struct ipa3_context','ipa_num_pipes')
valid_offset = ram_dump.field_offset('struct ipa3_ep_context','valid')
gsi_chan_hdl_offset = ram_dump.field_offset('struct ipa3_ep_context','gsi_chan_hdl')
gsi_evt_ring_hdl_offset = ram_dump.field_offset('struct ipa3_ep_context','gsi_evt_ring_hdl')
client_offset = ram_dump.field_offset('struct ipa3_ep_context','client')
sys_offset = ram_dump.field_offset('struct ipa3_ep_context','sys')
repl_offset = ram_dump.field_offset('struct ipa3_sys_context','repl')
len_offset = ram_dump.field_offset('struct ipa3_sys_context','len')
curr_polling_state_offset = ram_dump.field_offset('struct ipa3_sys_context','curr_polling_state')
head_idx_offset = ram_dump.field_offset('struct ipa3_repl_ctx','head_idx')
tail_idx_offset = ram_dump.field_offset('struct ipa3_repl_ctx','tail_idx')
capacity_offset = ram_dump.field_offset('struct ipa3_repl_ctx','capacity')
pending_offset = ram_dump.field_offset('struct ipa3_repl_ctx','pending')
page_recycle_repl_offset = ram_dump.field_offset('struct ipa3_sys_context','page_recycle_repl')
page_recycle_repl_offset = ram_dump.field_offset('struct ipa3_sys_context','page_recycle_repl')
ep_addr = ipa3_ctx_addr + ep_offset
gsi_ctx_offset = ram_dump.address_of('gsi_ctx')
gsi_ctx_addr = ram_dump.read_u64(gsi_ctx_offset)
chan_offset = ram_dump.field_offset('struct gsi_ctx','chan')
state_offset = ram_dump.field_offset('struct gsi_chan_ctx','state')
poll_mode_offset = ram_dump.field_offset('struct gsi_chan_ctx','poll_mode')
ring_offset = ram_dump.field_offset('struct gsi_chan_ctx','ring')
stats_offset = ram_dump.field_offset('struct gsi_chan_ctx','stats')
props_offset = ram_dump.field_offset('struct gsi_chan_ctx','props')
evtr_offset = ram_dump.field_offset('struct gsi_chan_ctx','evtr')
prot_offset = ram_dump.field_offset('struct gsi_chan_props','prot')
ch_id_offset = ram_dump.field_offset('struct gsi_chan_props','ch_id')
queued_offset = ram_dump.field_offset('struct gsi_chan_stats', 'queued')
completed_offset = ram_dump.field_offset('struct gsi_chan_stats', 'completed')
callback_to_poll_offset = ram_dump.field_offset('struct gsi_chan_stats', 'callback_to_poll')
poll_to_callback_offset = ram_dump.field_offset('struct gsi_chan_stats', 'poll_to_callback')
poll_pending_irq_offset = ram_dump.field_offset('struct gsi_chan_stats', 'poll_pending_irq')
invalid_tre_error_offset = ram_dump.field_offset('struct gsi_chan_stats', 'invalid_tre_error')
poll_ok_offset = ram_dump.field_offset('struct gsi_chan_stats', 'poll_ok')
poll_empty_offset = ram_dump.field_offset('struct gsi_chan_stats', 'poll_empty')
userdata_in_use_offset = ram_dump.field_offset('struct gsi_chan_stats', 'userdata_in_use')
dp_offset = ram_dump.field_offset('struct gsi_chan_stats', 'dp')
ch_below_lo_offset = ram_dump.field_offset('struct gsi_chan_dp_stats', 'ch_below_lo')
ch_below_hi_offset = ram_dump.field_offset('struct gsi_chan_dp_stats', 'ch_below_hi')
ch_above_hi_offset = ram_dump.field_offset('struct gsi_chan_dp_stats', 'ch_above_hi')
empty_time_offset = ram_dump.field_offset('struct gsi_chan_dp_stats', 'empty_time')
last_timestamp_offset = ram_dump.field_offset('struct gsi_chan_dp_stats', 'last_timestamp')
base_va_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'base_va')
base_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'base')
wp_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'wp')
rp_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'rp')
wp_local_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'wp_local')
rp_local_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'rp_local')
rlen_offset = ram_dump.field_offset('struct gsi_ring_ctx', 'len')
evtr_state_offset = ram_dump.field_offset('struct gsi_evt_ctx', 'state')
evtr_id_offset = ram_dump.field_offset('struct gsi_evt_ctx', 'id')
evtr_ring_offset = ram_dump.field_offset('struct gsi_evt_ctx', 'ring')
evtr_stats_offset = ram_dump.field_offset('struct gsi_evt_ctx', 'stats')
evt_completed_offset = ram_dump.field_offset('struct gsi_evt_stats', 'completed')
IPA_EP_MAX = ram_dump.read_u32(ipa_num_pipes_offset + ipa3_ctx_addr)
while ep_idx < IPA_EP_MAX:
ep_addr_data = ep_addr + ram_dump.sizeof('struct ipa3_ep_context') * ep_idx
valid = ram_dump.read_u32(valid_offset + ep_addr_data)
if valid != 0:
curr_chan = ram_dump.read_u64(gsi_chan_hdl_offset + ep_addr_data)
client = ram_dump.read_u32(client_offset + ep_addr_data)
ipa_client_enum = self.ramdump.gdbmi.get_value_of('IPA_CLIENT_IPSEC_ENCAP_ERR_CONS')
# This means, to accomodate difference in IPA client counts from PL to PL, we are keeping an offset,
# so that there is no exception due to difference in the upperbound.
client_count_offset=50
client_names = ram_dump.gdbmi.get_enum_lookup_table(
'ipa_client_type',ipa_client_enum+client_count_offset)
print_out_ip("IPA Pipe: {0}".format(ep_idx))
print_out_ip("Pipe Name: {0}".format(client_names[client]))
sys = ram_dump.read_u64(sys_offset + ep_addr_data)
if sys and sys != 0x0:
len = ram_dump.read_u32(len_offset + sys)
counter = ram_dump.read_u32(curr_polling_state_offset + sys)
print_out_ip("sys_len: {0}".format(len))
print_out_ip("Current Polling State: {0}".format(counter))
repl = ram_dump.read_u64(repl_offset + sys)
page_recycle_repl = ram_dump.read_u64(page_recycle_repl_offset + sys)
if repl and repl != 0x0:
head_counter = ram_dump.read_u32(head_idx_offset + repl)
tail_counter = ram_dump.read_u32(tail_idx_offset + repl)
capacity = ram_dump.read_u32(capacity_offset + repl)
pending_counter = ram_dump.read_u32(pending_offset + repl)
print_out_ip("Repl Cache Head Index: {0}".format(head_counter))
print_out_ip("Repl Cache Tail Index: {0}".format(tail_counter))
print_out_ip("Repl Capacity: {0}".format(capacity))
print_out_ip("Repl Wq Pending: {0}".format(pending_counter))
if page_recycle_repl and page_recycle_repl != 0x0:
head_counter = ram_dump.read_u32(head_idx_offset + page_recycle_repl)
tail_counter = ram_dump.read_u32(tail_idx_offset + page_recycle_repl)
capacity = ram_dump.read_u32(capacity_offset + page_recycle_repl)
print_out_ip("Page Repl Cache Head Index: {0}".format(head_counter))
print_out_ip("Page Repl Cache Tail Index: {0}".format(tail_counter))
print_out_ip("page Repl Capacity: {0}".format(capacity))
else:
print_out_ip("sys is null")
print_out_ip("gsi_chan_hdl: 0x{0}".format(curr_chan))
gsi_evt_ring_hdl = ram_dump.read_u64(gsi_evt_ring_hdl_offset + ep_addr_data)
print_out_ip("gsi_evt_ring_hdl: 0x{0}".format(gsi_evt_ring_hdl))
chan = chan_offset + gsi_ctx_addr
chan_idx_addr = (chan + ram_dump.sizeof('struct gsi_chan_ctx') * curr_chan)
pros = props_offset + chan_idx_addr
prot = ram_dump.read_u32(prot_offset + pros)
prot_enum = ram_dump.gdbmi.get_value_of('GSI_CHAN_PROT_11AD')
offset_for_prot_names=15 # 15 additional values in the array. When new channels come up, they will be populated here.
prot_names = ram_dump.gdbmi.get_enum_lookup_table(
'gsi_chan_prot', prot_enum+offset_for_prot_names)
ch_id = ram_dump.read_u16(ch_id_offset + chan_idx_addr)
state = ram_dump.read_u16(state_offset + chan_idx_addr)
offset_for_gsi_state_enum=5 #5 additional values in the array. When new states come up, they will be populated here.
state_enum = ram_dump.gdbmi.get_value_of('GSI_CHAN_STATE_ERROR')
state_names = ram_dump.gdbmi.get_enum_lookup_table(
'gsi_chan_state', state_enum+offset_for_gsi_state_enum)
poll_mode = ram_dump.read_u32(poll_mode_offset + chan_idx_addr)
ring = ring_offset + chan_idx_addr
base_va = ram_dump.read_u64(ring + base_va_offset)
base = ram_dump.read_u64(ring + base_offset)
wp = ram_dump.read_u64(ring + wp_offset)
rp = ram_dump.read_u64(ring + rp_offset)
wp_local = ram_dump.read_u64(ring + wp_local_offset)
rp_local = ram_dump.read_u64(ring + rp_local_offset)
rlen = ram_dump.read_u16(ring + rlen_offset)
stats = stats_offset + chan_idx_addr
queued = ram_dump.read_u64(stats + queued_offset)
completed = ram_dump.read_u64(stats + completed_offset)
callback_to_poll = ram_dump.read_u64(stats + callback_to_poll_offset)
poll_to_callback = ram_dump.read_u64(stats + poll_to_callback_offset)
poll_pending_irq = ram_dump.read_u64(stats + poll_pending_irq_offset)
invalid_tre_error = ram_dump.read_u64(stats + invalid_tre_error_offset)
poll_ok = ram_dump.read_u64(stats + poll_ok_offset)
poll_empty = ram_dump.read_u64(stats + poll_empty_offset)
userdata_in_use = ram_dump.read_u64(stats + userdata_in_use_offset)
dp = stats + dp_offset
ch_below_lo = ram_dump.read_u64(dp + ch_below_lo_offset)
ch_below_hi = ram_dump.read_u64(dp + ch_below_hi_offset)
ch_above_hi = ram_dump.read_u64(dp + ch_above_hi_offset)
empty_time = ram_dump.read_u64(dp + empty_time_offset)
last_timestamp = ram_dump.read_u64(dp + last_timestamp_offset)
print_out_ip("prot: {0}".format(prot_names[prot]))
print_out_ip("chan_id: {0}".format(ch_id))
if state > state_enum:
print_out_ip("state: {0}".format(state))
else:
print_out_ip("state: {0}".format(state_names[state]))
print_out_ip("poll_mode_counter: {0}".format(poll_mode))
print_out_ip("ring (")
print_out_ip(" base_va:0x{0:02x}".format(base_va))
print_out_ip(" base:0x{0:02x}".format(base))
print_out_ip(" wp:0x{0:02x}".format(wp))
print_out_ip(" rp:0x{0:02x}".format(rp))
print_out_ip(" wp_local:0x{0:02x}".format(wp_local))
print_out_ip(" rp_local:0x{0:02x}".format(rp_local))
print_out_ip(" ring_len: {0}".format(rlen))
print_out_ip(")")
print_out_ip("stats (")
print_out_ip(" queued: {0}".format(queued))
print_out_ip(" completed: {0}".format(completed))
print_out_ip(" callback_to_poll: {0}".format(callback_to_poll))
print_out_ip(" poll_to_callback: {0}".format(poll_to_callback))
print_out_ip("poll_pending_irq: {0}".format(poll_pending_irq))
print_out_ip(" invalid_tre_error: {0}".format(invalid_tre_error))
print_out_ip(" poll_ok: {0}".format(poll_ok))
print_out_ip(" poll_empty: {0}".format(poll_empty))
print_out_ip(" userdata_in_use: {0}".format(userdata_in_use))
print_out_ip(" ep (")
print_out_ip(" ch_below_lo: {0}".format(ch_below_lo))
print_out_ip(" ch_below_hi: {0}".format(ch_below_hi))
print_out_ip(" ch_above_hi: {0}".format(ch_above_hi))
print_out_ip(" empty_time: {0}".format(empty_time))
print_out_ip(" last_timestamp: {0}".format(last_timestamp))
print_out_ip(" )")
print_out_ip(")")
evtr = ram_dump.read_u64(chan_idx_addr + evtr_offset)
if evtr and evtr != 0x0:
evtr_state = ram_dump.read_u16(evtr + evtr_state_offset)
evtr_state_enum = self.ramdump.gdbmi.get_value_of('GSI_EVT_RING_STATE_ERROR')
evtr_state_names = ram_dump.gdbmi.get_enum_lookup_table(
'gsi_evt_ring_state', evtr_state_enum)
evtr_id = ram_dump.read_byte(evtr + evtr_id_offset)
evtr_ring = evtr + evtr_ring_offset
evtr_ring_base_va = ram_dump.read_u64(evtr_ring + base_va_offset)
evtr_ring_base = ram_dump.read_u64(evtr_ring + base_offset)
evtr_ring_wp = ram_dump.read_u64(evtr_ring + wp_offset)
evtr_ring_rp = ram_dump.read_u64(evtr_ring + rp_offset)
evtr_ring_wp_local = ram_dump.read_u64(evtr_ring + wp_local_offset)
evtr_ring_rp_local = ram_dump.read_u64(evtr_ring + rp_local_offset)
evt_len = ram_dump.read_u16(evtr_ring + rlen_offset)
evtr_stats = evtr + evtr_stats_offset
evtr_stats_completed = ram_dump.read_u64(evtr_stats + evt_completed_offset)
print_out_ip("evtr (")
print_out_ip(" evtr_state: {0}".format(evtr_state_names[evtr_state]))
print_out_ip(" evtr_id: {0}".format(evtr_id))
print_out_ip(" evtr_base_va:0x{0:02x}".format(evtr_ring_base_va))
print_out_ip(" evtr_base:0x{0:02x}".format(evtr_ring_base))
print_out_ip(" evtr_wp:0x{0:02x}".format(evtr_ring_wp))
print_out_ip(" evtr_rp:0x{0:02x}".format(evtr_ring_rp))
print_out_ip(" evtr_wp_local:0x{0:02x} ".format(evtr_ring_wp_local))
print_out_ip(" evtr_rp_local:0x{0:02x} ".format(evtr_ring_rp_local))
print_out_ip(" evtr_len: {0}".format(evt_len))
print_out_ip(" evtr_completed: {0}".format(evtr_stats_completed))
print_out_ip(")")
else:
print_out_ip("evtr ()")
print_out_ip(" --- ")
ep_idx = ep_idx + 1
#HOLB Stats
uc_ctx_offset = ram_dump.field_offset('struct ipa3_context','uc_ctx')
uc_inited_offset = ram_dump.field_offset('struct ipa3_uc_ctx','uc_inited')
uc_loaded_offset = ram_dump.field_offset('struct ipa3_uc_ctx','uc_loaded')
uc_failed_offset = ram_dump.field_offset('struct ipa3_uc_ctx','uc_failed')
holb_monitor_offset = ram_dump.field_offset('struct ipa3_uc_ctx','holb_monitor')
num_holb_clients_offset = ram_dump.field_offset('struct ipa_holb_monitor','num_holb_clients')
poll_period_offset = ram_dump.field_offset('struct ipa_holb_monitor','poll_period')
max_cnt_wlan_offset = ram_dump.field_offset('struct ipa_holb_monitor','max_cnt_wlan')
max_cnt_usb_offset = ram_dump.field_offset('struct ipa_holb_monitor','max_cnt_usb')
max_cnt_11ad_offset = ram_dump.field_offset('struct ipa_holb_monitor','max_cnt_11ad')
uc_ctx = ipa3_ctx_addr + uc_ctx_offset
holb_monitor = holb_monitor_offset + uc_ctx
num_holb_clients = ram_dump.read_u32(holb_monitor + num_holb_clients_offset)
max_cnt_wlan = ram_dump.read_u32(holb_monitor + max_cnt_wlan_offset)
max_cnt_usb = ram_dump.read_u32(holb_monitor + max_cnt_usb_offset)
max_cnt_11ad = ram_dump.read_u32(holb_monitor + max_cnt_11ad_offset)
hlob_monitor_client_offset = ram_dump.field_offset('struct ipa_holb_monitor','client')
gsi_chan_hdl_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','gsi_chan_hdl')
action_mask_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','action_mask')
max_stuck_cnt_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','max_stuck_cnt')
ee_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','ee')
debugfs_param_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','debugfs_param')
state_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','state')
enable_cnt_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','enable_cnt')
disable_cnt_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','disable_cnt')
current_idx_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','current_idx')
events_offset = ram_dump.field_offset('struct ipa_uc_holb_client_info','events')
hlob_monitor_client_addr = holb_monitor + hlob_monitor_client_offset
hold_idx=0
print_out_ip("HOLB Clients Info:")
while hold_idx < num_holb_clients:
print_out_ip("")
hlob_monitor_client = hlob_monitor_client_addr + ram_dump.sizeof('struct ipa_uc_holb_client_info') * hold_idx
print_out_ip("HOLB Client index: {0}".format(hold_idx))
gsi_chan_hdl = ram_dump.read_u16(hlob_monitor_client + gsi_chan_hdl_offset)
action_mask = ram_dump.read_u32(hlob_monitor_client + action_mask_offset)
max_stuck_cnt = ram_dump.read_u32(hlob_monitor_client + max_stuck_cnt_offset)
ee = ram_dump.read_byte(hlob_monitor_client + ee_offset)
debugfs_param = ram_dump.read_bool(hlob_monitor_client + debugfs_param_offset)
state = ram_dump.read_u64(hlob_monitor_client + state_offset)
enable_cnt = ram_dump.read_u64(hlob_monitor_client + enable_cnt_offset)
disable_cnt = ram_dump.read_u64(hlob_monitor_client + disable_cnt_offset)
current_idx = ram_dump.read_u64(hlob_monitor_client + current_idx_offset)
events = ram_dump.read_u64(hlob_monitor_client + events_offset)
print_out_ip("HOLB GSI Channel Handle: {0}".format(gsi_chan_hdl))
print_out_ip("HOLB Action Mask: {0}".format(action_mask))
print_out_ip("HOLB MAX Stuck Count: {0}".format(max_stuck_cnt))
print_out_ip("HOLB EE: {0}".format(ee))
print_out_ip("HOLB enabled via debugfs: {0}".format(debugfs_param))
print_out_ip("HOLB State: {0}".format(state))
print_out_ip("HOLB enable cnt: {0}".format(enable_cnt))
print_out_ip("HOLB disable cnt: {0}".format(disable_cnt))
print_out_ip("HOLB Events current idx: {0}".format(current_idx))
print_out_ip("")
print_out_ip("Events {0}".format(events))
print_out_ip("")
hold_idx=hold_idx+1
def parse(self):
setup_out_file(ipa_log, self)
self.ipa_parse(self.ramdump)

View File

@@ -0,0 +1,255 @@
"""
Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Changes from Qualcomm Innovation Center are provided under the following license:
Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause-Clear
IPC Logging Extraction Tool
---------------------------
Can be used on RAM dumps (files containing binary data that are produced after
a crash) to extract logs generated by IPC Logging. The dumps must be given in
the order they were written, so that all data can be found.
"""
import os
import struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
import linux_list as llist
TSV_TYPE_INVALID = 0
TSV_TYPE_TIMESTAMP = 1
TSV_TYPE_POINTER = 2
TSV_TYPE_INT32 = 3
TSV_TYPE_BYTE_ARRAY = 4
TSV_TYPE_QTIMER = 5
IPC_LOG_CONTEXT_MAGIC_NUM = 0x25874452
IPC_LOGGING_MAGIC_NUM = 0x52784425
@register_parser('--ipc_logging', 'print ipc_logging information')
class ipc_logging_cn(RamParser):
def __init__(self, *args):
super(ipc_logging_cn, self).__init__(*args)
def next_page(self, ipc_log_context, curr_read_page):
hdr_addr = self.ramdump.struct_field_addr(
curr_read_page, 'struct ipc_log_page', 'hdr')
list_addr = self.ramdump.struct_field_addr(
hdr_addr, 'struct ipc_log_page_header', 'list')
next = self.ramdump.read_word(list_addr)
pagelist_addr = self.ramdump.struct_field_addr(
ipc_log_context, 'struct ipc_log_context', 'page_list')
if next == pagelist_addr:
next = self.ramdump.read_word(next)
hdr = self.ramdump.container_of(
next, 'struct ipc_log_page_header', 'list')
ipc_log_page = self.ramdump.container_of(
hdr, 'struct ipc_log_page', 'hdr')
return ipc_log_page
def decode_buffer(self, final_buf, output):
tsv_header_struct_size = self.ramdump.sizeof('struct tsv_header')
pos = 0
while pos < len(final_buf):
tsv_msg_type, tsv_msg_size = struct.unpack_from("BB", final_buf, pos)
pos += tsv_header_struct_size
this_msg = pos
# TSV_TYPE_TIMESTAMP
this_msg_type, this_msg_size = struct.unpack_from("BB", final_buf, this_msg)
if this_msg_type == TSV_TYPE_TIMESTAMP:
this_msg = this_msg + tsv_header_struct_size
fmt = 'L'
if this_msg_size == 8:
fmt = 'Q'
TimeStamp, = struct.unpack_from(fmt, final_buf, this_msg)
this_msg = this_msg + this_msg_size
# TSV_TYPE_QTIMER
TimeQtimer = 0
this_msg_type, this_msg_size = struct.unpack_from("BB", final_buf, this_msg)
if this_msg_type == TSV_TYPE_QTIMER:
this_msg = this_msg + tsv_header_struct_size
fmt = 'L'
if this_msg_size == 8:
fmt = 'Q'
TimeQtimer, = struct.unpack_from(fmt, final_buf, this_msg)
this_msg = this_msg + this_msg_size
# TSV_TYPE_BYTE_ARRAY
this_msg_type, this_msg_size = struct.unpack_from("BB", final_buf, this_msg)
if this_msg_type == TSV_TYPE_BYTE_ARRAY:
this_msg = this_msg + tsv_header_struct_size
output_str = final_buf[this_msg:this_msg + this_msg_size].decode('utf-8', 'ignore')
if '\n' in output_str:
output.write(
"[ {0:10.9f} 0x{1:x}] {2}".format(
TimeStamp / 1000000000.0, TimeQtimer, output_str))
else:
output.write(
"[ {0:10.9f} 0x{1:x}] {2}\n".format(
TimeStamp / 1000000000.0, TimeQtimer, output_str))
pos += tsv_msg_size
def do_ipc_log_context_parser(self, ipc_log_context):
name_addr = self.ramdump.struct_field_addr(
ipc_log_context, 'struct ipc_log_context', 'name')
name = self.ramdump.read_cstring(name_addr)
nd_read_page = self.ramdump.read_structure_field(
ipc_log_context, 'struct ipc_log_context', 'nd_read_page')
start_read_page = nd_read_page
curr_read_page = start_read_page
hdr_addr = self.ramdump.struct_field_addr(
start_read_page, 'struct ipc_log_page', 'hdr')
magic = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'magic')
if magic != IPC_LOGGING_MAGIC_NUM:
print_out_str("wrong magic in ipc_log_page_header 0x{0:x} "
.format(hdr_addr))
return
file_name = "{0}.txt".format(name)
f_path = os.path.join(self.output_dir, file_name)
output = open(f_path, "w")
output.write("=====================================================\n")
output.write("IPC log for {0} \nv.v (struct ipc_log_context *)0x{1:x}\n"
.format(name, ipc_log_context))
output.write("=====================================================\n")
LOG_PAGE_DATA_OFFSET = self.ramdump.field_offset(
'struct ipc_log_page', 'data')
STRUCT_IPC_LOG_PAGE_SIZE = self.ramdump.sizeof('struct ipc_log_page')
LOG_PAGE_DATA_SIZE = STRUCT_IPC_LOG_PAGE_SIZE - LOG_PAGE_DATA_OFFSET
nd_read_offset = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'nd_read_offset')
write_offset = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'write_offset')
if nd_read_offset <= write_offset:
wrapped_around = 0
else:
wrapped_around = 1
final_buf = bytes()
stop_copy = 0
while stop_copy != 1:
hdr_addr = self.ramdump.struct_field_addr(
curr_read_page, 'struct ipc_log_page', 'hdr')
nd_read_offset = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'nd_read_offset')
write_offset = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'write_offset')
if nd_read_offset is None:
break
start_addr = curr_read_page + LOG_PAGE_DATA_OFFSET + nd_read_offset
if nd_read_offset <= write_offset:
bytes_to_copy = write_offset - nd_read_offset
else:
bytes_to_copy = LOG_PAGE_DATA_SIZE - nd_read_offset
if bytes_to_copy < 0:
stop_copy = 1
break
next_page = self.next_page(ipc_log_context, curr_read_page)
if next_page == start_read_page:
stop_copy = 1
buf = self.ramdump.read_physical(
self.ramdump.virt_to_phys(start_addr), bytes_to_copy)
final_buf += buf
if (wrapped_around == 0) and (write_offset < LOG_PAGE_DATA_SIZE):
break
curr_read_page = next_page
if wrapped_around == 1:
hdr_addr = self.ramdump.struct_field_addr(
start_read_page, 'struct ipc_log_page', 'hdr')
write_offset = self.ramdump.read_structure_field(
hdr_addr, 'struct ipc_log_page_header', 'write_offset')
bytes_to_copy = write_offset
start_addr = start_read_page + LOG_PAGE_DATA_OFFSET
buf = self.ramdump.read_physical(
self.ramdump.virt_to_phys(start_addr), bytes_to_copy)
final_buf += buf
self.decode_buffer(final_buf, output)
output.close()
def ipc_log_context_list_func(self, ipc_log_context):
magic = self.ramdump.read_structure_field(
ipc_log_context, 'struct ipc_log_context', 'magic')
if magic != IPC_LOG_CONTEXT_MAGIC_NUM:
print_out_str("wrong magic in ipc_log_context 0x{0:x} "
.format(ipc_log_context))
return
try:
self.do_ipc_log_context_parser(ipc_log_context)
except:
pass
def get_ipc_log_context_list(self, ram_dump):
ipc_log_context_list = ram_dump.address_of('ipc_log_context_list')
list_offset = self.ramdump.field_offset(
'struct ipc_log_context', 'list')
list_walker = llist.ListWalker(
self.ramdump, ipc_log_context_list, list_offset)
list_walker.walk(ipc_log_context_list, self.ipc_log_context_list_func)
def parse(self):
self.output_dir = os.path.join(os.path.abspath(
self.ramdump.outdir), "ipc_logging")
if os.path.exists(self.output_dir) is False:
os.makedirs(self.output_dir)
if self.ramdump.minidump and self.ramdump.autodump:
for file in os.listdir(self.ramdump.autodump):
if file.startswith('md_ipc_ctxt'):
ipc_file = os.path.splitext(file)[0].replace("md_", "")
ipc_seg = next((s for s in self.ramdump.elffile.iter_sections() if s.name == ipc_file), None)
if ipc_seg is not None:
ipc_log_context = ipc_seg['sh_addr']
self.ipc_log_context_list_func(ipc_log_context)
else:
self.get_ipc_log_context_list(self.ramdump)

View File

@@ -0,0 +1,299 @@
# Copyright (c) 2012-2015, 2017, 2020 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
import maple_tree
@register_parser('--print-irqs', 'Print all the irq information', shortopt='-i')
class IrqParse(RamParser):
def print_irq_state_3_0(self, ram_dump):
print_out_str(
'=========================== IRQ STATE ===============================')
per_cpu_offset_addr = ram_dump.address_of('__per_cpu_offset')
cpus = ram_dump.get_num_cpus()
irq_desc = ram_dump.address_of('irq_desc')
foo, irq_desc_size = ram_dump.unwind_lookup(irq_desc, 1)
h_irq_offset = ram_dump.field_offset('struct irq_desc', 'handle_irq')
irq_num_offset = ram_dump.field_offset('struct irq_data', 'irq')
hwirq_num_offset = ram_dump.field_offset('struct irq_data', 'hwirq')
if ram_dump.kernel_version >= (4,4,0) :
affinity_offset = ram_dump.field_offset(
'struct irq_common_data', 'affinity')
irq_common_data_offset = ram_dump.field_offset(
'struct irq_desc', 'irq_common_data')
else:
affinity_offset = ram_dump.field_offset(
'struct irq_data', 'affinity')
irq_data_offset = ram_dump.field_offset('struct irq_desc', 'irq_data')
irq_count_offset = ram_dump.field_offset(
'struct irq_desc', 'irq_count')
irq_chip_offset = ram_dump.field_offset('struct irq_data', 'chip')
irq_action_offset = ram_dump.field_offset('struct irq_desc', 'action')
action_name_offset = ram_dump.field_offset('struct irqaction', 'name')
kstat_irqs_offset = ram_dump.field_offset(
'struct irq_desc', 'kstat_irqs')
chip_name_offset = ram_dump.field_offset('struct irq_chip', 'name')
irq_desc_entry_size = ram_dump.sizeof('irq_desc[0]')
cpu_str = ''
for i in ram_dump.iter_cpus():
cpu_str = cpu_str + '{0:10} '.format('CPU{0}'.format(i))
print_out_str('{0:4} {1:12} {2:10} {3} {4:30} {5:10}'
.format('IRQ', 'HWIRQ', 'affinity',
cpu_str, 'Name', 'Chip'))
for i in range(0, irq_desc_size, irq_desc_entry_size):
irqnum = ram_dump.read_word(irq_desc + i + irq_num_offset)
hwirq = ram_dump.read_word(irq_desc + i + hwirq_num_offset)
if ram_dump.kernel_version >= (4,4,0):
affinity = ram_dump.read_int(
irq_desc + irq_common_data_offset + affinity_offset)
else:
affinity = ram_dump.read_int(
irq_desc + affinity_offset)
irqcount = ram_dump.read_word(irq_desc + i + irq_count_offset)
action = ram_dump.read_word(irq_desc + i + irq_action_offset)
kstat_irqs_addr = ram_dump.read_word(
irq_desc + i + kstat_irqs_offset)
irq_stats_str = ''
for j in ram_dump.iter_cpus():
if per_cpu_offset_addr is None:
offset = 0
else:
offset = ram_dump.read_word(per_cpu_offset_addr + 4 * j)
irq_statsn = ram_dump.read_word(kstat_irqs_addr + offset)
irq_stats_str = irq_stats_str + \
'{0:10} '.format('{0}'.format(irq_statsn))
chip = ram_dump.read_word(
irq_desc + i + irq_data_offset + irq_chip_offset)
chip_name_addr = ram_dump.read_word(chip + chip_name_offset)
chip_name = ram_dump.read_cstring(chip_name_addr, 48)
if action != 0:
name_addr = ram_dump.read_word(action + action_name_offset)
name = ram_dump.read_cstring(name_addr, 48)
str = '{0:4} {1:12} {2:10} {3} {4:30} {5:10}'
print_out_str(
str.format(irqnum, hex(hwirq), hex(affinity),
irq_stats_str, name, chip_name))
def shift_to_maxindex(self, shift):
radix_tree_map_shift = 6
if int(self.ramdump.get_config_val("CONFIG_BASE_SMALL")) == 1:
radix_tree_map_shift = 4
radix_tree_map_size = 1 << radix_tree_map_shift
return (radix_tree_map_size << shift) - 1
def is_internal_node(self, addr):
radix_tree_entry_mask = 0x3
if self.ramdump.kernel_version > (4, 20, 0):
radix_tree_internal_node = 0x2
else:
radix_tree_internal_node = 0x1
return (addr & radix_tree_entry_mask) == radix_tree_internal_node
def entry_to_node(self, addr):
if self.ramdump.kernel_version > (4, 20, 0):
return addr & 0xfffffffffffffffd
else:
return addr & 0xfffffffffffffffe
def radix_tree_lookup_element_v2(self, ram_dump, root_addr, index):
if self.ramdump.kernel_version > (4, 20,0 ):
rnode_offset = ram_dump.field_offset('struct xarray', 'xa_head')
rnode_shift_offset = ram_dump.field_offset('struct xa_node', 'shift')
slots_offset = ram_dump.field_offset('struct xa_node', 'slots')
pointer_size = ram_dump.sizeof('struct xa_node *')
else:
rnode_offset = ram_dump.field_offset('struct radix_tree_root', 'rnode')
rnode_shift_offset = ram_dump.field_offset('struct radix_tree_node', 'shift')
slots_offset = ram_dump.field_offset('struct radix_tree_node', 'slots')
pointer_size = ram_dump.sizeof('struct radix_tree_node *')
# if CONFIG_BASE_SMALL=0: radix_tree_map_shift = 6
maxindex = 0
radix_tree_map_shift = 6
radix_tree_map_mask = 0x3f
# if CONFIG_BASE_SMALL=1: radix_tree_map_shift = 4
if int(ram_dump.get_config_val("CONFIG_BASE_SMALL")) == 1:
radix_tree_map_shift = 4
radix_tree_map_mask = 0xf
rnode_addr = ram_dump.read_word(root_addr + rnode_offset)
if self.is_internal_node(rnode_addr):
node_addr = self.entry_to_node(rnode_addr)
shift = ram_dump.read_byte(node_addr + rnode_shift_offset)
maxindex = self.shift_to_maxindex(shift)
if index > maxindex:
return None
while self.is_internal_node(rnode_addr):
parent_addr = self.entry_to_node(rnode_addr)
parent_shift = ram_dump.read_byte(parent_addr + rnode_shift_offset)
offset = (index >> parent_shift) & radix_tree_map_mask
rnode_addr = ram_dump.read_word(parent_addr + slots_offset +
(offset * pointer_size))
if rnode_addr == 0:
return None
return rnode_addr
def radix_tree_lookup_element(self, ram_dump, root_addr, index):
rnode_offset = ram_dump.field_offset('struct radix_tree_root', 'rnode')
if (ram_dump.kernel_version[0], ram_dump.kernel_version[1]) >= (3, 18):
rnode_height_offset = ram_dump.field_offset(
'struct radix_tree_node', 'path')
else:
rnode_height_offset = ram_dump.field_offset(
'struct radix_tree_node', 'height')
slots_offset = ram_dump.field_offset('struct radix_tree_node', 'slots')
pointer_size = ram_dump.sizeof('struct radix_tree_node *')
# if CONFIG_BASE_SMALL=0: radix_tree_map_shift = 6
radix_tree_map_shift = 6
radix_tree_map_mask = 0x3f
radix_tree_height_mask = 0xfff
height_to_maxindex = [0x0, 0x3F, 0x0FFF,
0x0003FFFF, 0x00FFFFFF, 0x3FFFFFFF, 0xFFFFFFFF]
if ram_dump.read_word(root_addr + rnode_offset) & 1 == 0:
if index > 0:
return None
return (ram_dump.read_word(root_addr + rnode_offset) & 0xfffffffffffffffe)
node_addr = ram_dump.read_word(root_addr + rnode_offset) & 0xfffffffffffffffe
height = ram_dump.read_int(node_addr + rnode_height_offset)
if (ram_dump.kernel_version[0], ram_dump.kernel_version[1]) >= (3, 18):
height = height & radix_tree_height_mask
if height > len(height_to_maxindex):
return None
if index > height_to_maxindex[height]:
return None
shift = (height - 1) * radix_tree_map_shift
for h in range(height, 0, -1):
node_addr = ram_dump.read_word(
node_addr + slots_offset + ((index >> shift) & radix_tree_map_mask) * pointer_size)
if node_addr == 0:
return None
shift -= radix_tree_map_shift
return (node_addr & 0xfffffffffffffffe)
def save_irq_desc(self, node, irq_desc):
if node:
irq_desc.append(node)
def dump_sparse_irq_state(self, irq_desc_addr):
irq_desc = self.ramdump.read_datatype(irq_desc_addr, 'struct irq_desc', attr_list=['irq_data', 'irq_common_data', 'irq_count', 'action', 'kstat_irqs'])
irqnum = irq_desc.irq_data.irq
hwirq = irq_desc.irq_data.hwirq
if self.ramdump.kernel_version >= (4,4,0):
affinity = irq_desc.irq_common_data.affinity.bits[0] & 0xFFFFFFFF
else:
affinity = irq_desc.irq_data.affinity.bits[0] & 0xFFFFFFFF
irqcount = irq_desc.irq_count
action = irq_desc.action
irq_stats_str = ''
if irq_desc.kstat_irqs is None:
return
for j in self.ramdump.iter_cpus():
irq_statsn = self.ramdump.read_int(irq_desc.kstat_irqs, cpu=j)
irq_stats_str = irq_stats_str + \
'{0:10} '.format('{0}'.format(irq_statsn))
try:
chip = self.ramdump.read_datatype(irq_desc.irq_data.chip, 'struct irq_chip', attr_list=['name'])
chip_name = self.ramdump.read_cstring(chip.name, 48)
except:
chip_name = "None"
if irq_desc.action != 0:
irqaction = self.ramdump.read_datatype(irq_desc.action, 'struct irqaction', attr_list=['name'])
if not irqaction.name:
name = "None"
else:
name = self.ramdump.read_cstring(irqaction.name, 48)
str = "{0:4} {1:12} {2:10} {3} {4:30} {5:15} " \
"v.v (struct irq_desc *)0x{6:<20x}"
print_out_str(
str.format(irqnum, hex(hwirq), hex(affinity),
irq_stats_str, name, chip_name, irq_desc_addr))
def get_all_irqs_desc(self, ram_dump):
irq_desc_tree = ram_dump.address_of('irq_desc_tree')
nr_irqs = ram_dump.read_int(ram_dump.address_of('nr_irqs'))
major, minor, patch = ram_dump.kernel_version
irq_descs = []
if (major, minor) >= (6, 4):
mt_walk = maple_tree.MapleTreeWalker(ram_dump)
sparse_irqs_addr = ram_dump.address_of('sparse_irqs')
mt_walk.walk(sparse_irqs_addr, self.save_irq_desc, irq_descs)
else:
for i in range(0, nr_irqs):
if (major, minor) >= (4, 9):
irq_desc = self.radix_tree_lookup_element_v2(
ram_dump, irq_desc_tree, i)
else:
irq_desc = self.radix_tree_lookup_element(
ram_dump, irq_desc_tree, i)
if irq_desc is None:
continue
irq_descs.append(irq_desc)
return irq_descs
def print_irq_state_sparse_irq(self, ram_dump):
irq_descs = []
nr_irqs = ram_dump.read_int(ram_dump.address_of('nr_irqs'))
cpu_str = ''
for i in ram_dump.iter_cpus():
cpu_str = cpu_str + '{0:10} '.format('CPU{0}'.format(i))
print_out_str('{0:4} {1:12} {2:10} {3} {4:30} {5:15} {6:20}'\
.format('IRQ', 'HWIRQ', 'affinity', cpu_str,
'Name', 'Chip', 'IRQ Structure'))
if nr_irqs > 50000:
return
irq_descs = self.get_all_irqs_desc(ram_dump)
for i in range(len(irq_descs)):
self.dump_sparse_irq_state(irq_descs[i])
def parse(self):
irq_desc = self.ramdump.address_of('irq_desc')
if self.ramdump.is_config_defined('CONFIG_SPARSE_IRQ'):
self.print_irq_state_sparse_irq(self.ramdump)
if irq_desc is None:
return
self.print_irq_state_3_0(self.ramdump)

View File

@@ -0,0 +1,96 @@
# Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 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.
from parser_util import register_parser, RamParser
@register_parser('--kbootlog', 'Print the kernel boot log', shortopt='-k')
class KBootLog(RamParser):
def __init__(self, *args):
super(KBootLog, self).__init__(*args)
self.wrap_cnt = 0
self.outfile = self.ramdump.open_file('kernel_boot_log.txt')
if (self.ramdump.sizeof('struct printk_log') is None):
self.struct_name = 'struct log'
else:
self.struct_name = 'struct printk_log'
def parse(self):
if self.ramdump.kernel_version >= (5, 10):
self.extract_kernel_boot_log()
else:
self.extract_kboot_log()
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 extract_kernel_boot_log(self):
logbuf_addr = self.ramdump.read_word(self.ramdump.address_of(
'boot_log_buf'))
logbuf_size = self.ramdump.read_u32("boot_log_buf_size")
if logbuf_size is None:
logbuf_pos = self.ramdump.read_word(self.ramdump.address_of(
'boot_log_pos'))
logbuf_left = self.ramdump.read_u32("boot_log_buf_left")
if logbuf_pos is not None and logbuf_left is not None:
logbuf_size = logbuf_pos - logbuf_addr + logbuf_left
else:
logbuf_size = 524288
if logbuf_addr:
data = self.ramdump.read_binarystring(logbuf_addr, logbuf_size)
self.outfile.write(data.decode('ascii', 'ignore').replace('\x00', ''))
else:
self.outfile.write("kernel boot log support is not present\n")
return
def extract_kboot_log(self):
last_idx_addr = self.ramdump.address_of('log_next_idx')
logbuf_addr = None
if self.ramdump.kernel_version >= (5, 4):
logbuf_addr = self.ramdump.read_word(self.ramdump.address_of(
'boot_log_buf'))
elif self.ramdump.kernel_version >= (4, 19):
logbuf_addr = self.ramdump.read_word(self.ramdump.address_of(
'init_log_buf'))
if logbuf_addr is None:
self.outfile.write("kernel boot log support is not present\n")
return
time_offset = self.ramdump.field_offset(self.struct_name, 'ts_nsec')
text_len_offset = self.ramdump.field_offset(self.struct_name,
'text_len')
log_size = self.ramdump.sizeof(self.struct_name)
first_idx = 0
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 < 1:
timestamp = self.ramdump.read_dword(curr_idx + time_offset)
text_len = self.ramdump.read_u16(curr_idx + text_len_offset)
text_str = self.ramdump.read_cstring(curr_idx + log_size, text_len)
for partial in text_str.split('\n'):
if text_len == 0:
break
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)

View File

@@ -0,0 +1,107 @@
# Copyright (c) 2017, 2020 The Linux Foundation. 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 os
import re
from parser_util import register_parser, RamParser
file_names = ['MSM_DUMP_DATA_L1_DATA_CACHE_0x0',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x1',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x2',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x3',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x4',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x5',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x6',
'MSM_DUMP_DATA_L1_DATA_CACHE_0x7']
@register_parser('--l1-compare', 'Compare L1 Cache Data with DDR contents')
class L1_Cache_Compare(RamParser):
def parse(self):
with self.ramdump.open_file('l1_cache.txt') as out_l1_cache:
for file_name in file_names:
file_path = os.path.join(self.ramdump.outdir, file_name)
if os.path.isfile(file_path):
out_l1_cache.write('\n-----begin ' + file_name + '-----\n')
addr = None
values_from_file = None
values_from_dump = None
fin = self.ramdump.open_file(file_path, 'r')
line_no = 0
for line in fin:
line_no += 1
if line == '':
continue
elif re.match('^Way Set P MOESI RAW_MOESI N.*', line):
continue
elif len(line) >= 195:
colm = line.split()
if len(colm) < 28:
out_l1_cache.write('Unexepected file format\n')
break
# Read address value from file
addr = colm[6]
# Read values from file
values_from_file = []
for i in range(11, 27):
values_from_file.append(colm[i])
# Read values from dump
values_from_dump = []
temp_addr = int(addr, 16)
val = None
if temp_addr > self.ramdump.phys_offset:
for i in range(0, 16):
try:
val = self.ramdump.read_physical(
temp_addr, 4)
except:
out_l1_cache.write(
'Exception while reading {0:x}'
.format(temp_addr))
val = val[::-1]
val = val.encode('hex')
values_from_dump.append(val)
temp_addr += 4
# compare both values
if values_from_dump != [] and \
values_from_dump != values_from_file:
out_l1_cache.write(
'phy addr: {0} Way:{1} Set:{2} P:{3} MOESI:{4}'
.format(addr,
colm[0],
colm[1],
colm[2],
colm[3]) +
'\n')
out_l1_cache.write('Cache content: ' +
' '.join(values_from_file) +
'\n')
out_l1_cache.write('DDR content : ' +
' '.join(values_from_dump) +
'\n\n')
fin.close()
out_l1_cache.write('------end '+file_name+'------\n')

View File

@@ -0,0 +1,70 @@
# Copyright (c) 2023-2024 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.
from parser_util import register_parser, RamParser, cleanupString
import linux_list as llist
@register_parser('--print-devices', 'Print devices info')
class DevicesList(RamParser):
def __init__(self, ramdump):
self.ramdump = ramdump
self.kobj_offset = self.ramdump.field_offset('struct device', 'kobj')
self.driver_data_offset = self.ramdump.field_offset('struct device', 'driver_data')
self.dev_busname_offset = self.ramdump.field_offset('struct device', 'bus')
self.dma_ops_offset = self.ramdump.field_offset('struct device', 'dma_ops')
self.archdata_offset = self.ramdump.field_offset('struct device', 'archdata')
self.entry_offset = self.ramdump.field_offset('struct kobject', 'entry')
self.name_offset = self.ramdump.field_offset('struct kobject', 'name')
self.device_lists = []
def list_func(self, device, fout):
kobj = self.ramdump.read_word(device + self.kobj_offset)
name = cleanupString(
self.ramdump.read_cstring(kobj + self.name_offset, 128))
if name == None or name == "":
return
driver_data = self.ramdump.read_word(device + self.driver_data_offset)
bus_name_addr = self.ramdump.read_word(device + self.dev_busname_offset)
bus_name = self.ramdump.read_cstring(self.ramdump.read_word(bus_name_addr))
dma_ops = self.ramdump.read_structure_field(device, 'struct device', 'dma_ops')
cma_area = self.ramdump.read_structure_field(device, 'struct device', 'cma_area')
archdata = (device + self.archdata_offset)
cma_name = ''
if cma_area != 0:
cma_name_addr_offset = self.ramdump.field_offset('struct cma', 'name')
cma_name = self.ramdump.read_cstring(cma_area + cma_name_addr_offset, 48)
a_ops_name = self.ramdump.unwind_lookup(dma_ops)
dma_ops_name = ''
if a_ops_name is not None:
dma_ops_name, a = a_ops_name
a_ops_string = '[{0}]'.format(dma_ops_name)
try:
if fout != None:
print("0x%x %-100s %-16s 0x%-32x 0x%-16x %s %-48s"
% (device, name, bus_name, driver_data, cma_area, cma_name, a_ops_string), file = fout)
except Exception as e: print_out_str(e)
self.device_lists.append([device, name, bus_name, driver_data, archdata])
def get_device_list(self, fout = None):
devices_kset_addr = self.ramdump.address_of('devices_kset')
list_head = devices_kset_addr
list_offset = self.kobj_offset + self.entry_offset
list_walker = llist.ListWalker(self.ramdump, list_head, list_offset)
list_walker.walk(list_head, self.list_func, fout)
return self.device_lists
def parse(self):
fout = open(self.ramdump.outdir + "/devices.txt", "w")
print("v.v (struct device) name bus_name driver_data v.v (struct cma) dma_ops \n", file=self.f)
self.get_device_list(fout)
fout.close()

View File

@@ -0,0 +1,220 @@
# Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2023-2024 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.
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
def test_bit(nr, addr, ramdump, my_task_out):
BITS_PER_LONG = 64
if not ramdump.arm64:
BITS_PER_LONG = 32
index = int(nr / BITS_PER_LONG)
data = ramdump.read_ulong(addr + index * ramdump.sizeof('unsigned long'))
#my_task_out.write("\ntest_bit: index = 0x{:x}, data: 0x{:x}".format(index, data))
if 1 & (data >> (nr & (BITS_PER_LONG - 1))):
#my_task_out.write("\ntest bit returned true")
return True
#my_task_out.write("\ntest bit returned false")
return False
def parse_held_locks(ramdump, task, my_task_out):
task_held_locks_offset = ramdump.field_offset('struct task_struct', 'held_locks')
held_locks = task + task_held_locks_offset
sizeof_held_lock = ramdump.sizeof('struct held_lock')
sizeof_lock_class = ramdump.sizeof('struct lock_class')
lock_classes_in_use_bitmap = ramdump.address_of('lock_classes_in_use')
lock_classes = ramdump.address_of('lock_classes')
#my_task_out.write("lock_classes : {:x} , lock_classes_bitmap : {:x}, lock_class_size : {:x}".format(lock_classes, lock_classes_in_use_bitmap, sizeof_lock_class))
task_lockdep_depth = ramdump.read_structure_field(task, 'struct task_struct', 'lockdep_depth')
my_task_out.write('\nlockdep_depth: {0}\n'.format(hex(task_lockdep_depth)))
for i in range (0, task_lockdep_depth):
held_lock_indx = held_locks + (i * sizeof_held_lock)
hl_prev_chain_key = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'prev_chain_key')
if not hl_prev_chain_key:
break
hl_acquire_ip = ramdump.read_structure_field(held_lock_indx, 'struct held_lock','acquire_ip')
hl_acquire_ip_caller = ramdump.read_structure_field(held_lock_indx, 'struct held_lock','acquire_ip_caller')
if hl_acquire_ip_caller is None:
hl_acquire_ip_caller = 0x0
hl_instance = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'instance')
hl_nest_lock = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'nest_lock')
if (ramdump.is_config_defined('CONFIG_LOCK_STAT')):
hl_waittime_stamp = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'waittime_stamp')
hl_holdtime_stamp = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'holdtime_stamp')
hl_class_idx_full = hl_class_idx = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'class_idx')
hl_class_idx = hl_class_idx_full & 0x00001FFF
hl_name = None
if test_bit(hl_class_idx, lock_classes_in_use_bitmap, ramdump, my_task_out):
#my_task_out.write("\nLock class stuct @ {:x}".format(lock_classes + sizeof_lock_class*hl_class_idx))
hl_name = ramdump.read_structure_field(lock_classes + sizeof_lock_class*hl_class_idx, 'struct lock_class', 'name')
hl_name = ramdump.read_cstring(hl_name)
else:
continue
lock_type = ramdump.type_of(hl_name)
hl_irq_context = (hl_class_idx_full & 0x00006000) >> 13
hl_trylock = (hl_class_idx_full & 0x00008000) >> 15
# 0 - exclusive
# 1 - shared
# 2 - shared_recursive
hl_read = (hl_class_idx_full & 0x00030000) >> 16
if hl_read:
# Handling for percpu_rw_semaphore
# if the task in writer is not NULL, it means that the reader is blocking the writer
try:
if "struct percpu_rw_semaphore" in lock_type:
my_task_out.write("\n lock type : {}".format(lock_type))
lock_struct = ramdump.container_of(hl_instance, lock_type, 'dep_map')
my_task_out.write("\n lock addr : 0x{:x}".format(lock_struct))
writer = lock_struct + ramdump.field_offset("struct percpu_rw_semaphore", "writer")
my_task_out.write("\n writer : 0x{:x}".format(writer))
writer_task = ramdump.read_structure_field(writer, 'struct rcuwait', 'task')
my_task_out.write("\n writer task : 0x{:x}".format(writer_task))
if writer_task != 0:
my_task_out.write("\n the reader task [Process: {0}, Pid: {1}] is blocking the writer task [Process: {2}, Pid: {3}]".format(
cleanupString(ramdump.read_cstring(task + ramdump.field_offset("struct task_struct", "comm"), 16)),
ramdump.read_int(task + ramdump.field_offset("struct task_struct", "pid")),
cleanupString(ramdump.read_cstring(writer_task + ramdump.field_offset("struct task_struct", "comm"), 16)),
ramdump.read_int(writer_task + ramdump.field_offset("struct task_struct", "pid"))))
except Exception as err:
my_task_out.write("\nError encountered while resolving read lock ownership")
my_task_out.write("\n{}\n".format(err))
pass
hl_check = (hl_class_idx_full & 0x00040000) >> 18
hl_hardirqs_off = (hl_class_idx_full & 0x00080000) >> 19
hl_references = (hl_class_idx_full & 0xFFF00000) >> 20
hl_pin_count = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'pin_count')
if (ramdump.is_config_defined('CONFIG_LOCKDEP_CROSSRELEASE')):
hl_gen_id = ramdump.read_structure_field(held_lock_indx, 'struct held_lock', 'gen_id')
hl_acquire_ip_name_func = 'n/a'
wname = ramdump.unwind_lookup(hl_acquire_ip)
if wname is not None:
hl_acquire_ip_name_func, a = wname
my_task_out.write(
'\nheld_locks[{0}] [0x{1:x}]:\
\n\tprev_chain_key = {2},\
\n\tacquire_ip = {3},\
\n\tacquire_ip_caller = {4},\
\n\tinstance = {5},\
\n\tnest_lock = {6}\
\n\tclass_idx = {7},\
\n\tirq_context = {8},\
\n\ttrylock = {9},\
\n\tread = {10},\
\n\tcheck = {11},\
\n\thardirqs_off = {12},\
\n\treferences = {13},\
\n\tpin_count = {14},\
\n\tname = {15},\
\n\tacquire_ip_func = {16}'.format(
i, held_lock_indx,
hex(hl_prev_chain_key),
hex(hl_acquire_ip),
hex(hl_acquire_ip_caller),
hex(hl_instance),
hex(hl_nest_lock),
hex(hl_class_idx),
hex(hl_irq_context),
hex(hl_trylock),
hex(hl_read),
hex(hl_check),
hex(hl_hardirqs_off),
hex(hl_references),
hex(hl_pin_count),
hl_name,
hl_acquire_ip_name_func))
if (ramdump.is_config_defined('CONFIG_LOCK_STAT')):
my_task_out.write(
'\n\twaittime_stamp = {0}s\
\n\tholdtime_stamp = {1}s'.format(
(hl_waittime_stamp / 1000000000.0),
(hl_holdtime_stamp / 1000000000.0)))
"""
#define LOCK_CONTENDED(_lock, try, lock) \
do { \
if (!try(_lock)) { \
lock_contended(&(_lock)->dep_map, _RET_IP_); \
lock(_lock); \
} \
lock_acquired(&(_lock)->dep_map, _RET_IP_); \
} while (0)
void __sched down_read(struct rw_semaphore *sem)
{
might_sleep();
rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
}
"""
# for rwlock_t and rw_semaphore, lock operation is expanded by macro LOCK_CONTENDED
# there are 3 cases: here assume holdtime_stamp value is initialized to 100 and waittime_stamp will be initialized to 0.
# 1. if success to acquire lock in try(_lock), waittime_stamp value won't be updated. also, holdtime_stamp will not be updated in lock_acquired().
# waittime_stamp: 0
# holdtime_stamp: 100
# 2. if fail to acquire lock in try(_lock), waittime_stamp will be updated in lock_contended(). here assume it's updated to 105.
# a. if success to acquire lock in lock(_lock), holdtime_stamp will be updated in lock_acquired(). here assume it's updated to 110.
# waittime_stamp: 105
# holdtime_stamp: 110
# b. if fail to acquire lock in lock(_lock), will stuck at lock(_lock).
# waittime_stamp: 105
# holdtime_stamp: 100
# based on above, we can say that if waittime_stamp is greater than holdtime_stamp, the lock is not acquired.
if lock_type and ('struct rwlock_t' in lock_type or 'struct rw_semaphore' in lock_type):
if hl_waittime_stamp > hl_holdtime_stamp:
lock_acquired = 0
else:
lock_acquired = 1
my_task_out.write(
'\n\tlock_type = {0}\
\n\tlock_acquired = {1}'.format(lock_type, lock_acquired))
if (ramdump.is_config_defined('CONFIG_LOCKDEP_CROSSRELEASE')):
my_task_out.write(
'\n\tgen_id = {0}'.format( hex(hl_gen_id)))
my_task_out.write('\n\n')
def parse_mytaskstruct(ramdump):
my_task_out = ramdump.open_file('lockdep.txt')
my_task_out.write('============================================\n')
task_comm_offset = ramdump.field_offset('struct task_struct', 'comm')
task_pid_offset = ramdump.field_offset('struct task_struct', 'pid')
for process in ramdump.for_each_process():
for task in ramdump.for_each_thread(process):
thread_comm = task + task_comm_offset
thread_task_name = cleanupString(ramdump.read_cstring(thread_comm, 16))
thread_pid = task + task_pid_offset
thread_task_pid = ramdump.read_int(thread_pid)
my_task_out.write('\nProcess: {0}, [Pid: {1} Task: 0x{2:x}]\n'.format(
thread_task_name, thread_task_pid, task))
parse_held_locks(ramdump, task, my_task_out)
my_task_out.write('============================================\n')
my_task_out.close()
print_out_str('----wrote lockdep held locks info')
@register_parser('--lockdep-heldlocks', 'Extract lockdep held locks info per task from ramdump')
class LockdepParser(RamParser):
def parse(self):
if (self.ramdump.is_config_defined('CONFIG_LOCKDEP')):
print_out_str('----dumping lockdep held locks info')
parse_mytaskstruct(self.ramdump)
else:
print_out_str('CONFIG_LOCKDEP not present')

View File

@@ -0,0 +1,378 @@
# Copyright (c) 2019-2020, 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.
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
import struct
from parsers.properties import Properties
import traceback
from utasklib import UTaskLib
from utasklib import ProcessNotFoundExcetion
@register_parser('--logcat', 'Extract logcat logs from ramdump ')
class Logcat(RamParser):
LOGCAT_BIN = "logcat.bin"
def __init__(self, *args):
super(Logcat, self).__init__(*args)
self.f_path_offset = self.ramdump.field_offset('struct file', 'f_path')
self.dentry_offset = self.ramdump.field_offset('struct path', 'dentry')
self.d_iname_offset = self.ramdump.field_offset('struct dentry', 'd_iname')
self.limit_size = int("0x20000000", 16)
self.vma_list = []
def swap64(self, val):
return struct.unpack("<Q", struct.pack(">Q", val))[0]
def empty_vma_list(self):
for i in range(len(self.vma_list)):
del self.vma_list[0]
return
def get_logd_cnt_and_addr(self, logdmap):
logdcount = 0
logdaddr = 0
# 3 logd vm_area, then heap
# 4 logd + 1 bss, then heap in case of Android Q
while logdmap != 0:
tmpstartVm = self.ramdump.read_structure_field(
logdmap, 'struct vm_area_struct', 'vm_start')
file = self.ramdump.read_structure_field(
logdmap, 'struct vm_area_struct', 'vm_file')
if file != 0:
dentry = self.ramdump.read_word(file + self.f_path_offset +
self.dentry_offset)
file_name = cleanupString(self.ramdump.read_cstring(
dentry + self.d_iname_offset, 16))
if file_name == "logd":
logdcount = logdcount + 1
if logdcount == 1:
logdaddr = self.ramdump.read_structure_field(
logdmap, 'struct vm_area_struct', 'vm_start')
logdaddr = (logdaddr & 0xFFFF000000) >> 0x18
elif logdaddr != 0:
checkaddr = tmpstartVm >> 0x18
if checkaddr == logdaddr:
logdcount = logdcount + 1
logdmap = self.ramdump.read_structure_field(
logdmap, 'struct vm_area_struct', 'vm_next')
if logdcount < 3:
print_out_str("found logd region {0} is smaller than expected. set " \
"logdcount to 3".format(logdcount))
logdcount = 3
return logdcount, logdaddr
def flattened_range(self, mmap, logdcount, logdaddr):
min = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_start')
max = 0
count = 0
vma_info = {}
while mmap != 0:
tmpstartVm = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_start')
if count == logdcount:
min = tmpstartVm
count = count + 1 # do not enter here again
file = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_file')
if file != 0:
dentry = self.ramdump.read_word(file + self.f_path_offset +
self.dentry_offset)
file_name = cleanupString(self.ramdump.read_cstring(
dentry + self.d_iname_offset, 16))
if file_name == "logd":
count = count + 1
if file_name.find("linker") == 0:
va_end = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_end')
if va_end > max:
max = va_end
elif logdaddr != 0:
checkaddr = (tmpstartVm) >> 0x18
if checkaddr == logdaddr:
count = count + 1
mmap = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_next')
size = max - min
if size > self.limit_size:
size = self.limit_size
vma_info['header'] = None
vma_info['start'] = min
vma_info['size'] = size
self.vma_list.append(vma_info)
return
def scattered_range(self, mmap, logdcount, logdaddr):
min = 0
max = 0
count = 0
prev_vmstart = 0
prev_vmend = 0
total_size = 0
store_offset = 0
meta_size = 32
magic = 0xCECEC0DE
while mmap != 0:
vm_start = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_start')
vm_end = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_end')
vm_flags = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_flags')
vm_file = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_file')
set_min = False
file_name = None
if vm_file != 0:
dentry = self.ramdump.read_word(vm_file + self.f_path_offset +
self.dentry_offset)
file_name = cleanupString(self.ramdump.read_cstring(
dentry + self.d_iname_offset, 16))
if count == logdcount:
if vm_file != 0 or vm_flags & 0x3 != 0x3:
mmap = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_next')
continue
min = vm_start
count = count + 1 # do not enter here again
elif count < logdcount:
if file_name == "logd":
count = count + 1
elif logdaddr != 0:
checkaddr = (vm_start) >> 0x18
if checkaddr == logdaddr:
count = count + 1
else:
if min == 0:
if vm_file == 0 and vm_flags & 0x3 == 0x3:
min = vm_start
mmap = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_next')
if mmap != 0:
prev_vmstart = vm_start
prev_vmend = vm_end
continue
if vm_file != 0 or vm_flags & 0x3 != 0x3:
max = prev_vmend
elif prev_vmend != 0 and prev_vmend != vm_start:
max = prev_vmend
set_min = True
elif prev_vmend != 0 and prev_vmend == vm_start:
max = 0
mmap = self.ramdump.read_structure_field(
mmap, 'struct vm_area_struct', 'vm_next')
if mmap != 0:
prev_vmstart = vm_start
prev_vmend = vm_end
if min != 0 and max != 0 and min < max:
vma_info = {}
size = max - min
total_size = total_size + size
vma_info['header'] = "{0:016x}{1:016x}{2:016x}{3:016x}".format(
self.swap64(magic), self.swap64(min), self.swap64(size),
self.swap64(store_offset + meta_size))
vma_info['start'] = min
vma_info['size'] = size
self.vma_list.append(vma_info)
store_offset = store_offset + size + meta_size
min = 0
max = 0
if set_min is True:
min = vm_start
if mmap == 0: #last
if vm_start != 0 and prev_vmend != vm_start and vm_file == 0 and vm_flags & 0x3 == 0x3:
min = vm_start
max = vm_end
elif vm_start != 0 and prev_vmend == vm_start:
max = vm_end
if min != 0 and max != 0 and min < max:
vma_info = {}
size = max - min
total_size = total_size + size
vma_info['header'] = "{0:016x}{1:016x}{2:016x}{3:016x}".format(
self.swap64(magic), self.swap64(min), self.swap64(size),
self.swap64(store_offset + meta_size))
vma_info['start'] = min
vma_info['size'] = size
self.vma_list.append(vma_info)
store_offset = store_offset + size + meta_size
if total_size > self.limit_size:
print_out_str("size({0:d}) is too big".format(total_size))
self.empty_vma_list()
return
def get_range(self, mmap, logdcount, logdaddr):
if self.ramdump.arm64:
self.scattered_range(mmap, logdcount, logdaddr)
else:
self.flattened_range(mmap, logdcount, logdaddr)
return
def generate_bin(self, mmu):
self.ramdump.remove_file(self.LOGCAT_BIN)
if len(self.vma_list) == 0:
print_out_str("Failed to generate "+self.LOGCAT_BIN)
else:
print_out_str(self.LOGCAT_BIN+" base address is {0:x}".format(self.vma_list[0]['start']))
with self.ramdump.open_file(self.LOGCAT_BIN, 'ab') as out_file:
for vma_info in self.vma_list:
min = vma_info['start']
size = vma_info['size']
header = vma_info['header']
if header is not None:
header = bytearray.fromhex(header)
out_file.write(header)
max = min + size
while(min < max):
phys = mmu.virt_to_phys(min)
if phys is None:
min = min + 0x1000
out_file.write(b'\x00' * 0x1000)
continue
out_file.write(self.ramdump.read_physical(phys, 0x1000))
min = min + 0x1000
return
def generate_logcat_bin(self, taskinfo):
'''
generate logcat.bin for the older android version
param taskinfo: utasklib.UTaskInfo
'''
meta_size = 32
magic = 0xCECEC0DE
store_offset = 0
mm_offset = self.ramdump.field_offset('struct task_struct', 'mm')
mm_addr = self.ramdump.read_word(taskinfo.task_addr + mm_offset)
mmap = self.ramdump.read_structure_field(mm_addr, 'struct mm_struct',
'mmap')
if mmap:
logdcount, logdaddr = self.get_logd_cnt_and_addr(mmap)
self.get_range(mmap, logdcount, logdaddr)
self.generate_bin(taskinfo.mmu)
else:
for vma in taskinfo.vmalist:
if vma.file != 0 or vma.flags & 0b11 != 0b11:
continue
vma_info = {}
size = vma.vm_end - vma.vm_start
vma_info['header'] = "{0:016x}{1:016x}{2:016x}{3:016x}".format(
self.swap64(magic), self.swap64(vma.vm_start), self.swap64(size),
self.swap64(store_offset + meta_size))
vma_info['start'] = vma.vm_start
vma_info['size'] = size
self.vma_list.append(vma_info)
store_offset = store_offset + size + meta_size
self.generate_bin(taskinfo.mmu)
def is_LE_process(self, taskinfo):
for vma in taskinfo.vmalist:
if vma.file_name == "liblog.so.0.0.0":
return True
return False
def is_openwrt_process(self, taskinfo):
for vma in taskinfo.vmalist:
if "libubus.so." in vma.file_name:
return True
return False
def parse(self):
if self.ramdump.logcat_limit_time == 0:
self.__parse()
else:
from func_timeout import func_timeout
print_out_str("Limit logcat parser running time to {}s".format(self.ramdump.logcat_limit_time))
func_timeout(self.ramdump.logcat_limit_time, self.__parse)
def __parse(self):
try:
try:
taskinfo = UTaskLib(self.ramdump).get_utask_info("logd")
except ProcessNotFoundExcetion:
print_out_str("logd process is not started")
return
propertyParser = Properties(self.ramdump)
ver = -1
try:
# generate system/vendor properties to Properties.txt
propertyParser.parse()
for name, value in propertyParser.proplist:
if name == "ro.build.version.sdk" or name == "ro.vndk.version":
ver = int(value)
except:
ver = -1
print_out_str("Current sdk version is "+ str(ver))
if ver >= 31: # Android S
from parsers.logcat_v3 import Logcat_v3
logcat = Logcat_v3(self.ramdump, taskinfo)
try:
is_success = logcat.parse()
except Exception as e:
is_success = False
print_out_str("logcat_v3 parser failed " + str(e))
traceback.print_exc()
if is_success:
print_out_str("logcat_v3 parse logcat success")
return
try:
from parsers.logcat_v3 import Logcat_vma
logcat = Logcat_vma(self.ramdump, taskinfo)
is_success = logcat.parse()
except Exception as e:
is_success = False
print_out_str("logcat_vma parser failed" + str(e))
traceback.print_exc()
if is_success:
print_out_str("logcat_vma parse logcat success")
else:
# generate logcat.bin when both logcat_v3 and logcat_vma parse failed
self.generate_logcat_bin(taskinfo)
elif self.is_LE_process(taskinfo):
print_out_str("LE ramdump")
from parsers.logcat_m import Logcat_m
#parser to supprot Android M
logcat = Logcat_m(self.ramdump, taskinfo)
logcat.parse()
elif self.is_openwrt_process(taskinfo):
print_out_str("Openwrt ramdump")
from parsers.logcat_openwrt import Logcat_openwrt
#parser to supprot openwrt platform
logcat = Logcat_openwrt(self.ramdump, taskinfo)
logcat.parse()
else:
self.generate_logcat_bin(taskinfo)
except Exception as result:
print_out_str(str(result))
traceback.print_exc()

View File

@@ -0,0 +1,261 @@
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause-Clear
# 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.
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
import struct
from utasklib import UTaskLib
from utasklib import ProcessNotFoundExcetion
import datetime
class Logcat_m(RamParser):
'''
Logcat parser to support extract logcat from Androd M ram dump
'''
LOG_NAME = [ "main", "radio", "events", "system", "crash", "kernel"]
#log id
LOG_ID_MIN = 0
LOG_ID_MAIN = 0
LOG_ID_RADIO = 1
LOG_ID_EVENTS = 2
LOG_ID_SYSTEM = 3
LOG_ID_CRASH = 4
LOG_ID_KERNEL = 5
LOG_ID_MAX = 5
#log level
ANDROID_LOG_DEFAULT =1
ANDROID_LOG_VERBOSE = 2
ANDROID_LOG_DEBUG = 3
ANDROID_LOG_INFO = 4
ANDROID_LOG_WARN = 5
ANDROID_LOG_ERROR = 6
ANDROID_LOG_FATAL = 7
ANDROID_LOG_SILENT = 8
def __init__(self, ramdump, logd_task):
super().__init__(ramdump)
self.vmas = []
self.logd_task = logd_task
if self.ramdump.arm64:
self.addr_length = 8
else:
self.addr_length = 4
def find_logbuf_elem_list_addr(self, vma):
vma_size = vma["size"]
vma_data = vma["data"]
offset = 0
is_chunk = False
while offset < vma_size:
is_chunk = self.is_logbuf_elem_list_addr(vma_data, offset)
if is_chunk:
break
offset = offset + 4
if is_chunk:
return offset
return 0
def parse(self):
startTime = datetime.datetime.now()
self.get_vmas_with_rw()
# find address of std::list<LogBufferElement *> mLogElements
logbuf_addr = 0
for vma in self.vmas:
offset = self.find_logbuf_elem_list_addr(vma)
if offset != 0:
logbuf_addr = vma["vmstart"]+offset
print("found address",hex(logbuf_addr))
break
if logbuf_addr:
print_out_str("LogBuffer address 0x%x"% logbuf_addr)
self.process_logbuf_and_save(logbuf_addr)
print_out_str("Logcat parse cost "+str((datetime.datetime.now()-startTime).total_seconds())+" s")
else:
print_out_str("LogBuffer address was not found")
def is_logbuf_elem_list_addr(self, vma_data, offset):
if offset+24 > len(vma_data):
return False
nodes = struct.unpack('<QQQ', vma_data[offset:offset+24])
tail_node_addr = nodes[0]
head_node_addr = nodes[1]
list_count = nodes[2]
if tail_node_addr ==0 or head_node_addr ==0:
return False
next_node_addr = head_node_addr
prev_node = self.read_bytes(head_node_addr, self.addr_length) # prev_node = next_node_addr->prev
if prev_node == 0:
return False
if list_count == 0 and head_node_addr == tail_node_addr: # empty list
return False
index = 0
while next_node_addr != 0 and index < list_count:
next_prev_node = self.read_bytes(next_node_addr, self.addr_length)
if not next_prev_node or next_prev_node != prev_node:
return False
current_node_addr = next_node_addr + self.addr_length *2
current_node = self.read_bytes(current_node_addr, self.addr_length)
is_chunk = self.is_log_elem(current_node)
if not is_chunk:
return False
if next_node_addr == tail_node_addr: # loop complete
return True
prev_node = next_node_addr
next_node_addr = self.read_bytes(next_node_addr + self.addr_length, self.addr_length)
index = index +1
return False
def is_log_elem(self, addr):
log_id = self.read_bytes(addr + 0x8, 4)
uid = self.read_bytes(addr + 0xc, 4)
pid = self.read_bytes(addr + 0x10, 4)
msg_addr = self.read_bytes(addr + 0x18, 8)
msg_len = self.read_bytes(addr + 0x20, 2)
dropped = msg_len
if log_id < 0 or log_id > self.LOG_ID_MAX:
return False
if msg_len < 1 or msg_len > 4068: # max_payload
return False
if (pid < 0 or pid >= 65536) or (uid < 0 or uid >= 65536):
return False
if (log_id == 2) or (dropped == 1):
return True
else:
priority = self.read_bytes(msg_addr, 1)
if priority > self.ANDROID_LOG_SILENT or priority < self.ANDROID_LOG_DEFAULT:
return False
return True
def process_logbuf_and_save(self, logbuf_addr):
head_node_addr = self.read_bytes(logbuf_addr + self.addr_length, self.addr_length)
list_count = self.read_bytes(logbuf_addr + self.addr_length*2, self.addr_length)
next_node_addr = head_node_addr
index = 0
log_file = [0] * (self.LOG_ID_MAX + 1)
while index <= self.LOG_ID_MAX:
log_file[index] = self.ramdump.open_file(self.get_output_filename(index))
index = index + 1
index = 0
log_list = []
while next_node_addr != 0 and index < list_count:
current_node = self.read_bytes(next_node_addr + self.addr_length *2, self.addr_length)
self.save_log_elem(current_node, log_list)
next_node_addr = self.read_bytes(next_node_addr + self.addr_length, self.addr_length)
index = index +1
log_list.sort(key=lambda x: float(x['timestamp']))
for item in log_list:
log_file[item["logid"]].write(item["msg"])
index = 0
while index <= self.LOG_ID_MAX:
log_file[index].close()
index = index + 1
return
def save_log_elem(self, addr, log_list):
log_id = self.read_bytes(addr + 0x8, 4)
uid = self.read_bytes(addr + 0xc, 4)
pid = self.read_bytes(addr + 0x10, 4)
tid = self.read_bytes(addr + 0x14, 4)
msg_addr = self.read_bytes(addr + 0x18, 8)
msg_len = self.read_bytes(addr + 0x20, 2)
dropped = msg_len
tv_sec = self.read_bytes(addr + 0x30, 4)
tv_nsec = self.read_bytes(addr + 0x34, 4)
if (log_id == 2) or (dropped == 1):
return
if msg_len == 0:
return
priority = self.read_bytes(msg_addr, 1)
msg = self.read_binary(msg_addr + 1, msg_len - 1)
if not msg:
return
msgList = msg.decode('ascii', 'ignore').split('\0')
timestamp = self.format_time(tv_sec, tv_nsec)
if len(msgList) >= 2:
time_f = float("{}.{}".format(tv_sec,tv_nsec))
msg = "%s %5d %5d %5d %c %-8.*s: %s\n" % (timestamp, uid, pid, tid,
self.filter_pri_to_char(priority), len(msgList[0]),
cleanupString(msgList[0].strip()),
cleanupString(msgList[1].strip()))
log = {}
log['timestamp'] = time_f
log['msg'] = msg
log["logid"] = log_id
log_list.append(log)
def filter_pri_to_char(self, pri) :
if pri == self.ANDROID_LOG_VERBOSE:
return 'V'
elif pri == self.ANDROID_LOG_DEBUG:
return 'D'
elif pri == self.ANDROID_LOG_INFO:
return 'I'
elif pri == self.ANDROID_LOG_WARN:
return 'W'
elif pri == self.ANDROID_LOG_ERROR:
return 'E'
elif pri == self.ANDROID_LOG_FATAL:
return 'F'
elif pri == self.ANDROID_LOG_SILENT:
return 'S'
else:
return '?'
def get_vmas_with_rw(self):
'''
return vma list with read+write permissions
'''
for vma in self.logd_task.vmalist:
if vma.flags & 0b11 != 0b11:
continue
item = {}
item["vmstart"] = vma.vm_start
item["size"] = vma.vm_end - vma.vm_start
item["data"] = self.read_binary(item["vmstart"], item["size"])
self.vmas.append(item)
def read_bytes(self, addr, len):
val = UTaskLib.read_bytes(self.ramdump, self.logd_task.mmu, addr, len)
if not val:
val = 0
return val
def read_binary(self, addr, len):
return UTaskLib.read_binary(self.ramdump, self.logd_task.mmu, addr, len)
def get_output_filename(self, log_id):
if log_id >= self.LOG_ID_MIN and log_id <= self.LOG_ID_MAX:
return "Logcat_{}.txt".format(self.LOG_NAME[log_id])
else:
return None
def format_time(self, tv_sec, tv_nsec):
tv_nsec = str(tv_nsec // 1000000)
tv_nsec = str(tv_nsec).zfill(3)
date = datetime.datetime.utcfromtimestamp(tv_sec)
timestamp = date.strftime("%m-%d %H:%M:%S") + '.' + tv_nsec
return timestamp

View File

@@ -0,0 +1,261 @@
# Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
# Copyright (C) 2013 John Crispin <blogic@openwrt.org>
# Copyright (c) 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.
from parser_util import RamParser
from print_out import print_out_str
from utasklib import UTaskLib
import datetime
import logging
import os
class Logcat_openwrt(RamParser):
'''
Logcat parser to support extract logcat from Openwrt platform
'''
LOG_EMERG = 0
LOG_ALERT = 1
LOG_CRIT = 2
LOG_ERR = 3
LOG_WARNING = 4
LOG_NOTICE = 5
LOG_INFO = 6
LOG_DEBUG = 7
LOG_PRIMASK = 7
INTERNAL_NOPRI = 0x10
LOG_KERN = (0 << 3)
LOG_USER = (1 << 3)
LOG_MAIL = (2 << 3)
LOG_DAEMON = (3 << 3)
LOG_AUTH = (4 << 3)
LOG_SYSLOG = (5 << 3)
LOG_LPR = (6 << 3)
LOG_NEWS = (7 << 3)
LOG_UUCP = (8 << 3)
LOG_CRON = (9 << 3)
LOG_AUTHPRIV = (10 << 3)
LOG_FTP = (11 << 3)
LOG_LOCAL0 = (16 << 3)
LOG_LOCAL1 = (17 << 3)
LOG_LOCAL2 = (18 << 3)
LOG_LOCAL3 = (19 << 3)
LOG_LOCAL4 = (20 << 3)
LOG_LOCAL5 = (21 << 3)
LOG_LOCAL6 = (22 << 3)
LOG_LOCAL7 = (23 << 3)
LOG_NFACILITIES = 24
INTERNAL_MARK = (LOG_NFACILITIES << 3)
LOG_PID = 0x01
LOG_CONS = 0x02
LOG_ODELAY = 0x04
LOG_NDELAY = 0x08
LOG_NOWAIT = 0x10
LOG_PERROR = 0x20
LOG_FACMASK = 0x3f8
prioritynames = {
"alert": LOG_ALERT,
"crit": LOG_CRIT,
"debug": LOG_DEBUG,
"emerg": LOG_EMERG,
"err": LOG_ERR,
"error": LOG_ERR,
"info": LOG_INFO,
"none": INTERNAL_NOPRI,
"notice": LOG_NOTICE,
"panic": LOG_EMERG,
"warn": LOG_WARNING,
"warning": LOG_WARNING,
"0": -1
}
facilitynames = {
"auth": LOG_AUTH,
"authpriv": LOG_AUTHPRIV,
"cron": LOG_CRON,
"daemon": LOG_DAEMON,
"ftp": LOG_FTP,
"kern": LOG_KERN,
"lpr": LOG_LPR,
"mail": LOG_MAIL,
"mark": INTERNAL_MARK,
"news": LOG_NEWS,
"security": LOG_AUTH,
"sysLOG": LOG_SYSLOG,
"user": LOG_USER,
"uucp": LOG_UUCP,
"local0": LOG_LOCAL0,
"local1": LOG_LOCAL1,
"local2": LOG_LOCAL2,
"local3": LOG_LOCAL3,
"local4": LOG_LOCAL4,
"local5": LOG_LOCAL5,
"local6": LOG_LOCAL6,
"local7": LOG_LOCAL7,
"0": -1
}
def __init__(self, ramdump, logd_task):
super().__init__(ramdump)
self.logd_task = logd_task
if self.ramdump.arm64:
self.addr_length = 8
else:
self.addr_length = 4
self.SIZEOF_LOG_HEAD = 32
# logger init
self.logger = logging.getLogger(__name__)
path = os.path.join(self.ramdump.outdir, 'logcat_debug_log.txt')
self.logger.addHandler(logging.FileHandler(path, mode='w'))
self.logger.setLevel(logging.INFO)
def LOG_PRI(self, p):
return (p) & self.LOG_PRIMASK
def LOG_FAC(self, p):
return ((p) & self.LOG_FACMASK) >> 3
def getcodetext(self, value, codetable):
for name, val in codetable.items():
if val == value:
return name
return "<unknown>"
def PAD(self, x):
return (((x) - (x % 4)) + 4) if (x % 4) else (x)
def log_next(self, log_head_addr, size):
n = log_head_addr + self.SIZEOF_LOG_HEAD + self.PAD(size)
return self.log_start_addr if n > self.log_end_addr else n
def log_list(self):
h = self.log_oldest_addr
while h != self.log_newest_addr and h <= self.log_end_addr and h >= self.log_start_addr:
size = self.read_bytes(h, 4)
if size > 1024:
self.logger.warning("invalid size(%x), expected <= 1024" % size)
break
yield h
# read next log element
h = self.log_next(h, size)
size = self.read_bytes(h, 4)
if size == 0 and h > self.log_newest_addr: # reach to end of buffer
h = self.log_start_addr
def process_logbuf_and_save(self):
log_file = self.ramdump.open_file("Logcat_openwrt.txt")
for log_head_addr in self.log_list():
size = self.read_bytes(log_head_addr, 4)
id = self.read_bytes(log_head_addr + 4, 4)
priority = self.read_bytes(log_head_addr + 8, 4)
source = self.read_bytes(log_head_addr + 12, 4)
tv_sec = self.read_bytes(log_head_addr + 16, 8)
tv_nsec = self.read_bytes(log_head_addr + 24, 8)
timestamp = self.format_time(tv_sec, tv_nsec)
msg = self.read_binary(log_head_addr + self.SIZEOF_LOG_HEAD, size)
msg = msg.decode('ascii', 'ignore').strip('\0')
fmt_msg = "%s %s.%s %s %s\n" % (timestamp,
self.getcodetext(self.LOG_FAC(priority) << 3, self.facilitynames),
self.getcodetext(self.LOG_PRI(priority), self.prioritynames),
"" if source else (" kernel:"), msg)
log_file.write(fmt_msg)
def read_bytes(self, addr, len):
val = UTaskLib.read_bytes(self.ramdump, self.logd_task.mmu, addr, len)
if not val:
val = 0
return val
def read_binary(self, addr, len):
return UTaskLib.read_binary(self.ramdump, self.logd_task.mmu, addr, len)
def format_time(self, tv_sec, tv_nsec):
tv_sec = tv_sec & 0xffffffff
tv_nsec = str(tv_nsec // 1000000)
tv_nsec = str(tv_nsec).zfill(3)
date = datetime.datetime.utcfromtimestamp(tv_sec)
timestamp = date.strftime("%m-%d %H:%M:%S") + '.' + tv_nsec
return timestamp
def is_valid_logd_addr(self, addr):
try:
self.log_start_addr = self.read_bytes(addr + self.addr_length, self.addr_length)
self.log_end_addr = self.read_bytes(addr, self.addr_length)
self.log_oldest_addr = self.read_bytes(addr + 11 * self.addr_length, self.addr_length)
self.log_newest_addr = self.read_bytes(addr + 10 * self.addr_length, self.addr_length)
if not self.log_start_addr or not self.log_end_addr \
or not self.log_oldest_addr or not self.log_newest_addr:
self.logger.debug("Can't find log/log_end/oldest/newest address, LogBuffer address was not found on addr=0x%x" % addr)
return False
if self.log_start_addr < self.log_end_addr and \
self.log_start_addr <= self.log_oldest_addr <= self.log_end_addr and \
self.log_start_addr <= self.log_newest_addr <= self.log_end_addr:
self.logger.debug("log_start 0x%x log_end 0x%x log_oldest 0x%x log_newest 0x%x" % (
self.log_start_addr, self.log_end_addr, self.log_oldest_addr, self.log_newest_addr))
## check buffer size is valid
buffer_size = int((self.log_end_addr - self.log_start_addr)/1024)
if buffer_size > 1024:
return False
print_out_str("log buffer size %d k" % buffer_size)
return True
else:
self.logger.debug("invalid address--> log_start 0x%x log_end 0x%x log_oldest 0x%x log_newest 0x%x" % (
self.log_start_addr, self.log_end_addr, self.log_oldest_addr, self.log_newest_addr))
return False
except:
return False
def parse(self):
startTime = datetime.datetime.now()
data_start = 0
for vma in self.logd_task.vmalist:
if vma.file_name == "logd" and vma.flags & 0b11 == 0b11:
data_start = vma.vm_start
break
if not data_start:
print_out_str("logd process didn't have data section, LogBuffer address was not found")
return
if self.ramdump.arm64:
log_end_offset = 0x328
addr_length = 8
else:
log_end_offset = 0x1e0
addr_length = 4
valid = self.is_valid_logd_addr(vma.vm_start + log_end_offset)
if not valid:
## continue to search
print_out_str("log address was not found with offset 0x%x, go through whole vma" % log_end_offset)
addr = vma.vm_start
while addr < (vma.vm_end):
valid = self.is_valid_logd_addr(addr)
if valid:
print_out_str("found logd addr offset is 0x%x" % (addr - vma.vm_start))
break
addr += addr_length
if valid:
self.process_logbuf_and_save()
print_out_str("Logcat parse cost " + str((datetime.datetime.now() - startTime).total_seconds()) + " s")

View File

@@ -0,0 +1,322 @@
# Copyright (c) 2020-2021 The Linux Foundation. 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.
from parser_util import register_parser, RamParser, cleanupString
from mmu import Armv8MMU, Armv7MMU
from print_out import print_out_str
import struct
import linecache
import datetime
import os
import subprocess
def read_bytes(mmu, ramdump, addr, len):
addr = mmu.virt_to_phys(addr)
s = ramdump.read_physical(addr, len)
if (s is None) or (s == ''):
return None
if len == 8:
s = struct.unpack('<Q', s)
elif len == 4:
s = struct.unpack('<I', s)
elif len == 2:
s = struct.unpack('<H', s)
elif len == 1:
s = struct.unpack('<B', s)
else:
print_out_str("This api used to unpack 1/2/4/8 bytes data, check the len\n")
exit()
return s[0]
def parse_logBuf_offset(ramdump):
space = " "
bss_sec_start = 0
logBuf_addr = 0
file_name = "symbols/logd"
objdump_path = ramdump.objdump_path
option_header = "-h"
option_symbols = "-t"
logd_file_path = os.path.join(ramdump.outdir, file_name)
if not os.path.exists(logd_file_path):
logd_tmp = ramdump.vmlinux.split("product")
product_tmp = logd_tmp[1].split("\\")
logd_file_path = logd_tmp[0] + "product/" + product_tmp[1] + "\\" + "symbols/system/bin/logd"
if not os.path.exists(logd_file_path):
logd_tmp = ramdump.vmlinux.split("product")
## android Q
logd_file_path = logd_tmp[0] + "product/" + product_tmp[0] + "\\" + "symbols/system/bin/logd"
if not os.path.exists(logd_file_path):
##android R
logd_file_path = logd_tmp[0] + "product/" + "qssi/symbols/system/bin/logd"
if os.path.exists(logd_file_path):
print_out_str("logd_file_path = %s\n" %(logd_file_path))
cmd = objdump_path + space + option_header + space + logd_file_path
objdump = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
out, err = objdump.communicate()
## merge the spaces
out = ' '.join(out.split())
## split by space
data = out.split(space)
i = 0
while True:
if i >=len(data):
break
if ".bss" in data[i]:
bss_sec_start = int(data[i+2], 16)
break
i = i + 1
if bss_sec_start == 0:
print_out_str("bss section not found in the logd")
exit()
cmd = objdump_path + space + option_symbols + space + logd_file_path
objdump = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
out, err = objdump.communicate()
## merge the spaces
out = ' '.join(out.split())
## split by space
data = out.split(space)
i = 0
while True:
if i >=len(data):
break
if ("logBuf" in data[i]) and ("ZL" in data[i]):
logBuf_addr = int(data[i-5], 16)
break
i = i + 1
if logBuf_addr == 0:
print_out_str("logBuf not found in logd\n")
exit()
else:
print_out_str("%s is not found. you can copy the logd symbols file to here.\n" %(logd_file_path));
return logBuf_addr - bss_sec_start;
def parse_logcat_v2(ramdump):
timestamp="timestamp"
MSG="msg"
uid="uid"
pid="pid"
tid="tid"
log_type="log_type"
priority="level"
logcat_file_list = ["logcat_v2_all.txt", "logcat_v2_main.txt", "logcat_v2_radio.txt", "logcat_v2_event.txt", "logcat_v2_system.txt",
"logcat_v2_crash.txt", "logcat_v2_stats.txt", "logcat_v2_security.txt", "logcat_v2_kernel.txt"]
log_type_list = ["0:main", "1:radio", "2:event", "3:system", "4:crash", "5:stats", "6:security", "7:kernel", "unknown"]
priority_list = ["0:UNKNOWN", "1:DEFAULT", "2:VERBOSE", "3:DEBUG", "4:INFO", "5:WARN", "6:ERR", "7:FATAL", "8:SILENT", "wrong priority"]
logcat_file_id = 0
log_id_max = 7
priority_max = 8
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
mm_offset = ramdump.field_offset('struct task_struct', 'mm')
f_path_offset = ramdump.field_offset('struct file', 'f_path')
dentry_offset = ramdump.field_offset('struct path', 'dentry')
d_iname_offset = ramdump.field_offset('struct dentry', 'd_iname')
if ramdump.arm64:
addr_length = 8
else:
addr_length = 4
first_node_offset = addr_length * 2
next_node_offset = addr_length
element_obj_offset = addr_length * 2
msg_addr_offset = 0x14
msg_size_offset = msg_addr_offset + addr_length
logdaddr = 0
bss_start = 0
for task in ramdump.for_each_process():
task_name = task + offset_comm
task_name = cleanupString(ramdump.read_cstring(task_name, 16))
if task_name == 'logd':
mm_addr = ramdump.read_word(task + mm_offset)
mmap = ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'mmap')
pgd = ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'pgd')
pgdp = ramdump.virt_to_phys(pgd)
start_data = ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'start_data')
end_data = ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'end_data')
logdmap = mmap
# bss section is after data section
logd_count = 0
while logdmap != 0:
tmpstartVm = ramdump.read_structure_field(logdmap, 'struct vm_area_struct', 'vm_start')
tmpendVm = ramdump.read_structure_field(logdmap, 'struct vm_area_struct', 'vm_end')
logd_count = logd_count + 1
if (end_data > tmpstartVm) and (end_data < tmpendVm):
# android P and older : 3 logd vma, bss section is just after end_data
if logd_count < 3:
# data section is 4bytes align while bss section is 8 bytes align
bss_start = (end_data + 7) & 0x000000fffffffff8
else:
# android Q: 2 code vma + 2 data vma + 1bss, bss section is individual vma after end_data
if (start_data < tmpstartVm):
logdmap = ramdump.read_structure_field(logdmap, 'struct vm_area_struct', 'vm_next')
bss_start = ramdump.read_structure_field(logdmap, 'struct vm_area_struct', 'vm_start')
else:
# android R: 3 code vma and 1 data+bss vma, bss section is just after end_data, data section is addr_length align and bss section is 8 bytes align
if ramdump.arm64:
bss_start = end_data
else:
bss_start = (end_data + 7) & 0xfffffff8
first_node_offset = addr_length
print_out_str("bss_start: 0x%x\n" %(bss_start))
break
logdmap = ramdump.read_structure_field(logdmap, 'struct vm_area_struct', 'vm_next')
break
if ramdump.arm64:
mmu = Armv8MMU(ramdump, pgdp)
else:
mmu = Armv7MMU(ramdump, pgdp)
logbuf_offset = parse_logBuf_offset(ramdump);
print_out_str("logbuf_offset = 0x%x" %(logbuf_offset))
logbuf_addr = read_bytes(mmu, ramdump, bss_start + logbuf_offset, addr_length)
print_out_str("logbuf_addr = 0x%x" %(logbuf_addr))
first_node_addr = read_bytes(mmu, ramdump, logbuf_addr + first_node_offset, addr_length)
next_node_addr = first_node_addr
if next_node_addr is not None:
print_out_str("first_node_addr = 0x%x\n" %(next_node_addr))
index = 0
log_file = [0] * 9
while index < 9:
log_file[index] = ramdump.open_file(logcat_file_list[index])
log_file[index].write("\n\n========== PARSE START ==========\n\n\n")
log_file[index].write(timestamp.ljust(32))
log_file[index].write(uid.rjust(8))
log_file[index].write(pid.rjust(8))
log_file[index].write(tid.rjust(8))
log_file[index].write("\t")
log_file[index].write(log_type.ljust(16))
log_file[index].write(priority.center(16))
log_file[index].write(MSG.ljust(8))
log_file[index].write("\n")
index = index + 1
while next_node_addr != 0:
logbuffer_ele_obj = read_bytes(mmu, ramdump, next_node_addr + element_obj_offset, addr_length)
next_node_addr = read_bytes(mmu, ramdump, next_node_addr + next_node_offset, addr_length)
if next_node_addr == first_node_addr:
break
logbuffer_ele_obj_phys = mmu.virt_to_phys(logbuffer_ele_obj)
## uid pid tid and etc parsed from LogBufferElement
uid = read_bytes(mmu, ramdump, logbuffer_ele_obj, 4)
pid = read_bytes(mmu, ramdump, logbuffer_ele_obj + 0x4, 4)
tid = read_bytes(mmu, ramdump, logbuffer_ele_obj + 0x8, 4)
tv_second = read_bytes(mmu, ramdump, logbuffer_ele_obj + 0xC, 4)
tv_second_nano = read_bytes(mmu, ramdump, logbuffer_ele_obj + 0x10, 4)
msg_addr = read_bytes(mmu, ramdump, logbuffer_ele_obj + msg_addr_offset, addr_length)
msg_size = read_bytes(mmu, ramdump, logbuffer_ele_obj + msg_size_offset, 2)
log_id = read_bytes(mmu, ramdump, logbuffer_ele_obj + msg_size_offset + 0x2, 1)
dropped = read_bytes(mmu, ramdump, logbuffer_ele_obj + msg_size_offset + 0x3, 1)
if (log_id == 2) or (dropped == 1) :
continue
if logbuffer_ele_obj_phys is None or msg_size == 0:
break
uid = str(uid)
pid = str(pid)
tid = str(tid)
tv_second_nano = str(tv_second_nano // 1000)
tv_second_nano = str(tv_second_nano).zfill(6)
date = datetime.datetime.utcfromtimestamp(tv_second)
timestamp = date.strftime("%Y-%m-%d %H:%M:%S") + '.' + tv_second_nano
min = 0
x = 0
level = -1
MSG = ""
while msg_size != 0:
i = 0
msg_addr = msg_addr + min
# msg located in the same page
if msg_size < (0x1000 - msg_addr % 0x1000):
min = msg_size
# msg separated in two pages
else:
min = 0x1000 - msg_addr % 0x1000
msg_size = msg_size - min
msg_addr_phys = mmu.virt_to_phys(msg_addr)
if msg_addr_phys is None:
break
if x == 0:
## level is at the first Byte of MSG
level = read_bytes(mmu, ramdump, msg_addr, 1)
MSG_tmp = ramdump.read_physical(msg_addr_phys + 1, min - 1)
else:
if min == 0:
break
else:
MSG_tmp = ramdump.read_physical(msg_addr_phys, min)
MSG_ascii = list(MSG_tmp)
while i < len(MSG_ascii):
MSG_ascii[i] = chr(MSG_tmp[i] & 0x7F)
i = i + 1
MSG = MSG + ''.join(MSG_ascii)
MSG = MSG.replace('\000', '\040')
MSG = MSG.replace('\015', '\040')
MSG = MSG.replace('\012', '\040')
x = x + 1
if (log_id > log_id_max) or (log_id < 0):
continue
else:
logcat_file_id = log_id + 1
if (level > priority_max) or (level < 0):
level = priority_max + 1
log_type = log_type_list[log_id]
priority = priority_list[level]
log_file[0].write(timestamp.ljust(32))
log_file[0].write(uid.rjust(8))
log_file[0].write(pid.rjust(8))
log_file[0].write(tid.rjust(8))
log_file[0].write("\t")
log_file[0].write(log_type.ljust(16))
log_file[0].write(priority.center(16))
log_file[0].write(MSG)
log_file[0].write("\n")
log_file[logcat_file_id].write(timestamp.ljust(32))
log_file[logcat_file_id].write(uid.rjust(8))
log_file[logcat_file_id].write(pid.rjust(8))
log_file[logcat_file_id].write(tid.rjust(8))
log_file[logcat_file_id].write("\t")
log_file[logcat_file_id].write(log_type.ljust(16))
log_file[logcat_file_id].write(priority.center(16))
log_file[logcat_file_id].write(MSG)
log_file[logcat_file_id].write("\n")
if next_node_addr == first_node_addr:
break
index = 0
while index < 9:
log_file[index].close()
index = index + 1
return
@register_parser('--logcat-v2', 'Extract logcat logs from ramdump ')
class Logcat_v2(RamParser):
def parse(self):
parse_logcat_v2(self.ramdump)

View File

@@ -0,0 +1,936 @@
# Copyright (c) 2021-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.
from parser_util import RamParser, cleanupString
from print_out import print_out_str
import struct
import datetime
import dmesglib
import print_out
from parsers.zram import Zram
from utasklib import UTaskLib
from concurrent import futures
import traceback
class Constants:
LOG_NAME = [ "main", "radio", "events", "system", "crash", "stats", "security", "kernel"]
#log id
LOG_ID_MIN = 0
LOG_ID_MAIN = 0
LOG_ID_RADIO = 1
LOG_ID_EVENTS = 2
LOG_ID_SYSTEM = 3
LOG_ID_CRASH = 4
LOG_ID_STATS = 5
LOG_ID_SECURITY = 6
LOG_ID_KERNEL = 7
LOG_ID_MAX = 7
#log level
ANDROID_LOG_DEFAULT =1
ANDROID_LOG_VERBOSE = 2
ANDROID_LOG_DEBUG = 3
ANDROID_LOG_INFO = 4
ANDROID_LOG_WARN = 5
ANDROID_LOG_ERROR = 6
ANDROID_LOG_FATAL = 7
ANDROID_LOG_SILENT = 8
SIZEOF_LOG_ENTRY = 30
SIZEOF_HEADER_T = 4
SIZEOF_EVT_LIST_T = 2
SIZEOF_EVT_INT_T = 5
SIZEOF_EVT_LONG_T = 9
SIZEOF_EVT_FLOAT_T = 5
SIZEOF_EVT_STRING_T = 5
#event type
EVENT_TYPE_INT = 0 #/* int32_t */
EVENT_TYPE_LONG = 1 #/* int64_t */
EVENT_TYPE_STRING = 2
EVENT_TYPE_LIST = 3
EVENT_TYPE_FLOAT = 4
NS_PER_SEC = 1000000000
def filter_pri_to_char(self, pri) :
if pri == self.ANDROID_LOG_VERBOSE:
return 'V'
elif pri == self.ANDROID_LOG_DEBUG:
return 'D'
elif pri == self.ANDROID_LOG_INFO:
return 'I'
elif pri == self.ANDROID_LOG_WARN:
return 'W'
elif pri == self.ANDROID_LOG_ERROR:
return 'E'
elif pri == self.ANDROID_LOG_FATAL:
return 'F'
elif pri == self.ANDROID_LOG_SILENT:
return 'S'
else:
return '?'
class LogEntry(Constants):
def __init__(self):
self.tv_sec = 0
self.tv_nsec = 0
self.pid = 0
self.uid = 0
self.tid = 0
self.prior = self.ANDROID_LOG_INFO
self.tag = ""
self.msg = ""
self.is_binary = False
self.is_dmesg = False
self.tz_minuteswest = 0
def wallTime(self):
return self.tv_sec * self.NS_PER_SEC + self.tv_nsec
def format_time(self):
sec = self.tv_sec
nsec = self.tv_nsec
sec -= 60 * self.tz_minuteswest
nsec = str(nsec // 1000000)
nsec = str(nsec).zfill(3)
date = datetime.datetime.utcfromtimestamp(sec)
rtc_timestamp = date.strftime("%m-%d %H:%M:%S") + '.' + nsec
return rtc_timestamp
def set_msg(self, msg):
self.msg = msg
def content(self):
if self.is_dmesg and self.tag == "":
return "%5d %5d %5d %c %-8.*s\n" % (
self.uid, self.pid, self.tid, \
self.filter_pri_to_char(self.prior), \
len(self.msg), self.msg)
elif self.is_binary:
return "%5d %5d %5d %c %s: %s\n" % (
self.uid, self.pid, self.tid, \
self.filter_pri_to_char(self.prior), \
cleanupString(self.tag), \
cleanupString(self.msg))
else:
preifx_line = "%5d %5d %5d %c %-8.*s: " % (
self.uid, self.pid, self.tid, \
self.filter_pri_to_char(self.prior), \
len(self.tag), cleanupString(self.tag))
multilines = self.msg.split("\n")
if len(multilines) > 1:
ret = []
for line in multilines:
if(len(cleanupString(line.strip())) > 0):
ret.append(preifx_line + "%s\n" % cleanupString(line))
return ret
else:
return preifx_line + "%s\n" % cleanupString(self.msg.strip())
def __str__(self):
content = self.content()
if type(content) is str:
return "%s %s" % (self.format_time(), content)
else:
ret = ""
for item in content:
ret += "%s %s" % (self.format_time(), item)
return ret
class LogEntry_Dmesg(LogEntry):
def __init__(self):
super().__init__()
self.mono_format = False
self.mono_tv_sec = 0
self.mono_tv_nsec = 0
self.msg_without_space = ""
self.is_dmesg = True
def set_msg(self, msg):
self.msg = msg
self.msg_without_space = msg.replace(" ", "")
def mono_time(self):
return self.mono_tv_sec * self.NS_PER_SEC + self.mono_tv_nsec
def is_same_content(self, other):
return self.msg_without_space.endswith(other.msg_without_space) or \
other.msg_without_space.endswith(self.msg_without_space)
def is_same_time(self, other):
return self.mono_tv_sec == other.mono_tv_sec and \
abs(self.mono_tv_nsec - other.mono_tv_nsec) <= 1000000
# self > other
def __cmp__(self,other):
if self.is_same_time(other) and \
self.is_same_content(other):
return 0
else:
return self.mono_time() - other.mono_time()
def __eq__(self, other):
return self.is_same_time(other) and \
self.is_same_content(other)
def __hash__(self):
walltime_nsec = self.wallTime()
walltime_msec = (int)(walltime_nsec / 1000000)
return hash(walltime_msec)*31 + hash(self.msg_without_space)
def set_mono_time(self, mono_nsec, wall_to_monotonic_tv_sec, wall_to_monotonic_tv_nsec):
self.mono_tv_sec = (int)(mono_nsec / self.NS_PER_SEC)
self.mono_tv_nsec = mono_nsec % self.NS_PER_SEC
self.tv_sec = self.mono_tv_sec + wall_to_monotonic_tv_sec
self.tv_nsec = self.mono_tv_nsec + wall_to_monotonic_tv_nsec
if self.tv_nsec >= self.NS_PER_SEC:
self.tv_sec += 1
self.tv_nsec -= self.NS_PER_SEC
def set_rtc_time(self, tv_sec, tv_nsec, wall_to_monotonic_tv_sec, wall_to_monotonic_tv_nsec):
self.tv_sec = tv_sec
self.tv_nsec = tv_nsec
sec = tv_sec
nsec = tv_nsec
if nsec <= wall_to_monotonic_tv_nsec:
sec -=1
nsec = self.NS_PER_SEC + nsec - wall_to_monotonic_tv_nsec
else:
nsec = nsec - wall_to_monotonic_tv_nsec
if sec < wall_to_monotonic_tv_sec:
sec = 0
nsec = 0
else:
sec = sec - wall_to_monotonic_tv_sec
self.mono_tv_sec = sec
self.mono_tv_nsec = nsec
def format_full_time(self):
rtc_timestamp = self.format_time()
nsec = str(self.mono_tv_nsec // 1000)
nsec = str(nsec).zfill(6)
return rtc_timestamp + "[" + str(self.mono_tv_sec)+"."+str(nsec) + "]"
def __str__(self):
if self.mono_format:
content = self.content()
if type(content) is str:
return "%s %s" % (self.format_full_time(), content)
else:
ret = ""
for item in content:
ret += "%s %s" % (self.format_time(), item)
return ret
else:
return super().__str__()
class Logcat_base(RamParser, Constants):
def __init__(self, ramdump, taskinfo):
super().__init__(ramdump)
self.taskinfo = taskinfo
self.mmu = taskinfo.mmu
self.logd_task = taskinfo.task_addr
self.sizeUsed = {}
self.maxSize = {}
self.is_success = False
self.zstd = None
try:
self.zstd = __import__('zstandard')
except ImportError as result:
print_out_str(str(result)+", try to use command 'py -3 -m pip install zstandard' to install")
print("\033[1;31m" + str(result) +
", try to use command 'py -3 -m pip install zstandard' to install \033[0m")
if self.ramdump.arm64:
self.addr_length = 8
else:
self.addr_length = 4
self.extra_offset = 0
sys_tz_addr = self.ramdump.address_of('sys_tz')
tz_minuteswest_offset = self.ramdump.field_offset(
'struct timezone ', 'tz_minuteswest')
self.tz_minuteswest = self.ramdump.read_s32(sys_tz_addr + tz_minuteswest_offset)
print_out_str("struct timezone --> tz_minuteswest= "+str(self.tz_minuteswest)+"min")
print("struct timezone --> tz_minuteswest= "+str(self.tz_minuteswest)+"min")
self.wall_to_mono_found = False
self.wall_to_monotonic_tv_sec = 0
self.wall_to_monotonic_tv_nsec = 0
self.dmesg_list={}
self.zram_parser = Zram(ramdump)
def find_bss_addrs(self):
""" find vma list of bss section """
bss_vms_list = []
# bss section is after data section
for index, vma in enumerate(self.taskinfo.vmalist):
tmpstartVm = vma.vm_start
tmpendVm = vma.vm_end
# rw flags
if vma.file_name == "logd" and vma.flags & 0b11 == 0b11:
bss_vms_list.append([tmpstartVm, tmpendVm])
# anon page
vma_next = self.taskinfo.vmalist[index + 1]
if not vma_next.file_name and vma_next.flags & 0b11 == 0b11:
bss_vms_list.append([vma_next.vm_start, vma_next.vm_end])
return bss_vms_list
#return offset of RTC to Mono
def findCorrection(self):
sec = 0
nsec = 0
found = False
correction_addr = 0
bss_addrs = self.find_bss_addrs()
for bss_start, bss_end in bss_addrs:
idx = 0
bss_size = bss_end - bss_start
while idx < bss_size:
if self.is_equal(bss_start + idx, 8, 3) and \
self.is_equal(bss_start + idx + 8*2, 8, 7) and \
self.is_equal(bss_start + idx + 8*4, 8, 5) and \
self.is_equal(bss_start + idx + 8*6, 8, 4):
correction_addr = bss_start + idx - 40
sec = self.read_bytes(correction_addr, 4)
nsec = self.read_bytes(correction_addr + 4, 4)
found = True
break
idx += 8
if found:
break
if found:
print_out_str(("Found &LogBuffer::Correction=0x%x LogBuffer::Correction=%ld.%ld")
% (correction_addr, sec, nsec))
else:
print_out_str("&LogBuffer::Correction not found")
return found, sec, nsec
def is_equal(self, addr, lengh, value):
val = self.read_bytes(addr, lengh)
return val == value
def read_bytes(self, addr, len):
return UTaskLib.read_bytes(self.ramdump, self.mmu, addr, len, self.zram_parser)
def read_binary(self, addr, len):
return UTaskLib.read_binary(self.ramdump, self.mmu, addr, len, self.zram_parser)
def get_output_filename(self, log_id):
if log_id >= self.LOG_ID_MIN and log_id <= self.LOG_ID_MAX:
return "{}_{}.txt".format(self.__class__.__name__, self.LOG_NAME[log_id])
else:
return None
def get_evt_data(self, data_array, pos):
if (pos + 1) > len(data_array):
return -1, -1, -1
evt_type = struct.unpack('<B', data_array[pos : pos + 1])[0]
length = 0
msg=""
if evt_type == self.EVENT_TYPE_INT :
if (pos + self.SIZEOF_EVT_INT_T) > len(data_array):
return -1, -1, -1
msg = str(struct.unpack('<I', data_array[pos+1 : pos + self.SIZEOF_EVT_INT_T])[0])
length = self.SIZEOF_EVT_INT_T
elif evt_type == self.EVENT_TYPE_LONG:
if (pos + self.SIZEOF_EVT_LONG_T) > len(data_array):
return -1, -1, -1
msg = str(struct.unpack('<Q', data_array[pos+1 : pos + self.SIZEOF_EVT_LONG_T])[0])
length = self.SIZEOF_EVT_LONG_T
elif evt_type == self.EVENT_TYPE_FLOAT:
if (pos + self.SIZEOF_EVT_FLOAT_T) > len(data_array):
return -1, -1, -1
msg = str(struct.unpack('<f', data_array[pos+1 : pos + self.SIZEOF_EVT_FLOAT_T])[0])
length = self.SIZEOF_EVT_FLOAT_T
elif evt_type == self.EVENT_TYPE_STRING:
if (pos + self.SIZEOF_EVT_STRING_T) > len(data_array):
return -1, -1, -1
#for event log, msg_len may be 0 like "I 1397638484: [121035042,4294967295,]"
msg_len = struct.unpack('I', data_array[pos+1 : pos + self.SIZEOF_EVT_STRING_T])[0]
# last msg_len-1 bytes
tmpmsg = data_array[pos + self.SIZEOF_EVT_STRING_T : pos+self.SIZEOF_EVT_STRING_T+msg_len]
length = self.SIZEOF_EVT_STRING_T + msg_len
msg = tmpmsg.decode('ascii', 'ignore').strip()
return evt_type, msg, length
def process_log_and_save(self, _data, log_id):
ret=[]
pos = 0
while pos < len(_data):
if pos +self.SIZEOF_LOG_ENTRY > len(_data):
break
# first [0-30] total 31 bytes
logEntry = struct.unpack('<IIIQIIHB', _data[pos:pos+self.SIZEOF_LOG_ENTRY+1])
pos = pos+self.SIZEOF_LOG_ENTRY + 1 + self.extra_offset
uid = logEntry[0]
pid = logEntry[1]
tid = logEntry[2]
sequence = logEntry[3]
tv_sec = logEntry[4]
tv_nsec = logEntry[5]
msg_len = logEntry[6]
priority = logEntry[7]
if msg_len is None or msg_len < 1:
break;
msg = _data[pos:pos+msg_len-1] # last msg_len-1 bytes
msgList = msg.decode('ascii', 'ignore').split('\0')
pos = pos + msg_len-1
if len(msgList) <2:
continue
try:
if log_id == self.LOG_ID_KERNEL:
entry = LogEntry_Dmesg()
entry.mono_format = self.wall_to_mono_found
entry.set_rtc_time(tv_sec, tv_nsec, self.wall_to_monotonic_tv_sec, self.wall_to_monotonic_tv_nsec)
else:
entry = LogEntry()
entry.tv_sec = tv_sec
entry.tv_nsec = tv_nsec
entry.pid = pid
entry.uid = uid
entry.tid = tid
entry.prior = priority
entry.tag = cleanupString(msgList[0].strip())
entry.set_msg(msgList[1])
entry.tz_minuteswest = self.tz_minuteswest
ret.append(entry)
except Exception as result:
print_out_str(str(result))
traceback.print_exc()
return ret
def process_binary_log_and_save(self, _data):
ret=[]
pos = 0
while pos < len(_data):
if pos +self.SIZEOF_LOG_ENTRY > len(_data):
break
# first [0-30] total 31 bytes
logEntry = struct.unpack('<IIIQIIH', _data[pos : pos + self.SIZEOF_LOG_ENTRY])
pos = pos + self.SIZEOF_LOG_ENTRY + self.extra_offset
uid = logEntry[0]
pid = logEntry[1]
tid = logEntry[2]
sequence = logEntry[3]
tv_sec = logEntry[4]
tv_nsec = logEntry[5]
msg_len = logEntry[6]
priority = self.ANDROID_LOG_INFO
if pos + self.SIZEOF_HEADER_T > len(_data):
break
tagidx = struct.unpack('<I', _data[pos : pos + self.SIZEOF_HEADER_T])[0] #4 bytes
pos = pos + self.SIZEOF_HEADER_T
evt_type, tmpmsg, length = self.get_evt_data(_data,pos)
pos = pos + length
if evt_type == -1:
break
if evt_type != self.EVENT_TYPE_LIST:
entry = LogEntry()
entry.is_binary = True
entry.tv_sec = tv_sec
entry.tv_nsec = tv_nsec
entry.pid = pid
entry.uid = uid
entry.tid = tid
entry.prior = priority
entry.tag = str(tagidx)
entry.set_msg(tmpmsg)
entry.tz_minuteswest = self.tz_minuteswest
ret.append(entry)
continue #--> read next log entry
if pos + self.SIZEOF_EVT_LIST_T > len(_data):
break
list_t = struct.unpack('<BB', _data[pos : pos + self.SIZEOF_EVT_LIST_T])
pos = pos + self.SIZEOF_EVT_LIST_T
evt_type = list_t[0]
evt_cnt = list_t[1]
i = 0
msg = ""
while i < evt_cnt:
evt_type, tmpmsg, length = self.get_evt_data(_data,pos)
if evt_type == -1:
break
pos = pos + length
msg = msg + tmpmsg
if i < evt_cnt -1:
msg = msg + ","
i = i+1
entry = LogEntry()
entry.is_binary = True
entry.tv_sec = tv_sec
entry.tv_nsec = tv_nsec
entry.pid = pid
entry.uid = uid
entry.tid = tid
entry.prior = priority
entry.tag = str(tagidx)
entry.set_msg("[" + msg + "]")
entry.tz_minuteswest = self.tz_minuteswest
ret.append(entry)
return ret
def process_work_chunk(self, _data, log_id, section, is_binary, write_active):
if write_active == 0:##parse zipped buffer
if not self.zstd: # no zstd library
return log_id, section, None
try:
_data = self.zstd.ZstdDecompressor().decompress(_data)
except:
print_out_str("decompress caused error on logid:section(%d:%d), size(%d)" %(log_id, section, len(_data)))
traceback.format_exc()
_data = None
ret = None
if _data:
try:
if is_binary:
ret = self.process_binary_log_and_save(_data)
else:
ret = self.process_log_and_save(_data, log_id)
except:
traceback.print_exc()
return log_id, section, ret
# loglist is a dict type
def save_log_to_file(self, loglist):
if not loglist or len(loglist) == 0:
return
if not self.is_success: # parse success and save to file
self.is_success = True
for log_id in loglist.keys():
if log_id == self.LOG_ID_KERNEL and self.wall_to_mono_found:
continue
sections = loglist[log_id]
if not sections:
continue
filename = self.get_output_filename(log_id)
if filename is None:
return
log_file = self.ramdump.open_file(filename)
for section in sorted(sections.keys()):
if sections[section] and len(sections[section]) >= 0:
if section ==0 or len(sections[section]) == 1:
head = "{} log buffer used: {}k Max size:{}k\n".format(
self.LOG_NAME[log_id],
round(self.sizeUsed[log_id]/1024,1),
round(self.maxSize[log_id]/1024,1))
log_file.write(head)
head="--------- beginning of {} section: {}\n".format(
self.LOG_NAME[log_id], str(section))
log_file.write(head)
for item in sections[section]:
log_file.write(str(item))
if not self.wall_to_mono_found:
return
# start to combine dmesg
dmesgDict = []
if self.LOG_ID_KERNEL in loglist.keys():
sections = loglist[self.LOG_ID_KERNEL]
if sections:
for section in sorted(sections.keys()):
dmesgDict.extend(sections[section])
filename = self.get_output_filename(self.LOG_ID_KERNEL)
log_file = self.ramdump.open_file(filename)
if len(dmesgDict) > 0:
head = "{} log buffer used: {}k Max size:{}k\n".format(
self.LOG_NAME[self.LOG_ID_KERNEL],
round(self.sizeUsed[self.LOG_ID_KERNEL]/1024,1),
round(self.maxSize[self.LOG_ID_KERNEL]/1024,1))
log_file.write(head)
same_log_count = 0
log_added_count = 0
if len(self.dmesg_list) <= 0:
for item in dmesgDict:
log_file.write(str(item))
else:
self.combine_dmesg(dmesgDict, log_file)
def combine_dmesg(self, dmesgDict, log_file):
# compare dmesg with kernel log from logd
same_log_count = 0
log_added_count = 0
keys = sorted(self.dmesg_list)
dmesg_time_start = keys[0]
index = 0
for item in dmesgDict:
if item.mono_time() < dmesg_time_start:
log_file.write(str(item))
else:
should_delete = []
while index < len(keys):
mono_time = keys[index]
s_pid = self.dmesg_list[mono_time][0]
s_line = self.dmesg_list[mono_time][1]
entry = LogEntry_Dmesg()
entry.mono_format = self.wall_to_mono_found
entry.set_mono_time(mono_time, self.wall_to_monotonic_tv_sec, self.wall_to_monotonic_tv_nsec)
entry.pid = s_pid
entry.uid = 0
entry.tid = s_pid
entry.set_msg(cleanupString(s_line))
entry.tz_minuteswest = self.tz_minuteswest
cmpval = item.__cmp__(entry)
if cmpval > 0: # time before
log_added_count += 1
index += 1
log_file.write(str(entry))
should_delete.append(mono_time)
continue
elif cmpval == 0:
index += 1
same_log_count += 1
should_delete.append(mono_time)
continue
else:
log_file.write(str(item))
break
for time in should_delete:
del self.dmesg_list[time]
for mono_time in self.dmesg_list:
s_pid = self.dmesg_list[mono_time][0]
s_line = self.dmesg_list[mono_time][1]
entry = LogEntry_Dmesg()
entry.mono_format = self.wall_to_mono_found
entry.set_mono_time(mono_time, self.wall_to_monotonic_tv_sec, self.wall_to_monotonic_tv_nsec)
entry.pid = s_pid
entry.uid = 0
entry.tid = s_pid
entry.set_msg(cleanupString(s_line))
entry.tz_minuteswest = self.tz_minuteswest
log_added_count += 1
log_file.write(str(entry))
print_out_str("Total dmesg log count %d, same count %d, added count %d" % \
(len(self.dmesg_list), same_log_count, log_added_count))
def read_dmesg(self):
self.dmesg_list = dmesglib.DmesgLib(self.ramdump, print_out.out_file).get_dmesg_as_dict()
def process_chunklist_and_save(self, logchunk_list_addr):
log_id = 0
threads = []
with futures.ThreadPoolExecutor(8) as executor:
while log_id <= self.LOG_ID_MAX:
is_binary = (log_id == self.LOG_ID_EVENTS) or (
log_id == self.LOG_ID_STATS) or (log_id == self.LOG_ID_SECURITY)
#--> address of std::list<SerializedLogChunk>
_addr = logchunk_list_addr + log_id * 0x18
#the first element of std::list<SerializedLogChunk>
first_node_addr = self.read_bytes(_addr + self.addr_length, self.addr_length)
list_count = self.read_bytes(_addr + self.addr_length *2, self.addr_length )
section = 0;
next_node_addr = first_node_addr
self.sizeUsed[log_id] = 0
self.maxSize[log_id] = 0
while (section < list_count):
current_node = next_node_addr + self.addr_length * 2 #-->SerializedLogChunk
write_offset = self.read_bytes(current_node + 0x10, 4) #--write_offset_
write_active = self.read_bytes(current_node + 0x18, 1) #--write_active_
_data = None
if write_active == 0: ##parse zipped buffer
if self.zstd:
compressed_log_addr = current_node + 0x28
_data_addr = self.read_bytes(compressed_log_addr, self.addr_length)
_data_size = self.read_bytes(
compressed_log_addr + self.addr_length, self.addr_length)
_data = self.read_binary(_data_addr, _data_size)
self.sizeUsed[log_id] = self.sizeUsed[log_id] + _data_size
else:
_data_addr = self.read_bytes(current_node, self.addr_length)
_data_size = self.read_bytes(current_node + self.addr_length, self.addr_length)
self.sizeUsed[log_id] = self.sizeUsed[log_id] + write_offset
self.maxSize[log_id] = _data_size * 4
# write_offset is data size for uncompressed secion
_data = self.read_binary(_data_addr, write_offset)
if _data:
future = executor.submit(self.process_work_chunk, _data, log_id, section, is_binary, write_active)
threads.append(future)
section = section + 1 # next loop
next_node_addr = self.read_bytes(next_node_addr + self.addr_length, self.addr_length)
log_id = log_id + 1
loglist = {}
for future in futures.as_completed(threads):
log_id, section, ret = future.result()
if not ret:
continue
if log_id in loglist:
sections = loglist[log_id]
else:
sections = {}
loglist[log_id] = sections
sections[section] = ret
self.save_log_to_file(loglist)
class Logcat_v3(Logcat_base):
def __init__(self, ramdump, taskinfo):
super().__init__(ramdump, taskinfo)
def get_logbuffer_addr(self):
stack_offset = self.ramdump.field_offset('struct task_struct', 'stack')
stack_addr = self.ramdump.read_word(self.logd_task + stack_offset)
pt_regs_size = self.ramdump.sizeof('struct pt_regs')
pt_regs_addr = self.ramdump.thread_size + stack_addr - pt_regs_size
user_regs_addr = pt_regs_addr + self.ramdump.field_offset('struct pt_regs', 'user_regs')
#find x22 register value
x22_r_addr = self.ramdump.array_index(user_regs_addr, 'unsigned long', 22)
x22_value = self.ramdump.read_word(x22_r_addr)
x22_logbuf_addr = self.read_bytes(x22_value + 0x88, self.addr_length)
logbuf_addrs = []
if x22_logbuf_addr and x22_logbuf_addr != 0: # for logd orginal code
logbuf_addrs.append(x22_logbuf_addr)
print_out_str("logbuf_addr from x22 = 0x%x" %(x22_logbuf_addr))
x21_r_addr = self.ramdump.array_index(user_regs_addr, 'unsigned long', 21)
x21_value = self.ramdump.read_word(x21_r_addr)
x21_logbuf_addr = self.read_bytes(x21_value + 0x88, self.addr_length)
if x21_logbuf_addr and x21_logbuf_addr != 0:
logbuf_addrs.append(x21_logbuf_addr)
print_out_str("logbuf_addr from x21 = 0x%x" %(x21_logbuf_addr))
x23_r_addr = self.ramdump.array_index(user_regs_addr, 'unsigned long', 23)
x23_value = self.ramdump.read_word(x23_r_addr)
x23_logbuf_addr = self.read_bytes(x23_value + 0x88, self.addr_length)
if x23_logbuf_addr or x23_logbuf_addr == 0:
logbuf_addrs.append(x23_logbuf_addr)
print_out_str("logbuf_addr from x23 = 0x%x" %(x23_logbuf_addr))
return logbuf_addrs
def parse(self):
self.read_dmesg()
self.wall_to_mono_found, self.wall_to_monotonic_tv_sec, self.wall_to_monotonic_tv_nsec = self.findCorrection()
logbuf_addrs = self.get_logbuffer_addr()
for __logbuf_addr in logbuf_addrs:
logchunk_list_addr = __logbuf_addr + 0x60
try:
self.process_chunklist_and_save(logchunk_list_addr)
except Exception as e:
print(str(e))
traceback.print_exc()
if self.is_success:
print_out_str("logbuf_addr = 0x%x" %(__logbuf_addr))
break
return self.is_success
class Logcat_vma(Logcat_base):
def __init__(self, ramdump, taskinfo):
super().__init__(ramdump, taskinfo)
self.HEAD_SIZE = 32
self.vmas = []
if int(ramdump.get_config_val("CONFIG_BASE_SMALL")) == 0:
self.PID_MAX = 0x8000
else:
self.PID_MAX = 0x1000
print_out_str("max pid = " + str(self.PID_MAX))
def read_bytes(self, addr, len):
addr = addr & 0x0000ffffffffffff
for vma in self.vmas:
if addr >= vma["vmstart"] and addr+len <= vma["vmstart"]+vma["size"]:
offset = addr - vma["vmstart"]
if len == 8 and offset+8 <=vma["size"]:
s = struct.unpack('<Q', vma["data"][offset:offset+8])
return s[0]
elif len == 4 and offset+4 <=vma["size"]:
s = struct.unpack('<I', vma["data"][offset:offset+4])
return s[0]
elif len == 2 and offset+2 <=vma["size"]:
s = struct.unpack('<H', vma["data"][offset:offset+2])
return s[0]
elif len == 1 and offset+1 <=vma["size"]:
s = struct.unpack('<B', vma["data"][offset:offset+1])
return s[0]
else:
print_out_str("This api used to unpack 1/2/4/8 bytes data, check the len\n")
exit()
return 0
def read_binary(self, addr, len):
addr = addr & 0x0000ffffffffffff
for vma in self.vmas:
if addr >= vma["vmstart"] and addr+len <= vma["vmstart"]+vma["size"]:
offset = addr - vma["vmstart"]
if offset + len >= vma["size"]:
len = vma["size"] - offset
return vma["data"][offset:offset+len]
return b''
def get_vmas_with_rw(self):
'''
return vma list with read+write permissions
'''
for vma in self.taskinfo.vmalist:
if vma.flags & 0b11 != 0b11:
continue
item = {}
item["vmstart"] = vma.vm_start
item["size"] = vma.vm_end - vma.vm_start
item["data"] = super().read_binary(item["vmstart"], item["size"])
self.vmas.append(item)
def has_valid_log(self, main_chunklist_addr):
end_node_addr = self.read_bytes(main_chunklist_addr, self.addr_length)
current_node = end_node_addr + self.addr_length * 2 #-->SerializedLogChunk
_data_addr = self.read_bytes(current_node, self.addr_length)
_data_size = self.read_bytes(current_node + self.addr_length, self.addr_length)
if _data_size <= self.SIZEOF_LOG_ENTRY:
return False
valid = self.has_valid_LogEntrys(_data_addr, _data_size, 10)
if valid is False: ## retry
extra_log_entry_offset = 4
valid = self.has_valid_LogEntrys(_data_addr, _data_size, 10, extra_log_entry_offset)
if valid:
self.extra_offset = extra_log_entry_offset
return valid
def has_valid_LogEntrys(self, _addr, _data_size, valid_count, extra_offset=0):
i = 0
valid = False
offset = 0
while i < valid_count:
valid, length = self.is_valid_LogEntry(_addr+offset)
if not valid:
return False
offset = offset + length + extra_offset
if offset > _data_size:
return True
i = i + 1
return True
def is_valid_LogEntry(self, _addr):
uid = self.read_bytes(_addr, 4)
pid = self.read_bytes(_addr + 0x4, 4)
tid = self.read_bytes(_addr + 0x8, 4)
sequence = self.read_bytes(_addr + 0xc, 8)
tv_sec = self.read_bytes(_addr + 0x14, 4)
tv_nsec = self.read_bytes(_addr + 0x18, 4)
msg_len = self.read_bytes(_addr + 0x1c, 2)
priority = self.read_bytes(_addr + self.SIZEOF_LOG_ENTRY, 1)
if pid <= self.PID_MAX and uid <= self.PID_MAX and priority \
<= self.ANDROID_LOG_SILENT and priority >= self.ANDROID_LOG_VERBOSE:
if msg_len >=1 and msg_len <= 4068: #max_payload
return True, 30 + msg_len
return False, 0
def parse(self):
self.read_dmesg()
startTime = datetime.datetime.now()
self.get_vmas_with_rw()
# find address of std::list<SerializedLogChunk>
chunklist_addr = 0
for vma in self.vmas:
offset = self.find_log_chunklist_addr(vma)
if offset != 0:
chunklist_addr = vma["vmstart"]+offset
break
if chunklist_addr == 0:
print("logbuf_addr was not found")
return False
# start parsing
is_valid_chunklist = self.has_valid_log(chunklist_addr + self.LOG_ID_MAIN * 0x18)
if not is_valid_chunklist:
print_out_str("There is no valid log in logbuf_addr = 0x%x" %(chunklist_addr-0x60))
return False
# start parsing
self.process_chunklist_and_save(chunklist_addr)
print_out_str("logbuf_addr = 0x%x" % (chunklist_addr-0x60))
print("logcat_vma parse logcat cost "+str((datetime.datetime.now()-startTime).total_seconds())+" s")
return self.is_success
def find_log_chunklist_addr(self, vma):
vma_size = vma["size"]
vma_data = vma["data"]
vma_startaddr = vma["vmstart"]
offset = 0
is_chunk = False
while offset < vma_size:
is_chunk = self.is_log_chunklist_addr(vma_data, offset)
if is_chunk:
log_id = 1
while log_id <= self.LOG_ID_MAX:
is_chunk = self.is_log_chunklist_addr(vma_data, offset+0x18*log_id)
if not is_chunk:
return 0
log_id = log_id + 1
break
offset = offset + 4
if is_chunk:
return offset
return 0
def is_log_chunklist_addr(self, vma_data, offset):
if offset+24 > len(vma_data):
return False
nodes = struct.unpack('<QQQ', vma_data[offset:offset+24])
tail_node_addr = nodes[0]
head_node_addr = nodes[1]
list_count = nodes[2]
if tail_node_addr ==0 or head_node_addr ==0 or list_count > 1000:
return False
next_node_addr = head_node_addr
prev_node = self.read_bytes(head_node_addr, self.addr_length) # prev_node = next_node_addr->prev
if prev_node == 0:
return False
if list_count == 0 and head_node_addr == tail_node_addr: # empty list
return True
index = 0
while next_node_addr != 0 and index < list_count:
next_prev_node = self.read_bytes(next_node_addr, self.addr_length)
if not next_prev_node or next_prev_node != prev_node:
return False
current_node = next_node_addr + self.addr_length *2
is_chunk = self.is_log_chunk_addr(current_node)
if not is_chunk:
return False
if next_node_addr == tail_node_addr: # loop complete
return True
prev_node = next_node_addr
next_node_addr = self.read_bytes(next_node_addr + self.addr_length, self.addr_length)
index = index +1
return False
def is_log_chunk_addr(self, addr):
data_addr = self.read_bytes(addr, self.addr_length)
write_offset = self.read_bytes(addr + 0x10, 4) #--write_offset_
write_active = self.read_bytes(addr + 0x18, 1) #--write_active_
data_size = self.read_bytes(addr + self.addr_length, self.addr_length)
compress_data_addr = self.read_bytes(addr + 0x28, self.addr_length)
compress_data_size = self.read_bytes(addr + 0x28 + self.addr_length, self.addr_length)
if (write_active == 1 and data_addr != 0 and (data_size
!=0) and write_offset !=0 and write_offset < data_size): #uncompressed chunk
return True
elif write_active == 0 and compress_data_addr != 0 and (compress_data_size
!= 0) and write_offset !=0 and compress_data_size<write_offset: #compressed chunk
return True
return False

View File

@@ -0,0 +1,598 @@
# Copyright (c) 2015-2018, 2020-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022 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 linux_list
from parser_util import register_parser, RamParser
from operator import itemgetter
from collections import OrderedDict
@register_parser('--lpm', 'Parse LPM Driver info')
class lpm(RamParser):
def __init__(self, *args):
super(lpm, self).__init__(*args)
self.head = ''
self.output = []
self.clusters = []
self.cpu_possible_bits = None
self.cpu_online_bits = 0
self.lpm_debug = []
self.related_cpus_bits = None
def get_bits(self):
bits_addr = self.ramdump.address_of('cpu_possible_bits')
if bits_addr is None:
bits_addr = self.ramdump.address_of('__cpu_possible_mask')
if bits_addr is None:
self.output.append("NOTE: 'cpu_possible_bits' not found")
return
self.cpu_possible_bits = self.ramdump.read_int(bits_addr)
cpus = bin(self.cpu_possible_bits).count('1')
self.output.append("{}\n".format('Available CPUs'))
for i in self.ramdump.iter_cpus():
self.output.append("{:10}{}:{}\n".format("", "CPU", i))
self.output.append("\n")
if (self.ramdump.kernel_version >= (4, 9, 0)):
if self.ramdump.is_config_defined('CONFIG_SMP'):
runqueues_addr = self.ramdump.address_of('runqueues')
online_offset = self.ramdump.field_offset('struct rq', 'online')
for i in self.ramdump.iter_cpus():
online = self.ramdump.read_int(runqueues_addr + online_offset, cpu=i)
self.cpu_online_bits |= (online << i)
else:
bits_addr = self.ramdump.address_of('cpu_online_bits')
if bits_addr is None:
bits_addr = self.ramdump.address_of('__cpu_online_mask')
if bits_addr is None:
self.output.append("NOTE: 'cpu_online_bits' not found")
return
self.cpu_online_bits = self.ramdump.read_int(bits_addr)
self.output.append("{}\n".format('Online CPUs'))
index = 0
while self.cpu_online_bits:
if self.cpu_online_bits & 1:
self.output.append("{:10}{}:{}\n".format("", "CPU", index))
self.cpu_online_bits = self.cpu_online_bits >> 1
index += 1
self.output.append("{}{}{}".format("\n", "-" * 120, "\n"))
def get_cluster_level_info(self, lpm_cluster):
offset = self.ramdump.field_offset('struct lpm_cluster', 'nlevels')
nlevels = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}\n".format("number of levels", nlevels))
offset = self.ramdump.field_offset('struct lpm_cluster', 'min_child_level')
node = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}\n".format("min child level", node))
offset = self.ramdump.field_offset('struct lpm_cluster', 'default_level')
node = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}\n".format("default level", node))
offset = self.ramdump.field_offset('struct lpm_cluster', 'last_level')
node = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}\n".format("last_level", node))
offset = self.ramdump.field_offset('struct lpm_cluster', 'levels')
levels = lpm_cluster + offset
self.output.append("\n")
cluster_level_size = self.ramdump.sizeof('struct lpm_cluster_level')
for i in range(nlevels):
# ToDo: Need a better way to arrive at the next level info.
level = levels + (i * cluster_level_size)
# Case to handle 'mode'.'mode' removed in kernel version ( >= 4.9) for core power lpm drivers.
if (self.ramdump.kernel_version < (4,9,0) ):
offset = self.ramdump.field_offset('struct lpm_cluster_level', 'mode')
addr = self.ramdump.read_word(level + offset, True)
node = self.ramdump.read_int(addr)
self.output.append("{:20}:{}\n".format("level mode", node))
offset = self.ramdump.field_offset('struct lpm_cluster_level', 'level_name')
addr = self.ramdump.read_word(level + offset, True)
name = self.ramdump.read_cstring(addr, 48)
self.output.append("{:20}:{}\n".format("level name", name))
offset = self.ramdump.field_offset('struct lpm_cluster_level', 'min_child_level')
addr = level + offset
node = self.ramdump.read_int(addr)
self.output.append("{:20}:{}\n".format("min child level", node))
offset = self.ramdump.field_offset('struct lpm_cluster_level', 'num_cpu_votes')
addr = level + offset
node = self.ramdump.read_int(addr)
self.output.append("{:20}:{}({})\n".format("num cpu votes", hex(node).rstrip("L"), bin(node).lstrip("0b")))
offset = self.ramdump.field_offset('struct lpm_cluster_level', 'available')
addr = level + offset
offset = self.ramdump.field_offset('struct lpm_level_avail', 'idle_enabled')
node = self.ramdump.read_bool(addr + offset)
self.output.append("{:20}:{}\n".format("idle_enabled", node))
offset = self.ramdump.field_offset('struct lpm_level_avail', 'suspend_enabled')
node = self.ramdump.read_bool(addr + offset)
self.output.append("{:20}:{}\n".format("suspend_enabled", node))
self.output.append("\n")
def get_cluster_info(self, lpm_cluster):
offset = self.ramdump.field_offset('struct lpm_cluster', 'cluster_name')
addr = self.ramdump.read_word(lpm_cluster + offset, True)
node = self.ramdump.read_cstring(addr, 48)
self.output.append("{:20}:{}\n".format("Cluster Name", node))
offset = self.ramdump.field_offset('struct lpm_cluster', 'child_cpus')
node = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}({})\n".format("child_cpus", hex(node).rstrip("L"), bin(node).lstrip("0b")))
if (self.ramdump.kernel_version >= (3, 18, 0) or
self.ramdump.kernel_version < (3, 14, 0) ):
offset = self.ramdump.field_offset(
'struct lpm_cluster', 'num_children_in_sync')
else:
offset = self.ramdump.field_offset(
'struct lpm_cluster', 'num_childs_in_sync')
node = self.ramdump.read_int(lpm_cluster + offset)
self.output.append("{:20}:{}({})\n".format(
"num_children_in_sync", hex(node).rstrip("L"),
bin(node).lstrip("0b")))
self.output.append("\n")
def lpm_walker(self, lpm_cluster):
if lpm_cluster == self.head:
return
self.clusters.append(lpm_cluster)
def get_pm_domains(self):
gpd_offset = self.ramdump.field_offset('struct generic_pm_domain', 'gpd_list_node')
head = self.ramdump.read_word(self.ramdump.address_of('gpd_list'), True)
self.head = head
gpd_walker = linux_list.ListWalker(self.ramdump, head, gpd_offset)
gpd_walker.walk(head, self.get_pm_domain_info)
def get_pm_domain_info(self, node):
if node == self.head:
return
name_offset = self.ramdump.field_offset('struct generic_pm_domain', 'name')
name = self.ramdump.read_cstring(self.ramdump.read_word(node + name_offset))
if not name:
return
name += ":idle_states:S{}"
state_count = self.ramdump.read_structure_field(node, 'struct generic_pm_domain', 'state_count')
#sanity check for state count , CPUIDLE_STATE_MAX
if state_count >= 10:
return
accounting_time = self.ramdump.read_structure_field(node, 'struct generic_pm_domain', 'accounting_time')
gpd_power_state_size = self.ramdump.sizeof('struct genpd_power_state')
power_state_offset = self.ramdump.field_offset('struct generic_pm_domain', 'states')
for i in range(state_count):
power_state_addr = self.ramdump.read_word(node + power_state_offset + i * gpd_power_state_size)
msec = self.ramdump.read_structure_field(power_state_addr, 'struct genpd_power_state', 'idle_time')
ktime = self.ramdump.read_s64('last_jiffies_update') - accounting_time
ktime = ktime // 10 ** 6
if msec:
msec += ktime
usage = self.ramdump.read_structure_field(power_state_addr, 'struct genpd_power_state', 'usage')
rejected = self.ramdump.read_structure_field(power_state_addr, 'struct genpd_power_state', 'rejected')
residency_ns = self.ramdump.read_structure_field(power_state_addr, 'struct genpd_power_state','residency_ns')
if not msec:
msec = "0"
if not usage:
usage = "0"
if not rejected:
rejected = "0"
if name:
msg = u"{name:30} {msec:30} {usage:30} {rejected:30}".format(name=name.format(i),msec=msec,usage=usage,rejected=rejected)
self.output.append(msg)
self.output.append("\n")
return
def print_pm_domain_info(self):
self.output.append("{}\n".format('Power Domain Info: '))
self.output.append("{:30}{:30}{:30}{:30} \n".format("Power domain", "Time Spent(msec)", "Usage ", "Rejected "))
self.get_pm_domains()
self.output.append("{}{}".format("-" * 81, "\n"))
def get_clusters(self):
lpm_root_node = self.ramdump.read_word(
self.ramdump.address_of('lpm_root_node'), True)
if lpm_root_node is None:
self.output_file.write("NOTE: 'lpm_root_node' not found\n")
return
self.clusters.append(lpm_root_node)
offset = self.ramdump.field_offset('struct lpm_cluster', 'child')
lpm_cluster = self.ramdump.read_word(lpm_root_node + offset, True)
self.head = lpm_root_node + offset
offset = self.ramdump.field_offset('struct lpm_cluster', 'list')
lpm_walker = linux_list.ListWalker(self.ramdump, lpm_cluster, offset)
lpm_walker.walk(lpm_cluster, self.lpm_walker)
def get_cpu_level_info(self, cpu_cluster_base, cpu, cpu_level):
self.output.append("{:20}:{}\n".format("CPU", cpu))
if self.ramdump.kernel_version >= (4,9,0):
cpu_level = cpu_level
else:
cpu_cluster = self.ramdump.read_word(cpu_cluster_base, cpu=cpu)
offset = self.ramdump.field_offset('struct lpm_cluster', 'cpu')
cpu_level = self.ramdump.read_word(cpu_cluster + offset, True)
offset = self.ramdump.field_offset('struct lpm_cpu', 'nlevels')
nlevels = self.ramdump.read_int(cpu_level + offset, True)
self.output.append("{:20}:{}\n".format("number of levels", nlevels))
offset = self.ramdump.field_offset('struct lpm_cpu', 'levels')
levels = cpu_level + offset
self.output.append("\n")
cpu_level_available = self.ramdump.address_of('cpu_level_available')
if cpu_level_available is None:
self.output.append("NOTE: 'cpu_level_available' not found\n")
return
cpu_level_available = cpu_level_available + self.ramdump.sizeof('long') * cpu
cpu_level_available = self.ramdump.read_word(cpu_level_available, True)
for i in range(0, nlevels):
level = levels + (i * self.ramdump.sizeof('struct lpm_cpu_level'))
offset = self.ramdump.field_offset('struct lpm_cpu_level', 'name')
addr = self.ramdump.read_word(level + offset, True)
node = self.ramdump.read_cstring(addr, 48)
self.output.append("{:20}:{}\n".format("level name", node))
# Case to handle 'mode'.'mode' removed in kernel version ( > = 4.9) for core power lpm drivers.
if (self.ramdump.kernel_version < (4,9,0) ):
offset = self.ramdump.field_offset('struct lpm_cpu_level', 'mode')
node = self.ramdump.read_int(level + offset, True)
self.output.append("{:20}:{}\n".format("level mode", node))
level_available = cpu_level_available + i * self.ramdump.sizeof('struct lpm_level_avail')
offset = self.ramdump.field_offset('struct lpm_level_avail', 'idle_enabled')
node = self.ramdump.read_bool(level_available + offset)
self.output.append("{:20}:{}\n".format("idle enabled", node))
offset = self.ramdump.field_offset('struct lpm_level_avail', 'suspend_enabled')
node = self.ramdump.read_bool(level_available + offset, True)
self.output.append("{:20}:{}\n".format("suspend enabled", node))
self.output.append("\n")
self.output.append("{}{}".format("-" * 120, "\n"))
def get_lpm(self):
self.get_clusters()
for i in self.clusters:
self.get_cluster_info(i)
self.get_cluster_level_info(i)
self.output.append("{}{}".format("-" * 120, "\n"))
if self.ramdump.kernel_version >= (4,9,0):
cpu_cluster_base = self.ramdump.address_of('lpm_root_node')
else:
cpu_cluster_base = self.ramdump.address_of('cpu_cluster')
if cpu_cluster_base is None:
self.output.append("NOTE: 'cpu_cluster' not found\n")
return
if self.ramdump.kernel_version >= (4,9,0):
cpu_cluster = self.ramdump.read_word(cpu_cluster_base)
related_cpus_offset = self.ramdump.field_offset('struct lpm_cpu', 'related_cpus')
bits_offset = self.ramdump.field_offset('struct cpumask', 'bits')
offset = self.ramdump.field_offset('struct lpm_cluster', 'cpu')
clust_node_list = ['next','prev']
for clust_node in clust_node_list:
cpunode_offset = self.ramdump.field_offset('struct list_head', clust_node)
offset = offset + cpunode_offset
cpu_level = self.ramdump.read_word(cpu_cluster + offset, True)
self.related_cpus_bits = self.ramdump.read_int(cpu_level + related_cpus_offset + bits_offset, True)
cpus = bin(self.related_cpus_bits).count('1')
cpu_info = self.related_cpus_bits
cpu_count = 0
while (cpu_info):
if ( cpu_info & 0x1):
self.get_cpu_level_info(cpu_cluster_base, cpu_count,cpu_level)
cpu_info = cpu_info >> 0x1
cpu_count = cpu_count + 1
else:
cpus = bin(self.cpu_possible_bits).count('1')
for i in range(0, cpus):
self.get_cpu_level_info(cpu_cluster_base, i,0x0)
def get_time_stats(self, tstats, nlevels):
for i in range(nlevels):
lstats = tstats + i * self.ramdump.sizeof('struct level_stats')
offset = self.ramdump.field_offset('struct level_stats', 'name')
addr = self.ramdump.read_word(lstats + offset, True)
self.output.append("{:20}:{}\n".format("lpm name", self.ramdump.read_cstring(addr + offset, 48)))
offset = self.ramdump.field_offset('struct level_stats', 'success_count')
self.output.append("{:20}:{}\n".format("success_count", self.ramdump.read_int(lstats + offset, True)))
offset = self.ramdump.field_offset('struct level_stats', 'failed_count')
self.output.append("{:20}:{}\n".format("failed_count", self.ramdump.read_int(lstats + offset, True)))
self.output.append("\n")
def get_cluster_stats(self, cluster):
offset = self.ramdump.field_offset('struct lpm_cluster', 'stats')
stats = self.ramdump.read_word(cluster + offset, True)
offset = self.ramdump.field_offset('struct lpm_stats', 'name')
self.output.append("{} {}\n\n".format(self.ramdump.read_cstring(stats + offset, 48), "lpm stats"))
offset = self.ramdump.field_offset('struct lpm_stats', 'num_levels')
nlevels = self.ramdump.read_int(stats + offset, True)
offset = self.ramdump.field_offset('struct lpm_stats', 'time_stats')
tstats = self.ramdump.read_word(stats + offset, True)
self.get_time_stats(tstats, nlevels)
self.output.append("{}{}".format("-" * 120, "\n"))
def get_cpu_stats(self, cpu_stats_base, cpu):
stats = cpu_stats_base + self.ramdump.per_cpu_offset(cpu)
offset = self.ramdump.field_offset('struct lpm_stats', 'name')
self.output.append("{} {}\n\n".format(self.ramdump.read_cstring(stats + offset, 48), "lpm stats"))
offset = self.ramdump.field_offset('struct lpm_stats', 'num_levels')
nlevels = self.ramdump.read_int(stats + offset, True)
offset = self.ramdump.field_offset('struct lpm_stats', 'time_stats')
tstats = self.ramdump.read_word(stats + offset, True)
self.get_time_stats(tstats, nlevels)
self.output.append("{}{}".format("-" * 120, "\n"))
def get_stats(self):
for i in self.clusters:
self.get_cluster_stats(i)
cpu_stats_base = self.ramdump.address_of('cpu_stats')
if cpu_stats_base is None:
self.output.append("NOTE: 'cpu_stats' not found\n")
return
cpus = bin(self.cpu_possible_bits).count('1')
for i in self.ramdump.iter_cpus():
self.get_cpu_stats(cpu_stats_base, i)
def get_debug_phys(self):
lpm_debug_phys = self.ramdump.address_of('lpm_debug_phys')
if lpm_debug_phys is None:
self.output.append("NOTE: 'lpm_debug data' not found\n")
return
lpm_debug_phys = self.ramdump.read_word(lpm_debug_phys, True)
for i in range(0, 256):
debug = []
addr = lpm_debug_phys + i * self.ramdump.sizeof('struct lpm_debug')
offset = self.ramdump.field_offset('struct lpm_debug', 'time')
time = self.ramdump.read_word(addr + offset, False)
debug.append(time)
offset = self.ramdump.field_offset('struct lpm_debug', 'evt')
evt = self.ramdump.read_int(addr + offset, False)
debug.append(evt)
offset = self.ramdump.field_offset('struct lpm_debug', 'cpu')
cpu = self.ramdump.read_int(addr + offset, False)
debug.append(cpu)
offset = self.ramdump.field_offset('struct lpm_debug', 'arg1')
arg1 = self.ramdump.read_int(addr + offset, False)
debug.append(arg1)
offset = self.ramdump.field_offset('struct lpm_debug', 'arg2')
arg2 = self.ramdump.read_int(addr + offset, False)
debug.append(arg2)
offset = self.ramdump.field_offset('struct lpm_debug', 'arg3')
arg3 = self.ramdump.read_int(addr + offset, False)
debug.append(arg3)
offset = self.ramdump.field_offset('struct lpm_debug', 'arg4')
arg4 = self.ramdump.read_int(addr + offset, False)
debug.append(arg4)
self.lpm_debug.append(debug)
def print_debug_phys(self):
debug = []
lpm_debug = []
self.output.append("\n")
self.output.append("{:16}".format("TimeStamp"))
self.output.append("{:8} {:8} {:8} ".format("Event", "CPU", "arg1"))
self.output.append("{:16}{:16}{:16}\n".format("arg2", "arg3", "arg4"))
self.output.append("{}{}".format("-" * 120, "\n"))
lpm_debug = sorted(self.lpm_debug, key=itemgetter(0))
for i in range(len(lpm_debug)):
debug = lpm_debug[i]
for j in range(len(debug)):
if j == 0 or j > 3:
self.output.append("{:16}".format(hex(debug[j]).rstrip("L")))
else:
self.output.append("{}{:8}".format(debug[j], ""))
self.output.append("\n")
def get_cpuidle_usage_details(self, state_usage_addr):
usage_stats = OrderedDict()
usage_stats['disable'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
'disabled')
usage_stats['usage'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
'usage')
usage_stats['time_ns'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
'time_ns')
usage_stats['rejected'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
'rejected')
usage_stats['s2idle_usage'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
's2idle_usage')
usage_stats['s2idle_time'] = self.ramdump.read_structure_field(state_usage_addr,
'struct cpuidle_state_usage',
's2idle_time')
return usage_stats
def print_state_description(self, data):
self.output.append("\n\n")
self.output.append("CPUIdle States Description\n\n")
self.output.append("{}{}".format("-" * 120, "\n"))
self.output.append(" {:^30} {:^50}\n".format("State", "Description"))
for key, value in data.items():
self.output.append("{:^30} {:^50}\n".format(key, value))
def print_cpuidle_stats(self, data):
self.output.append("\n\n")
self.output.append("CPUIdle Statistics\n\n")
self.output.append("{:^8}{:^20}{:^16}{:^16}{:^25}{:^16}{:^16}{:^16}\n".format("CPU",
"State", "Disabled", "Usage", "Time_ns",
"Rejected", "S2idle_usage", "S2idle_time"))
self.output.append("{}{}".format("-" * 120, "\n"))
for cpu in data.keys():
self.output.append("{:^8}\n".format(cpu))
for state in data[cpu].keys():
self.output.append("{:^8}".format(""))
self.output.append("{:^20}".format(state))
for key, value in data[cpu][state].items():
if not value:
value = "0"
if key == "time_ns":
self.output.append("{:^25}".format(value))
else:
self.output.append("{:^16}".format(value))
self.output.append("\n")
self.output.append("\n{}{}".format("-" * 120, "\n"))
def get_cpuidle_statistics(self):
try:
# Read cpuidle_device
cpuidle_devices = self.ramdump.address_of('cpuidle_devices')
cpuidle_drivers = self.ramdump.address_of('cpuidle_drivers')
usage_details = {}
state_dict = OrderedDict()
for i in self.ramdump.iter_cpus():
# Check for current cpu
cpuidle_dev = self.ramdump.read_word(cpuidle_devices, cpu=i)
cpuidle_drv = self.ramdump.read_word(cpuidle_drivers, cpu=i)
state_count = self.ramdump.read_structure_field(cpuidle_drv,
'struct cpuidle_driver',
'state_count')
cpuidle_drv_states = cpuidle_drv + self.ramdump.field_offset(
'struct cpuidle_driver',
'states')
if (self.ramdump.kernel_version >= (5, 10, 0)):
cpuidle_kobj_offset = self.ramdump.field_offset(
'struct cpuidle_device',
'kobjs')
kobjs_base = cpuidle_dev + cpuidle_kobj_offset
cpu = "CPU{}".format(i)
usage_details[cpu] = {}
for state_idx in range(state_count):
if state_count < 10: # CPUIDLE_STATE_MAX
temp = kobjs_base + state_idx * self.ramdump.sizeof('void*')
cpuidle_state_usage_base = self.ramdump.read_u64(kobjs_base + state_idx * self.ramdump.sizeof('void*'))
if cpuidle_state_usage_base != 0x0:
state_usage_offset = cpuidle_state_usage_base + self.ramdump.field_offset('struct cpuidle_state_kobj','state_usage')
state_usage_addr = self.ramdump.read_u64(state_usage_offset)
cpuidle_drv_offset = cpuidle_state_usage_base + self.ramdump.field_offset('struct cpuidle_state_kobj','state')
cpuidle_drv_state = self.ramdump.read_u64(cpuidle_drv_offset)
state_name = self.ramdump.read_cstring(cpuidle_drv_state, 16)
desc_offset = self.ramdump.field_offset('struct cpuidle_state', 'desc')
state_desc = self.ramdump.read_cstring(cpuidle_drv_state + desc_offset, 32)
# Usage data per cpu per state
state = "state{}({})".format(state_idx, state_name)
state_dict[state_name] = state_desc
usage_details[cpu][state] = self.get_cpuidle_usage_details(state_usage_addr)
else:
usage_details[cpu][state] = 0
else:
break
else:
cpuidle_state_usage_offset = self.ramdump.field_offset(
'struct cpuidle_device',
'states_usage')
cpuidle_state_usage_base = cpuidle_dev + cpuidle_state_usage_offset
cpu = "CPU{}".format(i)
usage_details[cpu] = {}
for state in range(state_count):
if state_count < 10: # CPUIDLE_STATE_MAX
state_usage_addr = cpuidle_state_usage_base + \
state * self.ramdump.sizeof('struct cpuidle_state_usage')
cpuidle_drv_state = cpuidle_drv_states + \
state * self.ramdump.sizeof('struct cpuidle_state')
state_name = self.ramdump.read_cstring(cpuidle_drv_state, 16)
desc_offset = self.ramdump.field_offset('struct cpuidle_state', 'desc')
state_desc = self.ramdump.read_cstring(cpuidle_drv_state + desc_offset, 32)
# Usage data per cpu per state
state = "state{}({})".format(state, state_name)
state_dict[state_name] = state_desc
usage_details[cpu][state] = self.get_cpuidle_usage_details(state_usage_addr)
else:
break
self.print_state_description(state_dict)
self.print_cpuidle_stats(usage_details)
except Exception as err:
self.output.append("\nUnable to extract CPUIdle Statistics\n\n")
self.output.append("\n{}{}".format("-" * 120, "\n"))
def parse(self):
self.output_file = self.ramdump.open_file('lpm.txt')
self.get_bits()
self.get_lpm()
self.get_stats()
self.get_debug_phys()
self.print_debug_phys()
self.print_pm_domain_info()
self.get_cpuidle_statistics()
for i in self.output:
self.output_file.write(i)
self.output_file.close()

View File

@@ -0,0 +1,198 @@
"""
Copyright (c) 2016, 2020 The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Changes from Qualcomm Innovation Center are provided under the following license:
Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause-Clear
"""
from parser_util import register_parser, RamParser
from parser_util import cleanupString
TASK_NAME_LENGTH = 16
def do_dump_lsof_info(self, ramdump, lsof_info):
for task_struct in ramdump.for_each_process():
file_descriptor = []
task_comm_offset = ramdump.field_offset('struct task_struct', 'comm')
client_name = ramdump.read_cstring(task_struct + task_comm_offset, TASK_NAME_LENGTH)
task_pid = ramdump.read_structure_field(task_struct, 'struct task_struct', 'pid')
files = ramdump.read_structure_field(task_struct, 'struct task_struct', 'files')
file_descriptor.append(files)
str_task_file = '\n Task: 0x{0:x}, comm: {1}, pid : {2:1}, files : 0x{3:x}'
lsof_info.write(str_task_file.format(task_struct, client_name, task_pid, files))
parse_task(self, ramdump, task_struct, lsof_info)
for curr in ramdump.for_each_thread(task_struct):
file_pointer = ramdump.read_structure_field(curr, 'struct task_struct', 'files')
#skip if fd is same as parent process or other child process or fd is Null
if ((len(file_descriptor) and file_pointer in file_descriptor) or file_pointer == 0x0):
continue
else:
file_descriptor.append((file_pointer))
str_task_file = '\n Thread: 0x{0:x}, thread_name: {1}, thread_pid : {2:1}, thread_files : 0x{3:x}'
lsof_info.write(str_task_file.format(curr,
ramdump.read_cstring(curr + task_comm_offset, TASK_NAME_LENGTH),
ramdump.read_structure_field(curr, 'struct task_struct', 'pid'),
file_pointer))
parse_task(self, ramdump, curr, lsof_info)
lsof_info.write("\n*********************************")
def get_dname_of_dentry(ramdump, dentry):
dentry_name_offset = ramdump.field_offset(
'struct dentry', 'd_name')
len_offset = ramdump.field_offset(
'struct qstr', 'len')
qst_name_offset = ramdump.field_offset(
'struct qstr', 'name')
name_address = ramdump.read_word(dentry + dentry_name_offset + qst_name_offset)
len_address = dentry + dentry_name_offset + len_offset
len = ramdump.read_u32(len_address)
name = cleanupString(ramdump.read_cstring(
name_address, len))
return name
def get_pathname_by_file(ramdump, file):
f_pathoffset = ramdump.field_offset(
'struct file', 'f_path')
f_path = f_pathoffset + file
mnt_offset_in_path = ramdump.field_offset('struct path', 'mnt')
mnt = ramdump.read_word(f_path + mnt_offset_in_path)
mnt_offset_in_mount = ramdump.field_offset('struct mount', 'mnt')
mnt_parent_offset = ramdump.field_offset('struct mount', 'mnt_parent')
mount = mnt - mnt_offset_in_mount
mnt_mountpoint_offset = ramdump.field_offset(
'struct mount', 'mnt_mountpoint')
d_parent_offset = ramdump.field_offset(
'struct dentry', 'd_parent')
mnt_parent_pre = 0
mnt_parent = mount
mount_name = []
while mnt_parent_pre != mnt_parent:
mnt_parent_pre = mnt_parent
mnt_mountpoint = ramdump.read_word(mnt_parent + mnt_mountpoint_offset)
name = get_dname_of_dentry(ramdump, mnt_mountpoint)
mnt_parent = ramdump.read_word(mnt_parent + mnt_parent_offset)
if name == None or name == '/':
break
if mnt_parent == 0:
break
mount_name.append(name)
# walk to get the fullname of mountpoint
d_parent = ramdump.read_word(mnt_mountpoint + d_parent_offset)
d_parent_pre = 0
while d_parent_pre != d_parent:
d_parent_pre = d_parent
name = get_dname_of_dentry(ramdump, d_parent)
d_parent = ramdump.read_word(d_parent + d_parent_offset)
if name == None or name == '/':
break
mount_name.append(name)
if d_parent == 0:
break
dentry = ramdump.read_structure_field(
f_path, 'struct path', 'dentry')
d_parent = dentry
d_parent_pre = 0
names = []
while d_parent_pre != d_parent:
d_parent_pre = d_parent
name = get_dname_of_dentry(ramdump, d_parent)
d_parent = ramdump.read_word(d_parent + d_parent_offset)
if name == None or name == '/':
break
names.append(name)
if d_parent == 0:
break
full_name = ''
for item in mount_name:
names.append(item)
names.reverse()
for item in names:
full_name += '/' + item
return full_name
def parse_task(self, ramdump, task, lsof_info):
index = 0
if self.ramdump.arm64:
addressspace = 8
else:
addressspace = 4
files = ramdump.read_structure_field(
task, 'struct task_struct', 'files')
if files == 0x0:
return
fdt = ramdump.read_structure_field(
files, 'struct files_struct', 'fdt')
max_fds = ramdump.read_structure_field(
fdt, 'struct fdtable', 'max_fds')
fd = ramdump.read_structure_field(
fdt, 'struct fdtable', 'fd')
ion_str = "\n {0:8d} file : 0x{1:16x} {2:32s} {3:32s} client : 0x{4:x}"
str = "\n {0:8d} file : 0x{1:16x} {2:32s} {3:32s}"
while index < max_fds:
file = ramdump.read_word(fd + (index * addressspace))
if file != 0:
fop = ramdump.read_structure_field(
file, 'struct file', 'f_op')
priv_data = ramdump.read_structure_field(
file, 'struct file', 'private_data')
look = ramdump.unwind_lookup(fop)
if look is None:
index = index + 1
continue
fop, offset = look
iname = get_pathname_by_file(ramdump, file)
if fop.find("ion_fops", 0, 8) != -1:
lsof_info.write(ion_str.format(
index, file, fop, iname, priv_data))
else:
lsof_info.write(str.format(index, file, fop, iname))
index = index + 1
return
@register_parser('--print-lsof', 'Print list of open files', optional=True)
class DumpLsof(RamParser):
def parse(self):
with self.ramdump.open_file('lsof.txt') as lsof_info:
if (self.ramdump.kernel_version < (3, 18, 0)):
lsof_info.write('Kernel version 3.18 \
and above are supported, current version {0}.\
{1}'.format(self.ramdump.kernel_version[0],
self.ramdump.kernel_version[1]))
return
do_dump_lsof_info(self, self.ramdump, lsof_info)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
from parser_util import register_parser, RamParser, cleanupString
class memory_area:
def __init__(self, name, base, size):
self.name = name
self.base = base
self.size = size
@register_parser('--print-memory_map', 'Print memory_map information')
class memory_map(RamParser):
def get_reserved_mem(self, ramdump, list_memory_area):
reserved_mem_addr = ramdump.address_of('reserved_mem')
reserved_mem_count_addr = ramdump.address_of('reserved_mem_count')
reserved_mem_count = ramdump.read_int(reserved_mem_count_addr)
base_offset = ramdump.field_offset('struct reserved_mem', 'base')
size_offset = ramdump.field_offset('struct reserved_mem', 'size')
for i in range(0, reserved_mem_count):
addr_index = ramdump.array_index(reserved_mem_addr, 'struct reserved_mem', i)
name = ramdump.read_structure_cstring(addr_index, 'struct reserved_mem', 'name')
if ramdump.arm64:
base = ramdump.read_u64(addr_index + base_offset)
size = ramdump.read_word(addr_index + size_offset)
else:
base = ramdump.read_u32(addr_index + base_offset)
size = ramdump.read_u32(addr_index + size_offset)
memory_area_instance = memory_area(name, base, size)
list_memory_area.append(memory_area_instance)
list_memory_area.sort(key=lambda c: c.base)
def get_kernel_resource(self, list_memory_area):
mem_res_mem_addr = self.ramdump.address_of('mem_res')
start_offset = self.ramdump.field_offset('struct resource', 'start')
end_offset = self.ramdump.field_offset('struct resource', 'end')
start = 0
end = 0
for i in range(0, 2):
mem_res_mem_addr_index = self.ramdump.array_index(mem_res_mem_addr, 'struct resource', i)
name = self.ramdump.read_structure_cstring(mem_res_mem_addr_index, 'struct resource', 'name')
if self.ramdump.arm64:
start = self.ramdump.read_u64(mem_res_mem_addr_index + start_offset)
end = self.ramdump.read_u64(mem_res_mem_addr_index + end_offset)
else:
start = self.ramdump.read_u32(mem_res_mem_addr_index + start_offset)
end = self.ramdump.read_u32(mem_res_mem_addr_index + end_offset)
memory_area_instance = memory_area(name, start, end - start)
list_memory_area.append(memory_area_instance)
def get_cma_areas(self, ramdump, list_memory_area):
cma_area_count = ramdump.read_u32('cma_area_count')
cma_area_base_addr = ramdump.address_of('cma_areas')
for cma_index in range(0, cma_area_count):
cma_area = ramdump.array_index(cma_area_base_addr, 'struct cma', cma_index)
base_pfn = ramdump.read_structure_field(
cma_area, 'struct cma', 'base_pfn')
cma_size = ramdump.read_structure_field(
cma_area, 'struct cma', 'count')
if (ramdump.kernel_version >= (5, 10, 0)):
name_addr_offset = ramdump.field_offset('struct cma', 'name')
name_addr = (cma_area + name_addr_offset)
name = ramdump.read_cstring(name_addr, 64)
else:
name_addr = ramdump.read_structure_field(cma_area, 'struct cma', 'name')
name = ramdump.read_cstring(name_addr, 48)
memory_area_instance = memory_area(name, base_pfn << 12, cma_size << 12)
if any(x.base == memory_area_instance.base for x in list_memory_area) == False:
list_memory_area.append(memory_area_instance)
def get_memory_block(self, ramdump, list_memory_area):
cnt = ramdump.read_structure_field('memblock', 'struct memblock', 'memory.cnt')
total_size = ramdump.read_structure_field('memblock', 'struct memblock', 'memory.total_size')
region = ramdump.read_structure_field('memblock', 'struct memblock', 'memory.regions')
for i in range(cnt):
start = ramdump.read_structure_field(region, 'struct memblock_region', 'base')
size = ramdump.read_structure_field(region, 'struct memblock_region', 'size')
memory_area_instance = memory_area("memory", start, size)
if any(x.base == memory_area_instance.base for x in list_memory_area) == False:
list_memory_area.append(memory_area_instance)
cnt = ramdump.read_structure_field('memblock', 'struct memblock', 'reserved.cnt')
region = ramdump.read_structure_field('memblock', 'struct memblock', 'reserved.regions')
for i in range(cnt):
start = ramdump.read_structure_field(region, 'struct memblock_region', 'base')
size = ramdump.read_structure_field(region, 'struct memblock_region', 'size')
memory_area_instance = memory_area("reserved", start, size)
if any(x.base == memory_area_instance.base for x in list_memory_area) == False:
list_memory_area.append(memory_area_instance)
def get_iomem_resource(self, ramdump, list_memory_area):
iomem_resource_addr = ramdump.address_of('iomem_resource')
iomem_resource_start = iomem_resource_addr
start = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'start')
end = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'end')
offset_name = ramdump.field_offset('struct resource', 'name')
sibling = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'sibling')
child = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'child')
name_address = ramdump.read_pointer(iomem_resource_start + offset_name)
name = cleanupString(ramdump.read_cstring(name_address, 16))
iomem_resource_start = child
while iomem_resource_start != 0:
start = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'start')
end = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'end')
offset_name = ramdump.field_offset('struct resource', 'name')
sibling = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'sibling')
child = ramdump.read_structure_field(iomem_resource_start, 'struct resource', 'child')
name_address = ramdump.read_pointer(iomem_resource_start + offset_name)
name = cleanupString(ramdump.read_cstring(name_address))
memory_area_instance = memory_area(name, start, end - start)
list_memory_area.append(memory_area_instance)
if sibling == 0:
break
iomem_resource_start = sibling
def parse(self):
list_memory_area = []
self.get_reserved_mem(self.ramdump, list_memory_area)
self.get_cma_areas(self.ramdump, list_memory_area)
self.get_kernel_resource(list_memory_area)
self.get_memory_block(self.ramdump, list_memory_area)
self.get_iomem_resource(self.ramdump, list_memory_area)
fmap = open(self.ramdump.outdir + "/memory_map.txt", "w")
print("name base end size size in KB\n", file = fmap)
new_list = sorted(list_memory_area, key=lambda c: c.base, reverse=False)
for i in range(len(new_list)):
memory_area_instance = new_list[i]
print("----------------------------------------------------------------------------------------------------------------------------------------------",
file=fmap)
print("%-64s 0x%-16x 0x%-16x 0x%-16x %16d" % (memory_area_instance.name,
memory_area_instance.base,
memory_area_instance.base + memory_area_instance.size,
memory_area_instance.size, memory_area_instance.size / 1024),
file=fmap)
fmap.close()

View File

@@ -0,0 +1,365 @@
# Copyright (c) 2016-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.
from parser_util import register_parser, RamParser
import os
import linux_list as llist
import linux_radix_tree
VM_ALLOC = 0x00000002
@register_parser('--print-memstat', 'Print memory stats ')
class MemStats(RamParser):
def __init__(self, dump):
super(MemStats, self).__init__(dump)
if (self.ramdump.kernel_version >= (5, 4)):
self.zram_dev_rtw = linux_radix_tree.RadixTreeWalker(self.ramdump)
self.zram_mem_mb = 0
def list_func(self, vmlist):
vm = self.ramdump.read_word(vmlist + self.vm_offset)
if vm is None:
return
pages = self.ramdump.read_structure_field(
vm, 'struct vm_struct', 'nr_pages')
vm_flags = self.ramdump.read_structure_field(
vm, 'struct vm_struct', 'flags')
if vm_flags is None:
return
if (vm_flags & VM_ALLOC == VM_ALLOC):
self.vmalloc_size = self.vmalloc_size + pages
def pages_to_mb(self, pages):
val = 0
if pages != 0:
val = ((pages * 4) // 1024)
return val
def bytes_to_mb(self, bytes):
val = 0
if bytes != 0:
val = (bytes // 1024) // 1024
return val
def pages_to_mb(self, pages):
val = 0
if pages != 0:
val = (pages * 4) // 1024
return val
def calculate_vmalloc(self):
if self.ramdump.address_of('nr_vmalloc_pages') is None:
next_offset = self.ramdump.field_offset('struct vmap_area', 'list')
vmlist = self.ramdump.read_word('vmap_area_list')
vm_offset = self.ramdump.field_offset('struct vmap_area', 'vm')
self.vm_offset = vm_offset
list_walker = llist.ListWalker(self.ramdump, vmlist, next_offset)
list_walker.walk(vmlist, self.list_func)
self.vmalloc_size = self.pages_to_mb(self.vmalloc_size)
else:
val = self.ramdump.read_word('nr_vmalloc_pages')
self.vmalloc_size = self.pages_to_mb(val)
def calculate_vm_stat(self):
# Other memory : NR_ANON_PAGES + NR_FILE_PAGES + NR_PAGETABLE \
# + NR_KERNEL_STACK - NR_SWAPCACHE
vmstat_anon_pages = self.ramdump.read_word(
'vm_stat[NR_ANON_PAGES]')
vmstat_file_pages = self.ramdump.read_word(
'vm_stat[NR_FILE_PAGES]')
vmstat_pagetbl = self.ramdump.read_word(
'vm_stat[NR_PAGETABLE]')
vmstat_kernelstack = self.ramdump.read_word(
'vm_stat[NR_KERNEL_STACK]')
vmstat_swapcache = self.ramdump.read_word(
'vm_stat[NR_SWAPCACHE]')
other_mem = (vmstat_anon_pages + vmstat_file_pages + vmstat_pagetbl +
vmstat_kernelstack - vmstat_swapcache)
other_mem = self.pages_to_mb(other_mem)
return other_mem
def calculate_cached(self):
if self.ramdump.kernel_version >= (4, 9):
vmstat_file_pages = self.ramdump.read_word(
'vm_node_stat[NR_FILE_PAGES]')
cached = self.pages_to_mb(vmstat_file_pages)
else:
vmstat_file_pages = self.ramdump.read_word(
'vm_stat[NR_FILE_PAGES]')
cached = self.pages_to_mb(vmstat_file_pages)
return cached
def calculate_vm_node_zone_stat(self):
# Other memory : NR_ANON_MAPPED + NR_FILE_PAGES + NR_PAGETABLE \
# + NR_KERNEL_STACK_KB
vmstat_anon_pages = self.ramdump.read_word(
'vm_node_stat[NR_ANON_MAPPED]')
vmstat_file_pages = self.ramdump.read_word(
'vm_node_stat[NR_FILE_PAGES]')
if self.ramdump.kernel_version >= (5, 15):
vmstat_pagetbl = self.ramdump.read_word(
'vm_node_stat[NR_PAGETABLE]')
vmstat_kernelstack = self.ramdump.read_word(
'vm_node_stat[NR_KERNEL_STACK_KB]')
else:
vmstat_pagetbl = self.ramdump.read_word(
'vm_zone_stat[NR_PAGETABLE]')
vmstat_kernelstack = self.ramdump.read_word(
'vm_zone_stat[NR_KERNEL_STACK_KB]')
other_mem = (vmstat_anon_pages + vmstat_file_pages + vmstat_pagetbl +
(vmstat_kernelstack // 4))
other_mem = self.pages_to_mb(other_mem)
return other_mem
def calculate_ionmem(self):
if self.ramdump.kernel_version >= (5, 10):
grandtotal = 0
elif self.ramdump.kernel_version >= (5, 4):
grandtotal = self.ramdump.read_u64('total_heap_bytes')
else:
number_of_ion_heaps = self.ramdump.read_int('num_heaps')
heap_addr = self.ramdump.read_word('heaps')
offset_total_allocated = \
self.ramdump.field_offset(
'struct ion_heap', 'total_allocated')
size = self.ramdump.sizeof(
'((struct ion_heap *)0x0)->total_allocated')
if offset_total_allocated is None:
return "ion buffer debugging change is not there in this kernel"
if self.ramdump.arm64:
addressspace = 8
else:
addressspace = 4
heap_addr_array = []
grandtotal = 0
for i in range(0, number_of_ion_heaps):
heap_addr_array.append(heap_addr + i * addressspace)
temp = self.ramdump.read_word(heap_addr_array[i])
if size == 4:
total_allocated = self.ramdump.read_int(
temp + offset_total_allocated)
if size == 8:
total_allocated = self.ramdump.read_u64(
temp + offset_total_allocated)
if total_allocated is None:
total_allocated = 0
break
grandtotal = grandtotal + total_allocated
grandtotal = self.bytes_to_mb(grandtotal)
return grandtotal
def calculate_zram_dev_mem_allocated(self, zram):
mem_pool = zram + self.ramdump.field_offset('struct zram', 'mem_pool')
mem_pool = self.ramdump.read_word(mem_pool)
pages_allocated = mem_pool + self.ramdump.field_offset('struct zs_pool',
'pages_allocated')
stat_val = self.ramdump.read_word(pages_allocated)
if stat_val is None:
stat_val = 0
else:
stat_val = self.pages_to_mb(stat_val)
self.zram_mem_mb += stat_val
def print_mem_stats(self, out_mem_stat):
# Total memory
if(self.ramdump.kernel_version > (4, 20, 0)):
total_mem = self.ramdump.read_word('_totalram_pages')
else:
total_mem = self.ramdump.read_word('totalram_pages')
total_mem = self.pages_to_mb(total_mem)
if (self.ramdump.kernel_version < (4, 9, 0)):
# Free Memory
total_free = self.ramdump.read_word('vm_stat[NR_FREE_PAGES]')
total_free = self.pages_to_mb(total_free)
# slab Memory
slab_rec = \
self.ramdump.read_word('vm_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = \
self.ramdump.read_word('vm_stat[NR_SLAB_UNRECLAIMABLE]')
total_slab = self.pages_to_mb(slab_rec + slab_unrec)
#others
other_mem = self.calculate_vm_stat()
else:
# Free Memory
total_free = self.ramdump.read_word('vm_zone_stat[NR_FREE_PAGES]')
total_free = self.pages_to_mb(total_free)
# slab Memory
if self.ramdump.kernel_version >= (5, 10):
slab_rec = self.ramdump.read_word(
'vm_node_stat[NR_SLAB_RECLAIMABLE_B]')
slab_unrec = self.ramdump.read_word(
'vm_node_stat[NR_SLAB_UNRECLAIMABLE_B]')
elif (self.ramdump.kernel_version >= (4, 14)):
slab_rec = self.ramdump.read_word(
'vm_node_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = self.ramdump.read_word(
'vm_node_stat[NR_SLAB_UNRECLAIMABLE]')
else:
slab_rec = self.ramdump.read_word(
'vm_zone_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = self.ramdump.read_word(
'vm_zone_stat[NR_SLAB_UNRECLAIMABLE]')
total_slab = self.pages_to_mb(slab_rec + slab_unrec)
# others
other_mem = self.calculate_vm_node_zone_stat()
cached = self.calculate_cached()
# ion memory
ion_mem = self.calculate_ionmem()
# kgsl memory
# Duplicates gpuinfo_510.py@parse_kgsl_mem()'s 'KGSL Total'
try:
kgsl_memory = self.ramdump.read_word(
'kgsl_driver.stats.page_alloc')
kgsl_memory += self.ramdump.read_word(
'kgsl_driver.stats.coherent')
kgsl_memory += self.ramdump.read_word(
'kgsl_driver.stats.secure')
kgsl_memory = self.bytes_to_mb(kgsl_memory)
except TypeError as e:
out_mem_stat.write("Failed to retrieve total kgsl memory\n")
kgsl_memory = 0
# zcompressed ram
if self.ramdump.kernel_version >= (4, 4):
if self.ramdump.kernel_version >= (4, 14):
zram_index_idr = self.ramdump.address_of('zram_index_idr')
else:
zram_index_idr = self.ramdump.read_word('zram_index_idr')
if zram_index_idr is None:
stat_val = 0
else:
#'struct radix_tree_root' was replaced by 'struct xarray' on kernel 5.4+
if self.ramdump.kernel_version >= (5, 4):
self.zram_dev_rtw.walk_radix_tree(zram_index_idr,
self.calculate_zram_dev_mem_allocated)
stat_val = self.zram_mem_mb
else:
if self.ramdump.kernel_version >= (4, 14):
idr_layer_ary_offset = self.ramdump.field_offset(
'struct radix_tree_root', 'rnode')
idr_layer_ary = self.ramdump.read_word(zram_index_idr +
idr_layer_ary_offset)
else:
idr_layer_ary_offset = self.ramdump.field_offset(
'struct idr_layer', 'ary')
idr_layer_ary = self.ramdump.read_word(zram_index_idr +
idr_layer_ary_offset)
try:
zram_meta = idr_layer_ary + self.ramdump.field_offset(
'struct zram', 'meta')
zram_meta = self.ramdump.read_word(zram_meta)
mem_pool = zram_meta + self.ramdump.field_offset(
'struct zram_meta', 'mem_pool')
mem_pool = self.ramdump.read_word(mem_pool)
except TypeError:
mem_pool = idr_layer_ary + self.ramdump.field_offset(
'struct zram', 'mem_pool')
mem_pool = self.ramdump.read_word(mem_pool)
if mem_pool is None:
stat_val = 0
else:
page_allocated = mem_pool + self.ramdump.field_offset(
'struct zs_pool', 'pages_allocated')
stat_val = self.ramdump.read_word(page_allocated)
if stat_val is None:
stat_val = 0
stat_val = self.pages_to_mb(stat_val)
else:
zram_devices_word = self.ramdump.read_word('zram_devices')
if zram_devices_word is not None:
zram_devices_stat_offset = self.ramdump.field_offset(
'struct zram', 'stats')
stat_addr = zram_devices_word + zram_devices_stat_offset
stat_val = self.ramdump.read_u64(stat_addr)
stat_val = self.bytes_to_mb(stat_val)
else:
stat_val = 0
self.out_mem_stat = out_mem_stat
self.vmalloc_size = 0
# vmalloc area
self.calculate_vmalloc()
# Output prints
out_mem_stat.write('{0:30}: {1:8} MB'.format(
"Total RAM", total_mem))
out_mem_stat.write('\n{0:30}: {1:8} MB\n'.format(
"Free memory:", total_free))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"Total Slab memory:", total_slab))
if self.ramdump.kernel_version >= (5, 10):
log_location = os.path.dirname(out_mem_stat.name)
try:
dma_heap_file = os.path.join(log_location, "total_dma_heap.txt")
if os.path.isfile(dma_heap_file):
fin = open(dma_heap_file, 'r')
fin_list = fin.readlines()
fin.close()
ion_mem = int(fin_list[0].split(" ")[-1].replace("MB", ""))
out_mem_stat.write("\n{0:30}: {1:8} MB".format("Total DMA memory", ion_mem))
else:
out_mem_stat.write("\n{0:30}: Please parse ionbuffer first, use --print-ionbuffer.".format(
"Total DMA memory"))
except:
ion_mem = "Please refer total_dma_heap.txt"
out_mem_stat.write('\nTotal ion memory: Please refer total_dma_heap.txt')
else:
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"Total ion memory:", ion_mem))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"KGSL ", kgsl_memory))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"ZRAM compressed ", stat_val))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"vmalloc ", self.vmalloc_size))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"Others ", other_mem))
out_mem_stat.write('\n{0:30}: {1:8} MB'.format(
"Cached ",cached))
if type(ion_mem) is str:
accounted_mem = total_free + total_slab + kgsl_memory + stat_val + \
self.vmalloc_size + other_mem
else:
accounted_mem = total_free + total_slab + ion_mem + kgsl_memory + \
stat_val + self.vmalloc_size + other_mem
unaccounted_mem = total_mem - accounted_mem
out_mem_stat.write('\n\n{0:30}: {1:8} MB'.format(
"Total Unaccounted Memory ",unaccounted_mem))
def parse(self):
with self.ramdump.open_file('mem_stat.txt') as out_mem_stat:
if (self.ramdump.kernel_version < (3, 18, 0)):
out_mem_stat.write('Kernel version 3.18 \
and above are supported, current version {0}.\
{1}'.format(self.ramdump.kernel_version[0],
self.ramdump.kernel_version[1]))
return
self.print_mem_stats(out_mem_stat)

View File

@@ -0,0 +1,222 @@
# Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2023-2024 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
from linux_list import ListWalker
from parsers.filetracking import FileTracking
""" Returns number of pages """
def get_shmem_swap_usage(ramdump, memory_file):
shmem_swaplist = ramdump.address_of("shmem_swaplist")
if not shmem_swaplist:
return 0
offset = ramdump.field_offset('struct shmem_inode_info', 'swaplist')
if not offset:
return 0
inode_offset = ramdump.field_offset('struct shmem_inode_info', 'vfs_inode')
if not inode_offset:
return 0
iter = ListWalker(ramdump, shmem_swaplist, offset)
total = 0
seen = {}
for shmem_inode_info in iter:
swap_pages = ramdump.read_structure_field(
shmem_inode_info, 'struct shmem_inode_info', 'swapped')
if swap_pages is None:
print_out_str("Invalid addr is found: {}".format(hex(shmem_inode_info)))
break
inode = shmem_inode_info + inode_offset
addres_space = ramdump.read_structure_field(inode, 'struct inode',
'i_mapping')
if addres_space in seen:
seen[addres_space] = seen[addres_space] + swap_pages
else:
seen[addres_space] = swap_pages
total += swap_pages
sortlist = sorted(seen.items(), key=lambda kv: kv[1],
reverse=True)
i = 0
string = "TOP 3 swapped SHMEM files are:\n"
pathtracking = FileTracking(ramdump)
for k,v in sortlist:
#k is struct address_space
if i < 3:
i = i + 1
addr_space_format = "Address_space 0x{0:x} Allocated {1} pages\n".format(k,v)
string = string + addr_space_format
inode = ramdump.read_structure_field(k, 'struct address_space',
'host')
if inode is not None:
dentry_list = ramdump.read_structure_field(inode, 'struct inode',
'i_dentry')
if dentry_list is not None:
dentry = ramdump.container_of(dentry_list, 'struct dentry',
'd_u')
if dentry is not None:
d_name_ptr = (dentry + ramdump.field_offset('struct dentry ',
'd_name')) + ramdump.field_offset('struct qstr', 'name')
name = ramdump.read_cstring(ramdump.read_pointer(d_name_ptr),
100)
if name is not None:
path, cycle_flag = pathtracking.get_filepath('', name, dentry)
else:
path = 'None'
path = "file name: " + path + '\n'
string = string + path
else:
break
return total,string
def do_dump_process_memory(ramdump):
if ramdump.kernel_version < (4, 9):
total_free = ramdump.read_word('vm_stat[NR_FREE_PAGES]')
slab_rec = ramdump.read_word('vm_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = ramdump.read_word('vm_stat[NR_SLAB_UNRECLAIMABLE]')
total_shmem = ramdump.read_word('vm_stat[NR_SHMEM]')
else:
total_free = ramdump.read_word('vm_zone_stat[NR_FREE_PAGES]')
# slab memory
if ramdump.kernel_version >= (5, 10):
slab_rec = ramdump.read_word('vm_node_stat[NR_SLAB_RECLAIMABLE_B]')
slab_unrec = ramdump.read_word(
'vm_node_stat[NR_SLAB_UNRECLAIMABLE_B]')
elif ramdump.kernel_version >= (4, 14):
slab_rec = ramdump.read_word('vm_node_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = ramdump.read_word(
'vm_node_stat[NR_SLAB_UNRECLAIMABLE]')
else:
slab_rec = ramdump.read_word('vm_zone_stat[NR_SLAB_RECLAIMABLE]')
slab_unrec = ramdump.read_word(
'vm_zone_stat[NR_SLAB_UNRECLAIMABLE]')
total_shmem = ramdump.read_word('vm_node_stat[NR_SHMEM]')
memory_file = ramdump.open_file('memory.txt')
total_shmem_swap, shmem_swap_file = get_shmem_swap_usage(ramdump,memory_file)
total_slab = slab_rec + slab_unrec
if(ramdump.kernel_version > (4, 20, 0)):
total_mem = ramdump.read_word('_totalram_pages') * 4
else:
total_mem = ramdump.read_word('totalram_pages') * 4
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
offset_signal = ramdump.field_offset('struct task_struct', 'signal')
offset_adj = ramdump.field_offset('struct signal_struct', 'oom_score_adj')
offset_pid = ramdump.field_offset('struct task_struct', 'pid')
task_info = []
memory_file.write('Total RAM: {0:,}kB\n'.format(total_mem))
memory_file.write('Total free memory: {0:,}kB({1:.1f}%)\n'.format(
total_free * 4, (100.0 * total_free * 4) / total_mem))
memory_file.write('Slab reclaimable: {0:,}kB({1:.1f}%)\n'.format(
slab_rec * 4, (100.0 * slab_rec * 4) / total_mem))
memory_file.write('Slab unreclaimable: {0:,}kB({1:.1f}%)\n'.format(
slab_unrec * 4, (100.0 * slab_unrec * 4) / total_mem))
memory_file.write('Total Slab memory: {0:,}kB({1:.1f}%)\n'.format(
total_slab * 4, (100.0 * total_slab * 4) / total_mem))
memory_file.write('Total SHMEM (PAGECACHE): {0:,}kB({1:.1f}%)\n'.format(
total_shmem * 4, (100.0 * total_shmem * 4) / total_mem))
memory_file.write('Total SHMEM (SWAP): {0:,}kB({1:.1f}%)\n\n'.format(
total_shmem_swap * 4, (100.0 * total_shmem_swap * 4) / total_mem))
memory_file.write('{0}\n'.format(shmem_swap_file))
for task in ramdump.for_each_process():
next_thread_comm = task + offset_comm
thread_task_name = cleanupString(
ramdump.read_cstring(next_thread_comm, 16))
next_thread_pid = task + offset_pid
thread_task_pid = ramdump.read_int(next_thread_pid)
signal_struct = ramdump.read_word(task + offset_signal)
if signal_struct == 0 or signal_struct is None:
continue
adj = ramdump.read_u16(signal_struct + offset_adj)
if adj & 0x8000:
adj = adj - 0x10000
rss, swap, anon_rss, file_rss, shmem_rss = get_rss(ramdump, task)
if rss != 0 or swap != 0:
task_info.append([thread_task_name, thread_task_pid, rss, swap, rss + swap, adj, anon_rss, file_rss, shmem_rss])
task_info = sorted(task_info, key=lambda l: l[4], reverse=True)
str = '{0:<17s}{1:>8s}{2:>19s}{3:>19s}{4:>6}{5:>16}{6:>16}{7:>16}\n'.format(
'Task name', 'PID', 'RSS in kB', 'SWAP in kB', 'ADJ', "anon_rss in kB", "file_rss in kB", "shmem_rss in kB")
memory_file.write(str)
for item in task_info:
str = '{taskname:<17s}{pid:8d}{rss:13,d}({rss_pct:4.1f}%){swap:13,d}({swap_pct:2.1f}%){adj:6} {anon_rss:>16,d} {file_rss:>16,d} {shmem_rss:>10,d}\n'.format(
taskname = item[0], pid = item[1],
rss = item[2], rss_pct = (100.0 * item[2]) / total_mem,
swap = item[3], swap_pct = (100.0 * item[3]) / total_mem,
adj = item[5], anon_rss=item[6], file_rss=item[7], shmem_rss=item[8])
memory_file.write(str)
memory_file.close()
print_out_str('---wrote meminfo to memory.txt')
def percpu_counter_rss_stat(ramdump, rss_stat):
count = rss_stat.count
for core in ramdump.iter_cpus():
count += ramdump.read_int(rss_stat.counters + ramdump.per_cpu_offset(core))
return count
def get_mm_counter(ramdump, rss_stat):
count = rss_stat.count
return count
def get_rss(ramdump, task_struct):
offset_mm = ramdump.field_offset('struct task_struct', 'mm')
offset_rss_stat = ramdump.field_offset('struct mm_struct', 'rss_stat')
mm_struct = ramdump.read_word(task_struct + offset_mm)
if mm_struct == 0:
return 0, 0, 0, 0, 0
if ramdump.kernel_version >= (6, 2):
# /* 6.2: struct percpu_counter rss_stat[NR_MM_COUNTERS] */
mm = ramdump.read_datatype(mm_struct, 'struct mm_struct')
file_rss = get_mm_counter(ramdump, mm.rss_stat[0])
anon_rss = get_mm_counter(ramdump, mm.rss_stat[1])
swap_rss = get_mm_counter(ramdump, mm.rss_stat[2])
shmem_rss = get_mm_counter(ramdump, mm.rss_stat[3])
else:
offset_file_rss = ramdump.field_offset('struct mm_rss_stat', 'count')
offset_anon_rss = ramdump.field_offset('struct mm_rss_stat', 'count[1]')
offset_swap_rss = ramdump.field_offset('struct mm_rss_stat', 'count[2]')
if ramdump.kernel_version >= (4, 9):
offset_shmem_rss = ramdump.field_offset('struct mm_rss_stat', 'count[3]')
anon_rss = ramdump.read_word(mm_struct + offset_rss_stat + offset_anon_rss)
swap_rss = ramdump.read_word(mm_struct + offset_rss_stat + offset_swap_rss)
file_rss = ramdump.read_word(mm_struct + offset_rss_stat + offset_file_rss)
if ramdump.kernel_version >= (4, 9):
shmem_rss = ramdump.read_word(mm_struct + offset_rss_stat + offset_shmem_rss)
else:
shmem_rss = 0
# Ignore negative RSS values
if anon_rss > 0x80000000:
anon_rss = 0
if swap_rss > 0x80000000:
swap_rss = 0
if file_rss > 0x80000000:
file_rss = 0
if shmem_rss > 0x80000000:
shmem_rss = 0
total_rss = anon_rss + file_rss + shmem_rss
return total_rss * 4 , swap_rss * 4 , anon_rss * 4 , file_rss * 4, shmem_rss * 4
@register_parser('--print-memory-info', 'Print memory usage info')
class DumpProcessMemory(RamParser):
def parse(self):
do_dump_process_memory(self.ramdump)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,630 @@
# Copyright (c) 2016 The Linux Foundation. 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 parser_util
from parser_util import register_parser, RamParser
from print_out import print_out_str
@register_parser(
'--print-wlan',
'Print WLAN debugging information(if enabled)',
optional=True)
class ModuleWlan(RamParser):
""" This class defines when WLAN module is loaded """
def __init__(self, *args):
super(ModuleWlan, self).__init__(*args)
self.dir_char = ''
self.dir_char_out = ''
self.opt_dbg = False
self.wlan_path = ''
self.wlan_module_addr = 0
self.wlan_text_addr = 0
self.wlan_data_addr = 0
self.wlan_bss_addr = 0
def convert_dir_for_arg(self, ori):
"""
Convert full path as an argument of function
"""
if self.opt_dbg is True:
print_out_str('** convert_dir_for_arg() **')
if self.dir_char == '':
if parser_util.get_system_type() == 'Linux':
self.dir_char = '/'
self.dir_char_out = '/'
else:
self.dir_char = '\\'
self.dir_char_out = '\\\\'
dst = ''
for c in ori:
if c == self.dir_char:
dst = dst + self.dir_char_out
else:
dst = dst + c
if self.opt_dbg is True:
print_out_str('ori - [{}]'.format(ori))
print_out_str('dst - [{}]'.format(dst))
return dst
def load_wlan_ko(self):
""" Load wlan.ko to GDB """
if self.opt_dbg is True:
print_out_str('** load_wlan_ko() **')
if self.wlan_text_addr == 0:
print_out_str('self.wlan_text_addr is zero')
return False
cmd = 'add-symbol-file '
cmd = cmd + self.convert_dir_for_arg(self.wlan_path)
cmd = cmd + ' {} -s .data {} -s .bss {}'.format(
self.wlan_text_addr, self.wlan_data_addr,
self.wlan_bss_addr)
return self.ramdump.gdbmi._run_for_first(cmd)
def get_sections_of_wlan(self):
"""
Get wlan.ko's sectino addresses
"""
if self.opt_dbg is True:
print_out_str('** get_sections_of_wlan() **')
# Step-A) Find wlan.ko
modules_addr = self.ramdump.address_of('modules')
next_module_addr = self.ramdump.read_structure_field(
modules_addr, 'struct list_head', 'next')
name_offset = self.ramdump.field_offset('struct module', 'name')
module_addr = 0
idx = 0
while modules_addr != next_module_addr:
module_addr = self.ramdump.container_of(
next_module_addr, 'struct module', 'list')
module_name_addr = module_addr + name_offset
module_name_str = self.ramdump.read_cstring(
module_name_addr, 32, True)
if module_name_str == 'wlan':
self.wlan_module_addr = module_addr
break
if self.opt_dbg is True:
print_out_str(
'[{}]th - next_module[{}], module[{}], name[{}]'.format(
hex(idx), hex(next_module_addr),
hex(module_addr), module_name_str))
# move the list entry to the next
next_module_addr = self.ramdump.read_structure_field(
modules_addr, 'struct list_head', 'next')
idx = idx + 1
if self.wlan_module_addr == 0:
print_out_str('[Caution] Fail to find wlan.ko')
return False
# Step-B) get sections in wlan.ko
sect_attrs_addr = self.ramdump.read_structure_field(
module_addr, 'struct module', 'sect_attrs')
nsections = self.ramdump.read_structure_field(
sect_attrs_addr,
'struct module_sect_attrs',
'nsections')
attrs_offset = self.ramdump.field_offset(
'struct module_sect_attrs', 'attrs')
attrs_addr = sect_attrs_addr + attrs_offset
module_sect_attr_size = self.ramdump.sizeof('struct module_sect_attr')
if self.opt_dbg is True:
print_out_str('module_addr : {}'.format(hex(module_addr)))
print_out_str('sect_attrs_addr : {}'.format(hex(sect_attrs_addr)))
print_out_str('nsections : {}'.format(hex(nsections)))
print_out_str('attrs_offset : {}'.format(hex(attrs_offset)))
if attrs_addr is not None:
print_out_str('attrs_addr : {}'.format(hex(attrs_addr)))
else:
print_out_str('attrs_addr : {}'.format(attrs_addr))
section_name_offset = self.ramdump.field_offset(
'struct module_sect_attr', 'name')
idx = 0
while idx < nsections:
section_attr_address = attrs_addr + idx * module_sect_attr_size
section_name_addr = self.ramdump.read_pointer(
section_attr_address + section_name_offset)
section_name_str = self.ramdump.read_cstring(
section_name_addr, 32, True)
section_address = self.ramdump.read_structure_field(
section_attr_address,
'struct module_sect_attr', 'address')
if self.opt_dbg is True:
print_out_str('section[{}]th - name[{}], attr[{}]'.format(
hex(idx), section_name_str, hex(section_address)))
if section_name_str == '.text':
self.wlan_text_addr = section_address
if section_name_str == '.data':
self.wlan_data_addr = section_address
if section_name_str == '.bss':
self.wlan_bss_addr = section_address
idx = idx + 0x1
print_out_str('wlan_text_addr : {}'.format(hex(self.wlan_text_addr)))
print_out_str('wlan_data_addr : {}'.format(hex(self.wlan_data_addr)))
print_out_str('wlan_bss_addr : {}'.format(hex(self.wlan_bss_addr)))
return True
def run(self):
"""
Main
"""
if self.ramdump.arm64 is None:
print_out_str('[Caution] this script supports on ARM64')
return False
if self.ramdump.wlan == "INTEGRATED":
print_out_str('self.wlan doen\'t exist, skip')
else:
print_out_str('self.wlan exist {}'.format(self.ramdump.wlan))
self.wlan_path = self.ramdump.wlan
if self.get_sections_of_wlan() is False:
print_out_str('wlan.ko is not loaded.')
return False
else:
print_out_str('** Find WLAN Module **')
self.load_wlan_ko()
self.get_wmi_command_log_buffer()
self.get_host_wmi_command_tx_cmp_buf()
self.get_host_wmi_event_buf()
self.get_host_wmi_rx_event_buf()
self.get_host_extract_log()
return True
###################################
# Parse internal variables
def get_wmi_command_log_buffer(self):
"""
Parse 'struct wmi_command_debug'
"""
if self.opt_dbg is True:
print_out_str('*** get_wmi_command_log_buffer() ***')
element_size = self.ramdump.sizeof('struct wmi_command_debug')
if (element_size is None):
print_out_str('[Caution] symbols of host driver do not exist')
return False
out_file = self.ramdump.open_file("wmi_command_log_buffer.txt")
wmi_total_size = self.ramdump.sizeof('wmi_command_log_buffer')
num_elements = wmi_total_size / element_size
if self.opt_dbg is True:
print_out_str('** wlan_host_wmi_command_log_buffer **')
print_out_str('*************************************')
print_out_str('wmi_total_size({})'.format(hex(wmi_total_size)))
print_out_str('element_size({})'.format(hex(element_size)))
print_out_str('num_elements({})'.format(hex(num_elements)))
print_out_str('*************************************')
# info of the data structure
command_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'command')
data0_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[0]')
data1_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[1]')
data2_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[2]')
data3_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[3]')
time_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'time')
if self.opt_dbg is True:
print_out_str('command_offset({})'.format(command_offset))
print_out_str('data0_offset({})'.format(data0_offset))
print_out_str('data1_offset({})'.format(data1_offset))
print_out_str('data2_offset({})'.format(data2_offset))
print_out_str('data3_offset({})'.format(data3_offset))
print_out_str('time_offset({})'.format(time_offset))
print_out_str('*************************************')
buffer_start_address = self.ramdump.address_of(
'wmi_command_log_buffer')
wmi_command_buf_idx = self.ramdump.read_u32(
self.ramdump.address_of(
'g_wmi_command_buf_idx'))
cnt = 0
idx = wmi_command_buf_idx
while cnt < num_elements:
if idx == num_elements:
idx = 0
buffer_address = buffer_start_address + idx * element_size
command = self.ramdump.read_u32(buffer_address)
data0 = self.ramdump.read_u32(buffer_address + data0_offset)
data1 = self.ramdump.read_u32(buffer_address + data1_offset)
data2 = self.ramdump.read_u32(buffer_address + data2_offset)
data3 = self.ramdump.read_u32(buffer_address + data3_offset)
time = self.ramdump.read_u64(buffer_address + time_offset)
idx = idx + 1
cnt = cnt + 1
out_buf = '{0} us'.format(float(time/100000.0))
out_buf = out_buf + ' : command({})'.format(hex(command))
out_buf = out_buf + ', data[{}'.format(hex(data0))
out_buf = out_buf + ', {}'.format(hex(data1))
out_buf = out_buf + ', {}'.format(hex(data2))
out_buf = out_buf + ', {}]'.format(hex(data3))
if self.opt_dbg is True:
print_out_str(out_buf)
out_file.write(out_buf + '\n')
out_file.close()
return True
def get_host_wmi_command_tx_cmp_buf(self):
"""
Parse 'struct wmi_command_debug wmi_command_tx_cmp_log_buffer'
"""
if self.opt_dbg is True:
print_out_str('*** get_host_wmi_command_tx_cmp_buf() ***')
element_size = self.ramdump.sizeof('struct wmi_command_debug')
if (element_size is None):
print_out_str('[Caution] symbols of host driver do not exist')
return False
out_file = self.ramdump.open_file("wmi_command_tx_cmp_buf.txt")
wmi_total_size = self.ramdump.sizeof('wmi_command_tx_cmp_log_buffer')
num_elements = wmi_total_size / element_size
if self.opt_dbg is True:
print_out_str('** wlan_host_wmi_command_tx_cmp_buf **')
print_out_str('*************************************')
print_out_str('wmi_total_size({})'.format(hex(wmi_total_size)))
print_out_str('element_size({})'.format(hex(element_size)))
print_out_str('num_elements({})'.format(hex(num_elements)))
print_out_str('*************************************')
# info of the data structure
command_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'command')
data0_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[0]')
data1_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[1]')
data2_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[2]')
data3_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'data[3]')
time_offset = self.ramdump.field_offset(
'struct wmi_command_debug', 'time')
if self.opt_dbg is True:
print_out_str("command_offset({})".format(command_offset))
print_out_str("data0_offset({})".format(data0_offset))
print_out_str("data1_offset({})".format(data1_offset))
print_out_str("data2_offset({})".format(data2_offset))
print_out_str("data3_offset({})".format(data3_offset))
print_out_str("time_offset({})".format(time_offset))
print_out_str('*************************************')
log_buffer_address = self.ramdump.address_of(
'wmi_command_tx_cmp_log_buffer')
wmi_command_buf_idx = self.ramdump.read_u32(
self.ramdump.address_of(
'g_wmi_command_tx_cmp_buf_idx'))
cnt = 0
idx = wmi_command_buf_idx
while cnt < num_elements:
if idx == num_elements:
idx = 0
buffer_address = log_buffer_address + idx * element_size
command = self.ramdump.read_u32(buffer_address)
data0 = self.ramdump.read_u32(buffer_address + data0_offset)
data1 = self.ramdump.read_u32(buffer_address + data1_offset)
data2 = self.ramdump.read_u32(buffer_address + data2_offset)
data3 = self.ramdump.read_u32(buffer_address + data3_offset)
time = self.ramdump.read_u64(buffer_address + time_offset)
idx = idx + 1
cnt = cnt + 1
out_buf = '{0} us'.format(float(time/100000.0))
out_buf = out_buf + ' : command({})'.format(hex(command))
out_buf = out_buf + ', data[{}'.format(hex(data0))
out_buf = out_buf + ', {}'.format(hex(data1))
out_buf = out_buf + ', {}'.format(hex(data2))
out_buf = out_buf + ', {}]'.format(hex(data3))
if self.opt_dbg is True:
print_out_str(out_buf)
out_file.write(out_buf + '\n')
out_file.close()
return True
def get_host_wmi_event_buf(self):
"""
Parse 'struct wmi_event_debug wmi_event_log_buffer[]'
"""
if self.opt_dbg is True:
print_out_str('*** get_host_wmi_event_buf() ***')
element_size = self.ramdump.sizeof('struct wmi_event_debug')
if (element_size is None):
print_out_str('[Caution] symbols of host driver do not exist')
return False
out_file = self.ramdump.open_file("wmi_event_log_buffer.txt")
wmi_total_size = self.ramdump.sizeof('wmi_event_log_buffer')
num_elements = wmi_total_size / element_size
if self.opt_dbg is True:
print_out_str('[Debug] wmi_total_size({})'.format(
hex(wmi_total_size)))
print_out_str('[Debug] element_size({})'.format(hex(element_size)))
print_out_str('[Debug] num_elements({})'.format(hex(num_elements)))
# info of the data structure
event_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'event')
data0_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[0]')
data1_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[1]')
data2_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[2]')
data3_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[3]')
time_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'time')
if self.opt_dbg is True:
print_out_str("[Debug] event_offset({})".format(event_offset))
print_out_str("[Debug] data0_offset({})".format(data0_offset))
print_out_str("[Debug] data1_offset({})".format(data1_offset))
print_out_str("[Debug] data2_offset({})".format(data2_offset))
print_out_str("[Debug] data3_offset({})".format(data3_offset))
print_out_str("[Debug] time_offset({})".format(time_offset))
wmi_log_address = self.ramdump.address_of('wmi_event_log_buffer')
wmi_event_buf_idx = self.ramdump.read_u32(
self.ramdump.address_of('g_wmi_event_buf_idx'))
cnt = 0
idx = wmi_event_buf_idx
while cnt < num_elements:
if idx == num_elements:
idx = 0
buffer_address = wmi_log_address + idx * element_size
event = self.ramdump.read_u32(buffer_address)
data0 = self.ramdump.read_u32(buffer_address + data0_offset)
data1 = self.ramdump.read_u32(buffer_address + data1_offset)
data2 = self.ramdump.read_u32(buffer_address + data2_offset)
data3 = self.ramdump.read_u32(buffer_address + data3_offset)
time = self.ramdump.read_u64(buffer_address + time_offset)
idx = idx + 1
cnt = cnt + 1
out_buf = '{0} us'.format(float(time/100000.0))
out_buf = out_buf + ' : event({})'.format(hex(event))
out_buf = out_buf + ', data[{}'.format(hex(data0))
out_buf = out_buf + ', {}'.format(hex(data1))
out_buf = out_buf + ', {}'.format(hex(data2))
out_buf = out_buf + ', {}]'.format(hex(data3))
if self.opt_dbg is True:
print_out_str(out_buf)
out_file.write(out_buf + '\n')
out_file.close()
return True
def get_host_wmi_rx_event_buf(self):
"""
Parse 'struct wmi_event_debug wmi_rx_event_log_buffer'
"""
if self.opt_dbg is True:
print_out_str('*** get_host_wmi_rx_event_buf() ***')
wmi_elem_size = self.ramdump.sizeof('struct wmi_event_debug')
if (wmi_elem_size is None):
print_out_str('[Caution] symbols of host driver do not exist')
return False
wmi_total_size = self.ramdump.sizeof('wmi_rx_event_log_buffer')
num_elements = wmi_total_size / wmi_elem_size
out_file = self.ramdump.open_file("wmi_rx_event_log_buffer.txt")
# info of the data structure
event_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'event')
data0_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[0]')
data1_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[1]')
data2_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[2]')
data3_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'data[3]')
time_offset = self.ramdump.field_offset(
'struct wmi_event_debug', 'time')
wmi_event_address = self.ramdump.address_of('wmi_rx_event_log_buffer')
wmi_event_buf_idx = self.ramdump.read_u32(
self.ramdump.address_of(
'g_wmi_rx_event_buf_idx'))
if self.opt_dbg is True:
print_out_str('[Debug] wmi_total_size({})'.format(wmi_total_size))
print_out_str('[Debug] wmi_elem_size({})'.format(wmi_elem_size))
print_out_str('[Debug] num_elements({})'.format(num_elements))
print_out_str('[Debug] event_offset({})'.format(event_offset))
print_out_str('[Debug] data0_offset({})'.format(data0_offset))
print_out_str('[Debug] data1_offset({})'.format(data1_offset))
print_out_str('[Debug] data2_offset({})'.format(data2_offset))
print_out_str('[Debug] data3_offset({})'.format(data3_offset))
print_out_str('[Debug] time_offset({})'.format(time_offset))
cnt = 0
idx = wmi_event_buf_idx
while cnt < num_elements:
if idx == num_elements:
idx = 0
buffer_address = wmi_event_address + idx * wmi_elem_size
event = self.ramdump.read_u32(buffer_address)
data0 = self.ramdump.read_u32(buffer_address + data0_offset)
data1 = self.ramdump.read_u32(buffer_address + data1_offset)
data2 = self.ramdump.read_u32(buffer_address + data2_offset)
data3 = self.ramdump.read_u32(buffer_address + data3_offset)
time = self.ramdump.read_u64(buffer_address + time_offset)
out_buf = '{0} us'.format(float(time/100000.0))
out_buf = out_buf + ' : event({})'.format(hex(event))
out_buf = out_buf + ', data[{}'.format(hex(data0))
out_buf = out_buf + ', {}'.format(hex(data1))
out_buf = out_buf + ', {}'.format(hex(data2))
out_buf = out_buf + ', {}]'.format(hex(data3))
if self.opt_dbg is True:
print_out_str(out_buf)
out_file.write(out_buf + '\n')
idx = idx + 1
cnt = cnt + 1
out_file.close()
return True
def get_host_extract_log(self):
"""
refer functions in wlan_logging_sock_svc.c
"""
if self.opt_dbg is True:
print_out_str('*** wlan_host_extract_log() ***')
out_file = self.ramdump.open_file("gwlan_logging.txt")
# get number of struct wlan_logging
num_buf = self.ramdump.read_s32(
self.ramdump.address_of('gwlan_logging') +
self.ramdump.field_offset(
'struct wlan_logging', 'num_buf'))
if self.opt_dbg is True:
print_out_str('num_buf : {}'.format(num_buf))
# gwlan_logging
element_size = self.ramdump.sizeof('struct log_msg')
if element_size % 32:
elem_aligned_size = element_size + (element_size % 32)
if self.opt_dbg is True:
print_out_str('element_size({})'.format(hex(element_size)))
print_out_str('element_align_size({})'.format(
hex(elem_aligned_size)))
else:
elem_aligned_size = element_size
if self.opt_dbg is True:
print_out_str('element_size({})'.format(hex(element_size)))
print_out_str('element_align_size({})'.format(
hex(elem_aligned_size)))
filled_length_offset = self.ramdump.field_offset(
'struct log_msg',
'filled_length')
logbuf_offset = self.ramdump.field_offset(
'struct log_msg', 'logbuf')
logbuf_size = element_size - logbuf_offset
gplog_msg_address = self.ramdump.read_pointer('gplog_msg')
if self.opt_dbg is True:
print_out_str('filled_length_offset : {}'.format(
hex(filled_length_offset)))
print_out_str('logbuf_size : {}'.format(hex(logbuf_size)))
print_out_str('gplog_msg_address : {}'.format(
hex(gplog_msg_address)))
cnt = 0
while cnt < num_buf:
buffer_address = gplog_msg_address + cnt * elem_aligned_size
filled_length = self.ramdump.read_u32(
buffer_address + filled_length_offset)
v_address = buffer_address + logbuf_offset + 4
p_address = self.ramdump.virt_to_phys(v_address)
if self.opt_dbg is True:
print_out_str('** gplog_msg[{}] : {}, {}, VA{}-PA{} **'.format(
cnt,
hex(buffer_address),
hex(filled_length),
hex(v_address),
hex(p_address)))
out_file.write('** gplog_msg[{}] : {}, {}, VA{}-PA{} **\n'.format(
cnt,
hex(buffer_address),
hex(filled_length),
hex(v_address),
hex(p_address)))
if filled_length != 0:
left_bytes = filled_length
logbuf_str = ""
while left_bytes > 0:
p_address = self.ramdump.virt_to_phys(v_address)
logbuf_out = self.ramdump.read_physical(p_address, 4)
logbuf_str = logbuf_str + logbuf_out
v_address = v_address + 4
left_bytes = left_bytes - 4
if self.opt_dbg is True:
print_out_str(logbuf_str)
out_file.write(logbuf_str)
out_file.write('\n')
# We may be able to delete first []
# like [VosMCThread] or [kworker/0:0]
cnt = cnt + 1
out_file.close()
return True
def parse(self):
self.run()
return True

View File

@@ -0,0 +1,55 @@
# Copyright (c) 2022 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 print_out
from parser_util import RamParser, cleanupString, register_parser
import module_table
@register_parser('--modules_table', 'Dump modules_table')
class Modules_table(RamParser):
def retrieve_modules_cn(self):
mod_list = self.ramdump.address_of('modules')
next_offset = self.ramdump.field_offset('struct list_head', 'next')
list_offset = self.ramdump.field_offset('struct module', 'list')
name_offset = self.ramdump.field_offset('struct module', 'name')
scmversion_offset = self.ramdump.field_offset('struct module', 'scmversion')
if self.ramdump.kernel_version > (4, 9, 0):
module_core_offset = self.ramdump.field_offset('struct module', 'core_layout.base')
else:
module_core_offset = self.ramdump.field_offset('struct module', 'module_core')
kallsyms_offset = self.ramdump.field_offset('struct module', 'kallsyms')
next_list_ent = self.ramdump.read_pointer(mod_list + next_offset)
while next_list_ent and next_list_ent != mod_list:
mod_tbl_ent = module_table.module_table_entry()
module = next_list_ent - list_offset
name_ptr = module + name_offset
mod_tbl_ent.name = self.ramdump.read_cstring(name_ptr)
svmversion_addr = self.ramdump.read_pointer(scmversion_offset+module)
svmversion = self.ramdump.read_cstring(svmversion_addr)
mod_tbl_ent.module_offset = self.ramdump.read_pointer(module + module_core_offset)
if mod_tbl_ent.module_offset is None:
mod_tbl_ent.module_offset = 0
mod_tbl_ent.kallsyms_addr = self.ramdump.read_pointer(module + kallsyms_offset)
self.module_table_cn.add_entry(mod_tbl_ent)
self.modules_list.append((mod_tbl_ent.module_offset, mod_tbl_ent.name, module, svmversion))
next_list_ent = self.ramdump.read_pointer(next_list_ent + next_offset)
self.modules_list.sort()
for item in self.modules_list:
print("%-32s 0x%-32x v.v (struct module)0x%-32x %s" % (item[1], item[0], item[2], item[3]), file=self.f)
def parse(self):
self.module_table_cn = module_table.module_table_class()
self.f = open(self.ramdump.outdir + "/modules_table.txt", "w")
self.modules_list =[]
self.retrieve_modules_cn()
self.f.close();

View File

@@ -0,0 +1,429 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
from parser_util import register_parser, RamParser, cleanupString
from print_out import print_out_str
from utasklib import UTaskLib
from utasklib import ProcessNotFoundExcetion
import linux_list as llist
from collections import namedtuple
class BaseFs(RamParser):
def __init__(self, ramdump):
super().__init__(ramdump)
self.mnt_tuple = namedtuple("MntTuple", ["mount", "vfsmount", "super_block", "dname", "mpath"])
def get_dname_of_dentry(self, dentry):
dentry_name_offset = self.ramdump.field_offset(
'struct dentry', 'd_name')
len_offset = self.ramdump.field_offset(
'struct qstr', 'len')
qst_name_offset = self.ramdump.field_offset(
'struct qstr', 'name')
name_address = self.ramdump.read_word(dentry + dentry_name_offset + qst_name_offset)
len_address = dentry + dentry_name_offset + len_offset
len = self.ramdump.read_u32(len_address)
name = cleanupString(self.ramdump.read_cstring(name_address, len))
return name
def get_pathname(self, vfsmount):
mnt_root = self.ramdump.read_structure_field(vfsmount, 'struct vfsmount', 'mnt_root')
mnt_offset_in_mount = self.ramdump.field_offset('struct mount', 'mnt')
mnt_parent_offset = self.ramdump.field_offset('struct mount', 'mnt_parent')
mount = vfsmount - mnt_offset_in_mount
mnt_mountpoint_offset = self.ramdump.field_offset(
'struct mount', 'mnt_mountpoint')
d_parent_offset = self.ramdump.field_offset(
'struct dentry', 'd_parent')
mnt_parent_pre = 0
mnt_parent = mount
mount_name = []
while mnt_parent_pre != mnt_parent:
mnt_parent_pre = mnt_parent
mnt_mountpoint = self.ramdump.read_word(mnt_parent + mnt_mountpoint_offset)
name = self.get_dname_of_dentry(mnt_mountpoint)
mnt_parent = self.ramdump.read_word(mnt_parent + mnt_parent_offset)
if name == None or name == '/':
break
if mnt_parent == 0:
break
mount_name.append(name)
# walk to get the fullname of mountpoint
d_parent = self.ramdump.read_word(mnt_mountpoint + d_parent_offset)
d_parent_pre = 0
while d_parent_pre != d_parent:
d_parent_pre = d_parent
name = self.get_dname_of_dentry(d_parent)
d_parent = self.ramdump.read_word(d_parent + d_parent_offset)
if name == None or name == '/':
break
mount_name.append(name)
if d_parent == 0:
break
full_name = ''
names = []
for item in mount_name:
names.append(item)
names.reverse()
for item in names:
full_name += '/' + item
if len(names) == 0:
return '/'
return full_name
def parse(self):
pid = 1
try:
args = self.parse_param()
try:
pid = int(args["pid"])
except:
pid = args["proc"]
except:
pid = 1
finally:
print_out_str("Dump info from process {}".format(pid))
try:
taskinfo = UTaskLib(self.ramdump).get_utask_info(pid)
except ProcessNotFoundExcetion:
print_out_str("pid={} process is not started".format(pid))
return
nsproxy = self.ramdump.read_structure_field(taskinfo.task_addr, 'struct task_struct', 'nsproxy')
fs = self.ramdump.read_structure_field(taskinfo.task_addr, 'struct task_struct', 'fs')
root = fs + self.ramdump.field_offset('struct fs_struct', 'root')
mnt_ns = self.ramdump.read_structure_field(nsproxy, 'struct nsproxy', 'mnt_ns')
mount_list_addr = mnt_ns + self.ramdump.field_offset("struct mnt_namespace", 'list')
field_next_offset = self.ramdump.field_offset('struct mount', 'mnt_list')
self.output.write(f"Process: {taskinfo.name}, (struct task_struct*)=0x{taskinfo.task_addr:x} \
(struct nsproxy*)=0x{nsproxy:x} (struct mnt_namespace*)=0x{mnt_ns:x}\n\n\n")
self.print_header()
list_walker = llist.ListWalker(self.ramdump, mount_list_addr, field_next_offset)
list_walker.walk(mount_list_addr, self.__show_info, mount_list_addr)
def print_header(self):
pass
def __show_info(self, mount, mount_list_addr):
field_next_offset = self.ramdump.field_offset('struct mount', 'mnt_list')
if mount_list_addr == mount + field_next_offset:
return
vfsmount = mount + self.ramdump.field_offset('struct mount', 'mnt')
d_name_addr = self.ramdump.read_word(mount + self.ramdump.field_offset('struct mount', 'mnt_devname'))
d_name = self.ramdump.read_cstring(d_name_addr, 48)
if d_name == "rootfs":
return
mount_path = self.get_pathname(vfsmount)
mnt_root = self.ramdump.read_structure_field(vfsmount, 'struct vfsmount', 'mnt_root')
sb = self.ramdump.read_structure_field(mnt_root, 'struct dentry', 'd_sb')
mtuple = self.mnt_tuple(mount, vfsmount, sb, d_name, mount_path)
self.show_info(mtuple)
@register_parser('--mount', 'Extract mount info logs from ramdump', optional=True)
class Mount(BaseFs):
def __init__(self, ramdump):
super().__init__(ramdump)
self.output = self.ramdump.open_file("mounts.txt")
def is_anon_ns(self, mnt_namespace):
return self.ramdump.read_u64(mnt_namespace, "seq") == 0
def show_type(self, super_block):
s_type = self.ramdump.read_structure_field(super_block, "struct super_block", 's_type')
name = self.ramdump.read_structure_field(s_type, "struct file_system_type", 'name')
type_name = self.ramdump.read_cstring(name, 24)
s_subtype = self.ramdump.read_structure_field(super_block, "struct super_block", 's_subtype')
if s_subtype:
subname = self.ramdump.read_cstring(s_subtype, 24)
type_name = type_name + "." + subname
return type_name
def mnt_is_readonly(self, vfsmount):
MNT_READONLY = 0x40
SB_RDONLY = 1
self.mnt_flags = self.ramdump.read_int(vfsmount + self.ramdump.field_offset("struct vfsmount", 'mnt_flags'))
mnt_sb = self.ramdump.read_structure_field(vfsmount, "struct vfsmount", 'mnt_sb')
self.s_flags = self.ramdump.read_word(mnt_sb + self.ramdump.field_offset('struct super_block', 's_flags'))
return (self.mnt_flags & MNT_READONLY) or (self.s_flags &SB_RDONLY)
def show_sb_opts(self, super_block):
SB_SYNCHRONOUS = 1 << 4
SB_DIRSYNC = 1 << 7
SB_MANDLOCK = 1 << 6
SB_LAZYTIME = 1 << 25
fs_opts= {
SB_SYNCHRONOUS : ",sync",
SB_DIRSYNC : ",dirsync",
SB_MANDLOCK : ",mand",
SB_LAZYTIME :",lazytime",
}
ret = ""
for flag, flag_str in fs_opts.items():
if self.s_flags & flag:
ret = ret + flag_str
self.output.write(ret)
self.selinux_sb_show_options(super_block)
def selinux_superblock(self, super_block):
s_security = self.ramdump.read_structure_field(super_block, "struct super_block", 's_security')
selinux_blob_sizes = self.ramdump.address_of('selinux_blob_sizes')
try:
##lbs_superblock not exist on kernel 5.1
lbs_superblock = self.ramdump.read_int(
selinux_blob_sizes + self.ramdump.field_offset("struct lsm_blob_sizes", 'lbs_superblock'))
except:
lbs_superblock = 0
return s_security + lbs_superblock
def selinux_initialized(self):
selinux_state = self.ramdump.address_of('selinux_state')
initialized = self.ramdump.read_bool(
selinux_state + self.ramdump.field_offset("struct selinux_state", 'initialized'))
return initialized
def selinux_sb_show_options(self, super_block):
SE_SBINITIALIZED = 0x0100
sbsec = self.selinux_superblock(super_block)
s_flags = self.ramdump.read_u16(sbsec + self.ramdump.field_offset("struct superblock_security_struct", 'flags'))
if (s_flags & SE_SBINITIALIZED) == 0:
return
if not self.selinux_initialized():
return
CONTEXT_MNT = 0x01
FSCONTEXT_MNT = 0x02
ROOTCONTEXT_MNT = 0x04
DEFCONTEXT_MNT = 0x08
SBLABEL_MNT = 0x10
CONTEXT_STR = "context"
FSCONTEXT_STR = "fscontext"
ROOTCONTEXT_STR = "rootcontext"
DEFCONTEXT_STR = "defcontext"
SECLABEL_STR = "seclabel"
if s_flags & FSCONTEXT_MNT:
self.output.write("," + FSCONTEXT_STR)
return
if s_flags & CONTEXT_MNT:
self.output.write("," + CONTEXT_STR)
return
if s_flags & DEFCONTEXT_MNT:
self.output.write("," + DEFCONTEXT_STR)
return
if s_flags & ROOTCONTEXT_MNT:
self.output.write("," + ROOTCONTEXT_STR)
return
if s_flags & SBLABEL_MNT:
self.output.write("," + SECLABEL_STR)
return
def show_mnt_opts(self, vfsmount):
MNT_NOSUID = 0x01
MNT_NODEV = 0x02
MNT_NOEXEC = 0x04
MNT_NOATIME = 0x08
MNT_NODIRATIME = 0x10
MNT_RELATIME = 0x20
MNT_READONLY = 0x40
MNT_NOSYMFOLLOW = 0x80
mnt_opts = {
MNT_NOSUID : ",nosuid",
MNT_NODEV : ",nodev",
MNT_NOEXEC : ",noexec",
MNT_NOATIME : ",noatime",
MNT_NODIRATIME : ",nodiratime",
MNT_RELATIME : ",relatime",
MNT_NOSYMFOLLOW : ",nosymfollow",
}
ret = ""
for flag, flag_str in mnt_opts.items():
if self.mnt_flags & flag:
ret = ret + flag_str
if self.is_idmapped_mnt(vfsmount):
ret = ret + ",idmapped"
self.output.write(ret)
def is_idmapped_mnt(self, vfsmount):
mnt_idmap = self.ramdump.read_structure_field(vfsmount, "struct vfsmount", 'mnt_idmap')
nop_mnt_idmap = self.ramdump.address_of('nop_mnt_idmap')
return mnt_idmap != nop_mnt_idmap
def print_header(self):
self.output.write("{:<16s} {:<16s} {:<29s} {:<32s} {:<16s} {:<16s}\n".format(
"(struct mount *)", "(struct super_block *)", "dev_name", "mount_path", "fs_type", "flags"))
def show_info(self, mtuple):
mount = mtuple.mount
vfsmount = mtuple.vfsmount
sb = mtuple.super_block
dname = mtuple.dname
mount_path = mtuple.mpath
typename = self.show_type(sb)
self.output.write("0x{:16x} 0x{:16x} {:<32s} {:<32s} {:<16s} ".format(
mount, sb, dname, mount_path, typename))
if self.mnt_is_readonly(vfsmount):
self.output.write("ro")
else:
self.output.write("rw")
self.show_sb_opts(sb)
self.show_mnt_opts(vfsmount)
self.output.write("\n")
@register_parser('--df', 'Extract df info from ramdump', optional=True)
class Df(BaseFs):
def __init__(self, ramdump):
super().__init__(ramdump)
self.output = self.ramdump.open_file("df.txt")
self.dev_ids = []
def devid(self, s_dev):
MINORBITS = 20
MINORMASK = ((1 << MINORBITS) - 1)
major = s_dev >> MINORBITS
minor = s_dev & MINORMASK
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12)
def ext4_statfs(self, super_block, s_fs_info):
sbi = self.ramdump.read_datatype(s_fs_info, 'struct ext4_sb_info',
["s_es", "s_overhead", "s_resv_clusters",
"s_cluster_bits", "s_mount_opt",
"s_freeclusters_counter", "s_dirtyclusters_counter"])
_es = sbi.s_es
es = self.ramdump.read_datatype(_es, 'struct ext4_super_block',
["s_r_blocks_count_hi", "s_r_blocks_count_lo",
"s_blocks_count_hi", "s_blocks_count_lo"])
s_resv_clusters = sbi.s_resv_clusters.counter
s_cluster_bits = sbi.s_cluster_bits
resv_blocks = s_resv_clusters << s_cluster_bits
s_mount_opt = sbi.s_mount_opt
EXT4_MOUNT_MINIX_DF = 0x00080
overhead = 0
if s_mount_opt & EXT4_MOUNT_MINIX_DF == 0:
overhead = sbi.s_overhead
f_bsize = self.ramdump.read_word(super_block + self.ramdump.field_offset("struct super_block", "s_blocksize"))
s_blocks_count = es.s_blocks_count_hi << 32 | es.s_blocks_count_lo
f_blocks = s_blocks_count - overhead << s_cluster_bits
bfree = self.percpu_counter(sbi.s_freeclusters_counter.count, sbi.s_freeclusters_counter.counters) + \
self.percpu_counter(sbi.s_dirtyclusters_counter.count, sbi.s_dirtyclusters_counter.counters)
bfree = bfree if bfree > 0 else 0
f_bfree = bfree << s_cluster_bits
self.writeback(f_bsize, f_blocks, f_bfree)
def writeback(self, f_bsize, f_blocks, f_bfree):
if f_bsize * f_blocks <= 0:
self.output.write("{:<6s} {:<6s} {:<6s} {:<6.0%}".format("_","_","_",0))
return
total = f_bsize * f_blocks
used = (f_blocks - f_bfree) * f_bsize
free = f_bfree * f_bsize
used_per = used/total
self.output.write("{:<6s} {:<6s} {:<6s} {:<6.0%}".format(self.human_str(total), self.human_str(used), self.human_str(free), used_per))
def f2fs_statfs(self, super_block, s_fs_info):
sbi = self.ramdump.read_datatype(s_fs_info, 'struct f2fs_sb_info',
["raw_super", "blocksize", "user_block_count",
"total_valid_block_count", "current_reserved_blocks"])
raw_super = self.ramdump.read_datatype(sbi.raw_super, 'struct f2fs_super_block',
["block_count", "segment0_blkaddr"])
total_count = raw_super.block_count
start_count = raw_super.segment0_blkaddr
f_bsize = sbi.blocksize
f_blocks = total_count - start_count
f_bfree = sbi.user_block_count - sbi.total_valid_block_count - sbi.current_reserved_blocks
self.writeback(f_bsize, f_blocks, f_bfree)
def shmem_statfs(self, super_block, s_fs_info):
sbinfo = self.ramdump.read_datatype(s_fs_info, 'struct shmem_sb_info', ["max_blocks", "used_blocks"])
f_bsize = self.ramdump.get_page_size()
f_blocks = 0
f_bfree = 0
if sbinfo.max_blocks > 0:
f_blocks = sbinfo.max_blocks
used_blocks = self.percpu_counter(sbinfo.used_blocks.count, sbinfo.used_blocks.counters)
f_bfree = sbinfo.max_blocks - used_blocks
self.writeback(f_bsize, f_blocks, f_bfree)
def fat_statfs(self, super_block, s_fs_info):
sbi = self.ramdump.read_datatype(s_fs_info, 'struct msdos_sb_info',
["cluster_size", "max_cluster", "free_clusters"])
FAT_START_ENT = 2
f_bsize = sbi.cluster_size
f_blocks = sbi.max_cluster - FAT_START_ENT
f_bfree = sbi.free_clusters
self.writeback(f_bsize, f_blocks, f_bfree)
def fuse_statfs(self, super_block, s_fs_info):
return 0
def efivarfs_statfs(self, super_block, s_fs_info):
self.writeback(0, 0, 0)
def human_str(self, size):
if size < 1024:
return " %.0f " % (size)
if size < 1024 * 1024:
return " %.0fK " % (size/1024)
elif size < 1024 * 1024 * 1024:
return " %.0fM " % (size/(1024 * 1024))
else:
return " %.1fG " % (size/(1024 * 1024 * 1024))
def percpu_counter(self, count, counters):
for core in self.ramdump.iter_cpus():
try:
count += self.ramdump.read_int(counters + self.ramdump.per_cpu_offset(core))
except:
continue
return count
def print_header(self):
self.output.write("{:<16s} {:<16s} {:<28s} {:<6s} {:6s} {:<6s} {:<6s} {:<16s}\n".format(
"(struct mount *)", "(struct super_block *)", "dev_name",
"Size", "Used", "Avail", "Use%", "Mounted On"))
def show_info(self, mtuple):
mount = mtuple.mount
vfsmount = mtuple.vfsmount
sb = mtuple.super_block
dname = mtuple.dname
mount_path = mtuple.mpath
sbi = self.ramdump.read_datatype(sb, 'struct super_block', ['s_fs_info', "s_dev"])
if sbi.s_dev in self.dev_ids:
return
self.dev_ids.append(sbi.s_dev)
s_op = self.ramdump.read_structure_field(sb, 'struct super_block', 's_op')
statfs = self.ramdump.read_structure_field(s_op, 'struct super_operations', 'statfs')
look = self.ramdump.unwind_lookup(statfs)
if look:
fop, _ = look
if hasattr(self, fop):
self.output.write("0x{:16x} 0x{:16x} {:<32s}".format(mount, sb, dname))
eval("self." + fop)(sb, sbi.s_fs_info)
self.output.write("{:<32s} {:<16s}".format(mount_path, fop))
self.output.write("\n")

View File

@@ -0,0 +1,22 @@
# Copyright (c) 2013, The Linux Foundation. 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.
from parser_util import register_parser, RamParser
from print_out import print_out_str
@register_parser('--dump-page-tables', 'Dumps page tables')
class PageTableDump(RamParser):
def parse(self):
with self.ramdump.open_file('page_tables.txt') as f:
self.ramdump.mmu.dump_page_tables(f)
print_out_str('Page tables dumped to page_tables.txt')

View File

@@ -0,0 +1,108 @@
# Copyright (c) 2012,2014-2017 The Linux Foundation. All rights reserved.
# Copyright (c) 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 struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
from mm import pfn_to_page, page_buddy , page_address ,get_debug_flags
@register_parser('--print-pagealloccorruption', 'print pagealloc corruption information (if available)')
class PageallocCorruption(RamParser):
def parse(self):
if not self.ramdump.is_config_defined('CONFIG_DEBUG_PAGEALLOC'):
print_out_str('CONFIG_DEBUG_PAGEALLOC Not enabled')
return
out_corruption_summary = self.ramdump.open_file('page_corruption_summary.txt')
out_pfn_ranges = self.ramdump.open_file('page_ranges.txt')
memblock_addr = self.ramdump.address_of('memblock')
memblock_memory_offset = self.ramdump.field_offset('struct memblock', 'memory')
memblock_memory_cnt_offset = self.ramdump.field_offset('struct memblock_type', 'cnt')
cnt = self.ramdump.read_word(memblock_addr + memblock_memory_offset + memblock_memory_cnt_offset)
region_offset = self.ramdump.field_offset('struct memblock_type', 'regions')
regions_baseaddr = self.ramdump.read_word(memblock_addr + memblock_memory_offset + region_offset)
page_ext_offset = self.ramdump.field_offset(
'struct mem_section', 'page_ext')
page_flags_offset = self.ramdump.field_offset(
'struct page_ext', 'flags')
mem_section_size = self.ramdump.sizeof("struct mem_section")
mem_section = self.ramdump.read_word('mem_section')
page_ext_size = self.ramdump.sizeof("struct page_ext")
for r in range(0,cnt) :
region_addr = regions_baseaddr + r * self.ramdump.sizeof('struct memblock_region')
start_addr_offset = self.ramdump.field_offset('struct memblock_region', 'base')
start_addr = self.ramdump.read_u32(region_addr + start_addr_offset)
size_offset = self.ramdump.field_offset('struct memblock_region', 'size')
region_size = self.ramdump.read_u32(region_addr + size_offset)
end_addr = start_addr + region_size
min_pfn = start_addr >> self.ramdump.page_shift
max_pfn = end_addr >> self.ramdump.page_shift
out_pfn_ranges.write("min_pfn : %s,max_pfn: %s\n" %(hex(min_pfn),hex(max_pfn)))
for pfn in range(min_pfn, max_pfn):
page = pfn_to_page(self.ramdump, pfn)
page_pa = (pfn << self.ramdump.page_shift)
if (self.ramdump.kernel_version > (3, 18, 0)):
free = 0
offset = page_pa >> 30
mem_section_0_offset = (
mem_section + (offset * mem_section_size))
page_ext = self.ramdump.read_word(
mem_section_0_offset + page_ext_offset)
temp_page_ext = page_ext + (pfn * page_ext_size)
page_ext_flags = self.ramdump.read_word(
temp_page_ext + page_flags_offset)
# enum PAGE_EXT_DEBUG_POISON ( == 0th bit is set ) for page poisioning
free = page_ext_flags & 1
else:
# debug_flags value should be 1 for pages having poisoned value 0xaa
free = get_debug_flags(self.ramdump, page)
if free == 1:
flag = 0;
for i in range(0,1024):
readval = self.ramdump.read_u32(page_pa+i*4, False)
if readval == None:
break
if readval!=0xaaaaaaaa:
flag = 1
diff = 0xaaaaaaaa-readval
if diff < 0:
diff = diff * (-1)
isBitFlip = not (diff & diff-1)
if isBitFlip:
out_corruption_summary.write("Single Bit Error at %s" %("%#0.8x"%(page_pa+i*4)))
out_corruption_summary.write("\n")
else:
out_corruption_summary.write("Corruption at %s" %("%#0.8x"%(page_pa+i*4)))
out_corruption_summary.write("\n")
end_addr = page_pa + i*4 + 0x00000100
end_page_addr = page_pa | 0x00000fff
if end_addr > end_page_addr:
end_addr = end_page_addr
count = 0
for wordaddr in range(page_pa + i*4,end_addr,0x00000004):
if count == 0:
out_corruption_summary.write("%s " %("%#0.8x"%(wordaddr)))
readval = self.ramdump.read_u32(wordaddr, False)
out_corruption_summary.write("%s " %("%#0.8x"%(readval)))
count = count+1
if count == 8:
count = 0
out_corruption_summary.write ("\n");
break
if flag == 1 :
out_corruption_summary.write("\n")
out_corruption_summary.close()
out_pfn_ranges.close()

View File

@@ -0,0 +1,476 @@
# Copyright (c) 2012,2014-2015,2017-2020 The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
from mm import pfn_to_page, page_buddy, page_count, for_each_pfn
from mm import page_to_pfn, pfn_to_section
import sys
import os
from collections import defaultdict
class StackDepot(object):
def __init__(self, ramdump):
self.ramdump = ramdump
if self.ramdump.field_offset('union handle_parts', 'pool_index') is not None:
self.stack_slabs = self.ramdump.address_of('stack_pools')
else:
self.stack_slabs = self.ramdump.address_of('stack_slabs')
self.stack_slabs_size = self.ramdump.sizeof('void *')
self.stack_trace_entry_size = self.ramdump.sizeof('unsigned long')
self.stack_trace_entries_offset = self.ramdump.field_offset(
'struct stack_record', 'entries')
depot_stack_bits = self.ramdump.sizeof('depot_stack_handle_t') * 8
stack_alloc_null_protection_bits = 1
stack_alloc_order = 2
self.stack_alloc_align = 4
self.stack_alloc_offset_bits = stack_alloc_order + self.ramdump.page_shift - self.stack_alloc_align
self.stack_alloc_index_bits = depot_stack_bits - stack_alloc_null_protection_bits - self.stack_alloc_offset_bits
if self.ramdump.field_offset('union handle_parts', 'extra') is not None:
self.stack_alloc_index_bits -= 5
def stack_depot_fetch(self, handle, symbol=True):
if handle == 0 or handle == None:
return -1, None, None
slabindex = handle & ((1 << self.stack_alloc_index_bits) - 1)
handle_offset = (handle >> self.stack_alloc_index_bits) & \
((1 << self.stack_alloc_offset_bits) - 1)
handle_offset = handle_offset << self.stack_alloc_align
slab = self.ramdump.read_word(
self.stack_slabs + (self.stack_slabs_size * slabindex))
if slab is None:
return -1, None, None
stack = slab + handle_offset
stack_nr_entries = self.ramdump.read_structure_field(
stack, 'struct stack_record', 'size')
if stack_nr_entries is None:
return -1, None, None
if stack_nr_entries <= 0 or stack_nr_entries > 16:
return -1, None, None
stack_addr = []
stack_str = ''
for i in range(0, stack_nr_entries):
addr = self.ramdump.read_word(
stack + self.stack_trace_entries_offset + i *
self.stack_trace_entry_size)
if not addr:
break
stack_addr.append(addr)
if symbol:
look = self.ramdump.unwind_lookup(addr)
if look is None:
break
symname, offset = look
unwind_dat = ' [<{0:x}>] {1}+0x{2:x}\n'.format(
addr, symname, offset)
stack_str = stack_str + unwind_dat
else:
stack_str += str(addr)
return stack_nr_entries, stack_addr, stack_str
class PageTrace(object):
def __init__(self, ramdump):
self.ramdump = ramdump
self.trace_entry_size = self.ramdump.sizeof('unsigned long')
self.trace_offset = 0
self.nr_entries_offset = 0
self.trace_entries_offset = 0
if (self.ramdump.kernel_version <= (3, 19, 0)):
self.trace_offset = self.ramdump.field_offset('struct page', 'trace')
self.nr_entries_offset = self.ramdump.field_offset(
'struct stack_trace', 'nr_entries')
self.trace_entries_offset = self.ramdump.field_offset(
'struct page', 'trace_entries')
else:
self.trace_offset = self.ramdump.field_offset(
'struct page_ext', 'trace')
if self.ramdump.is_config_defined('CONFIG_STACKDEPOT'):
self.stackdepot = StackDepot(self.ramdump)
else:
self.trace_entries_offset = self.ramdump.field_offset(
'struct page_ext', 'trace_entries')
self.nr_entries_offset = self.ramdump.field_offset(
'struct page_ext', 'nr_entries')
self.page_ext_size = self.ramdump.sizeof("struct page_ext")
if self.ramdump.kernel_version >= (4, 9, 0):
self.page_owner_size = self.ramdump.sizeof("struct page_owner")
if self.ramdump.address_of("page_ext_size"):
self.page_ext_size = self.ramdump.read("page_ext_size")
else:
self.page_ext_size = self.page_ext_size + self.page_owner_size
self.page_owner_ops_offset = self.ramdump.read_structure_field(
'page_owner_ops', 'struct page_ext_operations', 'offset')
'''
Following based upon definition in include/linux/mmzone.h
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
'''
try:
self.max_order = int(self.ramdump.get_config_val(
"CONFIG_FORCE_MAX_ZONEORDER"))
except:
self.max_order = 11
self.offset_comm = self.ramdump.field_offset('struct page_owner', 'comm')
self.offset_freepid = self.ramdump.field_offset('struct page_owner', 'free_pid')
def page_trace(self, pfn, alloc):
offset = 0
struct_holding_trace_entries = 0
gfp = 0
comm = -1
if not alloc and self.ramdump.kernel_version < (5, 4, 0):
return -1, -1, -1, -1, -1, -1
page = pfn_to_page(self.ramdump, pfn)
order = 0
if (self.ramdump.kernel_version <= (3, 19, 0)):
nr_trace_entries = self.ramdump.read_int(
page + self.trace_offset + self.nr_entries_offset)
struct_holding_trace_entries = page
order = self.ramdump.read_structure_field(
page, 'struct page', 'order')
else:
phys = pfn << self.ramdump.page_shift
if phys is None or phys == 0:
return -1, -1, -1, -1, -1, -1, -1
page_ext = self.ramdump.mm.lookup_page_ext(pfn)
"""
page_ext will be null here if the first page of a section is not valid.
See page_ext_init().
"""
if not page_ext:
return -1, -1, -1, -1, -1, -1, -1
if self.ramdump.arm64:
temp_page_ext = page_ext + (pfn * self.page_ext_size)
else:
pfn_index = pfn - (self.ramdump.phys_offset >> self.ramdump.page_shift)
temp_page_ext = page_ext + (pfn_index * self.page_ext_size)
try:
page_ext_flags = self.ramdump.read_structure_field(
temp_page_ext, 'struct page_ext', 'flags')
except:
page_ext_flags = -1
if self.ramdump.kernel_version >= (4, 9, 0):
temp_page_owner = temp_page_ext + self.page_owner_ops_offset
order = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'order')
if alloc:
pid = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'pid')
ts_nsec = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'ts_nsec')
gfp = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'gfp_mask')
if self.offset_comm is not None:
comm = self.ramdump.read_cstring(temp_page_owner + self.offset_comm, 16)
else:
if self.offset_freepid is not None:
pid = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'free_pid')
else:
pid = -1
ts_nsec = self.ramdump.read_structure_field(temp_page_owner,
'struct page_owner', 'free_ts_nsec')
gfp = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', 'gfp_mask')
if ts_nsec is None:
ts_nsec = -1
else:
order = self.ramdump.read_structure_field(
temp_page_ext, 'struct page_ext', 'order')
alloc_str = ''
if not self.ramdump.is_config_defined('CONFIG_STACKDEPOT'):
nr_trace_entries = self.ramdump.read_int(
temp_page_ext + self.nr_entries_offset)
struct_holding_trace_entries = temp_page_ext
if nr_trace_entries is not None:
if nr_trace_entries > 0 and nr_trace_entries <= 16:
for i in range(0, nr_trace_entries):
addr = self.ramdump.read_word(
struct_holding_trace_entries + self.trace_entries_offset + i *
self.trace_entry_size)
if not addr:
break
look = self.ramdump.unwind_lookup(addr)
if look is None:
break
symname, offset = look
unwind_dat = ' [<{0:x}>] {1}+0x{2:x}\n'.format(
addr, symname, offset)
alloc_str = alloc_str + unwind_dat
else:
if self.ramdump.kernel_version >= (4, 9, 0):
if not alloc:
handle_str = 'free_handle'
else:
handle_str = 'handle'
handle = self.ramdump.read_structure_field(
temp_page_owner, 'struct page_owner', handle_str)
else:
handle = self.ramdump.read_structure_field(
temp_page_ext, 'struct page_ext', 'handle')
if handle == 0 or handle == None:
return -1, -1, -1, -1, -1, -1, -1
nr_trace_entries, stack_addrs, alloc_str = self.stackdepot.stack_depot_fetch(handle)
if nr_trace_entries is None:
return -1, -1, -1, -1, -1, -1, -1
if nr_trace_entries <= 0 or nr_trace_entries > 16:
return -1, -1, -1, -1, -1, -1, -1
if order >= self.max_order:
return -1, -1, -1, -1, -1, -1, -1
return alloc_str, order, pid, ts_nsec, gfp, comm, page_ext_flags
@register_parser('--print-pagetracking', 'print page tracking information (if available)')
class PageTracking(RamParser):
def __init__(self, *args):
super(PageTracking, self).__init__(*args)
'''
Following based upon definition in include/linux/mmzone.h
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
'''
if not self.ramdump.is_config_defined('CONFIG_PAGE_OWNER'):
print_out_str('CONFIG_PAGE_OWNER not defined')
return
try:
self.max_order = int(self.ramdump.get_config_val(
"CONFIG_FORCE_MAX_ZONEORDER"))
except:
self.max_order = 11
self.pagetrace = PageTrace(self.ramdump)
return
def parse_output(self, pfn, out_tracking, out_tracking_freed,
page_size, sorted_pages):
str_f = "PFN : 0x{0:x}-0x{1:x} Page : 0x{2:x} Order : {3} PID : {4} {5}ts_nsec {6} gfp 0x{7:x} pgext 0x{8:x}\n" \
"{9}\n"
page = pfn_to_page(self.ramdump, pfn)
order = 0
if (page_buddy(self.ramdump, page) or
page_count(self.ramdump, page) == 0):
function_list, order, pid, ts_nsec, gfp, comm, ext_flags = self.pagetrace.page_trace(pfn, False)
if function_list == -1:
return 0
nr_pages = (1 << order) - 1
out_tracking_freed.write(str_f.format(pfn, pfn + nr_pages,
page, order, pid,
"Comm: {} ".format(comm) if comm != -1 else "",
ts_nsec, gfp, ext_flags, function_list))
return nr_pages
function_list, order, pid, ts_nsec, gfp, comm, ext_flags = self.pagetrace.page_trace(pfn, True)
if function_list == -1:
return 0
if order >= self.max_order:
out_tracking.write('PFN 0x{:x} page 0x{:x} skip as order '
'0x{:x}\n'.format(pfn, page, order))
nr_pages = (1 << order) - 1
out_tracking.write(str_f.format(pfn, pfn + nr_pages,
page, order, pid, "Comm: {} ".format(comm) if comm != -1 else "",
ts_nsec, gfp, ext_flags, function_list))
if comm != -1:
pid = "{}-{}".format(pid, comm)
if function_list in sorted_pages:
sorted_pages[function_list]["page_count"] = \
sorted_pages[function_list]["page_count"] + 1
sorted_pages[function_list]["memory"] += page_size * (1 << int(order))
if pid in sorted_pages[function_list]:
if order in sorted_pages[function_list][pid]:
sorted_pages[function_list][pid][order] = \
sorted_pages[function_list][pid][order] + 1
else:
sorted_pages[function_list][pid][order] = 1
else:
sorted_pages[function_list][pid] = {}
sorted_pages[function_list][pid][order] = 1
else:
sorted_pages[function_list] = {}
sorted_pages[function_list]["page_count"] = 1
sorted_pages[function_list]["memory"] = page_size * (1<<int(order))
sorted_pages[function_list][pid] = {}
sorted_pages[function_list][pid][order] = 1
return nr_pages
def parse(self):
ranges = None
if self.ramdump.minidump:
for eb_file in self.ramdump.ebi_files:
path1 = eb_file[3]
path = os.path.join(os.path.dirname(path1), "md_PAGEOWNER.bin")
if not os.path.exists(path):
print_out_str(path + " not found")
return
input_file = open(path, 'r', errors="ignore")
lines = input_file.readlines()
i = 0
functions = defaultdict(list)
pfns = defaultdict(list)
pfns_size = defaultdict(list)
while i < len(lines):
line = lines[i];
try:
pfn, handle, n = [int(x) for x in line.split()]
except:
break
i = i + 1
if not functions[handle]:
for j in range(0, n):
line = lines[i]
try:
int(line, 16)
except:
break
functions[handle].append(line)
i = i+1
pfns[handle].append(pfn)
i = 0
for key in pfns:
pfns_size[key] = len(pfns[key])
output_file = self.ramdump.open_file("pageowner_dump.txt", 'w')
for key, value in sorted(pfns_size.items(), key=lambda item: item[1], reverse = True):
output_file.write("No of pfns :" + str(value))
output_file.write('\n')
output_file.write("Pfns :" + str(pfns[key]))
output_file.write('\n')
for key2 in functions:
if (key == key2):
output_file.write("call stack :\n")
for i in range(0,len(functions[key])):
look = self.ramdump.unwind_lookup(int(functions[key][i], 16))
if look is None:
continue
symname, offset = look
unwind_dat = ' [<{0:x}>] {1}+0x{2:x}\n'.format(
int(functions[key][i], 16), symname, offset)
output_file.write(unwind_dat)
output_file.write("\n")
output_file.close()
print_out_str(
'---wrote page tracking information to pageowner_dump.txt')
return
for arg in sys.argv:
if "ranges=" in arg:
g_optimization = True
k, ranges = arg.split("=")
start, end = ranges.split('-')
start_pfn = int(start, 16) >> self.ramdump.page_shift
end_pfn = int(end, 16) >> self.ramdump.page_shift
break
elif "page=" in arg:
g_optimization = True
k, page = arg.split('=')
page = int(page, 16)
start_pfn = page_to_pfn(self.ramdump, page)
end_pfn = start_pfn + 1
break
else:
g_optimization = False
if not self.ramdump.is_config_defined('CONFIG_PAGE_OWNER'):
print_out_str('CONFIG_PAGE_OWNER not defined')
return
if self.ramdump.kernel_version >= (4, 4):
if self.ramdump.kernel_version >= (5, 10):
page_owner_inited = self.ramdump.address_of("page_owner_inited.key.enabled.counter")
if self.ramdump.read_int(page_owner_inited) != 1:
print_out_str("page_owner is not set in cmdline")
return
else:
if not self.ramdump.is_config_defined('CONFIG_PAGE_OWNER_ENABLE_DEFAULT'):
print_out_str('CONFIG_PAGE_OWNER_ENABLE_DEFAULT not defined')
return
out_tracking = self.ramdump.open_file('page_tracking.txt')
out_frequency = self.ramdump.open_file('page_frequency.txt')
out_tracking_freed = self.ramdump.open_file('page_tracking_freed.txt')
sorted_pages = {}
page_size = self.ramdump.get_page_size() >> 10 #KB
nr_pages_skip = 0
if g_optimization is True:
for pfn in range(start_pfn, end_pfn):
if nr_pages_skip > 0:
nr_pages_skip -= 1
continue
nr_pages_skip = self.parse_output(pfn, out_tracking,
out_tracking_freed, page_size, sorted_pages)
else:
for pfn in for_each_pfn(self.ramdump):
if nr_pages_skip > 0:
nr_pages_skip -= 1
continue
nr_pages_skip = self.parse_output(pfn, out_tracking,
out_tracking_freed, page_size, sorted_pages)
sortdict = {}
for i in sorted_pages:
sortdict[i] = sorted_pages[i]["memory"]
sortdict = sorted(sortdict, key = lambda k: sortdict[k], reverse = True)
try:
for i in sortdict:
out_frequency.write('Allocated {0} times, total memory: {1} KB\n'.
format(sorted_pages[i]["page_count"], sorted_pages[i]["memory"]))
out_frequency.write(i)
for j in sorted_pages[i]:
if str(j) != "page_count" and str(j) != "memory":
for order in sorted_pages[i][j]:
memory_order = page_size * (1<<int(order)) * sorted_pages[i][j][order]
out_frequency.write("pid:{0} order:{1} frequency:{2} memory:{3} KB\n"\
.format(j, order, sorted_pages[i][j][order], memory_order))
out_frequency.write('\n')
except Exception as e:
print_out_str(str(e))
out_tracking.close()
out_frequency.close()
out_tracking_freed.close()
print_out_str(
'---wrote allocated page tracking information to page_tracking.txt')
print_out_str(
'---wrote freed page tracking information to page_tracking_freed.txt')
print_out_str(
'---wrote page frequency information to page_frequency.txt')

View File

@@ -0,0 +1,100 @@
# Copyright (c) 2012-2015, 2020 The Linux Foundation. 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.
from parser_util import register_parser, RamParser
import linux_list as llist
@register_parser('--print-pagetypeinfo', 'Print the pagetypeinfo')
class Pagetypeinfo(RamParser):
def pages_to_mb(self, nr_pages):
return (nr_pages >> 8)
def print_pagetype_info_per_zone(self, ramdump, zone, nr_migrate_types):
free_area_offset = ramdump.field_offset('struct zone', 'free_area')
free_area_size = ramdump.sizeof('struct free_area')
free_list_offset = ramdump.field_offset(
'struct free_area', 'free_list')
list_head_size = ramdump.sizeof('struct list_head')
migratetype_names = ramdump.address_of('migratetype_names')
zone_name_offset = ramdump.field_offset('struct zone', 'name')
zname_addr = ramdump.read_word(zone + zone_name_offset)
zname = ramdump.read_cstring(zname_addr, 12)
total_pages = 0
total_orders = [0]*11
prefix = "%12s"
self.f.write("Zone %s\n" % (zname))
self.f.write(prefix % ("order"))
for order in range(0, 11):
self.f.write("%8d" % (order))
self.f.write('\n')
self.f.write(prefix % ("type"))
for order in range(0, 11):
self.f.write("%6d%2s" % (1 << (order + 2), "KB"))
self.f.write('\n')
for mtype in range(0, nr_migrate_types):
total_migratetype_pages = 0
mname_addr = ramdump.read_word(ramdump.array_index(migratetype_names, 'char *', mtype))
mname = ramdump.read_cstring(mname_addr, 12)
self.f.write(prefix % (mname))
for order in range(0, 11):
area = zone + free_area_offset + order * free_area_size
free_list = area + free_list_offset + list_head_size * mtype
it = llist.ListWalker(ramdump, free_list, 0)
nr = 0
for i in it:
nr += 1
self.f.write('%8s' % (nr))
total_orders[order] += nr
total_migratetype_pages += (nr << order)
total_pages += (nr << order)
self.f.write(' = %s MB %s pages\n' %
(self.pages_to_mb(total_migratetype_pages),
total_migratetype_pages))
self.f.write(prefix % ("Total"))
for order in range(0, 11):
self.f.write('%8s' % (total_orders[order]))
self.f.write('\n')
self.f.write('Approximate total for zone {0}: {1} MB, {2} pages\n'.format(
zname, self.pages_to_mb(total_pages), total_pages))
def parse(self):
self.f = open(self.ramdump.outdir + "/pagetypeinfo.txt", "w")
max_nr_zones = self.ramdump.gdbmi.get_value_of('__MAX_NR_ZONES')
nr_migrate_types = self.ramdump.gdbmi.get_value_of('MIGRATE_TYPES')
contig_page_data = self.ramdump.address_of('contig_page_data')
node_zones_offset = self.ramdump.field_offset(
'struct pglist_data', 'node_zones')
present_pages_offset = self.ramdump.field_offset(
'struct zone', 'present_pages')
sizeofzone = self.ramdump.sizeof('struct zone')
zone = contig_page_data + node_zones_offset
while zone < (contig_page_data + node_zones_offset + max_nr_zones * sizeofzone):
present_pages = self.ramdump.read_word(zone + present_pages_offset)
if not not present_pages:
self.print_pagetype_info_per_zone(
self.ramdump, zone, nr_migrate_types)
zone = zone + sizeofzone
self.f.close()

View File

@@ -0,0 +1,44 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
from parser_util import register_parser, RamParser
@register_parser('--print-preempt-count', 'Print the preempt_count')
class PrintPreemptCount(RamParser):
def __init__(self, *args):
super(PrintPreemptCount, self).__init__(*args)
def parse(self):
if not self.ramdump.arm64:
return;
f = self.ramdump.open_file('preempt_count.txt')
runqueues_addr = self.ramdump.address_of('runqueues')
curr_offset = self.ramdump.field_offset('struct rq', 'curr')
pid_offset = self.ramdump.field_offset('struct task_struct', 'pid')
comm_offset = self.ramdump.field_offset('struct task_struct', 'comm')
for i in self.ramdump.iter_cpus():
rq_addr = runqueues_addr + self.ramdump.per_cpu_offset(i)
curr_addr = self.ramdump.read_word(rq_addr + curr_offset)
pid = self.ramdump.read_int(curr_addr + pid_offset)
comm = self.ramdump.read_cstring(curr_addr + comm_offset, 16)
f.write('CPU{} current task comm: {:<16} pid: {} \n'.format(i, comm, pid))
preempt_count = self.ramdump.read_structure_field(curr_addr, 'struct thread_info', 'preempt.count')
f.write('preempt.count: 0x{:x} \n'.format(preempt_count))
# PREEMPT_MASK: 0x000000ff
# SOFTIRQ_MASK: 0x0000ff00
# HARDIRQ_MASK: 0x000f0000
# NMI_MASK: 0x00f00000
f.write('\t preemption count: {0} \n'.format(preempt_count & 0x000000ff))
f.write('\t softirq count: {0} \n'.format((preempt_count & 0x0000ff00) >> 8))
f.write('\t hardirq count: {0} \n'.format((preempt_count & 0x000f0000) >> 16))
f.write('\t nmi count: {0} \n'.format((preempt_count & 0x00f00000) >> 20))
preempt_need_resched = self.ramdump.read_structure_field(curr_addr, 'struct thread_info', 'preempt.need_resched')
f.write('preempt.need_resched: 0x{:x} \n'.format(preempt_need_resched))
flags = self.ramdump.read_structure_field(curr_addr, 'struct thread_info', 'flags')
f.write('thread information flags: 0x{:x} \n'.format(flags))
f.write('\t TIF_NEED_RESCHED : {0} \n'.format((flags & 0x2) >> 1))
f.write('\n')
f.close()

View File

@@ -0,0 +1,220 @@
# Copyright (c) 2021-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.
from parser_util import register_parser, RamParser
from print_out import print_out_str
from utasklib import UTaskLib
from utasklib import ProcessNotFoundExcetion
import struct
@register_parser('--properties', 'Extract properties from ramdump ')
class Properties(RamParser):
def __init__(self, *args):
super(Properties, self).__init__(*args)
self.f_path_offset = self.ramdump.field_offset('struct file', 'f_path')
self.dentry_offset = self.ramdump.field_offset('struct path', 'dentry')
self.d_iname_offset = self.ramdump.field_offset('struct dentry', 'd_iname')
self.SIZEOF_PROP_BT=0x14
self.SIZEOF_PROP_INFO=0x60
self.SIZEOF_PROP_AREA=0x80
self.OFFSET_MAGIC = 0x8
self.OFFSET_DATA = self.SIZEOF_PROP_AREA
self.PROP_NAME_MAX=100
self.PROP_VALUE_MAX=92
self.proplist = []
self.data = ""
self.header = ""
def foreach_property(self, prop_bt):
if prop_bt == 0 or self.OFFSET_DATA + prop_bt+ self.SIZEOF_PROP_BT > len(self.data):
return False
btEntry = struct.unpack('<IIIII', self.data[
self.OFFSET_DATA+ prop_bt : self.OFFSET_DATA + prop_bt+ self.SIZEOF_PROP_BT])
name_length = btEntry[0]
prop = btEntry[1]
left = btEntry[2]
right = btEntry[3]
children = btEntry[4]
name = self.data[
self.OFFSET_DATA + prop_bt+ self.SIZEOF_PROP_BT :
self.OFFSET_DATA + prop_bt+ self.SIZEOF_PROP_BT + name_length]
if left != 0:
err = self.foreach_property(left)
if not err:
return False
if prop != 0:
if self.OFFSET_DATA + prop+ self.PROP_VALUE_MAX <= len(self.data):
value = self.data[
self.OFFSET_DATA + prop + 0x4 :
self.OFFSET_DATA + prop + self.PROP_VALUE_MAX
].decode('ascii', 'ignore').strip().split('\0')[0]
name = self.data[
self.OFFSET_DATA + prop + self.SIZEOF_PROP_INFO :
self.OFFSET_DATA + prop + self.SIZEOF_PROP_INFO + self.PROP_NAME_MAX
].decode('ascii', 'ignore').split('\0')[0]
self.proplist.append([name, value])
if children != 0:
err = self.foreach_property(children)
if not err:
return False
if right != 0:
err = self.foreach_property(right)
if not err:
return False
return True
def parse_property(self, taskinfo):
index = 0
for vma in taskinfo.vmalist:
if "u:object_r:" in vma.file_name:
self.data = UTaskLib.read_binary(
self.ramdump, taskinfo.mmu, vma.vm_start, vma.vm_end - vma.vm_start)
if index == 0 and self.OFFSET_MAGIC+8 <= len(self.data):
btEntry = struct.unpack('<II', self.data[self.OFFSET_MAGIC:self.OFFSET_MAGIC+8])
magic = btEntry[0]
version = btEntry[1]
self.header = "System Properties Magic:" + str(hex(magic)) \
+ " Version:" + str(hex(version)) + "\n---------------------\n"
index = index + 1
# root node of prop_bt
if self.SIZEOF_PROP_AREA + self.SIZEOF_PROP_BT <= len(self.data):
btEntry = struct.unpack('<IIIII', self.data[
self.SIZEOF_PROP_AREA : self.SIZEOF_PROP_AREA + self.SIZEOF_PROP_BT])
root_child = btEntry[4]
if root_child != 0:
self.foreach_property(root_child)
def find_property_from_file(self, taskinfo, prop_name, prop_file):
for vma in taskinfo.vmalist:
if vma.file_name == prop_file:
self.data = UTaskLib.read_binary(
self.ramdump, taskinfo.mmu, vma.vm_start, vma.vm_end - vma.vm_start)
if self.data and len(self.data) > 0:
value = self.find_property(prop_name)
return value
return -1
def find_property(self, prop_name):
remaining_name = prop_name
current = 0
count = 0
while True:
idx = remaining_name.find('.')
if idx == -1:
cname = remaining_name
else:
cname = remaining_name[:idx]
if self.OFFSET_DATA + current + self.SIZEOF_PROP_BT > len(self.data):
return None
btEntry = struct.unpack('<I', self.data[
self.OFFSET_DATA + current + self.SIZEOF_PROP_BT - 4:
self.OFFSET_DATA + current + self.SIZEOF_PROP_BT])
current = btEntry[0]
if current == 0:
return None
name = self.data[
self.OFFSET_DATA+current+self.SIZEOF_PROP_BT :
self.OFFSET_DATA + current+ self.SIZEOF_PROP_BT+self.PROP_NAME_MAX
].decode('ascii', 'ignore').split('\0')[0]
current = self.find_prop_bt(current, cname)
if current == 0:
return None
if idx == -1:
break
remaining_name = remaining_name[idx+1:]
count = count + 1
btEntry = struct.unpack('<IIIII', self.data[
self.OFFSET_DATA + current :
self.OFFSET_DATA + current + self.SIZEOF_PROP_BT])
prop = btEntry[1]
left = btEntry[2]
right = btEntry[3]
children = btEntry[4]
if prop != 0:
if self.OFFSET_DATA + prop+ self.PROP_VALUE_MAX < len(self.data):
value = self.data[
self.OFFSET_DATA+prop+0x4 :
self.OFFSET_DATA + prop+ self.PROP_VALUE_MAX
].decode('ascii', 'ignore').strip().split('\0')[0]
name = self.data[
self.OFFSET_DATA+prop+self.SIZEOF_PROP_INFO :
self.OFFSET_DATA + prop+ self.SIZEOF_PROP_INFO+self.PROP_NAME_MAX+1
].decode('ascii', 'ignore').split('\0')[0]
return value
def find_prop_bt(self, prop_bt, prop_name):
current = prop_bt
while True:
if current == 0:
break
if self.OFFSET_DATA + current + self.SIZEOF_PROP_BT > len(self.data):
return 0
btEntry = struct.unpack('<IIIII', self.data[
self.OFFSET_DATA + current :
self.OFFSET_DATA + current + self.SIZEOF_PROP_BT])
prop = btEntry[1]
left = btEntry[2]
right = btEntry[3]
children = btEntry[4]
name = self.data[
self.OFFSET_DATA+current+self.SIZEOF_PROP_BT :
self.OFFSET_DATA + current+ self.SIZEOF_PROP_BT+self.PROP_NAME_MAX
].decode('ascii', 'ignore').split('\0')[0]
if name == prop_name:
return current
if self.cmp_prop_name(prop_name, name) < 0:
if left == 0:
return 0
current = left
else:
if right == 0:
return 0
current = right
def cmp_prop_name(self, name1,name2):
if len(name1) < len(name2):
return -1
elif len(name1) > len(name2):
return 1
elif name1 > name2:
return 1
else:
return -1
def parse(self):
try:
taskinfo = UTaskLib(self.ramdump).get_utask_info("init", logging=True)
except ProcessNotFoundExcetion:
print_out_str("init process was not started")
return False
with self.ramdump.open_file("Properties.txt") as out_file:
self.parse_property(taskinfo)
if self.header:
out_file.write(self.header)
for name, value in self.proplist:
out_file.write("{}={}\n".format(name, value))
return len(self.proplist) > 0
# get property from init mmap
def property_get(self, prop_name, prop_file):
try:
taskinfo = UTaskLib(self.ramdump).get_utask_info("init")
except ProcessNotFoundExcetion:
print_out_str("init process was not started")
return
return self.find_property_from_file(taskinfo, prop_name, prop_file)

View File

@@ -0,0 +1,251 @@
# Copyright (c) 2018, 2020-2021 The Linux Foundation. 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.
from parser_util import cleanupString
from parser_util import register_parser, RamParser
from print_out import print_out_str
import struct
import datetime
import re
import collections
#PERSISTENT_RAM_SIG /* DBGC */
PERSISTENT_RAM_SIG_SIZE = 4
@register_parser('--pstore', 'Extract event logs from pstore')
class PStore(RamParser):
def calculate_percpu_eventbuf_size(self, base_addr):
try:
event_zone_addr = self.ramdump.read_u64(base_addr +
self.ramdump.field_offset('struct ramoops_context', 'eprzs'))
event_zone_addr = self.ramdump.read_u64(event_zone_addr)
start_addr = self.ramdump.read_u64(event_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'paddr'))
percpu_size = self.ramdump.read_u64(event_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'size'))
except:
return None, None
return start_addr, percpu_size
def print_console_logs(self, pstore_out, addr, size):
pstore = self.ramdump.read_physical(addr, size)
pstore_out.write(cleanupString(pstore.decode('ascii', 'ignore')) + '\n')
def sort_event_data(self, event_data, pstore_out):
'''
Event log buffer is circular, so this function
sorts the logs in ascending timestamp manner
'''
ordered_event_data = collections.OrderedDict(sorted(event_data.items()))
for ts, log in ordered_event_data.items():
pstore_out.write(log)
def print_event_logs(self, pstore_out, addr, size):
'''
This function tries to format the event logs before logging them to
the core specific rtb file. Raw Data will be in the format given below
io_read: type=readl cpu=1 ts:58270610802 data=0xffffff8009de8614
caller=qcom_geni_serial_start_tx+0x114/0x150
io_write: type=writel cpu=1 ts:58270618875 data=0xffffff8009de880c
caller=qcom_geni_serial_start_tx+0x130/0x150
Timestamp is extracted from raw data and converted to secs format and cpu
field is removed since it is redundant.
Final Formatting will be as shown below
[644.279442] io_write : writel from address 0xffffff8009673120(None) called
from qcom_cpufreq_hw_target_index+0x60/0x64
'''
pstore = self.ramdump.read_physical(addr, size)
event_log_data = cleanupString(pstore.decode('ascii', 'ignore'))
event_data = event_log_data.split('\n')
formatted_event_data = {}
for line in event_data:
expr = r'.*(io_.*):.*type=(.*)cpu=(.*)ts:(.*)data=(.*)caller=(.*).*'
regEx = re.search(expr, line)
if regEx:
event_type = regEx.group(2).strip()
timestamp = regEx.group(4).strip()
timestamp = round(float(timestamp)/10**9,9)
timestamp = format(timestamp,'.9f')
data = regEx.group(5).strip()
caller = regEx.group(6).strip()
log_string = "[{0}] {1} : {2} from address {3} called from {4}\n".format(timestamp,
regEx.group(1), event_type, data, caller)
formatted_event_data[timestamp] = log_string
else:
continue
self.sort_event_data(formatted_event_data, pstore_out)
def calculate_console_size(self, base_addr):
size = self.ramdump.read_u64(base_addr +
self.ramdump.field_offset('struct ramoops_context', 'console_size'))
if size == 0:
return None, size
console_zone_addr = self.ramdump.read_u64(base_addr +
self.ramdump.field_offset('struct ramoops_context', 'cprz'))
start_addr = self.ramdump.read_u64(console_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'paddr'))
console_buf = self.ramdump.read_u64(console_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'buffer'))
console_size = self.ramdump.read_u32(console_buf +
self.ramdump.field_offset('struct persistent_ram_zone', 'size'))
header = self.ramdump.sizeof('((struct persistent_ram_zone *)0x0)->paddr')
return start_addr + PERSISTENT_RAM_SIG_SIZE, console_size + header
def extract_console_logs(self, base_addr):
'''
Parses the console logs from pstore
'''
start_addr, console_size = self.calculate_console_size(base_addr)
if start_addr is None:
return
pstore_out = self.ramdump.open_file('console_logs.txt')
self.print_console_logs(pstore_out, start_addr, console_size)
pstore_out.close()
def extract_io_event_logs(self, base_addr):
'''
Parses the RTB data (register read/writes) stored in the persistent
ram zone. Data is extracted on per cpu basis into separate per core
files.
'''
start_addr, percpu_size = self.calculate_percpu_eventbuf_size(base_addr)
if start_addr is None:
return
nr_cpus = self.ramdump.get_num_cpus()
for cpu in range(0,nr_cpus):
pstore_out = self.ramdump.open_file('msm_rtb{0}.txt'.format(cpu))
cpu_offset = percpu_size*cpu
self.print_event_logs(pstore_out, start_addr+cpu_offset, percpu_size)
pstore_out.close()
def print_clockdata_info(self):
'''
Extracts the epoch data from clock data struct. This helps in converting
HLOS timestamps to non-HLOS timestamps and vica-versa. This also
additionally logs the Linux Banner for ease in post-processing.
'''
out_file = self.ramdump.open_file('epoch_info.txt')
banner_addr = self.ramdump.address_of('linux_banner')
if banner_addr is not None:
banner_addr = self.ramdump.kernel_virt_to_phys(banner_addr)
vm_v = self.ramdump.gdbmi.get_value_of_string('linux_banner')
if vm_v is not None:
out_file.write('Linux Banner : {}\n'.format(vm_v))
epoch_ns = self.ramdump.read_word('cd.read_data[0].epoch_ns')
epoch_cyc = self.ramdump.read_word('cd.read_data[0].epoch_cyc')
out_file.write('\nepoch_ns: {0}ns epoch_cyc: {1}\n'.format(epoch_ns,epoch_cyc))
out_file.close()
def extract_pmsg_logs(self, pmsg, pmsg_size):
'''
Parses the pmsg log stored in the persistent
ram zone.
'''
log_type_list = ["0:main", "1:radio", "2:event", "3:system", "4:crash",
"5:stats", "6:security", "7:kernel"]
priority_list = ["0:UNKNOWN", "1:DEFAULT", "2:VERBOSE", "3:DEBUG", "4:INFO",
"5:WARNING", "6:ERROR", "7:FATAL", "8:SILENT"]
pmsg_out = self.ramdump.open_file('pmsg_logs.txt')
pmsg_out.write("timestamp".ljust(26))
pmsg_out.write("uid".rjust(6))
pmsg_out.write("pid".rjust(6))
pmsg_out.write("tid".rjust(6))
pmsg_out.write(" ")
pmsg_out.write("log_type".ljust(10))
pmsg_out.write("level".ljust(12))
pmsg_out.write("message")
pmsg_out.write("\n")
next_addr = 4
content_err = False
while next_addr < (pmsg_size - 19):
magic, len, uid, pid, id, tid, tv_sec, tv_nsec, outtag = \
struct.unpack('<c3HbH2Ib', pmsg[next_addr:next_addr + 19])
if magic == b'l' and 0 <= outtag < 9 and 0 <= id < 7 and len > 19 and \
(content_err or pmsg[next_addr-1:next_addr] == b'\x00'):
tv_nsec = str(tv_nsec // 1000)
tv_nsec = tv_nsec.zfill(6)
date = datetime.datetime.utcfromtimestamp(tv_sec)
timestamp = date.strftime("%y-%m-%d %H:%M:%S") + '.' + tv_nsec
msg = pmsg[next_addr + 19:next_addr + len].replace(b'\x0a', b'\x20')
msg = msg.replace(b'\x00', b'\x20')
pmsg_out.write(timestamp.ljust(26))
pmsg_out.write(str(uid).rjust(6))
pmsg_out.write(str(pid).rjust(6))
pmsg_out.write(str(tid).rjust(6))
pmsg_out.write(" ")
pmsg_out.write(str(log_type_list[id]).ljust(10))
pmsg_out.write(str(priority_list[outtag]).ljust(12))
if next_addr + len < pmsg_size:
if pmsg[next_addr + len:next_addr + len + 1] != b'l':
content_err = True
next_addr += 19
pmsg_out.write("Content Missing!\n")
continue
pmsg_out.write(msg.decode("ascii","ignore"))
pmsg_out.write("\n")
content_err = False
next_addr += len
else:
next_addr += 1
pmsg_out.close()
def extract_pmsg_logs_fd(self, base_addr):
size = self.ramdump.read_u64(base_addr +
self.ramdump.field_offset('struct ramoops_context', 'pmsg_size'))
if size == 0:
return
pmsg_zone_addr = self.ramdump.read_u64(base_addr +
self.ramdump.field_offset('struct ramoops_context', 'mprz'))
start_addr = self.ramdump.read_u64(pmsg_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'paddr'))
pmsg_size = self.ramdump.read_u64(pmsg_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'size'))
zone_buffer_addr = self.ramdump.read_u64(pmsg_zone_addr +
self.ramdump.field_offset('struct persistent_ram_zone', 'buffer'))
buffer_start = self.ramdump.read_u32(zone_buffer_addr +
self.ramdump.field_offset('struct persistent_ram_buffer', 'start'))
buffer_size = self.ramdump.read_u32(zone_buffer_addr +
self.ramdump.field_offset('struct persistent_ram_buffer', 'size'))
pmsg = b""
if buffer_start < buffer_size:
pmsg = self.ramdump.read_physical(start_addr+buffer_start, buffer_size-buffer_start)
pmsg = pmsg + self.ramdump.read_physical(start_addr, buffer_start)
else:
pmsg = self.ramdump.read_physical(start_addr, buffer_size)
self.extract_pmsg_logs(pmsg, buffer_size)
def parse(self):
if not self.ramdump.minidump:
if not self.ramdump.is_config_defined('CONFIG_PSTORE'):
print_out_str('CONFIG_PSTORE is not defined')
return
base_addr = self.ramdump.address_of('oops_cxt')
self.extract_io_event_logs(base_addr)
self.extract_console_logs(base_addr)
self.print_clockdata_info()
self.extract_pmsg_logs_fd(base_addr)
else:
pmsg_addr = list(self.ramdump.ebi_pa_name_map.keys())[
list(self.ramdump.ebi_pa_name_map.values()).index("KPMSG")]
if pmsg_addr is not None:
for idx, pa, end, va, size in self.ramdump.ebi_files_minidump:
if pa == pmsg_addr:
break
pmsg_cxt = self.ramdump.read_physical(pmsg_addr, size)
self.extract_pmsg_logs(pmsg_cxt, size)

View File

@@ -0,0 +1,241 @@
# Copyright (c) 2021 The Linux Foundation. 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
import pdb
# Global variables
rnode_offset = 0
rnode_shift_offset = 0
slots_offset = 0
pointer_size = 0
count_offset = 0
sk_offset = 0
us_offset = 0
peer_offset = 0
node_offset = 0
port_offset = 0
sk_data_ready_offset = 0
qmi_data_read_addr = 0
node_id_offset = 0
ep_offset = 0
ep_xmit_offset = 0
read_data_offset = 0
rx_func_offset = 0
kobj_offset = 0
name_offset = 0
qrtr_tx_flow_offset = 0
tx_flow_pending_offset = 0
max_slots = 64
@register_parser('--qrtr-ports', 'Print all the qrtr ports information')
class QrtrParse(RamParser):
def is_internal_node(self, addr):
radix_tree_entry_mask = 0x3
if self.ramdump.kernel_version > (4, 20, 0):
radix_tree_internal_node = 0x2
else:
radix_tree_internal_node = 0x1
isInternal = (addr & radix_tree_entry_mask) == radix_tree_internal_node
return isInternal
def entry_to_node(self, addr):
if self.ramdump.kernel_version > (4, 20, 0):
return addr & 0xfffffffffffffffd
else:
return addr & 0xfffffffffffffffe
def print_qrtr_port_info(self, ram_dump, port_addr, qrtr_out):
global us_offset, peer_offset, node_offset, port_offset, sk_data_ready_offset, sk_offset
global qmi_data_read_addr
us_node = ram_dump.read_int(port_addr + us_offset + node_offset)
us_port = ram_dump.read_int(port_addr + us_offset + port_offset)
peer_node = ram_dump.read_int(port_addr + peer_offset + node_offset)
peer_port = ram_dump.read_int(port_addr + peer_offset + port_offset)
qrtr_out.write("v.v (struct qrtr_sock){0}\n".format(hex(port_addr).rstrip('L'), sep=''))
qrtr_out.write(" node:port US[{0}:{1}] PEER[{2}:{3}]\n".format(hex(us_node), hex(us_port).rstrip('L'), hex(peer_node),hex(peer_port),sep=''))
sk_ready_data = ram_dump.read_word(port_addr + sk_offset + sk_data_ready_offset)
#print(hex(sk_ready_data))
#if sk_ready_data == qmi_data_read_addr:
#qrtr_out.write("YES")
return
def print_qrtr_node_info(self, ram_dump, qn_desc, qrtr_out):
global node_id_offset, ep_offset, ep_xmit_offset, read_data_offset, rx_func_offset
global kobj_offset, name_offset
# Get the values to be printed
node_id = ram_dump.read_int(qn_desc + node_id_offset)
if node_id == 7:
dev_offset = ram_dump.field_offset('struct qrtr_mhi_dev', 'dev')
struct_name = '(struct qrtr_mhi_dev)'
else:
dev_offset = ram_dump.field_offset('struct qrtr_smd_dev', 'dev')
struct_name = '(struct qrtr_smd_dev)'
tx_func_ptr_val = ram_dump.read_u64(qn_desc + ep_offset + ep_xmit_offset)
tx_func_ptr = ram_dump.read_pointer(tx_func_ptr_val)
tx_func_name = ram_dump.get_symbol_info1(tx_func_ptr)
rx_func_ptr_val = ram_dump.read_pointer(qn_desc + read_data_offset + rx_func_offset)
rx_func_name = ram_dump.get_symbol_info1(rx_func_ptr_val)
ep_pointer = ram_dump.read_pointer(qn_desc + ep_offset)
dev_pointer = ram_dump.read_pointer(ep_pointer + dev_offset)
kobj_pointer = ram_dump.read_pointer(dev_pointer + kobj_offset)
dev_name = ram_dump.read_cstring(kobj_pointer + name_offset)
# Print qrtr_node details
qrtr_out.write("\n")
qrtr_out.write("===================================================================================\n")
qrtr_out.write("IDX: {0} NID: {1} | v.v (struct qrtr_node){2}\n".format(node_id, node_id, hex(qn_desc).rstrip('L'), sep=''))
qrtr_out.write("===================================================================================\n")
qrtr_out.write("tx: {0}\n".format(tx_func_name))
qrtr_out.write("rx: {0}\n".format(rx_func_name.strip('.cfi_jt')))
qrtr_out.write("\ndev: {0}| v.v {1}{2}\n".format(dev_name, struct_name, hex(ep_pointer).rstrip('L'), sep=''))
# Get qrtr_tx_flow information
self.process_qrtr_tx_flow(ram_dump, qn_desc, qrtr_out)
qrtr_out.write("-----------------------------------------------------------------------------------\n")
return
def print_tx_flow_info(self, ram_dump, qn_desc, qrtr_out):
global tx_flow_pending_offset
pending = ram_dump.read_pointer(qn_desc + tx_flow_pending_offset)
if pending >= 5:
print(hex(qn_desc))
return
def process_xa_node_slot(self, ram_dump, rnode_addr, qrtr_type, qrtr_out):
global slots_offset, pointer_size, count_offset, max_slots
if self.is_internal_node(rnode_addr):
node_addr = self.entry_to_node(rnode_addr)
count = ram_dump.read_word(node_addr, count_offset)
num_slots = (count >> 16) and 0xff
for i in range(0, max_slots):
slot = ram_dump.read_u64(node_addr + slots_offset + (pointer_size * i))
if slot != 0:
self.process_xa_node_slot(ram_dump, slot, qrtr_type, qrtr_out)
num_slots = num_slots - 1
if num_slots == 0:
break
else:
if qrtr_type == 1:
self.print_qrtr_node_info(ram_dump, rnode_addr, qrtr_out)
if qrtr_type == 2:
self.print_qrtr_port_info(ram_dump, rnode_addr, qrtr_out)
if qrtr_type == 3:
qrtr_out.write("TX flow: {0}\n".format(hex(rnode_addr).rstrip('L')))
self.print_tx_flow_info(ram_dump, rnode_addr, qrtr_out)
return
def process_qrtr_tx_flow(self, ram_dump, qn_desc, qrtr_out):
global qrtr_tx_flow_offset, rnode_offset
tx_flow_addr = qn_desc + qrtr_tx_flow_offset
rnode_addr = ram_dump.read_word(tx_flow_addr + rnode_offset)
self.process_xa_node_slot(ram_dump, rnode_addr, 3, qrtr_out)
return
def parse_qrtr_nodes(self, ram_dump, root_addr, qrtr_out):
rnode_addr = ram_dump.read_word(root_addr + rnode_offset)
if (rnode_addr is None) or (rnode_addr == 0):
qrtr_out.write("Error: qrtr nodes list not created!!!\n")
return
self.process_xa_node_slot(ram_dump, rnode_addr, 1, qrtr_out)
return
def parse_qrtr_ports(self, ram_dump, root_addr, qrtr_out):
# Get offsets for required members in the qrtr_port struct
idr_rt_offset = ram_dump.field_offset('struct idr', 'idr_rt')
global rnode_offset
xarray_addr = root_addr + idr_rt_offset
rnode_addr = ram_dump.read_word(xarray_addr + rnode_offset)
self.process_xa_node_slot(ram_dump, rnode_addr, 2, qrtr_out)
return
def qrtr_get_offsets(self):
# Get offsets
global rnode_offset, rnode_shift_offset, slots_offset, pointer_size, count_offset
rnode_offset = self.ramdump.field_offset('struct xarray', 'xa_head')
rnode_shift_offset = self.ramdump.field_offset('struct xa_node', 'shift')
slots_offset = self.ramdump.field_offset('struct xa_node', 'slots')
pointer_size = self.ramdump.sizeof('struct xa_node *')
count_offset = self.ramdump.field_offset('struct xa_node', 'count')
global us_offset, peer_offset, node_offset, port_offset, sk_offset, sk_data_ready_offset
sk_offset = self.ramdump.field_offset('struct qrtr_sock', 'sk')
sk_data_ready_offset = self.ramdump.field_offset('struct sock', 'sk_data_ready')
us_offset = self.ramdump.field_offset('struct qrtr_sock', 'us')
peer_offset = self.ramdump.field_offset('struct qrtr_sock', 'peer')
node_offset = self.ramdump.field_offset('struct sockaddr_qrtr', 'sq_node')
port_offset = self.ramdump.field_offset('struct sockaddr_qrtr', 'sq_port')
qmi_data_read_addr = self.ramdump.address_of('qmi_data_ready$365cec2a77bf8c8c807d4b217be1be23')
global node_id_offset, ep_offset, ep_xmit_offset, read_data_offset, rx_func_offset
global kobj_offset, name_offset, qrtr_tx_flow_offset, tx_flow_pending_offset
node_id_offset = self.ramdump.field_offset('struct qrtr_node', 'nid')
ep_offset = self.ramdump.field_offset('struct qrtr_node', 'ep')
ep_xmit_offset = self.ramdump.field_offset('struct qrtr_endpoint', 'xmit')
read_data_offset = self.ramdump.field_offset('struct qrtr_node', 'read_data')
rx_func_offset = self.ramdump.field_offset('struct kthread_work', 'func')
kobj_offset = self.ramdump.field_offset('struct device', 'kobj')
name_offset = self.ramdump.field_offset('struct kobject', 'name')
qrtr_tx_flow_offset = self.ramdump.field_offset('struct qrtr_node', 'qrtr_tx_flow')
tx_flow_pending_offset = self.ramdump.field_offset('struct qrtr_tx_flow', 'pending')
def parse(self):
qrtr_out = self.ramdump.open_file('qrtr_port.txt')
major, minor, patch = self.ramdump.kernel_version
if (major, minor) < (5, 4):
qrtr_out.write("Kernel version not supported\n")
return
# Get offsets from structure members used in qrtr_ports & qrtr_nodes
self.qrtr_get_offsets()
# Parse qrtr_nodes
qrtr_nodes = self.ramdump.address_of('qrtr_nodes')
if qrtr_nodes is not None:
qrtr_out.write("\n\n********************* NODE INFO *********************\n")
self.parse_qrtr_nodes(self.ramdump, qrtr_nodes, qrtr_out)
qrtr_out.write("\n\n")
else:
qrtr_out.write("Error: qrtr_nodes symbol not found in the ram dump!!!\n")
# Parse qrtr_ports
qrtr_ports = self.ramdump.address_of('qrtr_ports')
if qrtr_ports is not None:
qrtr_out.write("********************* PORT INFO *********************\n")
self.parse_qrtr_ports(self.ramdump, qrtr_ports, qrtr_out)
qrtr_out.write("\n***********************************************\n")
else:
qrtr_out.write("Error: qrtr_ports symbol not found in the ram dump!!!\n")
qrtr_out.close()
return

View File

@@ -0,0 +1,488 @@
# Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
import linux_list as llist
@register_parser('--regulator', 'dump regulator and regulator consumer status')
class RegulatorDump(RamParser):
def __init__(self, *args):
super(RegulatorDump, self).__init__(*args)
self.rdev_supply_map = {}
self.mutex_waiting_map = {}
self.mutex_waiting_name = {}
self.rdev = []
self.consumer = {}
self.mutex_waiters = {}
self.global_mutexes = [
('regulator_list_mutex', 'regulator_list_mutex'),
('regulator_nesting_mutex', 'regulator_nesting_mutex'),
('prepare_lock', 'prepare_lock (clock framework)'),
('icc_lock', 'icc_lock (interconnect framework)'),
]
def rdev_name(self, rdev):
constraints = self.ramdump.read_structure_field(rdev, 'struct regulator_dev',
'constraints')
if constraints:
name = self.ramdump.read_structure_field(constraints, 'struct regulation_constraints',
'name')
if name:
return self.ramdump.read_cstring(name)
desc = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'desc')
name = self.ramdump.read_structure_field(desc, 'struct regulator_desc', 'name')
if name:
return self.ramdump.read_cstring(name)
return '(NULL)'
def consumer_name(self, regulator):
dev = self.ramdump.read_structure_field(regulator, 'struct regulator', 'dev')
dev_name = 'NULL'
if dev:
init_name = self.ramdump.read_structure_field(dev, 'struct device', 'init_name')
if init_name:
dev_name = self.ramdump.read_cstring(init_name)
else:
kobj = self.ramdump.struct_field_addr(dev, 'struct device', 'kobj')
kobj_name = self.ramdump.read_structure_field(kobj, 'struct kobject', 'name')
if kobj_name:
dev_name = self.ramdump.read_cstring(kobj_name)
supply_name_addr = self.ramdump.read_structure_field(regulator, 'struct regulator',
'supply_name')
if supply_name_addr:
supply_name = self.ramdump.read_cstring(supply_name_addr)
else:
supply_name = 'NULL'
# Make regulator child consumer names more human readable
if (('regulator.' in supply_name) and ('-SUPPLY' in supply_name)
and (regulator in self.rdev_supply_map)):
rdev = self.rdev_supply_map[regulator]
rdev_name = self.rdev_name(rdev)
return supply_name + ' (' + rdev_name + ')'
else:
return dev_name + '-' + supply_name
def task_name(self, task):
if task is None:
return None
comm_addr = task + self.ramdump.field_offset('struct task_struct', 'comm')
return self.ramdump.read_cstring(comm_addr, max_length=16)
def mutex_owner(self, mutex):
owner = self.ramdump.read_structure_field(mutex, 'struct mutex', 'owner')
if owner:
# Remove flag bits
return owner & ~0x7
else:
return 0
def klist_node_to_rdev(self, klist_node):
# In Linux 4.19, 'knode_class' is an element in struct device
dev = self.ramdump.container_of(klist_node, 'struct device', 'knode_class')
if dev is None:
# In Linux 5.4, 'knode_class' is an element in struct device_private
dev_private = self.ramdump.container_of(klist_node, 'struct device_private',
'knode_class')
if dev_private is None:
return None
dev = self.ramdump.read_structure_field(dev_private, 'struct device_private',
'device')
if dev is None:
return None
return self.ramdump.container_of(dev, 'struct regulator_dev', 'dev')
def is_rpmh_regulator_rdev(self, rdev):
if (not self.rpmh_regulator_enable_addr) or (not self.vrm_ops_enable_addr):
return False
desc = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'desc')
ops = self.ramdump.read_structure_field(desc, 'struct regulator_desc', 'ops')
ops_enable_addr = self.ramdump.read_structure_field(ops, 'struct regulator_ops', 'enable')
if not ops_enable_addr:
return False
# Handle CFI branch table of regulator names
instr = self.ramdump.read_u32(ops_enable_addr)
mask = 0xFFFFFF
offset = instr & mask
# sign bit set
if offset & 0x800000:
offset = -((~offset & mask) + 1)
ops_enable_jump_addr = ops_enable_addr + 4 * offset
# Check if this regulator is an rpmh_regulator device by seeing if
# its enable() op is rpm_regulator_enable
return ((ops_enable_addr == self.rpmh_regulator_enable_addr)
or (ops_enable_jump_addr == self.rpmh_regulator_enable_addr)
or (ops_enable_addr == self.vrm_ops_enable_addr)
or (ops_enable_jump_addr == self.vrm_ops_enable_addr))
def dump_rpmh_regulator_req_data(self, aggr_vreg, regulator_type, set, req, reg_name):
rpmh_resource_name = self.ramdump.read_structure_cstring(aggr_vreg,
'struct rpmh_aggr_vreg', 'resource_name')
if rpmh_resource_name is None:
return
rpmh_resource_addr = self.ramdump.read_structure_field(aggr_vreg, 'struct rpmh_aggr_vreg',
'addr')
if rpmh_resource_addr is None:
return
sent_reg = self.ramdump.struct_field_addr(req, 'struct rpmh_regulator_request', 'reg')
valid = self.ramdump.read_structure_field(req, 'struct rpmh_regulator_request', 'valid')
self.output_file.write('\tRPMh rsc={}, addr=0x{:05X}, set={}; commands sent: '.format(
rpmh_resource_name, rpmh_resource_addr, set))
is_first = True
for i in range(4):
if valid & (1 << i):
reg = self.ramdump.read_u32(sent_reg + i * 4)
self.output_file.write('{}{}={}'.format((', ', '')[is_first], reg_name[i], reg))
is_first = False
# Print the vlvl mapped to ARC hlvl
if regulator_type == 1 and i == 0:
level_addr = self.ramdump.struct_field_addr(aggr_vreg, 'struct rpmh_aggr_vreg',
'level')
vlvl = self.ramdump.read_u32(level_addr + 4 * reg)
self.output_file.write(' (vlvl={})'.format(vlvl))
if not valid:
# No RPMh commands sent
self.output_file.write('N/A')
self.output_file.write('\n')
def dump_rpmh_regulator_data(self, rdev):
if not self.is_rpmh_regulator_rdev(rdev):
return
rpmh_vreg = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'reg_data')
if rpmh_vreg is None:
return
aggr_vreg = self.ramdump.read_structure_field(rpmh_vreg, 'struct rpmh_vreg', 'aggr_vreg')
if aggr_vreg is None:
# This condition is encountered when parsing a RAM dump which uses
# the upstream qcom-rpmh-regulator driver
return
regulator_type = self.ramdump.read_structure_field(aggr_vreg, 'struct rpmh_aggr_vreg',
'regulator_type')
if regulator_type is None:
return
if regulator_type == 0:
# VRM
reg_name = ['mv', 'en', 'mode', 'hr_mv']
elif regulator_type == 1:
# ARC
reg_name = ['hlvl', 'N/A', 'N/A', 'N/A']
elif regulator_type == 2:
# XOB
reg_name = ['N/A', 'en', 'N/A', 'N/A']
else:
self.output_file.write('Unknown type={}\n'.format(regulator_type))
return
sleep_request_sent_addr = self.ramdump.struct_field_addr(aggr_vreg,
'struct rpmh_aggr_vreg', 'sleep_request_sent')
sleep_request_sent = self.ramdump.read_bool(sleep_request_sent_addr)
if sleep_request_sent is None:
return
aggr_req_active = self.ramdump.struct_field_addr(aggr_vreg, 'struct rpmh_aggr_vreg',
'aggr_req_active')
aggr_req_sleep = self.ramdump.struct_field_addr(aggr_vreg, 'struct rpmh_aggr_vreg',
'aggr_req_sleep')
self.dump_rpmh_regulator_req_data(aggr_vreg, regulator_type,
('both', 'act')[sleep_request_sent], aggr_req_active, reg_name)
if sleep_request_sent:
self.dump_rpmh_regulator_req_data(aggr_vreg, regulator_type, 'slp', aggr_req_sleep,
reg_name)
def dump_consumer(self, regulator):
name = self.consumer_name(regulator)
enabled = '?'
enabled = self.ramdump.read_structure_field(regulator, 'struct regulator', 'enable_count')
if enabled is None:
enabled = self.ramdump.read_structure_field(regulator, 'struct regulator', 'enabled')
load_ua_addr = self.ramdump.struct_field_addr(regulator, 'struct regulator', 'uA_load')
load_ua = self.ramdump.read_s32(load_ua_addr)
voltage = self.ramdump.struct_field_addr(regulator, 'struct regulator', 'voltage')
if voltage:
# Linux 4.19 and 5.4: min_uV and max_uV are found in struct regulator_voltage
min_uv = self.ramdump.read_structure_field(voltage, 'struct regulator_voltage',
'min_uV')
max_uv = self.ramdump.read_structure_field(voltage, 'struct regulator_voltage',
'max_uV')
else:
# Linux 4.14: min_uV and max_uV are found in struct regulator
min_uv = self.ramdump.read_structure_field(regulator, 'struct regulator', 'min_uV')
max_uv = self.ramdump.read_structure_field(regulator, 'struct regulator', 'max_uV')
if min_uv is None:
min_uv = '?'
if max_uv is None:
max_uv = '?'
self.output_file.write(
'\t\t{:<64s} en={}, min_uV={:7d}, max_uV={:7d}, load_uA={:7d}\n'.format(
name, enabled, min_uv, max_uv, load_ua))
def dump_mutex(self, mutex, mutex_name):
owner = self.mutex_owner(mutex)
if owner:
comm = self.task_name(owner)
self.output_file.write('\t{} is locked:\n'.format(mutex_name))
self.output_file.write('\t\tOwner task = 0x{:016X} {}\n'.format(owner, comm))
for waiter_task in self.mutex_waiters[mutex]:
comm = self.task_name(waiter_task)
self.output_file.write(
'\t\tWaiter task = 0x{:016X} {}\n'.format(waiter_task, comm))
def dump_rdev(self, rdev):
name = self.rdev_name(rdev)
self.output_file.write('{}:\n'.format(name))
use_count = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'use_count')
self.output_file.write('\tuse_count = {}\n'.format(use_count))
open_count = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'open_count')
self.output_file.write('\topen_count = {}\n'.format(open_count))
constraints = self.ramdump.read_structure_field(rdev, 'struct regulator_dev',
'constraints')
min_uv = 0
max_uv = 0
if constraints != 0:
min_uv = self.ramdump.read_structure_field(
constraints, 'struct regulation_constraints', 'min_uV')
max_uv = self.ramdump.read_structure_field(
constraints, 'struct regulation_constraints', 'max_uV')
self.output_file.write('\tmin_uV = {}\n'.format(min_uv))
self.output_file.write('\tmax_uV = {}\n'.format(max_uv))
# Calculate total current of enabled voters. This is used in the Linux
# regulator framework regulator_set_load() function to configure the
# regulator mode.
total_ua = 0
for consumer in self.consumer[rdev]:
enabled = self.ramdump.read_structure_field(consumer, 'struct regulator',
'enable_count')
if enabled or enabled is None:
# The per-consumer enable condition when summing the current is
# only present if struct regulator contains an element named
# enable_count. This isn't present in older kernel versions.
load_ua_addr = self.ramdump.struct_field_addr(consumer, 'struct regulator',
'uA_load')
load_ua = self.ramdump.read_s32(load_ua_addr)
total_ua += load_ua
self.output_file.write('\ttotal_uA = {}\n'.format(total_ua))
self.output_file.write('\trdev = 0x{:016X}\n'.format(rdev))
try:
self.dump_rpmh_regulator_data(rdev)
except Exception:
pass
# Dump the mutex lock owner and waiters if rdev->mutex is locked
mutex = self.ramdump.struct_field_addr(rdev, 'struct regulator_dev', 'mutex')
self.dump_mutex(mutex, 'Mutex')
# Dump the consumer requests for the regulator
if self.consumer[rdev]:
self.output_file.write('\tConsumers:\n')
for consumer in self.consumer[rdev]:
self.dump_consumer(consumer)
self.output_file.write('\n')
def dump_regulators(self):
if self.rdev:
self.output_file.write('Regulators:\n\n')
for rdev in self.rdev:
self.dump_rdev(rdev)
def dump_regulator_top_level(self):
display_global_mutexes = False
for (mutex_symbol, mutex_label) in self.global_mutexes:
mutex = self.ramdump.address_of(mutex_symbol)
owner = self.mutex_owner(mutex)
if owner:
display_global_mutexes = True
break
if display_global_mutexes:
self.output_file.write('Top Level Mutexes:\n')
for (mutex_symbol, mutex_label) in self.global_mutexes:
mutex = self.ramdump.address_of(mutex_symbol)
self.dump_mutex(mutex, mutex_label)
self.output_file.write('\n')
def mutex_map_gen_walker(self, waiter, mutex, owner_task, lock_name):
task = self.ramdump.read_structure_field(waiter, 'struct mutex_waiter', 'task')
self.mutex_waiting_map[task] = owner_task
self.mutex_waiting_name[task] = lock_name
self.mutex_waiters[mutex].append(task)
def store_mutex_waiters(self, mutex, lock_name):
if mutex is None:
return
self.mutex_waiters[mutex] = []
# Fill mutex_waiting_map to facilitate mutex deadlock detection
mutex_owner = self.mutex_owner(mutex)
if mutex_owner:
wait_list = self.ramdump.struct_field_addr(mutex, 'struct mutex', 'wait_list')
offset = self.ramdump.field_offset('struct mutex_waiter', 'list')
wait_list_walker = llist.ListWalker(self.ramdump, wait_list, offset)
if not wait_list_walker.is_empty():
wait_list_walker.walk(wait_list_walker.next() + offset, self.mutex_map_gen_walker,
mutex, mutex_owner, lock_name)
def consumer_init_walker(self, regulator, rdev):
self.consumer[rdev].append(regulator)
def regulator_init_walker(self, klist_node):
rdev = self.klist_node_to_rdev(klist_node)
if rdev is None:
return
name = self.rdev_name(rdev)
if name is None:
return
self.rdev.append(rdev)
self.consumer[rdev] = []
# Fill rdev_supply_map
supply = self.ramdump.read_structure_field(rdev, 'struct regulator_dev', 'supply')
if supply:
self.rdev_supply_map[supply] = rdev
mutex = self.ramdump.struct_field_addr(rdev, 'struct regulator_dev', 'mutex')
self.store_mutex_waiters(mutex, 'rdev->mutex (' + name + ')')
consumer_list = self.ramdump.struct_field_addr(rdev, 'struct regulator_dev',
'consumer_list')
offset = self.ramdump.field_offset('struct regulator', 'list')
consumer_list_walker = llist.ListWalker(self.ramdump, consumer_list, offset)
if not consumer_list_walker.is_empty():
consumer_list_walker.walk(consumer_list_walker.next() + offset,
self.consumer_init_walker, rdev)
def get_class_to_subsys(self, kobj_addr, regulator_addr, found_sp:list):
if not bool(found_sp):
kset_addr = self.ramdump.container_of(kobj_addr, 'struct kset', 'kobj')
sp_addr = self.ramdump.container_of(kset_addr, 'struct subsys_private', 'subsys')
sp_class = self.ramdump.read_structure_field(sp_addr, 'struct subsys_private', 'class')
if regulator_addr == sp_class:
found_sp.append(sp_addr)
return
def init_regulators(self):
p = None
regulator_class = self.ramdump.address_of('regulator_class')
if regulator_class is None:
self.output.write("ERROR: 'regulator_class' not found\n")
return
if self.ramdump.field_offset('struct class', 'p') is None:
classkset_ptr = self.ramdump.read_pointer('class_kset')
classkset_list_ptr = self.ramdump.read_structure_field(classkset_ptr, 'struct kset', 'list.next')
kobj_offset = self.ramdump.field_offset('struct kobject', 'entry')
sp_buf = []
class_subsys_walker = llist.ListWalker(self.ramdump, classkset_list_ptr, kobj_offset)
class_subsys_walker.walk(classkset_list_ptr, self.get_class_to_subsys, regulator_class, sp_buf)
if bool(sp_buf):
p = sp_buf[0]
else:
p = self.ramdump.read_structure_field(regulator_class, 'struct class', 'p')
list_head = (p + self.ramdump.field_offset('struct subsys_private', 'klist_devices')
+ self.ramdump.field_offset('struct klist', 'k_list'))
list_offset = self.ramdump.field_offset('struct klist_node', 'n_node')
init_list_walker = llist.ListWalker(self.ramdump, list_head, list_offset)
if not init_list_walker.is_empty():
init_list_walker.walk(init_list_walker.next() + list_offset,
self.regulator_init_walker)
def init_regulator_top_level(self):
for (mutex_symbol, mutex_label) in self.global_mutexes:
mutex = self.ramdump.address_of(mutex_symbol)
self.store_mutex_waiters(mutex, mutex_label)
def init_rpmh_regulator(self):
# rpmh_regulator_enable_addr and vrm_ops_enable_addr should have the
# same value as they both correspond to the same function
# (rpmh_regulator_enable). However, self.ramdump.address_of() does not
# return the correct address for function symbols loaded from a DLKM.
# This occurs because the gdb program itself outputs incorrect addresses
# in this case. Thus work around the issue by getting the address two
# ways and using both in later checks.
self.rpmh_regulator_enable_addr = self.ramdump.address_of('rpmh_regulator_enable')
vrm_ops = self.ramdump.address_of('rpmh_regulator_vrm_ops')
if vrm_ops:
self.vrm_ops_enable_addr = self.ramdump.read_structure_field(vrm_ops, 'struct regulator_ops', 'enable')
else:
self.vrm_ops_enable_addr = 0
def dump_regulator_cycle(self, back_edge):
print_out_str('WARNING: REGULATOR MUTEX LOCK DEADLOCK DETECTED!')
self.output_file.write('REGULATOR MUTEX LOCK DEADLOCK DETECTED!\n')
self.output_file.write('Locked mutexes forming a cycle:\n')
waiter = back_edge
while True:
owner = self.mutex_waiting_map[waiter]
lock = self.mutex_waiting_name[waiter]
self.output_file.write(
'\tlock = {:<32s}: owner task = 0x{:016X} {:<17s}, '
'waiter task = 0x{:016X} {:<17s}\n'.format(
lock, owner, self.task_name(owner), waiter, self.task_name(waiter)))
waiter = owner
if waiter == back_edge:
break
self.output_file.write('\n')
def find_cycle(self, waiter, stack):
if waiter is None:
return None
owner = self.mutex_waiting_map.get(waiter)
if owner is None:
return None
if owner in stack:
return waiter
else:
return self.find_cycle(owner, stack + [owner])
def regulator_deadlock_check(self):
for waiter in self.mutex_waiting_map.keys():
back_edge = self.find_cycle(waiter, [waiter])
if back_edge:
self.dump_regulator_cycle(back_edge)
return
def parse(self):
with self.ramdump.open_file('regulator.txt') as self.output_file:
self.init_regulator_top_level()
self.init_rpmh_regulator()
self.init_regulators()
try:
self.regulator_deadlock_check()
except Exception as error:
# Treat deadlock detection exceptions as non-fatal so that
# critical regulator details are still dumped.
self.output_file.write('Regulator mutex deadlock check failed: {}\n'.format(error))
self.dump_regulator_top_level()
self.dump_regulators()
print_out_str("Regulator information written to regulator.txt")

View File

@@ -0,0 +1,377 @@
# Copyright (c) 2018-2020,2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 os, re
from parser_util import register_parser, RamParser
from print_out import print_out_str
from mm import get_vmemmap, page_buddy
from mm import pfn_to_page, page_buddy, page_count, for_each_pfn
from mm import page_to_pfn, pfn_to_section
from utils.anomalies import Anomaly
from .pagetracking import PageTrace
def print_reserved_mem(ramdump):
reserved_mem_addr = ramdump.address_of('reserved_mem')
output_file = ramdump.open_file("reserved_mem.txt")
reserved_mem_count = ramdump.read_int('reserved_mem_count')
size_of_reserved_mem = ramdump.sizeof('struct reserved_mem')
output_file.write("reserved_mem {\n")
str = "name = 0x{0:x} '{1}'\n\t\tbase = 0x{2:x}\n\t\tsize = 0x{" \
"3:x} ({4})KB\n\t\trange = <0x{5:x} - 0x{6:x}>\n\t"
index = 0
while index < reserved_mem_count:
reserved_mem = reserved_mem_addr + index*size_of_reserved_mem
name_addr = ramdump.read_structure_field(
reserved_mem, 'struct reserved_mem', 'name')
name = ramdump.read_cstring(name_addr, 48)
base = ramdump.read_structure_field(
reserved_mem, 'struct reserved_mem', 'base')
size = ramdump.read_structure_field(
reserved_mem, 'struct reserved_mem', 'size')
output_file.write("\t{\n\t\t")
output_file.write(str.format(name_addr, name, base, size, size // 1024,
base, base+size))
output_file.write("}\n")
index = index + 1
output_file.write("\n}")
output_file.close()
def print_tasklet_info(ramdump, core, tasklet):
tasklet_vec_addr = ramdump.address_of(tasklet)
tasklet_head = tasklet_vec_addr + ramdump.per_cpu_offset(core)
tasklet_head = ramdump.read_word(tasklet_head)
next_offset = ramdump.field_offset('struct tasklet_struct', 'next')
func_offset = ramdump.field_offset('struct tasklet_struct', 'func')
count_offset = ramdump.field_offset('struct tasklet_struct', 'count')
if tasklet_head != 0x0:
print_out_str("Pending Tasklet info for {0}:".format(tasklet))
while (tasklet_head != 0x0):
print_out_str("struct tasklet_struct: 0x{0:x}:".format(tasklet_head))
tasklet_func_addr = ramdump.read_word(tasklet_head + func_offset)
tasklet_func = ramdump.unwind_lookup(tasklet_func_addr)
if tasklet_func is None:
tasklet_func = "Dynamic module/symbol not found"
else:
tasklet_func = tasklet_func[0]
print_out_str("\tfunc : 0x{:<16x} -> {}".format(tasklet_func_addr, tasklet_func))
count = ramdump.read_int(tasklet_head + count_offset)
if count != 0:
print_out_str("\tcount: 0x{:<16x} -> this tasklet is disabled".format(count))
else:
print_out_str("\tcount: 0x{:<16x} -> this tasklet is enabled".format(count))
tasklet_head = ramdump.read_word(tasklet_head + next_offset)
def parse_softirq_stat(ramdump):
irq_stat_addr = ramdump.address_of('irq_stat')
softirq_name_addr = ramdump.address_of('softirq_to_name')
sizeof_softirq_name = ramdump.sizeof('softirq_to_name')
sofrirq_name_arr_size = sizeof_softirq_name // ramdump.sizeof('char *')
no_of_cpus = ramdump.get_num_cpus()
index = 0
size_of_irq_stat = ramdump.sizeof('irq_cpustat_t')
for index in ramdump.iter_cpus():
if ramdump.kernel_version >= (4, 19):
irq_stat = irq_stat_addr + ramdump.per_cpu_offset(index)
else:
irq_stat = irq_stat_addr + index*size_of_irq_stat
softirq_pending = ramdump.read_structure_field(
irq_stat, 'irq_cpustat_t', '__softirq_pending')
pending = ""
pos = sofrirq_name_arr_size - 1
while pos >= 0:
if softirq_pending & (1 << pos):
flag_addr = ramdump.read_word(ramdump.array_index(
softirq_name_addr, "char *", pos))
flag = ramdump.read_cstring(flag_addr, 48)
pending += flag
pending += " | "
pos = pos - 1
if pending == "":
pending = "None"
else:
pending = pending.rstrip().rstrip("|")
print_out_str("core {0} : __softirq_pending = {1}".format(
index, pending))
if "TASKLET" in pending or "HI" in pending:
print_tasklet_info(ramdump, index, 'tasklet_vec')
print_tasklet_info(ramdump, index, 'tasklet_hi_vec')
def check_qseecom_invalid_cmds(ramdump):
invalid_qsecom_cmds_id = ["3", "5", "7", "9", "14", "15", "16", "17", "19", "23" , "29"]
invalid_qsecom_cmds = []
return_string = ""
if os.path.exists(os.path.join(ramdump.outdir, "qsee_log.txt")):
if os.stat(os.path.join(ramdump.outdir, "qsee_log.txt")).st_size:
with open(os.path.join(ramdump.outdir, "qsee_log.txt"), "r+") as fd:
for line in fd:
if re.search("TZ App cmd handler, cmd_id", line):
cmd_id = line.split()[-1]
if cmd_id in invalid_qsecom_cmds_id:
invalid_qsecom_cmds.append(cmd_id)
if len(invalid_qsecom_cmds):
return_string += "qsecomm sample app running invalid cmds : "
for i in range(len(invalid_qsecom_cmds)):
return_string += invalid_qsecom_cmds[i] + " "
return (return_string + "\n")
return return_string
def do_parse_qsee_log(ramdump):
qsee_out = ramdump.open_file('qsee_log.txt')
g_qsee_log_addr = ramdump.address_of('g_qsee_log')
g_qsee_log_v2_addr = ramdump.address_of('g_qsee_log_v2')
tzdbg_addr = ramdump.address_of('tzdbg')
is_enlarged_buf_addr = ramdump.field_offset('struct tzdbg', 'is_enlarged_buf')
is_enlarged_buf = False
if is_enlarged_buf_addr is not None:
is_enlarged_buf = ramdump.read_bool(tzdbg_addr + is_enlarged_buf_addr)
log_buf_offset = ramdump.field_offset('struct tzdbg_log_t', 'log_buf')
qsee_log_buf_size = 0x8000 ##define QSEE_LOG_BUF_SIZE 0x8000
if is_enlarged_buf is True:
qsee_log_buf_size = 0x20000 ##define QSEE_LOG_BUF_SIZE_V2 0x20000
g_qsee_log_addr = g_qsee_log_v2_addr
log_buf_offset = ramdump.field_offset('struct tzdbg_log_v2_t', 'log_buf')
if g_qsee_log_addr is None:
print_out_str("!!! g_qsee_logs not found")
qsee_out.close()
return
try:
g_qsee_log_addr = ramdump.read_word(g_qsee_log_addr)
except Exception as e:
print_out_str('!!! Cannot read g_qsee_log_addr')
qsee_out.close()
return
log_buf_addr = g_qsee_log_addr + log_buf_offset
qsee_log_data = ramdump.read_cstring(log_buf_addr, qsee_log_buf_size)
qsee_log_data = qsee_log_data.rstrip(' \t\r\n\0')
qsee_out.write(qsee_log_data)
qsee_out.close()
@register_parser('--print-reserved-mem', 'Print reserved memory info ')
class ReservedMem(RamParser):
def parse(self):
print_reserved_mem(self.ramdump)
@register_parser('--print-cma-areas', 'Print cma memory region info ')
class CmaAreas(RamParser):
def __init__(self, *args):
super(CmaAreas, self).__init__(*args)
self.offset_comm = self.ramdump.field_offset('struct page_owner', 'comm')
if self.ramdump.is_config_defined('CONFIG_PAGE_OWNER'):
self.pagetrace = PageTrace(self.ramdump)
def parse_pfn(self, ramdump, pfn, cma, op_file, dict):
vmemmap = get_vmemmap(ramdump)
page_size = ramdump.sizeof('struct page')
page = vmemmap + pfn * page_size
str = "{0} pfn : 0x{1:x} page : 0x{2:x} flag : 0x{3:x} mapping : 0x{" \
"4:x} count : {5} _mapcount : {6:x} PID : {7}{8} {9}\n ts_nsec {10:>32d} \n free_ts_nsec {11:>32d} \n{12}\n"
str1 = "{0} pfn : 0x{1:x}--0x{2:x} head_page : 0x{3:x} flag : {4:x} " \
"mapping : 0x{5:x} count : {6} _mapcount : {7:x} {8}\n{9}\n"
str2 = "{0} pfn : 0x{1:x} pge : 0x{2:x} count : {3} _mapcount : " \
"{4:x} {5}\n"
str3 = "{0} pfn : 0x{1:x}--0x{2:x} head_page : 0x{3:x} count : {4} " \
"_mapcount : {5:x} {6}\n"
page_flags = ramdump.read_structure_field(page, 'struct page', 'flags')
tail_page = ramdump.read_structure_field(
page, 'struct page', 'compound_head')
if (tail_page & 1) == 1:
page = tail_page - 1
nr_pages = 1
page_count = ramdump.read_structure_field(
page, 'struct page', '_refcount.counter')
mapcount_offset = ramdump.field_offset('struct page', '_mapcount')
page_mapcount = ramdump.read_int(page + mapcount_offset)
if page_mapcount == 0xffffffff:
page_mapcount = -1
page_mapping = ramdump.read_structure_field(page, 'struct page', 'mapping')
is_pinned_str = ""
if (page_mapcount >= 0) and ((page_count - page_mapcount) >= 2):
is_pinned_str = "<===pinned"
if cma == 1:
cma_usage = "[devm]"
else:
cma_usage = "[ncma]"
# test if buddy
if page_mapcount == 0xffffff80:
cma_usage = "[budd]"
nr_pages = ramdump.read_structure_field(
page, 'struct page', 'private')
nr_pages = 1 << nr_pages
elif page_mapping != 0:
anon_page = page_mapping & 0x1
if anon_page != 0:
cma_usage = "[anon]"
else:
cma_usage = "[file]"
else:
cma_usage = "[unkw]"
if ramdump.is_config_defined('CONFIG_PAGE_OWNER'):
if (page_buddy(ramdump, page)) or page_count == 0:
function_list = ""
pid = -1
ts_nsec = -1
free_ts_nsec = -1
comm = -1
else:
function_list, order, pid, ts_nsec, gfp, comm, ext_flags = self.pagetrace.page_trace(pfn, True)
free_ts_nsec = 0
if pid in dict:
dict[pid] = dict[pid] + 1
else:
dict[pid] = 1
if nr_pages == 1:
op_file.write(str.format(
cma_usage, pfn, page, page_flags, page_mapping, page_count,
page_mapcount, pid, " comm: {}".format(comm) if self.offset_comm is not None else "", \
is_pinned_str, ts_nsec, free_ts_nsec, function_list))
else:
op_file.write(str1.format(cma_usage, pfn, pfn + nr_pages - 1,
page, page_flags, page_mapping,
page_count, page_mapcount,
is_pinned_str, function_list))
else:
if nr_pages == 1:
op_file.write(str2.format(cma_usage, pfn, page, page_count,
page_mapcount, is_pinned_str))
else:
op_file.write(str3.format(
cma_usage, pfn, pfn + nr_pages - 1, page, page_count,
page_mapcount, is_pinned_str))
return nr_pages
def cma_region_dump(self, ramdump, cma, cma_name):
base_pfn = ramdump.read_structure_field(
cma, 'struct cma', 'base_pfn')
cma_count = ramdump.read_structure_field(
cma, 'struct cma', 'count')
bitmap = ramdump.read_structure_field(
cma, 'struct cma', 'bitmap')
bitmap_end = bitmap + cma_count // 8
in_system = 1
end_pfn = base_pfn + cma_count
name = "cma_report_" + cma_name + ".txt"
op_file = ramdump.open_file(name)
op_file.write("CMA report\n")
op_file.write(" - name : {0}\n".format(cma_name))
op_file.write(" - base_pfn\t\t\t: 0x{0:x}\n".format(base_pfn))
op_file.write(" - end_pfn\t\t\t: 0x{0:x}\n".format(end_pfn))
op_file.write(" - count\t\t\t: 0x{0:x}\n".format(cma_count))
op_file.write(" - size\t\t\t\t: {0}KB\n".format(cma_count << 0x2))
op_file.write(" - bitmap_start\t\t: 0x{0:x}\n".format(bitmap))
op_file.write(" - bitmap_end\t\t: 0x{0:x}\n".format(bitmap_end))
op_file.write(" - in_system\t\t: {0}\n\n".format(in_system))
dict = {}
byte_index = 0
PFNS_PER_BYTE = 8
COUNT_TO_BYTE = cma_count // PFNS_PER_BYTE
list = []
while byte_index < COUNT_TO_BYTE:
value = ramdump.read_byte(bitmap + byte_index)
list.append([bitmap + byte_index, value])
pfn_index = 0
while pfn_index < PFNS_PER_BYTE:
pfn = base_pfn + byte_index * PFNS_PER_BYTE + pfn_index
bit_value = (value >> pfn_index) & 0x1
cma = 0
if bit_value != 0:
cma = 1
else:
cma = 0
if cma == 1:
self.parse_pfn(ramdump, pfn, cma, op_file, dict)
pfn_index = pfn_index + 1
byte_index = byte_index + 1
sort_list = sorted(dict.items(), key=lambda kv: kv[1], reverse=True)
for k, v in sort_list:
print("PID %-8d alloc times %-8d" % (k, v), file=op_file)
line_break = 0
print("bitmap: " % (), file=op_file)
for item in list:
print("%02x" % (item[1]), file=op_file, end="")
line_break = line_break + 1
if line_break == 16:
print("%s" % (" "), file=op_file)
line_break = 0
op_file.close()
def print_cma_areas(self, ramdump):
output_file = ramdump.open_file("cma_report_simple.txt")
cma_area_count = ramdump.read_u32('cma_area_count')
cma_area_base_addr = ramdump.address_of('cma_areas')
cma_index = 0
size_of_cma_area = ramdump.sizeof('struct cma')
str = "cma : 0x{0:x} cma_base_pfn : 0x{1:x} size : 0x{2:x} pages ({3}KB)\n"
str1 = "name : {0}\n\n"
cma = [0] * cma_area_count
cma_name = [None] * cma_area_count
while cma_index < cma_area_count:
cma_area = cma_area_base_addr + cma_index * size_of_cma_area
base_pfn = ramdump.read_structure_field(
cma_area, 'struct cma', 'base_pfn')
cma_size = ramdump.read_structure_field(
cma_area, 'struct cma', 'count')
if (ramdump.kernel_version >= (5, 10, 0)):
name_addr_offset = ramdump.field_offset('struct cma', 'name')
name_addr = (cma_area + name_addr_offset)
name = ramdump.read_cstring(name_addr, 64)
else:
name_addr = ramdump.read_structure_field(cma_area, 'struct cma', 'name')
name = ramdump.read_cstring(name_addr, 48)
if name == "linux,cma":
name = "dma_contiguous_default_area"
cma[cma_index] = cma_area
cma_name[cma_index] = name
output_file.write(str.format(cma_area, base_pfn, cma_size, cma_size * 4))
output_file.write(str1.format(name))
cma_index = cma_index + 1
output_file.close()
cma_index = 0
while cma_index < cma_area_count:
cma1 = cma[cma_index]
cma_name1 = cma_name[cma_index]
self.cma_region_dump(ramdump, cma1, cma_name1)
cma_index = cma_index + 1
def parse(self):
if self.ramdump.kernel_version < (4, 9):
print_out_str("Linux version lower than 4.9 is not supported!!")
return
else:
self.print_cma_areas(self.ramdump)
@register_parser('--print-softirq-stat', 'Print softirq pending info ')
class SoftirqStat(RamParser):
def parse(self):
parse_softirq_stat(self.ramdump)
@register_parser('--print-qsee-log', 'Extract qsee com logs')
class ParseQseeLog(RamParser):
def parse(self):
do_parse_qsee_log(self.ramdump)
anomaly = Anomaly()
anomaly.setOutputDir(self.ramdump.outdir)
return_string = check_qseecom_invalid_cmds(self.ramdump)
if len(return_string):
anomaly.addWarning("HLOS", "qsee_log.txt", "{0}".format(return_string))

View File

@@ -0,0 +1,29 @@
# Copyright (c) 2021 The Linux Foundation. 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 print_out
from parser_util import RamParser, cleanupString, register_parser
@register_parser('--rm-log', 'Dump resource manager log')
class RMLog(RamParser):
def parse(self):
if self.ramdump.rm_debug_addr is None:
raise Exception('Cannot find rm_debug_addr!!!')
rm_debug_addr = self.ramdump.read_u32(self.ramdump.rm_debug_addr, False)
rm_log_addr = self.ramdump.read_u32(rm_debug_addr + 0x20, False)
rm_log_size = self.ramdump.read_u32(rm_debug_addr + 0x28, False)
rm_log = self.ramdump.read_physical(rm_log_addr, rm_log_size)
if rm_log is None:
raise Exception('!!!Could not read rm_log from address {0:x}'.format(rm_log_addr))
rm_log_output = self.ramdump.open_file('rm_log.txt')
rm_log_output.write(cleanupString(rm_log.decode('ascii', 'ignore')))
rm_log_output.close()
print_out.print_out_str('Resource manager log successfully extracted!')

View File

@@ -0,0 +1,87 @@
#SPDX-License-Identifier: GPL-2.0-only
#Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
from mm import pfn_to_page, page_buddy, page_count, for_each_pfn
from mm import page_to_pfn
from parsers.uvtop import do_get_task_info
import sys
import rb_tree
def save_task_info(ramdump):
task_dict = dict()
for task in ramdump.for_each_process():
pid, thread_task_name, mm = do_get_task_info(ramdump, task)
task_dict[mm] = (pid, thread_task_name, task)
return task_dict
@register_parser('--print-rmap', 'print page rmap', optional=True)
class Rmap(RamParser):
def rmap_one(self, rb_node, extra):
page = extra[0]
fout = extra[1]
anon_vma_chain = self.ramdump.container_of(rb_node, 'struct anon_vma_chain', 'rb')
if anon_vma_chain == None or anon_vma_chain == 0:
return
vma = self.ramdump.read_structure_field(anon_vma_chain, 'struct anon_vma_chain', 'vma')
if vma == 0 or vma == None:
return
vm_mm = self.ramdump.read_structure_field(vma, 'struct vm_area_struct', 'vm_mm')
if vm_mm == 0 or vm_mm == None:
return
if self.task_dict.get(vm_mm) == None:
return
anon_name = self.ramdump.read_structure_field(vma, 'struct vm_area_struct', 'anon_name')
name_addr = self.ramdump.struct_field_addr(anon_name, 'struct anon_vma_name', 'name')
name = cleanupString(self.ramdump.read_cstring(name_addr))
task_info = self.task_dict[vm_mm]
pid = task_info[0]
thread_task_name = task_info[1]
print(" v.v (struct vm_area_struct)0x%x pid %-8d comm %-16s %s" % (vma, pid, thread_task_name, name), file = fout)
def rmap_walk_anon(self, page, fout):
PAGE_MAPPING_ANON = 1
if page == None or page == 0:
reurn
addr_space = self.ramdump.read_structure_field(page, 'struct page', 'mapping')
if addr_space == None or addr_space == 0:
return
if addr_space & 0x1 == 0:
return
anon_vma = addr_space - PAGE_MAPPING_ANON
print(" v.v (struct page)0x%x" % (page), file=fout)
rb_root_offset = self.ramdump.field_offset('struct anon_vma', 'rb_root')
rb_root = rb_root_offset + anon_vma
rb_node = self.ramdump.read_pointer(rb_root)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
extra = (page, fout)
rb_walker.walk(rb_node, self.rmap_one, extra)
return
def parse(self):
page = None
g_optimization = False
for arg in sys.argv:
if "page=" in arg:
g_optimization = True
k, page = arg.split('=')
page = int(page, 16)
break
self.task_dict = save_task_info(self.ramdump)
out_tracking = self.ramdump.open_file('rmap.txt')
if g_optimization:
self.rmap_walk_anon(page, out_tracking)
else:
ranges = for_each_pfn(self.ramdump)
for pfn in ranges:
page = pfn_to_page(self.ramdump, pfn)
if (page_buddy(self.ramdump, page) or \
page_count(self.ramdump, page) == 0):
continue
self.rmap_walk_anon(page, out_tracking)
out_tracking.close()
print_out_str('---wrote file tracking information to rmap.txt')

View File

@@ -0,0 +1,143 @@
# Copyright (c) 2013-2015, 2020-2021 The Linux Foundation. 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 struct
import string
from print_out import print_out_str
from collections import namedtuple
from parser_util import register_parser, RamParser
ELF32HEADERFORMAT = '<16sHHIIIIIHHHHHH'
ELF32HEADERSIZE = struct.calcsize(ELF32HEADERFORMAT)
ELF64HEADERFORMAT = '<16sHHIQQQIHHHHHH'
ELF64HEADERSIZE = struct.calcsize(ELF64HEADERFORMAT)
PRG32HEADERFORMAT = 'IIIIIIII'
PRG32HEADERSIZE = struct.calcsize(PRG32HEADERFORMAT)
PRG64HEADERFORMAT = 'IIQQQQQQ'
PRG64HEADERSIZE = struct.calcsize(PRG64HEADERFORMAT)
PF_W = 2
OUTPUT_SIZE=256
LUMP_SIZE=4096
@register_parser('--check-rodata', 'check rodata in dump against the static image')
class ROData(RamParser):
def parse(self):
with self.ramdump.open_file('roareadiff.txt') as roarea_out:
if self.ramdump.arm64:
elfheaderformat = ELF64HEADERFORMAT
elfheadersize = ELF64HEADERSIZE
prgheaderformat = PRG64HEADERFORMAT
prgheadersize = PRG64HEADERSIZE
else:
elfheaderformat = ELF32HEADERFORMAT
elfheadersize = ELF32HEADERSIZE
prgheaderformat = PRG32HEADERFORMAT
prgheadersize = PRG32HEADERSIZE
fd = open(self.ramdump.vmlinux, 'rb')
if not fd:
print_out_str('Could not open {0}.'.format(file_path))
return
ElfHeader = namedtuple(
'ElfHeader', 'ident type machine version entry phoff shoff flags ehsize phentsize phnum shentsize shnum shstrndx')
raw_elfheader = fd.read(elfheadersize)
elfheader = ElfHeader._make(
struct.unpack(elfheaderformat, raw_elfheader))
if self.ramdump.arm64:
PrgHeader = namedtuple(
'Prgheader', 'type flags offset vaddr paddr filesz memsz align')
else:
PrgHeader = namedtuple(
'Prgheader', 'type offset vaddr paddr filesz memsz flags align')
for i in range(elfheader.phnum):
fd.seek(elfheader.phoff + i * prgheadersize)
raw_prgheader = fd.read(prgheadersize)
prgheader = PrgHeader._make(
struct.unpack(prgheaderformat, raw_prgheader))
if not prgheader.flags & PF_W:
fd.seek(prgheader.offset)
count = prgheader.paddr
while count < prgheader.paddr + prgheader.memsz:
if prgheader.paddr + prgheader.memsz - count < LUMP_SIZE:
max_read_size = prgheader.paddr + prgheader.memsz - count
else:
max_read_size = LUMP_SIZE
ram_values = self.ramdump.read_physical(self.ramdump.virt_to_phys(count), max_read_size)
vm_values = fd.read(max_read_size)
if ram_values and ram_values != vm_values:
detect = 0xFFFFFFFF
print_out_str(
'Differences found! Differences written to roareadiff.txt')
i = 0
while i < max_read_size:
try:
ram_value = struct.unpack_from('I', ram_values, i)[0]
vm_value = struct.unpack_from('I', vm_values, i)[0]
if detect == 64:
ddr_str += ddr_ascii + '\n\n'
vmlinux_str += vm_ascii + '\n\n'
roarea_out.write(ddr_str)
roarea_out.write(vmlinux_str)
detect = 0xFFFFFFFF
if detect == 0xFFFFFFFF and ram_value != vm_value:
ddr_str = 'detect RO area differences between vmlinux and DDR at 0x{0:0>8x}\n'.format(
count + i)
ddr_str += 'from DDR:'
vmlinux_str = 'from vmlinux:'
ddr_ascii = ' '
vm_ascii = ' '
detect = 0
if max_read_size < i + OUTPUT_SIZE and max_read_size == LUMP_SIZE:
max_read_size = i + OUTPUT_SIZE
ram_values = self.ramdump.read_physical(self.ramdump.virt_to_phys(count), max_read_size)
fd.seek(prgheader.offset + count - prgheader.paddr)
vm_values = fd.read(max_read_size)
if 0 <= detect and detect < 64:
if detect % 8 == 0:
ddr_str += ddr_ascii + '\n{0:0>8x} '.format(count + i)
vmlinux_str += vm_ascii + '\n{0:0>8x} '.format(count + i)
ddr_ascii = ' '
vm_ascii = ' '
ddr_str += ' '
vmlinux_str += ' '
if ram_value != vm_value:
ddr_str += '*'
vmlinux_str += '*'
else:
ddr_str += ' '
vmlinux_str += ' '
ddr_str += '{0:0>8x}'.format(ram_value)
vmlinux_str += '{0:0>8x}'.format(vm_value)
for j in range(4):
c = '{0:c}'.format(struct.unpack_from('B', ram_values, i + j)[0]).rstrip()
ddr_ascii += c if c in string.printable else '.'
c = '{0:c}'.format(struct.unpack_from('B', vm_values, i + j)[0]).rstrip()
vm_ascii += c if c in string.printable else '.'
detect += 1
i = i + 4
except Exception as err:
roarea_out.write("Failed roaddress = {0}".format(count))
if detect != 0xFFFFFFFF:
ddr_str += ' ' * (7 - ((detect - 1) % 8)) + ddr_ascii + '\n\n'
vmlinux_str += ' ' * (7 - ((detect - 1) % 8)) + vm_ascii + '\n\n'
roarea_out.write(ddr_str)
roarea_out.write(vmlinux_str)
count += max_read_size
fd.close()

View File

@@ -0,0 +1,259 @@
# Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 2022 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.
from tempfile import NamedTemporaryFile
from print_out import print_out_str
from parser_util import register_parser, RamParser
# struct msm_rtb_layout {
# unsigned char sentinel[3];
# unsigned char log_type;
# unsigned long idx;
# void *caller;
# void *data;
# void *timestamp;
# void *cycle_count;
#} __attribute__ ((__packed__));
print_table = {
'LOGK_NONE': 'print_none',
'LOGK_READL': 'print_readlwritel',
'LOGK_WRITEL': 'print_readlwritel',
'LOGK_LOGBUF': 'print_logbuf',
'LOGK_HOTPLUG': 'print_hotplug',
'LOGK_CTXID': 'print_ctxid',
'LOGK_TIMESTAMP': 'print_timestamp',
'LOGK_L2CPREAD': 'print_cp_rw',
'LOGK_L2CPWRITE': 'print_cp_rw',
'LOGK_IRQ': 'print_irq',
}
@register_parser('--print-rtb', 'Print RTB (if enabled)', shortopt='-r')
class RTB(RamParser):
def __init__(self, *args):
super(RTB, self).__init__(*args)
self.name_lookup_table = []
def get_caller(self, caller):
return self.ramdump.gdbmi.get_func_info(caller)
def get_fun_name(self, addr):
l = self.ramdump.unwind_lookup(addr)
if l is not None:
symname, offset = l
else:
symname = 'Unknown function'
return symname
def get_timestamp(self, rtb_ptr):
timestamp = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'timestamp')
if timestamp == None:
return 0
timestamp = round(float(timestamp)/10**9,6)
timestamp = format(timestamp,'.6f')
return timestamp
def get_cycle_count(self, rtb_ptr):
cycle_count = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'cycle_count')
if cycle_count == None:
return 0
return cycle_count
def print_none(self, rtbout, rtb_ptr, logtype):
rtbout.write('{0} No data\n'.format(logtype))
def print_readlwritel(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
try:
physical = self.ramdump.virt_to_phys(data)
except:
physical = None
if physical is None:
physical = "no translation found"
else:
physical = hex(physical).rstrip("L").lstrip("0x")
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} from address {3:x}({4}) called from addr {5:x} {6} {7}\n'.format(
timestamp, cycle_count, logtype, data, physical, caller, func, line))
def print_logbuf(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} log end {3:x} called from addr {4:x} {5} {6}\n'.format(
timestamp, cycle_count, logtype, data, caller, func, line))
def print_hotplug(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} cpu data {3:x} called from addr {4:x} {5} {6}\n'.format(
timestamp, cycle_count, logtype, data, caller, func, line))
def print_ctxid(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} context id {3:x} called from addr {4:x} {5} {6}\n'.format(
timestamp, cycle_count, logtype, data, caller, func, line))
def print_timestamp(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] : [{1}] Timestamp: {2:x}{3:x}\n'.format(
cycle_count, logtype, data, caller))
def print_cp_rw(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} from offset {3:x} called from addr {4:x} {5} {6}\n'.format(
timestamp, cycle_count, logtype, data, caller, func, line))
def print_irq(self, rtbout, rtb_ptr, logtype):
data = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'data')
caller = self.ramdump.read_structure_field(rtb_ptr, 'struct msm_rtb_layout', 'caller')
func = self.get_fun_name(caller)
line = self.get_caller(caller)
timestamp = self.get_timestamp(rtb_ptr)
cycle_count = self.get_cycle_count(rtb_ptr)
rtbout.write('[{0}] [{1}] : {2} interrupt 0x{3:x} {4} handled from addr {5:x} {6} {7}\n'.format(
timestamp, cycle_count, logtype, data, data, caller, func, line))
def next_rtb_entry(self, index, step_size, mask):
unused_buffer_size = (mask + 1) % step_size
#check for wraparound
if ((index + step_size + unused_buffer_size) & mask) < (index & mask):
return (index + step_size + unused_buffer_size) & mask
return (index + step_size) & mask
def parse(self):
rtb_ptr = self.ramdump.address_of('msm_rtb_ptr')
if rtb_ptr is None:
rtb = self.ramdump.address_of('msm_rtb')
else:
rtb = self.ramdump.read_pointer(rtb_ptr)
if rtb is None and self.ramdump.minidump:
rtb_seg = next((s for s in self.ramdump.elffile.iter_sections() if s.name == 'KRTB_BUF'), None)
if rtb_seg is not None:
rtb = rtb_seg['sh_addr']
rtb_ptr = True # Not a pointer, but used below to determine whether new RTB layout is used
if rtb is None:
print_out_str(
'[!] RTB was not enabled in this build. No RTB files will be generated')
return
self.name_lookup_table = self.ramdump.gdbmi.get_enum_lookup_table(
'logk_event_type', 32)
step_size_offset = self.ramdump.field_offset(
'struct msm_rtb_state', 'step_size')
nentries_offset = self.ramdump.field_offset(
'struct msm_rtb_state', 'nentries')
rtb_entry_offset = self.ramdump.field_offset(
'struct msm_rtb_state', 'rtb')
idx_offset = self.ramdump.field_offset('struct msm_rtb_layout', 'idx')
rtb_entry_size = self.ramdump.sizeof('struct msm_rtb_layout')
step_size = self.ramdump.read_u32(rtb + step_size_offset)
total_entries = self.ramdump.read_int(rtb + nentries_offset)
rtb_read_ptr = self.ramdump.read_word(rtb + rtb_entry_offset)
if rtb_ptr is not None:
rtb_idx_array_offset = self.ramdump.field_offset('struct msm_rtb_state', 'msm_rtb_idx')
rtb_idx_size = self.ramdump.sizeof('struct rtb_idx')
rtb_idx_atomic_offset = self.ramdump.field_offset('struct rtb_idx', 'idx')
if step_size is None or step_size > self.ramdump.get_num_cpus():
print_out_str('RTB dump looks corrupt! Got step_size=%s' %
hex(step_size) if step_size is not None else None)
return
for i in self.ramdump.iter_cpus():
rtb_out = self.ramdump.open_file('msm_rtb{0}.txt'.format(i))
gdb_cmd = NamedTemporaryFile(mode='w+t', delete=False)
gdb_out = NamedTemporaryFile(mode='w+t', delete=False)
mask = self.ramdump.read_int(rtb + nentries_offset) - 1
if rtb_ptr is not None:
last = self.ramdump.read_int(rtb +
rtb_idx_array_offset + (i * rtb_idx_size) +
rtb_idx_atomic_offset)
else:
if step_size == 1:
last = self.ramdump.read_int(
self.ramdump.address_of('msm_rtb_idx'))
else:
last = self.ramdump.read_int(self.ramdump.address_of(
'msm_rtb_idx_cpu'), cpu=i)
last = last & mask
last_ptr = 0
next_ptr = 0
next_entry = 0
while True:
next_entry = self.next_rtb_entry(last, step_size, mask)
last_ptr = rtb_read_ptr + last * rtb_entry_size + idx_offset
next_ptr = rtb_read_ptr + next_entry * \
rtb_entry_size + idx_offset
a = self.ramdump.read_int(last_ptr)
b = self.ramdump.read_int(next_ptr)
if a < b:
last = next_entry
if next_entry != last:
break
stop = 0
rtb_logtype_offset = self.ramdump.field_offset(
'struct msm_rtb_layout', 'log_type')
rtb_idx_offset = self.ramdump.field_offset(
'struct msm_rtb_layout', 'idx')
while True:
ptr = rtb_read_ptr + next_entry * rtb_entry_size
stamp = self.ramdump.read_int(ptr + rtb_idx_offset)
if stamp is None:
break
rtb_out.write('{0:x} '.format(stamp))
item = self.ramdump.read_byte(ptr + rtb_logtype_offset)
item = item & 0x7F
name_str = '(unknown)'
if item >= len(self.name_lookup_table) or item < 0:
self.print_none(rtb_out, ptr, None)
else:
name_str = self.name_lookup_table[item]
if name_str not in print_table:
self.print_none(rtb_out, ptr, None)
else:
func = print_table[name_str]
getattr(RTB, func)(self, rtb_out, ptr, name_str)
if next_entry == last:
stop = 1
next_entry = self.next_rtb_entry(next_entry, step_size, mask)
if (stop == 1):
break
print_out_str('Wrote RTB to msm_rtb{0}.txt'.format(i))
rtb_out.close()

View File

@@ -0,0 +1,366 @@
# Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 rb_tree
from print_out import print_out_str
from parser_util import register_parser, RamParser
@register_parser('--print-runqueues', 'Print the runqueue status')
class RunQueues(RamParser):
def __init__(self, *args):
super(RunQueues, self).__init__(*args)
self.domain_list = []
self.tab_offset = 0
def ffs(self, num):
# Check there is at least one bit set.
if num == 0:
return None
# Right-shift until we have the first set bit in the LSB position.
i = 0
while (num % 2) == 0:
i += 1
num = num >> 1
return i
def print_out_str_with_tab(self, string):
string = self.tab_offset * ' | ' + ' |--' + string
print_out_str(string)
def print_task_state(self, status, task_addr):
pid_offset = self.ramdump.field_offset('struct task_struct', 'pid')
comm_offset = self.ramdump.field_offset('struct task_struct', 'comm')
if self.ramdump.kernel_version > (5, 2, 0):
affinity_offset = self.ramdump.field_offset('struct task_struct', 'cpus_mask')
else:
affinity_offset = self.ramdump.field_offset('struct task_struct', 'cpus_allowed')
if 0 < task_addr:
pid = self.ramdump.read_int(task_addr + pid_offset)
taskname = self.ramdump.read_cstring(task_addr + comm_offset, 16)
affinity = self.ramdump.read_u64(task_addr + affinity_offset)
se_offset = self.ramdump.field_offset('struct task_struct', 'se')
if se_offset is None:
vruntime = 0
else:
se = (task_addr + se_offset)
vruntime_offset = self.ramdump.field_offset('struct sched_entity', 'vruntime')
vruntime = self.ramdump.read_u64(se + vruntime_offset)
if self.ramdump.is_config_defined('CONFIG_FAIR_GROUP_SCHED'):
cfs_rq_offset = self.ramdump.field_offset('struct sched_entity', 'cfs_rq')
cfs_rq = None
if se is not None and se > 0:
cfs_rq = self.ramdump.read_word(se + cfs_rq_offset)
tg_offset = self.ramdump.field_offset('struct cfs_rq', 'tg')
tg = None
if cfs_rq is not None and cfs_rq > 0:
tg = self.ramdump.read_word(cfs_rq + tg_offset)
css_offset = self.ramdump.field_offset('struct task_group', 'css')
css = None
if tg is not None and tg > 0:
css = self.ramdump.read_word(tg + css_offset)
cgroup_offset = self.ramdump.field_offset('struct cgroup_subsys_state', 'cgroup')
cgroup = None
if css is not None and css > 0:
cgroup = self.ramdump.read_word(css + cgroup_offset)
kn_offset = self.ramdump.field_offset('struct cgroup', 'kn')
kn = None
if cgroup is not None and cgroup > 0:
kn = self.ramdump.read_word(cgroup + kn_offset)
name_offset = self.ramdump.field_offset('struct kernfs_node', 'name')
name = 'n/a'
if kn is not None and kn > 0:
name_addr = self.ramdump.read_word(kn + name_offset)
name = self.ramdump.read_cstring(name_addr)
else:
name ='n/a'
print_out_str(
'{0}: {1:16s}({2:6d}) [affinity=0x{3:02x}] [vruntime={4:16d}] {5:s}'.format(status, taskname, pid, affinity, vruntime, name))
else:
self.print_out_str_with_tab('{0}: None(0)'.format(status))
def print_cgroup_state(self, status, se_addr):
se_offset = self.ramdump.field_offset('struct task_struct', 'se')
cfs_nr_running_offset = self.ramdump.field_offset(
'struct cfs_rq', 'nr_running')
my_q_offset = self.ramdump.field_offset('struct sched_entity', 'my_q')
if se_addr == 0 or my_q_offset is None:
task = self.ramdump.container_of(se_addr, 'struct task_struct', 'se')
self.print_task_state(status, task)
else:
my_q_addr = self.ramdump.read_word(se_addr + my_q_offset)
if my_q_addr == 0:
self.print_task_state(status, se_addr - se_offset)
else:
cfs_nr_running = self.ramdump.read_int(
my_q_addr + cfs_nr_running_offset)
self.print_out_str_with_tab(
'{0}: {1} process is grouping'.format(status, cfs_nr_running))
self.tab_offset += 1
self.print_cfs_state(my_q_addr)
self.tab_offset -= 1
def cfs_node_func(self, node, extra):
run_node_offset = self.ramdump.field_offset(
'struct sched_entity', 'run_node')
task_se = node - run_node_offset
self.print_cgroup_state('pend', task_se)
def print_cfs_state(self, cfs_rq_addr):
tasks_timeline_offset = self.ramdump.field_offset(
'struct cfs_rq', 'tasks_timeline')
curr_offset = self.ramdump.field_offset('struct cfs_rq', 'curr')
next_offset = self.ramdump.field_offset('struct cfs_rq', 'next')
last_offset = self.ramdump.field_offset('struct cfs_rq', 'last')
skip_offset = self.ramdump.field_offset('struct cfs_rq', 'skip')
tasks_timeline_addr = self.ramdump.read_word(
cfs_rq_addr + tasks_timeline_offset)
curr_se = self.ramdump.read_word(cfs_rq_addr + curr_offset)
self.print_cgroup_state('curr', curr_se)
next_se = self.ramdump.read_word(cfs_rq_addr + next_offset)
self.print_cgroup_state('next', next_se)
if last_offset:
last_se = self.ramdump.read_word(cfs_rq_addr + last_offset)
self.print_cgroup_state('last', last_se)
if skip_offset:
skip_se = self.ramdump.read_word(cfs_rq_addr + skip_offset)
self.print_cgroup_state('skip', skip_se)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(tasks_timeline_addr, self.cfs_node_func)
def print_rt_cgroup_state(self, status, rt_addr):
rt_offset = self.ramdump.field_offset('struct task_struct', 'rt')
rt_nr_running_offset = self.ramdump.field_offset(
'struct rt_rq', 'nr_running')
my_q_offset = self.ramdump.field_offset(
'struct sched_rt_entity', 'my_q')
if rt_addr == 0:
self.print_task_state(status, se_addr)
else:
my_q_addr = self.ramdump.read_word(rt_addr + my_q_offset)
if my_q_addr == 0:
self.print_task_state(status, rt_addr - rt_offset)
else:
rt_nr_running = self.ramdump.read_word(
my_q_addr + rt_nr_running_offset)
self.print_out_str_with_tab(
'{0}: {1} process is grouping'.format(status, rt_nr_running))
self.tab_offset += 1
self.print_rt_state(my_q_addr)
self.tab_offset -= 1
def print_rt_state(self, rt_rq_addr):
active_offset = self.ramdump.field_offset('struct rt_rq', 'active')
queue_offset = self.ramdump.field_offset(
'struct rt_prio_array', 'queue')
rt_offset = self.ramdump.field_offset('struct task_struct', 'rt')
array_addr = rt_rq_addr + active_offset
if self.ramdump.arm64:
bitmap_range = 2
array_wsize = 8
idx_size = 64
else:
bitmap_range = 4
array_wsize = 4
idx_size = 32
seen_nodes = set()
for i in range(0, bitmap_range):
bitmap = self.ramdump.read_word(array_addr + i * array_wsize)
while True:
idx = self.ffs(bitmap)
if idx is not None and idx + i * idx_size < 100:
bitmap &= ~(1 << idx)
idx = (idx + i * idx_size) * array_wsize * 2
queue_addr = self.ramdump.read_word(
array_addr + queue_offset + idx)
while queue_addr != array_addr + queue_offset + idx:
if queue_addr in seen_nodes:
break
seen_nodes.add(queue_addr)
task_addr = queue_addr - rt_offset
self.print_task_state('pend', task_addr)
queue_addr = self.ramdump.read_word(queue_addr)
else:
break
def print_latest_callstack_maybe(self, task_addr):
text_start_addr = self.ramdump.address_of('_text')
text_end_addr = self.ramdump.address_of('_etext')
stack_offset = self.ramdump.field_offset('struct task_struct', 'stack')
stack_addr = self.ramdump.read_word(task_addr + stack_offset)
print_out_str('current callstack is maybe:')
if self.ramdump.arm64:
stack_align = 8
stack_size = 0x4000
else:
stack_align = 4
stack_size = 0x2000
for i in range(stack_addr, stack_addr + stack_size, stack_align):
callstack_addr = self.ramdump.read_word(i)
if text_start_addr <= callstack_addr and callstack_addr < text_end_addr:
wname = self.ramdump.unwind_lookup(callstack_addr)
if wname is not None:
print_out_str('0x{0:x}:{1}'.format(i, wname))
def print_md_latest_call_stack(self):
text_start_addr = self.ramdump.address_of('_text')
text_end_addr = self.ramdump.address_of('_etext')
minidump_stack_addr = next((s for s in self.ramdump.elffile.iter_sections() if s.name == 'KSTACK0_0'), None)
if minidump_stack_addr is None:
core_stack_addr = self.ramdump.address_of('md_stack_data')
if core_stack_addr is None:
print_out_str("\nCurrent call stack support is not present\n")
return
print_out_str('\ncurrent callstack is maybe\n')
no_of_cpus = self.ramdump.get_num_cpus()
index = 0
while index < no_of_cpus:
if minidump_stack_addr is None:
md_stack_addr = core_stack_addr + self.ramdump.per_cpu_offset(index)
if self.ramdump.arm64:
md_stack_addr = (md_stack_addr & 0xffffffffffffffff)
else:
md_stack_addr = (md_stack_addr & 0xffffffff)
stack_mdr = md_stack_addr + self.ramdump.field_offset('struct md_stack_cpu_data', 'stack_mdr')
stack_virt_addr = stack_mdr + self.ramdump.field_offset('struct md_region', 'virt_addr')
stack_virt_addr = self.ramdump.read_u64(stack_virt_addr)
if self.ramdump.arm64:
stack_align = 8
stack_size = 0x1000
loop = 4
else:
stack_align = 4
stack_size = 0x1000
loop = 2
print_out_str('\nCore_{} call stack :\n'.format(index))
for i in range(loop):
if minidump_stack_addr is not None:
section_name = 'KSTACK{0}_{1}'.format(index, i)
minidump_stack_addr = next((s for s in self.ramdump.elffile.iter_sections() if s.name == section_name), None)
stack_virt_addr = minidump_stack_addr['sh_addr']
for j in range(stack_virt_addr, stack_virt_addr + stack_size, stack_align):
callstack_addr = self.ramdump.read_word(j)
if callstack_addr is None:
continue
if text_start_addr <= callstack_addr and callstack_addr < text_end_addr:
wname = self.ramdump.unwind_lookup(callstack_addr)
if wname is not None:
print_out_str('0x{0:x}:{1}'.format(j, wname))
if minidump_stack_addr is None:
stack_mdr = stack_mdr + self.ramdump.sizeof('struct md_region')
stack_virt_addr = stack_mdr + self.ramdump.field_offset('struct md_region', 'virt_addr')
stack_virt_addr = self.ramdump.read_u64(stack_virt_addr)
index = index + 1
def print_irq_context(self):
text_start_addr = self.ramdump.address_of('_text')
text_end_addr = self.ramdump.address_of('_etext')
print_out_str('\ncurrent irq context callstack is maybe\n')
irq_stack_addr = self.ramdump.address_of('irq_stack_ptr')
if irq_stack_addr is None:
print_out_str("\nCurrent call stack support is not present\n")
return
no_of_cpus = self.ramdump.get_num_cpus()
index = 0
for index in self.ramdump.iter_cpus():
stack_addr = irq_stack_addr + self.ramdump.per_cpu_offset(index)
if self.ramdump.arm64:
stack_addr = stack_addr & 0xffffffffffffffff
else:
stack_addr = stack_addr & 0xFFFFFFFF
stack_addr = self.ramdump.read_u64(stack_addr)
if self.ramdump.arm64:
stack_align = 8
stack_size = 0x4000
else:
stack_align = 4
stack_size = 0x2000
print_out_str('\nCore_{} call stack :\n'.format(index))
for i in range(stack_addr, stack_addr + stack_size, stack_align):
callstack_addr = self.ramdump.read_word(i)
if callstack_addr is None:
continue
if text_start_addr <= callstack_addr and callstack_addr < text_end_addr:
wname = self.ramdump.unwind_lookup(callstack_addr)
if wname is not None:
print_out_str('0x{0:x}:{1}'.format(i, wname))
def parse(self):
print_out_str(
'======================= RUNQUEUE STATE ============================')
if self.ramdump.minidump:
self.print_md_latest_call_stack()
self.print_irq_context()
return
runqueues_addr = self.ramdump.address_of('runqueues')
nr_running_offset = self.ramdump.field_offset(
'struct rq', 'nr_running')
curr_offset = self.ramdump.field_offset('struct rq', 'curr')
idle_offset = self.ramdump.field_offset('struct rq', 'idle')
stop_offset = self.ramdump.field_offset('struct rq', 'stop')
cfs_rq_offset = self.ramdump.field_offset('struct rq', 'cfs')
rt_rq_offset = self.ramdump.field_offset('struct rq', 'rt')
cfs_nr_running_offset = self.ramdump.field_offset(
'struct cfs_rq', 'nr_running')
rt_nr_running_offset = self.ramdump.field_offset(
'struct rt_rq', 'rt_nr_running')
for i in self.ramdump.iter_cpus():
rq_addr = runqueues_addr + self.ramdump.per_cpu_offset(i)
nr_running = self.ramdump.read_int(rq_addr + nr_running_offset)
print_out_str(
'CPU{0} {1} process is running'.format(i, nr_running))
curr_addr = self.ramdump.read_word(rq_addr + curr_offset)
self.print_task_state('curr', curr_addr)
idle_addr = self.ramdump.read_word(rq_addr + idle_offset)
self.print_task_state('idle', idle_addr)
stop_addr = self.ramdump.read_word(rq_addr + stop_offset)
self.print_task_state('stop', stop_addr)
cfs_rq_addr = rq_addr + cfs_rq_offset
cfs_nr_running = self.ramdump.read_int(
cfs_rq_addr + cfs_nr_running_offset)
print_out_str('CFS {0} process is pending'.format(cfs_nr_running))
self.print_cfs_state(cfs_rq_addr)
rt_rq_addr = rq_addr + rt_rq_offset
rt_nr_running = self.ramdump.read_int(
rt_rq_addr + rt_nr_running_offset)
print_out_str('RT {0} process is pending'.format(rt_nr_running))
self.print_rt_state(rt_rq_addr)
self.print_latest_callstack_maybe(curr_addr)
print_out_str('')
self.print_irq_context()

View File

@@ -0,0 +1,814 @@
# Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 sys
from mm import page_address, pfn_to_page, get_pfn_range
from print_out import print_out_str
from parser_util import register_parser, RamParser
import operator
import os
from collections import defaultdict
from .pagetracking import StackDepot
SLAB_RED_ZONE = 0x400
SLAB_POISON = 0x800
SLAB_STORE_USER = 0x10000
OBJECT_POISON = 0x80000000
SLUB_RED_INACTIVE = 0xbb
SLUB_RED_ACTIVE = 0xcc
POISON_INUSE = 0x5a
POISON_FREE = 0x6b
POISON_END = 0xa5
FREQUENCY = 0
CALLSTACK_INDEX = 1
CALL_STACK = 2
TRACK_TYPE = 3
TRACK_PIDS = 4
# g_Optimization - if false will print :
# 1. Free objects callstacks
# 2. Object address range of allocated and FREE objects
g_Optimization = True
# When optimization is true, below two parameters will be used
# 1. MAX_NO_OF_CALLSTACK_TO_PRINT : Maximum number of callstacks to print
# in a slab.
# 2. CALLSTACK_FREQ_IN_SLAB: Print callstack only if occurance of
# each unique callstack in a slab is more than particular
# value(CALLSTACK_FREQ_IN_SLAB)
MAX_NO_OF_CALLSTACK_TO_PRINT = 5
MAX_NO_OF_PID_TO_PRINT = 5
CALLSTACK_FREQ_IN_SLAB = 10
class kmem_cache(object):
def __init__(self, ramdump, addr):
self.valid = False
offset = ramdump.field_offset(
'struct kmem_cache', 'flags')
self.flags = ramdump.read_word(addr + offset)
if self.flags is None:
return
offset = ramdump.field_offset(
'struct kmem_cache', 'size')
self.size = ramdump.read_int(addr + offset)
if self.size is None:
return
offset = ramdump.field_offset(
'struct kmem_cache', 'object_size')
self.object_size = ramdump.read_int(addr + offset)
if self.object_size is None:
return
offset = ramdump.field_offset(
'struct kmem_cache', 'offset')
self.offset = ramdump.read_int(addr + offset)
if self.offset is None:
return
offset = ramdump.field_offset(
'struct kmem_cache', 'inuse')
self.inuse = ramdump.read_int(addr + offset)
if self.inuse is None:
return
offset = ramdump.field_offset(
'struct kmem_cache', 'red_left_pad')
self.red_left_pad = ramdump.read_int(addr + offset)
if self.red_left_pad is None:
self.red_left_pad = 0
self.addr = addr
self.valid = True
if ramdump.is_config_defined('CONFIG_SLAB_FREELIST_HARDENED'):
self.random = ramdump.read_structure_field(
addr, 'struct kmem_cache', 'random')
class struct_member_offset(object):
def __init__(self, ramdump):
self.kmemcache_list = ramdump.field_offset(
'struct kmem_cache', 'list')
self.kmemcache_name = ramdump.field_offset(
'struct kmem_cache', 'name')
self.kmemcache_node = ramdump.field_offset(
'struct kmem_cache', 'node')
self.kmemcache_cpu_page = ramdump.field_offset(
'struct kmem_cache_cpu', 'page')
self.kmemcpucache_cpu_slab = ramdump.field_offset(
'struct kmem_cache', 'cpu_slab')
if ramdump.is_config_defined('CONFIG_SLUB_CPU_PARTIAL'):
self.kmemcache_cpu_partial = ramdump.field_offset(
'struct kmem_cache', 'cpu_partial')
self.kmemcachenode_partial = ramdump.field_offset(
'struct kmem_cache_node', 'partial')
self.kmemcachenode_full = ramdump.field_offset(
'struct kmem_cache_node', 'full')
self.page_flags = ramdump.field_offset(
'struct page', 'flags')
self.track_cpu = ramdump.field_offset(
'struct track', 'cpu')
self.track_when = ramdump.field_offset(
'struct track', 'when')
self.track_pid = ramdump.field_offset(
'struct track', 'pid')
self.sizeof_struct_track = ramdump.sizeof(
'struct track')
self.cpu_partial_offset = ramdump.field_offset(
'struct kmem_cache_cpu', 'partial')
if ramdump.kernel_version >= (5, 17):
self.page_freelist = ramdump.field_offset(
'struct slab', 'freelist')
self.cpu_cache_slab_offset = ramdump.field_offset(
'struct kmem_cache_cpu', 'slab')
self.page_objects = ramdump.field_offset(
'struct slab', 'objects')
self.page_next = ramdump.field_offset(
'struct slab', 'next')
self.track_addrs = ramdump.field_offset(
'struct track', 'handle')
else :
self.page_freelist = ramdump.field_offset(
'struct page', 'freelist')
self.cpu_cache_slab_offset = ramdump.field_offset(
'struct kmem_cache_cpu', 'page')
self.page_objects = ramdump.field_offset(
'struct page', 'objects')
self.page_next = ramdump.field_offset(
'struct page', 'next')
self.track_addrs = ramdump.field_offset(
'struct track', 'addrs')
if ramdump.kernel_version >= (6, 1):
self.page_offset = ramdump.field_offset(
'struct slab', 'slab_list')
elif ramdump.kernel_version >= (5, 4):
self.page_offset = ramdump.field_offset(
'struct page', 'slab_list')
else:
self.page_offset = ramdump.field_offset(
'struct page', 'lru')
self.sizeof_void_pointer = ramdump.sizeof(
"void *")
self.sizeof_unsignedlong = ramdump.sizeof(
"unsigned long")
@register_parser('--slabinfo', 'print information about slabs', optional=True)
class Slabinfo(RamParser):
g_allstacks = {} # hold callstack stack
g_index = 0
g_offsetof = None
def __init__(self, *args):
super(Slabinfo, self).__init__(*args)
self.stackdepot = StackDepot(self.ramdump)
return
def get_free_pointer(self, ramdump, s, obj):
# just like validate_slab_slab!
ptr = obj + s.offset
val = self.ramdump.read_word(ptr)
if self.ramdump.is_config_defined('CONFIG_SLAB_FREELIST_HARDENED'):
return ptr ^ s.random ^ val
else:
return val
def slab_index(self, ramdump, p, addr, slab):
return (p - addr) // slab.size
def get_map(self, ramdump, slab, page, bitarray):
freelist = self.ramdump.read_word(page + g_offsetof.page_freelist)
p = freelist
addr = page_address(self.ramdump, page)
seen = []
if addr is None:
return
while p != 0 and p is not None and p not in seen:
index = self.slab_index(self.ramdump, p, addr, slab)
if index >= len(bitarray) or index < 0:
return
bitarray[index] = 1
seen.append(p)
p = self.get_free_pointer(self.ramdump, slab, p)
def get_track(self, ramdump, slab, obj, track_type):
track_size = g_offsetof.sizeof_struct_track
if slab.flags & SLAB_RED_ZONE:
obj += slab.red_left_pad
if ramdump.kernel_version >= (5, 4):
if slab.offset >= slab.inuse:
p = obj + slab.inuse + g_offsetof.sizeof_void_pointer
else:
p = obj + slab.inuse
else:
if slab.offset != 0:
p = obj + slab.offset + g_offsetof.sizeof_void_pointer
else:
p = obj + slab.inuse
return p + track_type * track_size
def extract_callstack(self, ramdump, stack, out_file):
for a in stack:
look = ramdump.unwind_lookup(a)
if look is None:
out_file.write(
' [<{0:x}>] {1}\n'.format(a, "unknown symbol"))
continue
symname, offset = look
out_file.write(
' [<{0:x}>] {1}+0x{2:x}\n'.format(a, symname, offset))
return
def stack_depot_fetch(self, ramdump, start):
stack = []
stackstr = ""
if not self.ramdump.is_config_defined('CONFIG_STACKDEPOT'):
print("CONFIG_STACKDEPOT is absent.\n", file=self.slabs_object_out)
return None, None
handle = self.ramdump.read_word(start)
nr_entries, stack, stackstr = self.stackdepot.stack_depot_fetch(handle, symbol=False)
return stack, stackstr
def print_track(self, ramdump, slab, obj, track_type, out_file):
stack = []
stackstr = ""
p = self.get_track(ramdump, slab, obj, track_type)
track_addrs_offset = g_offsetof.track_addrs
start = p + track_addrs_offset
pointer_size = g_offsetof.sizeof_unsignedlong
if self.ramdump.kernel_version <= (5, 17):
for i in range(0, 16):
a = self.ramdump.read_word(start + pointer_size * i)
if a == 0:
continue
stack += [a]
stackstr += str(a)
else:
stack, stackstr = self.stack_depot_fetch(ramdump, start)
if stackstr == None:
return
stackstr_len = len(stackstr)
if stackstr_len == 0:
return
pid = self.ramdump.read_int(p + g_offsetof.track_pid)
if pid is None:
pid = -1
try:
self.g_allstacks[stackstr][FREQUENCY] += 1
pids = self.g_allstacks[stackstr][TRACK_PIDS]
if pid in pids:
pids[pid] += 1
else:
pids[pid] = 1
if self.g_allstacks[stackstr][FREQUENCY] > 1:
return
except KeyError:
if g_Optimization is True:
if track_type != 0:
# if free object and g_Optimization is True,
# ignore it for printing its call stack
return
pids = dict()
pids[pid] = 1
self.g_allstacks[stackstr] = [1, self.g_index, stack, track_type, pids]
self.g_index += 1
def print_slab(
self, ramdump, slab, page,
out_file, map_fn, out_slabs_addrs):
page_addr = page_address(ramdump, page)
p = page_addr
if page is None or page_addr is None:
return
n_objects = self.ramdump.read_word(page + g_offsetof.page_objects)
if n_objects is None:
return
n_objects = (n_objects >> 16) & 0x00007FFF
bitarray = [0] * n_objects
addr = page_address(self.ramdump, page)
self.get_map(self.ramdump, slab, page, bitarray)
while p < page_addr + (n_objects * slab.size):
bitidx = self.slab_index(self.ramdump, p, addr, slab)
if bitidx >= n_objects or bitidx < 0:
return
map_fn(
ramdump, p, bitarray[bitidx], slab,
page, out_file, out_slabs_addrs)
p = p + slab.size
def printpidsummary(self, track_pids, slab_out):
track_pids = sorted(
track_pids.items(),
key=operator.itemgetter(1), reverse=True)
nr = min(len(track_pids), MAX_NO_OF_PID_TO_PRINT)
for i in range(0, nr):
slab_out.write(" pid:{0} frequency:{1}\n".format(
track_pids[i][0], track_pids[i][1]))
def printsummary(self, slabs_output_summary, slab_out):
nCounter = 0
write_output = False
str = " Call stack index:{0} frequency: {1}\n"
sorted_val = sorted(
self.g_allstacks.items(),
key=operator.itemgetter(1), reverse=True)
if g_Optimization is False:
write_output = True
for key, val in sorted_val:
if g_Optimization is True:
if nCounter >= MAX_NO_OF_CALLSTACK_TO_PRINT:
break
if(
(nCounter < MAX_NO_OF_CALLSTACK_TO_PRINT)
and (val[FREQUENCY] > CALLSTACK_FREQ_IN_SLAB)):
write_output = True
else:
write_output = False
nCounter = nCounter + 1
if write_output is True:
if val[TRACK_TYPE] == 1: # free object
slab_out.write(
"\nFREE" + str.format(
val[CALLSTACK_INDEX], val[FREQUENCY]))
else: # allocated object
slab_out.write(
"\nALLOCATED" + str.format(
val[CALLSTACK_INDEX], val[FREQUENCY]))
self.extract_callstack(self.ramdump, val[CALL_STACK], slab_out)
self.printpidsummary(val[TRACK_PIDS], slab_out)
slabs_output_summary.write(
" stack index:{0} frequency:{1}\n".format(
val[CALLSTACK_INDEX], val[FREQUENCY]))
def print_slab_page_info(
self, ramdump, slab_obj, slab_node, start,
out_file, map_fn, out_slabs_addrs, cpu_partial=False):
page = self.ramdump.read_word(start)
if page == 0:
return
seen = []
pfn_range = get_pfn_range(ramdump)
max_page = pfn_to_page(ramdump, pfn_range['max'])
while True:
if page == start:
return
if page is None or page == 0:
return
if page in seen:
return
if page > max_page:
return
if cpu_partial is False:
page = page - g_offsetof.page_offset
seen.append(page)
self.print_slab(
self.ramdump, slab_obj, page, out_file, map_fn,
out_slabs_addrs)
if cpu_partial is False:
page = self.ramdump.read_word(page + g_offsetof.page_offset)
else:
page = self.ramdump.read_word(page + g_offsetof.page_next)
def print_per_cpu_slab_info(
self, ramdump, slab, slab_node, start, out_file, map_fn,
out_slabs_addrs):
page = self.ramdump.read_word(start)
if page == 0:
return
if page is None:
return
self.print_slab(
self.ramdump, slab, page, out_file, map_fn, out_slabs_addrs)
def print_objects_detail(self, ramdump, slab, obj, type):
stack = []
p = self.get_track(ramdump, slab, obj, type)
track_addrs_offset = g_offsetof.track_addrs
start = p + track_addrs_offset
pointer_size = g_offsetof.sizeof_unsignedlong
pid = self.ramdump.read_int(p + g_offsetof.track_pid)
if pid is None:
pid = -1
when = self.ramdump.read_ulong(p + g_offsetof.track_when)
if when is None:
when = 0
cpu = self.ramdump.read_int(p + g_offsetof.track_cpu)
if cpu is None:
cpu = -1
print("object 0x%x cpu %d pid %d when %d" % (obj, cpu, pid, when), file=self.slabs_object_out)
if type == 0:
print("ALLOCATED", file=self.slabs_object_out)
else:
print("FREE", file=self.slabs_object_out)
if when == 0:
print("object 0x%x is not %s until now\n" % (obj, "allocated" if type == 0 else "freed"), file=self.slabs_object_out)
return
if self.ramdump.kernel_version <= (5, 17):
for i in range(0, 16):
a = self.ramdump.read_word(start + pointer_size * i)
if a == 0:
continue
stack += [a]
else:
stack, stackstr = self.stack_depot_fetch(ramdump, start)
if stack == None:
return
for a in stack:
look = ramdump.unwind_lookup(a)
if look is None:
self.slabs_object_out.write(
' [<{0:x}>] {1}\n'.format(a, "unknown symbol"))
continue
symname, offset = look
self.slabs_object_out.write(
' [<{0:x}>] {1}+0x{2:x}\n'.format(a, symname, offset))
def print_all_objects(
self, ramdump, p, free, slab, page,
out_file, out_slabs_addrs):
if g_Optimization is False:
if free:
out_slabs_addrs.write(
'\n Object {0:x}-{1:x} FREE'.format(
p, p + slab.size))
else:
out_slabs_addrs.write(
'\n Object {0:x}-{1:x} ALLOCATED'.format(
p, p + slab.size))
if slab.flags & SLAB_STORE_USER:
if g_Optimization is False:
self.print_track(ramdump, slab, p, 0, out_file)
self.print_track(ramdump, slab, p, 1, out_file)
self.print_objects_detail(ramdump, slab, p, 0)
self.print_objects_detail(ramdump, slab, p, 1)
else:
self.print_track(ramdump, slab, p, free, out_file)
def print_check_poison(
self, ramdump, p, free, slab, page,
out_file, out_slabs_addrs):
if free:
self.check_object(slab, page, p, SLUB_RED_INACTIVE, out_file)
else:
self.check_object(slab, page, p, SLUB_RED_ACTIVE, out_file)
def initializeOffset(self):
global g_offsetof
g_offsetof = struct_member_offset(self.ramdump)
# based on validate_slab_cache. Currently assuming there
# is only one numa node in the system because the code to
# do that correctly is a big pain. This will
# need to be changed if we ever do NUMA properly.
def validate_slab_cache(self, slab_out, input_slabname, map_fn):
slab_name_found = False
original_slab = self.ramdump.address_of('slab_caches')
cpus = self.ramdump.get_num_cpus()
offsetof = struct_member_offset(self.ramdump)
self.initializeOffset()
slab_list_offset = g_offsetof.kmemcache_list
slab_name_offset = g_offsetof.kmemcache_name
slab_node_offset = g_offsetof.kmemcache_node
cpu_slab_offset = g_offsetof.kmemcpucache_cpu_slab
slab_partial_offset = g_offsetof.kmemcachenode_partial
slab_full_offset = g_offsetof.kmemcachenode_full
slab = self.ramdump.read_word(original_slab)
slabs_output_summary = self.ramdump.open_file('slabs_output.txt')
out_slabs_addrs = self.ramdump.open_file('out_slabs_addrs.txt')
if g_Optimization is True:
msg = (
"To optimize slab traverse time,"
"print of object address are skipped."
" Supply option perf_off in command"
"prompt to print object address as well."
)
out_slabs_addrs.write(msg)
while slab != original_slab:
slab = slab - slab_list_offset
slab_obj = kmem_cache(self.ramdump, slab)
if not slab_obj.valid:
slab_out.write(
'Invalid slab object {0:x}'.format(slab))
slab = self.ramdump.read_word(slab + slab_list_offset)
continue
slab_name_addr = self.ramdump.read_word(
slab + slab_name_offset)
slab_name = self.ramdump.read_cstring(
slab_name_addr, 48)
if input_slabname is not None:
if input_slabname != slab_name:
slab = self.ramdump.read_word(slab + slab_list_offset)
continue
else:
slab_name_found = True
# actually an array but again, no numa
slab_node_addr = self.ramdump.read_word(
slab + slab_node_offset)
slab_node = self.ramdump.read_word(
slab_node_addr)
print_out_str(
'\nExtracting slab details of : {0}'.format(
slab_name))
if g_Optimization is False:
out_slabs_addrs.write(
'\nslab address of : {0}'.format(slab_name))
cpu_slab_addr = self.ramdump.read_word(
slab + cpu_slab_offset)
if self.ramdump.is_config_defined('CONFIG_SLUB_DEBUG'):
nr_total_objects = self.ramdump.read_structure_field(
slab_node_addr,
'struct kmem_cache_node', 'total_objects')
else:
nr_total_objects = 'NA'
slab_out.write(
'\n {0:x} slab {1} {2:x} total objects: {3}\n'.format(
slab, slab_name, slab_node_addr, nr_total_objects))
self.slabs_object_out.write(
'\n {0:x} slab {1} {2:x} total objects: {3}\n'.format(
slab, slab_name, slab_node_addr, nr_total_objects))
self.print_slab_page_info(
self.ramdump, slab_obj, slab_node,
slab_node_addr + slab_partial_offset,
slab_out, map_fn, out_slabs_addrs)
if self.ramdump.is_config_defined('CONFIG_SLUB_DEBUG'):
self.print_slab_page_info(
self.ramdump, slab_obj, slab_node,
slab_node_addr + slab_full_offset,
slab_out, map_fn, out_slabs_addrs)
# per cpu slab
for i in self.ramdump.iter_cpus():
cpu_slabn_addr = cpu_slab_addr + self.ramdump.per_cpu_offset(i)
if cpu_slabn_addr == 0 or None:
break
self.print_per_cpu_slab_info(
self.ramdump, slab_obj,
slab_node, cpu_slabn_addr + offsetof.cpu_cache_slab_offset,
slab_out, map_fn, out_slabs_addrs)
if self.ramdump.is_config_defined('CONFIG_SLUB_CPU_PARTIAL'):
cpu_partial_val = self.ramdump.read_int(
slab + offsetof.kmemcache_cpu_partial)
if self.ramdump.is_config_defined('CONFIG_SLUB_CPU_PARTIAL') and cpu_partial_val != 0:
self.print_slab_page_info(self.ramdump, slab_obj, slab_node,
cpu_slabn_addr + offsetof.cpu_partial_offset,
slab_out, map_fn, out_slabs_addrs, True)
self.printsummary(slabs_output_summary, slab_out)
self.g_allstacks.clear()
if slab_name_found is True:
break
slab = self.ramdump.read_word(slab + slab_list_offset)
out_slabs_addrs.close()
slabs_output_summary.close()
def parse(self):
if self.ramdump.minidump:
for eb_file in self.ramdump.ebi_files:
path1 = eb_file[3]
path = os.path.join(os.path.dirname(path1), "md_SLABOWNER.bin")
if not os.path.exists(path):
print_out_str(path + " not found")
return
input_file = open(path, 'r', errors="ignore")
output_file = self.ramdump.open_file("slabowner_dump.txt", 'w')
lines = input_file.readlines()
i = 0
while i < len(lines):
functions = defaultdict(list)
objs = defaultdict(list)
objs_size = defaultdict(list)
line = lines[i];
output_file.write(line)
i = i+1
while (i < len(lines)):
if ('kmalloc-' not in lines[i]):
line = lines[i]
try:
txt = line.split()
obj = txt[0]
handle = int(txt[1])
n = int(txt[2])
objs[handle].append(obj)
except:
i = i + 1
continue
i = i+1
j = 0
if not functions[handle]:
for j in range(0, n):
line = lines[i]
try:
int(line, 16)
except:
i = i+1
continue
functions[handle].append(line)
i = i+1
else:
break
j = 0
for key in objs:
objs_size[key] = len(objs[key])
for key, value in sorted(objs_size.items(), key=lambda item: item[1], reverse = True):
output_file.write("No of objects :" + str(value))
output_file.write('\n')
output_file.write("Objs :" + str(objs[key]))
output_file.write('\n')
for key2 in functions:
if (key == key2):
output_file.write("call stack :\n")
for j in range(0,len(functions[key])):
look = self.ramdump.unwind_lookup(int(functions[key][j], 16))
if look is None:
continue
symname, offset = look
unwind_dat = ' [<{0:x}>] {1}+0x{2:x}\n'.format(
int(functions[key][j], 16), symname, offset)
output_file.write(unwind_dat)
output_file.write("\n")
output_file.close()
return
global g_Optimization
slabname = None
for arg in sys.argv:
if 'slabname=' in arg:
k, slabname = arg.split('=')
if 'perf_off' in arg:
g_Optimization = False
slab_out = self.ramdump.open_file('slabs.txt')
self.slabs_object_out = self.ramdump.open_file('slabs_object.txt')
self.validate_slab_cache(slab_out, slabname, self.print_all_objects)
self.slabs_object_out.close()
slab_out.close()
@register_parser('--slabpoison', 'check slab poison', optional=True)
class Slabpoison(Slabinfo):
"""Note that this will NOT find any slab errors which are printed out by the
kernel, because the slab object is removed from the freelist while being
processed"""
def print_section(self, text, addr, length, out_file):
out_file.write('{}\n'.format(text))
output = self.ramdump.hexdump(addr, length)
out_file.write(output)
def print_trailer(self, s, page, p, out_file):
addr = page_address(self.ramdump, page)
page_size = self.ramdump.get_page_size()
if self.ramdump.is_config_defined('CONFIG_SLUB_DEBUG_ON'):
self.print_track(self.ramdump, s.addr, p, 0, out_file)
self.print_track(self.ramdump, s.addr, p, 1, out_file)
out_file.write('INFO: Object 0x{:x} @offset=0x{:x} fp=0x{:x}\n\n'.format(
p, p - addr, self.get_free_pointer(self.ramdump, s, p)))
if (p > addr + 16):
self.print_section('Bytes b4 ', p - 16, 16, out_file)
self.print_section('Object ', p, min(s.object_size, page_size), out_file)
if (s.flags & SLAB_RED_ZONE):
self.print_section('Redzone ', p + s.object_size,
s.inuse - s.object_size, out_file)
if (s.offset):
off = s.offset + self.ramdump.sizeof('void *')
else:
off = s.inuse
if (s.flags & SLAB_STORE_USER):
off += 2 * self.ramdump.sizeof('struct track')
if (off != s.size):
# Beginning of the filler is the free pointer
self.print_section('Padding ', p + off, s.size - off, out_file)
def memchr_inv(self, addr, value, size):
data = self.read_byte_array(addr, size)
if data is None:
return 0
for i in range(len(data)):
if data[i] != value:
return i + addr
return 0
def check_bytes_and_report(self, s, page, object, what, start,
value, bytes, out_file):
fault = self.memchr_inv(start, value, bytes)
if (not fault):
return True
end = start + bytes
while (end > fault and (self.read_byte_array(end - 1, 1)[0]) == value):
end -= 1
out_file.write('{0} overwritten\n'.format(what))
out_file.write('INFO: 0x{:x}-0x{:x}. First byte 0x{:x} instead of 0x{:x}\n'.format(
fault, end - 1, self.ramdump.read_byte(fault), value))
self.print_trailer(s, page, object, out_file)
return False
def check_pad_bytes(self, s, page, p, out_file):
off = s.inuse
if (s.offset):
# Freepointer is placed after the object
off += self.ramdump.sizeof('void *')
if (s.flags & SLAB_STORE_USER):
# We also have user information there
off += 2 * self.ramdump.sizeof('struct track')
if (s.size == off):
return True
return self.check_bytes_and_report(s, page, p, 'Object padding',
p + off, POISON_INUSE, s.size - off, out_file)
def check_object(self, s, page, object, val, out_file):
p = object
endobject = object + s.object_size
if (s.flags & SLAB_RED_ZONE):
if (not self.check_bytes_and_report(s, page, object, 'Redzone',
endobject, val, s.inuse - s.object_size, out_file)):
return
else:
if ((s.flags & SLAB_POISON) and s.object_size < s.inuse):
self.check_bytes_and_report(s, page, p, 'Alignment padding',
endobject, POISON_INUSE,
s.inuse - s.object_size, out_file)
if (s.flags & SLAB_POISON):
if (val != SLUB_RED_ACTIVE and (s.flags & OBJECT_POISON) and
(not self.check_bytes_and_report(s, page, p, 'Poison', p,
POISON_FREE, s.object_size - 1, out_file) or
not self.check_bytes_and_report(s, page, p, 'Poison',
p + s.object_size - 1, POISON_END, 1, out_file))):
return
# check_pad_bytes cleans up on its own.
self.check_pad_bytes(s, page, p, out_file)
# since slabs are relatively "packed", caching has a large
# performance benefit
def read_byte_array(self, addr, size):
page_addr = addr & -0x1000
end_page_addr = (addr + size) & -0x1000
# in cache
if page_addr == end_page_addr and page_addr == self.cache_addr:
idx = addr - self.cache_addr
return self.cache[idx:idx + size]
# accessing only one page
elif page_addr == end_page_addr:
fmtstr = '<{}B'.format(self.ramdump.get_page_size())
self.cache = self.ramdump.read_string(page_addr, fmtstr)
self.cache_addr = page_addr
idx = addr - self.cache_addr
return self.cache[idx:idx + size]
else:
fmtstr = '<{}B'.format(size)
return self.ramdump.read_string(addr, fmtstr)
def parse(self):
self.cache = None
self.cache_addr = None
slab_out = self.ramdump.open_file('slabpoison.txt')
self.slabs_object_out = self.ramdump.open_file('slabpoison_object.txt')
self.validate_slab_cache(slab_out, None, self.print_check_poison)
print_out_str('---wrote slab information to slabpoison.txt')
self.slabs_object_out.close()
slab_out.close()

View File

@@ -0,0 +1,170 @@
# Copyright (c) 2012-2018, 2020, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024, 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 math
import operator
from mm import pfn_to_page, get_pfn_range
from parser_util import register_parser, RamParser
# /kernel/msm-4.4/mm/slub.c
OO_SHIFT = 16
OO_MASK = (1 << OO_SHIFT) - 1
@register_parser('--slabsummary', 'print summary of slab', optional=True)
class Slabinfo_summary(RamParser):
def cal_free_pages(
self, ramdump,
start, slab_lru_offset,
max_page, cpu):
totalfree = 0
page = self.ramdump.read_word(start)
if page == 0:
return totalfree
seen = []
count = 0
total_objects = 0
inuse = 0
while page != start:
if page is None:
return totalfree
#iterater partial slab may reach to NULL
if page == 0:
return totalfree
if page in seen:
return totalfree
if page > max_page:
return totalfree
seen.append(page)
#c->slab page->insue always equal to page->objects, need NOT to consider
#c->partial directly points to struct page(slab)
if cpu == False:
page = page - slab_lru_offset
if (self.ramdump.kernel_version <= (4, 14)):
count = self.ramdump.read_structure_field(
page, 'struct page', '_mapcount')
elif (self.ramdump.kernel_version <= (5, 17)):
count = self.ramdump.read_structure_field(
page, 'struct page', 'counters')
else:
#struct slab are pulled out from struct page
count = self.ramdump.read_structure_field(
page, 'struct slab', 'counters')
if not(count):
count = 0
inuse = count & 0x0000FFFF
total_objects = (count >> 16) & 0x00007FFF
freeobj = total_objects - inuse
totalfree = totalfree + freeobj
page = self.ramdump.read_word(page + slab_lru_offset)
return totalfree
# Currently with assumption there is only one numa node
def print_slab_summary(self, slab_out):
total_freeobjects = 0
slab_summary = {}
nCounter = 0
original_slab = self.ramdump.address_of('slab_caches')
cpus = self.ramdump.get_num_cpus()
slab_list_offset = self.ramdump.field_offset(
'struct kmem_cache', 'list')
slab_name_offset = self.ramdump.field_offset(
'struct kmem_cache', 'name')
slab_node_offset = self.ramdump.field_offset(
'struct kmem_cache', 'node')
cpu_partial_offset = self.ramdump.field_offset(
'struct kmem_cache_cpu', 'partial')
cpu_slab_offset = self.ramdump.field_offset(
'struct kmem_cache', 'cpu_slab')
slab_partial_offset = self.ramdump.field_offset(
'struct kmem_cache_node', 'partial')
slab = self.ramdump.read_word(original_slab)
slab_lru_offset = self.ramdump.field_offset(
'struct page', 'lru')
pfn_range = get_pfn_range(self.ramdump)
max_page = pfn_to_page(self.ramdump, pfn_range['max'])
format_string = '\n{0:35} {1:9} {2:10} {3:10} {4:10} {5:8}K {6:8}' \
' {7:10}K'
slab_out.write(
'{0:37} {1:12} {2:8} {3:10} {4:8} {5:12} {6:10} {7:10}'.format(
"NAME", "OBJSIZE", "SIZE","ALLOCATED",
"TOTAL", "TOTAL*SIZE", "SLABS",
"SSIZE"))
while slab != original_slab:
total_freeobjects = 0
slab = slab - slab_list_offset
slab_name_addr = self.ramdump.read_word(
slab + slab_name_offset)
# actually an array but again, no numa
slab_node_addr = self.ramdump.read_word(
slab + slab_node_offset)
slab_name = self.ramdump.read_cstring(
slab_name_addr, 48)
cpu_slab_addr = self.ramdump.read_word(
slab + cpu_slab_offset)
oo = self.ramdump.read_structure_field(
slab, 'struct kmem_cache', 'oo')
obj_size = self.ramdump.read_structure_field(
slab, 'struct kmem_cache', 'object_size')
objsize_w_metadata = self.ramdump.read_structure_field(
slab, 'struct kmem_cache', 'size')
nr_total_objects = self.ramdump.read_structure_field(
slab_node_addr,
'struct kmem_cache_node', 'total_objects')
num_slabs = self.ramdump.read_structure_field(
slab_node_addr,
'struct kmem_cache_node', 'nr_slabs')
# per cpu slab
for i in self.ramdump.iter_cpus():
cpu_slabn_addr = cpu_slab_addr + self.ramdump.per_cpu_offset(i)
if cpu_slabn_addr == 0 or cpu_slabn_addr is None:
break
total_freeobjects = total_freeobjects + self.cal_free_pages(
self.ramdump,
(cpu_slabn_addr + cpu_partial_offset),
slab_lru_offset,
max_page, True)
total_freeobjects = total_freeobjects + self.cal_free_pages(
self.ramdump,
slab_node_addr + slab_partial_offset,
slab_lru_offset, max_page, False)
total_allocated = nr_total_objects - total_freeobjects
page_order = (oo >> OO_SHIFT) & OO_MASK
slab_size = int(math.pow(2, page_order + self.ramdump.page_shift))
slab_size = slab_size // 1024
slab = self.ramdump.read_word(slab + slab_list_offset)
slab_summary[nCounter] = [
slab_name, obj_size, objsize_w_metadata,
total_allocated, nr_total_objects,
(objsize_w_metadata * nr_total_objects) // 1024,
num_slabs, slab_size]
nCounter += 1
sorted_summary = sorted(slab_summary.values(),
key=operator.itemgetter(5), reverse=True)
total_slab_object_size = 0
total_slab_page_size = 0
for val in sorted_summary:
total_slab_object_size += val[5]
total_slab_page_size += (val[6] * val[7])
slab_out.write(format_string.format(
val[0], val[1], val[2], val[3], val[4],
val[5], val[6], val[7]))
slab_out.write("\n\nTotal object size: {} KB\n".format(total_slab_object_size))
slab_out.write("Total page size: {} KB\n".format(total_slab_page_size))
return
def parse(self):
slab_out = self.ramdump.open_file('slabsummary.txt')
self.print_slab_summary(slab_out)
slab_out.close()

View File

@@ -0,0 +1,524 @@
# Copyright (c) 2021, The Linux Foundation. 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.
from parser_util import register_parser, RamParser
import string, os, re, print_out, dmesglib
@register_parser('--smmu-s1-fault', 'Parse SMMU Stage 1 context fault')
class SmmuParser(RamParser):
# Flag if the system did not have time to print hard/soft iova-to-phys on dmesg
__no_both_iova = True
# Global parsing metadata
__soft_iova_2_pa = ""
__hard_iova_2_pa = ""
__ioval_mismatch = False
__permission_fault = False
__atos_message = False
__global_text = ""
# Extract data and call the parser
def parse(self):
data = self.read_smmu_fault_data()
parsed_text = self.__parse_log(data)
# Write output to a file
file_name = "smmu_s1_fault.txt"
file = open(file_name,"w")
file.write(parsed_text)
file.close()
# Parse each line, add debugging advice
def __parse_log(self, text):
parsed_text = "\n---\n"
self.__global_text = parsed_text
# Call the SMMU parser
parsed_text = self.parse_lines(parsed_text, text)
# In case we replaced variables from previous lines
if parsed_text != self.__global_text:
parsed_text = self.__global_text
# Make it clear that the parser ran even if we did not find any SMMU S1 fault
if parsed_text.find("arm-smmu") == -1:
return "No SMMU S1 fault detected\n"
parsed_text = parsed_text + "---"
# Include debugging advice
parsed_text = parsed_text + "---\nDEBUGGING ADVICE:\n"
parsed_text = parsed_text + self.offer_advice()
return parsed_text
def extract_smmu_log_lines(self, lines):
aux = []
for line in lines:
if line.find("arm-smmu ") != -1:
aux.append(line)
return aux
def read_smmu_fault_data(self):
file_name = "log_scratch.txt"
scratch_file = open(file_name,"w+")
dmesglib.DmesgLib(self.ramdump, scratch_file).extract_dmesg()
scratch_file.seek(0,0)
dmesg_lines = scratch_file.readlines()
scratch_file.close()
os.remove(file_name)
smmu_log_lines = self.extract_smmu_log_lines(dmesg_lines)
return smmu_log_lines
# Parse Fault Address Register (FAR)
# Bits[63:N] Reserved
# FADDR, bits[N-1:0]
def parse_FAR(self, line):
far_line = "Faulting I/O Virtual Address (SMMU_CBn_FAR):"
far_line = far_line + line[line.index('=') + 1:]
return far_line
# Invert string
def reverse_binary(self, string):
return string[::-1]
# Translate the hexadecimal string to binary
def hex_to_binary(self, hexadecimal):
fill = 64
if (len(hexadecimal[hexadecimal.index('x') + 1:]) < 16):
fill = 32
zeroes = "0" * fill
# Sanitize input
original = hexadecimal
hexadecimal = hexadecimal[hexadecimal.index('x') + 1:]
if hexadecimal.find(' ') != -1:
hexadecimal = hexadecimal[:hexadecimal.index(' ')]
if self.all_zero(hexadecimal) is True:
binary = zeroes
else:
try:
binary = bin(int(hexadecimal, 16))[2:].zfill(fill)
# If we want the rest of the code to access the array using the same indexes
# provided in the documentation, we need to reverse this. For example, with
# 0x4 we get 100, and if the documentation refers to bit 0 it actually means
# (100)[2]
binary = self.reverse_binary(binary)
except Exception as e:
print("Error:" + original + "(" + hexadecimal + ") couldn't convert, assuming 0x0")
binary = zeroes
return binary
# Check value of certain position in the binary
def evaluate(self, binary, position):
string = "NO"
# Little Endian
if binary[position] == '1':
string = "YES"
return string
# Make output pretty
def pretty_align(self, line, length, size):
aux = line
for i in range(length,size):
aux = aux + " "
return aux
# Format line with a question and the evaluated answer
def question(self, question, bin, position, line=""):
line = "\n | " + question
# Make output pretty
line = self.pretty_align(line, len(question), 60)
value = self.evaluate(bin, position)
line = line + value
return line
# Last two arguments are optional, pass only when needed
def multi_question(self, question, bin, start, end, format):
line = "\n | " + question
val = bin[start:end + 1]
# Make output pretty
line = self.pretty_align(line,len(question), 60)
val = val[0 : (end - start + 1)]
val = self.reverse_binary(val)
# Check all the options in the dictionary
i = 0
for key in format:
if val == key:
line = line + format[key]
return line
i = i + 1
return "multi_question() couldn't find format for " + val
# Parse Fault Status Register (FSR), which contains information on the address fault type
def parse_FSR(self, line):
fsr_line = "Fault Status Register (SMMU_CBn_FSR):"
value = line[line.index('=') + 1:]
bin = self.hex_to_binary(value)
fsr_line = fsr_line + value
pos = 0
faults = [
"Reserved",
"Translation Fault",
"Access flag fault",
"Permission fault",
"External fault",
"TLB match conflict fault",
"TLB Lock Fault",
"Incorrect address size",
"Unsupported upstream transaction",
"Error finding fault on parse_FSR()" # Keep this option last
]
format_bits_dict = {
'00' :'AArch32 Short-descriptor scheme',
'01' :'AArch32 Long-descriptor translation scheme',
'10' :'AArch64 translation scheme',
'11' : 'AArch64 translation scheme'
}
fsr_line = fsr_line + self.question("Multiple faults when FSR was nonzero?", bin, 31)
fsr_line = fsr_line + self.question("Was the context stalled?", bin, 30)
fsr_line = fsr_line + self.multi_question("Translation scheme:", bin, 9, 10, format_bits_dict)
#The following faults are mutually exclusive so let us reduce the output
fsr_line = fsr_line + "\n | Fault type: "
fault_idx = bin[0:8].find('1')
if fault_idx == -1:
# Index to the error message of the array
print("Error finding fault on parse_FSR()")
fault_idx = len(faults) -1
aux = faults[fault_idx]
if fault_idx == 3:
self.__permission_fault = True
fsr_line = self.pretty_align(fsr_line, len("\n | Fault type: ") + 1, 65)
fsr_line = fsr_line + aux + "\n"
return fsr_line
# Parse FSYNR0, which holds fault syndrome information about the memory access that
# caused a synchronous abort exception.
def parse_FSYNR0(self, line):
FSYNR0_line = "Fault Syndrome Register 0 (SMMU_CBn_FSYNR0):"
value = line[line.index('=') + 1:]
bin = self.hex_to_binary(value)
aux = "\n | Type of fault:"
size = 64
FSYNR0_line = FSYNR0_line + value
format_bits_dict = {
'00' : 'Level 0, SMMUv2 only',
'01' : 'Level 1',
'10' : 'Level 2',
'11' : 'Level 3'
}
FSYNR0_line = FSYNR0_line + self.question("Was a fault recorded synchronously?", bin, 11)
FSYNR0_line = FSYNR0_line + self.question("Was there a Page Table Walk Fault?", bin, 10)
FSYNR0_line = FSYNR0_line + aux
# Make output pretty
FSYNR0_line = self.pretty_align(FSYNR0_line, len(aux), size)
if self.evaluate(bin, 4) == "YES":
FSYNR0_line = FSYNR0_line + "Write Fault"
else:
FSYNR0_line = FSYNR0_line + "Read Fault"
FSYNR0_line = FSYNR0_line + self.multi_question("Translation Table Level:", bin, 0, 1, format_bits_dict)
return FSYNR0_line + "\n"
# Parse context bank
def parse_context_bank(self, line):
context_bank_line = "Context bank:"
context_bank_line = context_bank_line + line[line.index('=') + 1:]
return context_bank_line
# Parse SCTLR, which provides top-level control of the translation system for the
# related translation context bank.
def parse_SCTLR(self, line):
SCTLR_line = "System Control Register (SMMU_CBn_SCTLR):"
value = line[line.index('=') + 1:]
bin = self.hex_to_binary(value)
value = value.replace(" ar","")
SCTLR_line = SCTLR_line + value
# The Write/Read allocate and Shared configurations are overwritten by the S1 page
# tables so their values provide no useful information.
SCTLR_line = SCTLR_line + self.question("Force Broadcast of TLB maintenance?", bin, 21)
SCTLR_line = SCTLR_line + self.question("Protected Translation Walk enabled?", bin, 13)
SCTLR_line = SCTLR_line + self.question("Stall instead of terminate transaction?", bin, 7)
SCTLR_line = SCTLR_line + self.question("Is Context Fault Interrupt enabled?", bin, 6)
SCTLR_line = SCTLR_line + self.question("Is Context Fault Report enabled?", bin, 5)
SCTLR_line = SCTLR_line + self.question("Big Endian translation table entries?", bin, 4)
SCTLR_line = SCTLR_line + self.question("Is TEX Remap enabled?", bin, 1)
SCTLR_line = SCTLR_line + "\n"
return SCTLR_line
# Parse CBAR
def parse_CBAR(self, line):
CBAR_line = "Context Bank Attribute Registers (SMMU_CBAR):"
CBAR_line = CBAR_line + line[line.index('=') + 1:]
return CBAR_line
# Parse MAIR0
def parse_MAIR0(self, line):
MAIR0_line = "Memory Attribute Indirection Registers 0 (SMMU_CBn_MAIR0):"
MAIR0_line = MAIR0_line + line[line.index('=') + 1:]
return MAIR0_line
# Parse SID
def parse_SID(self, line):
SID_line = "Context Bank Fault Restricted Syndrome Register (SMMU_CBFRSYNRA):"
SID_val = line[line.index('=') + 1:]
SID_line = SID_line + SID_val
SID_val = SID_val.replace(" ","")
SID_val = " ".join(SID_val.split()).strip()
SID_val = SID_val[:5]
self.__global_text = self.__global_text.replace("SID=?", "SID=" + SID_val)
return SID_line
# Parse Client info
def parse_client_info(self, line):
Client_info_line = ""
left = line[line.index(':') + 1:]
BID_val = left[left.index("=") + 1: left.index(",")]
PID_val = left[left.index(",") + 1: left.rfind("=") - 3]
MID_val = left[left.rfind("=") + 1: len(left)]
PID_val = PID_val.replace(" ","")
self.__global_text = self.__global_text.replace("BID=?","BID=" + BID_val)
self.__global_text = self.__global_text.replace("PID=?",PID_val)
self.__global_text = self.__global_text.replace("MID=?","MID=" + MID_val)
self.__global_text = self.__global_text.replace(", ,", ',')
self.__global_text = self.__global_text.replace(",,", ',')
return Client_info_line
# Parse soft iova-to-phys
def parse_soft_iova_to_phy(self, line):
val = line[line.index('=') + 1:]
soft_iova_to_phy_line = "Software table walk result: "
soft_iova_to_phy_line = soft_iova_to_phy_line + val
self.__soft_iova_2_pa = val
return soft_iova_to_phy_line
# Parse hard iova-to-phys
def parse_hard_iova_to_phy(self, line):
hard_iova_to_phy_line = "SMMU table walk result: "
val = line[line.index('=') + 1:]
self.__hard_iova_2_pa = val
self.__no_both_iova = False
# Do the software and HW translations of the IOVA match?
if self.__hard_iova_2_pa != self.__soft_iova_2_pa :
self.__ioval_mismatch = True
hard_iova_to_phy_line = hard_iova_to_phy_line + val
return hard_iova_to_phy_line
def parse_line(self, line):
new_line = ""
# Is this a completely unrelated debug message?
if line.find("arm-smmu") == -1:
self.__global_text = self.__global_text + new_line
return new_line
# Remove unnecessary left-side stuff such as the dmesg print time
line = line[line.index(':') + 2:]
# A switch statement would require a single variable against a range of constant values,
# so the correct logic here -even if utterly ugly- is an if/else.
if line.find("Unhandled") != -1:
new_line = line + " \nClient information: SID=?,BID=?,PID=?,MID=?"
elif line.find("FAR") != -1:
new_line = self.parse_FAR(line)
elif line.find("FSR") != -1:
new_line = self.parse_FSR(line)
elif line.find("FSYNR0") != -1:
new_line = self.parse_FSYNR0(line)
elif line.find("context bank") != -1:
new_line = self.parse_context_bank(line)
elif line.find("SCTLR") != -1:
new_line = self.parse_SCTLR(line)
elif line.find("CBAR") != -1:
new_line = self.parse_CBAR(line)
elif line.find("MAIR0") != -1:
new_line = self.parse_MAIR0(line)
elif line.find("SID") != -1:
new_line = self.parse_SID(line)
if new_line == "":
return new_line
elif line.find("Client info") != -1:
new_line = self.parse_client_info(line)
return ""
elif line.find("soft iova-to-phys") != -1:
new_line = self.parse_soft_iova_to_phy(line)
elif line.find("hard iova-to-phys") != -1:
new_line = self.parse_hard_iova_to_phy(line)
elif line.find("ATOS") != -1:
self.__atos_message = True
return ""
else:
other_regs = ["PAR","TTBR0","TTBR1","FSYNR1"]
for reg in other_regs:
if line.find(reg) != -1:
return ""
new_line = "Unknown message disregarded, line says: " + line
print(new_line)
self.__global_text = self.__global_text + new_line + "\n"
return new_line
def parse_lines(self, parsed_text, lines):
extra_text = parsed_text
for line in lines:
extra_text = extra_text + self.parse_line(line)
return extra_text
# Check if we have all zeroes in the value string
def all_zero(self, str):
ret = True
if isinstance(str, int) is True:
if str == 0:
ret = False
else:
for i in range(0, len(str)):
if (str[i] != '0' and not (i == 1 and str[i] == 'x')):
ret = False
break
return ret
# Offer debugging advice based on the global variables obtained parsing
def offer_advice(self):
advice = ""
io_mismatch = self.__ioval_mismatch
soft = self.__soft_iova_2_pa
permission = self.__permission_fault
atos = self.__atos_message
advice1 = ("The hardware and software translations of the I/O virtual address do not match,"
" this is an indication of a race condition. It is likely that you are trying"
" to access a memory resource while unmapping it elsewhere.")
advice2 = ("The hardware and software translations of the I/O virtual address match but "
"are zero, this is an indication of an access to an unmapped value.")
advice3 = ("The hardware and software translations of the I/O virtual address match"
" and are meaningful. A possible explanation is that between the"
" time the context fault was triggered -when hardware tried to "
"access a deallocated resource- and the handler started, you mapped"
" other buffer that happened to have the same IO virtual address.")
advice4 = ("There has been a permission fault (and we have meaningful and equal"
" I/O translations). To determine if this is a legitimate permission"
" fault, try to replicate the error mapping the buffers as privileged. Perhaps"
" you are mapping with the wrong permission flags?")
advice5 = ("If you repeat the experiment with privileged buffer after flushing the"
"TLB (iommu_flush_iotlb_all) and it succeeds, it is likely that"
" you are facing a Stale TLB entry. If so, contact the kernel team for support.")
advice6 = ("No accurate debugging advice can be given without knowing if the "
"hardware and software translations match.")
if self.__no_both_iova is True:
advice = advice6
elif io_mismatch:
advice = advice1
else:
if soft == "0x0" or soft == "0x0000000000000000" or self.all_zero(soft) is True:
advice = advice2
else:
if permission is False:
advice = advice3
else:
if atos is False:
advice = advice4
else:
advice = advice5
advice = advice + "\n\n"
return advice

View File

@@ -0,0 +1,111 @@
# Copyright (c) 2015, The Linux Foundation. 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 linux_list
from parser_util import register_parser, RamParser
@register_parser('--spm', 'Parse SPM Driver info')
class spm(RamParser):
def __init__(self, *args):
super(spm, self).__init__(*args)
self.head = ''
self.output = []
self.spm_shadow_reg = ('MSM_SPM_REG_SAW2_CFG',
'MSM_SPM_REG_SAW2_AVS_CTL',
'MSM_SPM_REG_SAW2_AVS_HYSTERESIS',
'MSM_SPM_REG_SAW2_SPM_CTL',
'MSM_SPM_REG_SAW2_PMIC_DLY',
'MSM_SPM_REG_SAW2_AVS_LIMIT',
'MSM_SPM_REG_SAW2_AVS_DLY',
'MSM_SPM_REG_SAW2_SPM_DLY',
'MSM_SPM_REG_SAW2_PMIC_DATA_0',
'MSM_SPM_REG_SAW2_PMIC_DATA_1',
'MSM_SPM_REG_SAW2_PMIC_DATA_2',
'MSM_SPM_REG_SAW2_PMIC_DATA_3',
'MSM_SPM_REG_SAW2_PMIC_DATA_4',
'MSM_SPM_REG_SAW2_PMIC_DATA_5',
'MSM_SPM_REG_SAW2_PMIC_DATA_6',
'MSM_SPM_REG_SAW2_PMIC_DATA_7',
'MSM_SPM_REG_SAW2_RST',
'MSM_SPM_REG_SAW2_ID',
'MSM_SPM_REG_SAW2_SECURE',
'MSM_SPM_REG_SAW2_STS0',
'MSM_SPM_REG_SAW2_STS1',
'MSM_SPM_REG_SAW2_STS2',
'MSM_SPM_REG_SAW2_VCTL',
'MSM_SPM_REG_SAW2_SEQ_ENTRY',
'MSM_SPM_REG_SAW2_SPM_STS',
'MSM_SPM_REG_SAW2_AVS_STS',
'MSM_SPM_REG_SAW2_PMIC_STS',
'MSM_SPM_REG_SAW2_VERSION')
def spm_walker(self, spm):
if spm == self.head:
return
offset = self.ramdump.field_offset('struct msm_spm_device', 'initialized')
if self.ramdump.read_bool(spm + offset) is False:
return
offset = self.ramdump.field_offset('struct msm_spm_device', 'name')
name = self.ramdump.read_cstring(self.ramdump.read_word(spm + offset, True), 48)
self.output.append("{:35}:{}\n".format("SPM Device Name", name))
offset = self.ramdump.field_offset('struct msm_spm_device', 'reg_data')
reg_data = spm + offset
offset = self.ramdump.field_offset('struct msm_spm_driver_data', 'major')
addr = reg_data + offset
major = self.ramdump.read_int(addr)
self.output.append("{:35}:{}".format("version", major))
offset = self.ramdump.field_offset('struct msm_spm_driver_data', 'minor')
addr = reg_data + offset
minor = self.ramdump.read_int(addr)
self.output.append(".{}\n".format(minor))
self.output.append("\n{}\n".format("Shadow Registers"))
self.output.append("{}{}".format("-" * 20, "\n"))
offset = self.ramdump.field_offset('struct msm_spm_driver_data', 'reg_shadow')
for i in range(len(self.spm_shadow_reg)):
addr = reg_data + offset + i * self.ramdump.sizeof('uint32_t')
val = self.ramdump.read_int(addr)
self.output.append("{:35}:{}\n".format(self.spm_shadow_reg[i], hex(val).rstrip("L")))
self.output.append("{}{}".format("-" * 81, "\n\n"))
def get_spm(self):
lpm_root_node = self.ramdump.read_word(
self.ramdump.address_of('lpm_root_node'), True)
if lpm_root_node is None:
self.output_file.write("NOTE: 'lpm_root_node' not found\n")
return
offset = self.ramdump.field_offset('struct lpm_cluster', 'lpm_dev')
lpm_dev = self.ramdump.read_word(lpm_root_node + offset, True)
offset = self.ramdump.field_offset('struct low_power_ops', 'spm')
spm = self.ramdump.read_word(lpm_dev + offset, True)
self.head = lpm_dev + offset
offset = self.ramdump.field_offset('struct msm_spm_device', 'list')
spm_walker = linux_list.ListWalker(self.ramdump, spm, offset)
spm_walker.walk(spm, self.spm_walker)
def parse(self):
self.output_file = self.ramdump.open_file('spm.txt')
self.get_spm()
for i in self.output:
self.output_file.write(i)
self.output_file.close()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,631 @@
# Copyright (c) 2012-2013, 2015, 2017-2020,2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 string
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
taskhighlight_out = None
highlight_tasks = "\n=====List of all runing and uninterruptable sleep process====\n"
panic_task_list = []
task_per_cpu_list = []
error= 0
import ctypes
def find_panic(ramdump, addr_stack, thread_task_name):
if ramdump.arm64:
stack_size = 0x4000
increment = 8
else:
stack_size = 0x2000
increment = 4
for i in range(addr_stack, addr_stack + stack_size, increment):
if ramdump.arm64:
pc = ramdump.read_word(i + 8) - 4
fp = ramdump.read_word(i)
spx = i + 16
lr = 0
else:
pc = ramdump.read_word(i)
lr = ramdump.read_word(i + 4)
spx = i + 4
fp = 0
l = ramdump.unwind_lookup(pc)
if l is not None:
s, offset = l
if s == 'panic':
print_out_str('Faulting process found! Name {0})'.format(thread_task_name))
ramdump.unwind.unwind_backtrace(spx, fp, pc, lr, '')
regspanic = ramdump.open_file('regs_panic.cmm')
if ramdump.arm64:
regspanic.write('r.s pc 0x{0:x}\n'.format(pc))
regspanic.write('r.s sp 0x{0:x}\n'.format(spx))
else:
regspanic.write('r.s pc 0x{0:x}\n'.format(pc))
regspanic.write('r.s r13 0x{0:x}\n'.format(i + 4))
regspanic.close()
return True
return False
#Definitions taken from include/linux/sched.h
TASK_REPORT = 0xff
TASK_REPORT_LATEST = 0x7f
task_state_short_name = "RSDTtXZPI"
def find_last_bit_index(task_state,task_state_exit,version):
if version >= (4, 14, 0):
count = (task_state | task_state_exit) & TASK_REPORT_LATEST
else:
count = task_state & TASK_REPORT
index = count.bit_length()
return index
def task_state_to_char(task_state,task_exit_state,version):
index = find_last_bit_index(task_state,task_exit_state,version)
task_state_str = task_state_short_name[index]
return task_state_str
def dump_thread_group(ramdump, task_addr, task_out, taskhighlight_out, check_for_panic=0):
global highlight_tasks
global panic_task_list
global task_per_cpu_list
global error
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
offset_pid = ramdump.field_offset('struct task_struct', 'pid')
offset_stack = ramdump.field_offset('struct task_struct', 'stack')
if ramdump.kernel_version >= (5, 14, 0):
offset_state = ramdump.field_offset('struct task_struct', '__state')
else:
offset_state = ramdump.field_offset('struct task_struct', 'state')
offset_prio = ramdump.field_offset('struct task_struct', 'prio')
if ramdump.is_config_defined('CONFIG_SMP'):
offset_oncpu = ramdump.field_offset('struct task_struct', 'on_cpu')
if ramdump.sizeof('struct sched_info') != 0:
offset_schedinfo = ramdump.field_offset('struct task_struct', 'sched_info')
offset_last_queued = offset_schedinfo + ramdump.field_offset('struct sched_info', 'last_queued')
offset_last_rundelay = offset_schedinfo + ramdump.field_offset('struct sched_info', 'run_delay')
offset_last_pcount = offset_schedinfo + ramdump.field_offset('struct sched_info', 'pcount')
offset_last_arrival = offset_schedinfo + ramdump.field_offset('struct sched_info', 'last_arrival')
else:
offset_schedinfo = None
offset_last_enqueued_ts = ramdump.field_offset('struct task_struct', 'last_enqueued_ts')
offset_last_sleep_ts = ramdump.field_offset('struct task_struct', 'last_sleep_ts')
if ramdump.kernel_version > (5, 2, 0):
offset_affine = ramdump.field_offset('struct task_struct', 'cpus_mask')
else:
offset_affine = ramdump.field_offset('struct task_struct', 'cpus_allowed')
offset_exit_state = ramdump.field_offset(
'struct task_struct', 'exit_state')
first = 0
task_on_cpu = 0
for next_thread_start in ramdump.for_each_thread(task_addr):
next_thread_comm = next_thread_start + offset_comm
next_thread_pid = next_thread_start + offset_pid
next_thread_prio = next_thread_start + offset_prio
if offset_schedinfo is not None:
next_thread_last_arrival = next_thread_start + offset_last_arrival
next_thread_last_queued = next_thread_start + offset_last_queued
next_thread_run_delay = next_thread_start + offset_last_rundelay
next_thread_pcount = next_thread_start + offset_last_pcount
next_thread_stack = next_thread_start + offset_stack
next_thread_state = next_thread_start + offset_state
next_thread_exit_state = next_thread_start + offset_exit_state
next_thread_affine = next_thread_start + offset_affine
if ramdump.is_config_defined('CONFIG_SMP'):
next_thread_oncpu = next_thread_start + offset_oncpu
task_on_cpu = ramdump.read_int(next_thread_oncpu)
next_thread_info = ramdump.get_thread_info_addr(next_thread_start)
thread_task_name = cleanupString(
ramdump.read_cstring(next_thread_comm, 16))
if thread_task_name is None or thread_task_name == "":
return
thread_task_prio = ramdump.read_int(next_thread_prio)
if thread_task_prio is None:
error = 1
return
# Task prio is an integer and it can be -1 for DL tasks.
thread_task_prio = ctypes.c_int(thread_task_prio).value
thread_task_pid = ramdump.read_int(next_thread_pid)
if thread_task_pid is None:
error = 1
return
thread_task_affine = ramdump.read_u64(next_thread_affine)
if thread_task_affine is None:
return
task_state = ramdump.read_word(next_thread_state)
if task_state is None:
error = 1
return
task_exit_state = ramdump.read_int(next_thread_exit_state)
if task_exit_state is None:
return
task_state_str = task_state_to_char(task_state,task_exit_state,ramdump.kernel_version)
addr_stack = ramdump.read_word(next_thread_stack)
if addr_stack is None:
error = 1
return
threadinfo = next_thread_info
if threadinfo is None:
error = 1
return
task_last_enqueued_ts = 0
task_last_sleep_ts = 0
if offset_last_enqueued_ts is None and (ramdump.is_config_defined('CONFIG_SCHED_WALT') or 'sched_walt' in ramdump.ko_file_names):
try:
offset_last_enqueued_ts = ramdump.field_offset('struct walt_task_struct', 'last_enqueued_ts')
if (ramdump.kernel_version >= (5, 10, 0)):
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'android_vendor_data1')
else:
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'wts')
offset_last_enqueued_ts = offset_last_enqueued_ts + walt_task_struct_offset
except:
pass
if offset_last_enqueued_ts:
next_thread_last_enqueued = next_thread_start + offset_last_enqueued_ts
task_last_enqueued_ts = ramdump.read_u64(next_thread_last_enqueued)
if task_last_enqueued_ts is None:
task_last_enqueued_ts = 0
if offset_last_sleep_ts is None and (ramdump.is_config_defined('CONFIG_SCHED_WALT') or 'sched_walt' in ramdump.ko_file_names):
try:
offset_last_sleep_ts = ramdump.field_offset('struct walt_task_struct', 'last_sleep_ts ')
if (ramdump.kernel_version >= (5, 10, 0)):
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'android_vendor_data1')
else:
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'wts')
offset_last_sleep_ts = offset_last_sleep_ts + walt_task_struct_offset
except:
pass
if offset_last_sleep_ts:
next_thread_last_sleep_ts = next_thread_start + offset_last_sleep_ts
task_last_sleep_ts = ramdump.read_u64(next_thread_last_sleep_ts)
if task_last_sleep_ts is None:
task_last_sleep_ts = 0
if not check_for_panic:
if task_state == 0:
panic_task_list.append([addr_stack, thread_task_name])
task_cpu = ramdump.get_task_cpu(next_thread_start, threadinfo)
#thread_line = thread_task_pid + task_cpu + task_state_str+ next_thread_start+thread_task_name
thread_line = "PID %6d cpu %1d state %16s hex 0x%06x start 0x%x comm %32s\n" %(thread_task_pid, task_cpu,
task_state_str, task_state, next_thread_start, thread_task_name)
if task_state != 1:
if not first:
highlight_tasks += "*" + thread_line
taskhighlight_out.write("**"+thread_line)
else:
highlight_tasks += " " + thread_line
taskhighlight_out.write(" " + thread_line)
if task_on_cpu == 1:
taskhighlight_out.write("Task currently running on CPU. Please check dmesg_tz for callstack")
else:
ramdump.unwind.unwind_backtrace(
ramdump.thread_saved_sp(next_thread_start),
ramdump.thread_saved_fp(next_thread_start),
ramdump.thread_saved_pc(next_thread_start),
0, ' ', taskhighlight_out)
thread_line = '+' + thread_line
else:
thread_line = ' ' + thread_line
if not first:
task_out.write('Process: {0}, [affinity: 0x{1:x}] cpu: {2} pid: {3} start: 0x{4:x}\n'.format(
thread_task_name, thread_task_affine, task_cpu, thread_task_pid, next_thread_start))
task_out.write(
'=====================================================\n')
first = 1
task_out.write(' Task name: {0} [affinity: 0x{11:x}] pid: {1} cpu: {2} prio: {7} start: {'
'6:x}\n state: 0x{3:x}[{8}] exit_state: 0x{4:x}'
' stack base: 0x{5:x}\n'
' Last_enqueued_ts:{9:18.9f} Last_sleep_ts:{10:18.9f}\n'.format(
thread_task_name, thread_task_pid, task_cpu, task_state,
task_exit_state, addr_stack, next_thread_start, thread_task_prio, task_state_str,
task_last_enqueued_ts/1000000000.0, task_last_sleep_ts/1000000000.0,thread_task_affine))
if task_on_cpu == 1:
taskhighlight_out.write("Task currently running on CPU. Please check dmesg_tz for callstack")
else:
task_out.write(' Stack:\n')
ramdump.unwind.unwind_backtrace(
ramdump.thread_saved_sp(next_thread_start),
ramdump.thread_saved_fp(next_thread_start),
ramdump.thread_saved_pc(next_thread_start),
0, ' ', task_out)
task_out.write(
'=======================================================\n')
cpu_no = ramdump.get_task_cpu(next_thread_start, threadinfo)
if cpu_no not in ramdump.iter_cpus():
error = 1
return
if offset_schedinfo is not None:
thread_last_arrival_val = ramdump.read_u64(next_thread_last_arrival)
thread_last_queued_val = ramdump.read_u64(next_thread_last_queued)
thread_run_delay_val = ramdump.read_u64(next_thread_run_delay)
thread_pcount_val = ramdump.read_word(next_thread_pcount)
else:
thread_last_arrival_val = 0
thread_last_queued_val = 0
thread_run_delay_val = 0
thread_pcount_val = 0
task_per_cpu_list[task_cpu].append([thread_task_name, thread_task_pid,
thread_last_arrival_val,
thread_last_queued_val,
thread_run_delay_val,
thread_pcount_val,
thread_task_prio, task_state_str,
task_last_enqueued_ts,
task_last_sleep_ts])
# Panicking tasks are expected to remain in a TASK_RUNNING state
elif task_state == 0:
find_panic(ramdump, addr_stack, thread_task_name)
def do_dump_stacks(ramdump, check_for_panic=0):
offset_tasks = ramdump.field_offset('struct task_struct', 'tasks')
prev_offset = ramdump.field_offset('struct list_head','prev')
init_addr = ramdump.address_of('init_task')
init_next_task = init_addr + offset_tasks
orig_init_next_task = init_next_task
init_thread_addr = init_addr
seen_tasks = []
global task_per_cpu_list
if check_for_panic == 0:
task_out = ramdump.open_file('tasks.txt')
taskhighlight_out = ramdump.open_file('tasks_highlight.txt')
else:
task_out = None
taskhighlight_out = None
no_of_cpus = ramdump.get_num_cpus()
if len(task_per_cpu_list) == 0:
task_per_cpu_list = [[] for j in range(max(ramdump.iter_cpus())+1)]
while True:
dump_thread_group(ramdump, init_thread_addr,
task_out, taskhighlight_out, check_for_panic)
next_task = ramdump.read_word(init_next_task)
if next_task is None:
init_next_task = init_addr + offset_tasks
init_next_task = init_next_task + prev_offset
init_next_task = ramdump.read_word(init_next_task)
init_thread_addr = init_next_task - offset_tasks
while True:
dump_thread_group(ramdump, init_thread_addr,
task_out, taskhighlight_out, check_for_panic)
init_next_task = init_next_task + prev_offset
orig_init_next_task = init_next_task
next_task = ramdump.read_word(init_next_task)
if next_task is None:
break
if (next_task == init_next_task) and (next_task !=
orig_init_next_task):
if not check_for_panic:
task_out.write(
'!!! Cycle in task list! the list is corrupt!\n')
break
if (next_task in seen_tasks):
break
seen_tasks.append(next_task)
init_next_task = next_task
init_thread_addr = init_next_task - offset_tasks
if init_next_task == orig_init_next_task:
break
task_out.write('\n\n!!!Some task might be missing in task.txt')
break
if (next_task == init_next_task) and (next_task != orig_init_next_task):
if not check_for_panic:
task_out.write(
'!!!! Cycle in task list! The list is corrupt!\n')
break
if (next_task in seen_tasks):
break
seen_tasks.append(next_task)
init_next_task = next_task
init_thread_addr = init_next_task - offset_tasks
if init_next_task == orig_init_next_task:
break
if check_for_panic == 0:
task_out.close()
taskhighlight_out.write(highlight_tasks + "\n")
taskhighlight_out.close()
print_out_str('---wrote tasks to tasks.txt')
def do_dump_task_timestamps(ramdump):
offset_tasks = ramdump.field_offset('struct task_struct', 'tasks')
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
offset_pid = ramdump.field_offset('struct task_struct', 'pid')
init_addr = ramdump.address_of('init_task')
prev_offset = ramdump.field_offset('struct list_head','prev')
init_next_task = init_addr + offset_tasks
orig_init_next_task = init_next_task
count = 0
seen_tasks = []
task_out = {}
global task_per_cpu_list
no_of_cpus = ramdump.get_num_cpus()
for i in ramdump.iter_cpus():
task_file = ramdump.open_file('tasks_sched_stats/' + 'tasks_sched_stats{0}.txt'.format(i))
task_out[i] = task_file
if len(task_per_cpu_list) == 0:
task_per_cpu_list = [[] for j in range(0, max(ramdump.iter_cpus())+1)]
while True:
ret = dump_thread_group_timestamps(ramdump, init_addr)
if ret is False:
count = 1
next_task = ramdump.read_word(init_next_task)
if next_task is None:
init_next_task = init_addr + offset_tasks
init_next_task = init_next_task + prev_offset
init_next_task = ramdump.read_word(init_next_task)
init_task_addr = init_next_task - offset_tasks
while True:
ret = dump_thread_group_timestamps(ramdump,
init_task_addr)
if ret is False:
count = 1
init_next_task = init_next_task + prev_offset
orig_init_next_task = init_next_task
next_task = ramdump.read_word(init_next_task)
next_task = ramdump.read_word(init_next_task)
if next_task is None:
break
if (next_task == init_next_task) and (
next_task != orig_init_next_task):
break
if (next_task in seen_tasks):
break
seen_tasks.append(next_task)
init_next_task = next_task
init_task_addr = init_next_task - offset_tasks
if init_next_task == orig_init_next_task:
break
break
if (next_task == init_next_task) and (next_task != orig_init_next_task):
break
if (next_task in seen_tasks):
break
seen_tasks.append(next_task)
init_next_task = next_task
init_task_addr = init_next_task - offset_tasks
if init_next_task == orig_init_next_task:
break
for i in ramdump.iter_cpus():
if error == 1 or count == 1:
task_out[i].write('!!!Note : Some thread may be missing\n\n')
task_per_cpu_list[i] = sorted(task_per_cpu_list[i], key=lambda l:l[2], reverse=True)
str = '{0:<17s}{1:>8s}{2:>18s}{3:>18s}{4:>18s}{5:>17s}' \
' {6:>8s}{7:>8s}{8:>18s}{9:>18s}{10:>20s}\n'.format(
'Task name', 'PID', 'Exec_Started_at',
'Last_Queued_at', 'Total_wait_time',
'No_of_times_exec', 'Prio', 'State',
'Last_enqueued_ts', 'Last_sleep_ts', 'Last runtime(msec)')
task_out[i].write(str)
for item in task_per_cpu_list[i]:
runtime = 0.0
if (item[8] < item[9]):
runtime = ((item[9]-item[8])/1000000.0)
str = '{0:<17s}{1:8d}{2:18.9f}{3:18.9f}{4:18.9f}{5:17d}{6:8d}{7:>9s}{8:18.9f}{9:18.9f} {10:18.9f}\n'\
.format(
item[0], item[1], item[2]/1000000000.0,
item[3]/1000000000.0, item[4]/1000000000.0,
item[5], item[6], item[7], item[8]/1000000000.0,
item[9]/1000000000.0, runtime)
task_out[i].write(str)
task_out[i].close()
print_out_str('---wrote tasks to tasks_sched_stats{0}.txt'.format(i))
def dump_thread_group_timestamps(ramdump, task_addr):
offset_thread_group = ramdump.field_offset(
'struct task_struct', 'thread_group')
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
offset_pid = ramdump.field_offset('struct task_struct', 'pid')
offset_task = ramdump.field_offset('struct thread_info', 'task')
offset_stack = ramdump.field_offset('struct task_struct', 'stack')
offset_prio = ramdump.field_offset('struct task_struct', 'prio')
if ramdump.sizeof('struct sched_info') != 0:
offset_schedinfo = ramdump.field_offset('struct task_struct', 'sched_info')
offset_last_arrival = offset_schedinfo + ramdump.field_offset('struct sched_info', 'last_arrival')
offset_last_queued = offset_schedinfo + ramdump.field_offset('struct sched_info', 'last_queued')
offset_last_pcount = offset_schedinfo + ramdump.field_offset('struct sched_info', 'pcount')
offset_last_rundelay = offset_schedinfo + ramdump.field_offset('struct sched_info', 'run_delay')
else:
offset_schedinfo = None
if ramdump.kernel_version >= (5, 14, 0):
offset_state = ramdump.field_offset('struct task_struct', '__state')
else:
offset_state = ramdump.field_offset('struct task_struct', 'state')
offset_exit_state = ramdump.field_offset('struct task_struct', 'exit_state')
offset_last_enqueued_ts = ramdump.field_offset('struct task_struct', 'last_enqueued_ts')
offset_last_sleep_ts = ramdump.field_offset('struct task_struct', 'last_sleep_ts')
offset_exit_state = ramdump.field_offset(
'struct task_struct', 'exit_state')
global panic_task_list
global task_per_cpu_list
for next_thread_start in ramdump.for_each_thread(task_addr):
next_thread_comm = next_thread_start + offset_comm
next_thread_pid = next_thread_start + offset_pid
next_thread_prio = next_thread_start + offset_prio
if offset_schedinfo is not None:
next_thread_last_arrival = next_thread_start + offset_last_arrival
next_thread_last_queued = next_thread_start + offset_last_queued
next_thread_pcount = next_thread_start + offset_last_pcount
next_thread_run_delay = next_thread_start + offset_last_rundelay
next_thread_stack = next_thread_start + offset_stack
next_thread_info = ramdump.get_thread_info_addr(next_thread_start)
next_thread_state = next_thread_start + offset_state
next_thread_exit_state = next_thread_start + offset_exit_state
if offset_last_enqueued_ts is None:
thread_last_enqueued_ts = 0
if ramdump.is_config_defined('CONFIG_SCHED_WALT') or 'sched_walt' in ramdump.ko_file_names:
offset_last_enqueued_ts = ramdump.field_offset('struct walt_task_struct', 'last_enqueued_ts')
if (ramdump.kernel_version >= (5, 10, 0)):
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'android_vendor_data1')
else:
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'wts')
offset_last_enqueued_ts = offset_last_enqueued_ts + walt_task_struct_offset
if offset_last_enqueued_ts is not None:
next_thread_last_enqueued_ts = next_thread_start + offset_last_enqueued_ts
thread_last_enqueued_ts = ramdump.read_u64(next_thread_last_enqueued_ts)
if thread_last_enqueued_ts is None:
thread_last_enqueued_ts = 0
if offset_last_sleep_ts is None:
thread_last_sleep_ts = 0
if ramdump.is_config_defined('CONFIG_SCHED_WALT') or 'sched_walt' in ramdump.ko_file_names:
offset_last_sleep_ts = ramdump.field_offset('struct walt_task_struct', 'last_sleep_ts ')
if (ramdump.kernel_version >= (5, 10, 0)):
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'android_vendor_data1')
else:
walt_task_struct_offset = ramdump.field_offset('struct task_struct', 'wts')
offset_last_sleep_ts = offset_last_sleep_ts + walt_task_struct_offset
if offset_last_sleep_ts:
next_thread_last_sleep_ts = next_thread_start + offset_last_sleep_ts
thread_last_sleep_ts = ramdump.read_u64(next_thread_last_sleep_ts)
if thread_last_sleep_ts is None:
thread_last_sleep_ts = 0
addr_stack = ramdump.read_word(next_thread_stack)
if addr_stack is None:
print_out_str('!!!! Task list corruption\n')
return False
threadinfo = next_thread_info
thread_task_name = cleanupString(
ramdump.read_cstring(next_thread_comm, 16))
thread_task_pid = ramdump.read_int(next_thread_pid)
thread_task_prio = ramdump.read_int(next_thread_prio)
if thread_task_prio is None:
return False
thread_task_state = ramdump.read_int(next_thread_state)
if thread_task_state is None:
return False
thread_exit_state = ramdump.read_int(next_thread_exit_state)
if thread_exit_state is None:
return False
thread_task_state_str = task_state_to_char(thread_task_state,thread_exit_state,ramdump.version)
cpu_no = ramdump.get_task_cpu(next_thread_start, threadinfo)
if cpu_no is None:
return False
if cpu_no >= ramdump.get_num_cpus():
return False
if not ramdump.is_thread_info_in_task():
thread_info_task = ramdump.read_word(threadinfo + offset_task)
if next_thread_start != thread_info_task:
print_out_str('!!!! Task list or Thread info corruption\n{0} {1}'.format(next_thread_start,thread_info_task))
return False
if offset_schedinfo is not None:
thread_last_arrival_val = ramdump.read_u64(next_thread_last_arrival)
thread_last_queued_val = ramdump.read_u64(next_thread_last_queued)
thread_run_delay_val = ramdump.read_u64(next_thread_run_delay)
thread_pcount_val = ramdump.read_word(next_thread_pcount)
else:
thread_last_arrival_val = 0
thread_last_queued_val = 0
thread_run_delay_val = 0
thread_pcount_val = 0
task_per_cpu_list[cpu_no].append([thread_task_name, thread_task_pid,
thread_last_arrival_val,
thread_last_queued_val,
thread_run_delay_val,
thread_pcount_val,
thread_task_prio,thread_task_state_str,
thread_last_enqueued_ts,
thread_last_sleep_ts])
if thread_task_state == 0:
panic_task_list.append([addr_stack, thread_task_name])
return True
def do_dump_cmdline(ramdump):
'''
As task_struct->comm[16] stored a small part of process name.
developer can't distinct which process it is.
this method is to dump full cmdline for each user space process
output like:
1365 android.hardwar /vendor/bin/hw/android.hardware.contexthub-service.qmi
1370 android.hardwar /vendor/bin/hw/android.hardware.drm-service.clearkey
'''
from utasklib import UTaskLib
task_out = ramdump.open_file('utask_cmdline.txt')
task_out.write("User space processes:\n\n")
task_out.write("{} {} {}\n".format(
"PID".ljust(10," "),
"COMM".ljust(20," "),
"CMDLINE".ljust(20," ")))
for cmdline_obj in UTaskLib(ramdump).for_each_cmdline():
task_out.write("{} {} {}\n".format(
str(cmdline_obj.pid).ljust(10," "),
cmdline_obj.comm.ljust(20," "),
cmdline_obj.cmdline.ljust(20," ")))
@register_parser('--print-tasks', 'Print all the task information', shortopt='-t')
class DumpTasks(RamParser):
def parse(self):
do_dump_stacks(self.ramdump, 0)
do_dump_cmdline(self.ramdump)
@register_parser('--print-tasks-timestamps', 'Print all the task sched stats per core sorted on arrival time', shortopt='-T')
class DumpTasksTimeStamps(RamParser):
def parse(self):
do_dump_task_timestamps(self.ramdump)
@register_parser('--check-for-panic', 'Check if a kernel panic occured', shortopt='-p')
class CheckForPanic(RamParser):
def parse(self):
global panic_task_list
addr = self.ramdump.address_of('in_panic')
result = self.ramdump.read_word(addr)
if result == 1:
print_out_str('-------------------------------------------------')
print_out_str('[!] KERNEL PANIC detected!')
print_out_str('-------------------------------------------------')
if len(panic_task_list) == 0:
do_dump_stacks(self.ramdump, 1)
else:
for item in panic_task_list:
find_panic(self.ramdump, item[0], item[1])
else:
print_out_str('No kernel panic detected')

View File

@@ -0,0 +1,404 @@
# Copyright (c) 2013-2018,2021, The Linux Foundation. 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 os
import ctypes
import struct
import sys
import pdb
from parser_util import register_parser, RamParser, cleanupString
from mmu import Armv8MMU, Armv7MMU
from print_out import print_out_str
from linux_list import ListWalker
from ramdump import Struct
from struct_print import struct_print_class
class tftp_hash_defines:
dbg_evt_array_size = 100
pkt_array_size = 50
pkt_payload_size = 50
class tftp_connection_debug_id:
invalid = 0
open = 1
tx = 2
rx = 3
get_new_addr = 4
connect = 5
close = 6
set_tx_timeout = 7
max = 8
class tftp_connection_debug_id_string:
str = dict([
(0, "invalid"),
(1, "open"),
(2, "tx"),
(3, "rx"),
(4, "get_new_addr"),
(5, "connect"),
(6, "close"),
(7, "set_tx_timeout"),
(8, "max")])
class tftp_sock_result:
success = 0
failed = 1
timeout = 2
hangup = 3
class tftp_sock_result_string:
str = dict([
(0, "success"),
(1, "failed"),
(2, "timeout"),
(3, "hangup"),])
class tftp_pkt_opcode_string:
str = dict([
(0, "unknown"),
(1, "rrq"),
(2, "wrq"),
(3, "data"),
(4, "ack"),
(5, "error"),
(6, "oack"),
(7, "max")])
class tftp_sock_addr(ctypes.Structure):
_pack_ = 1
_fields_ = [
('pad_1', ctypes.c_uint8 * 32),
]
class tftp_pkt_debug_info(ctypes.Structure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_uint32),
('seq_no', ctypes.c_uint32),
('time_delta', ctypes.c_uint64),
('sd', ctypes.c_uint32),
('local_addr', tftp_sock_addr),
('remote_addr', tftp_sock_addr),
('new_remote_addr', tftp_sock_addr),
('pkt_payload', ctypes.c_uint8 * tftp_hash_defines.pkt_payload_size),
('sock_res', ctypes.c_uint32),
('err_num', ctypes.c_uint32)
]
class tftp_events_debug_info(ctypes.Structure):
_pack_ = 1
_fields_ = [
('id', ctypes.c_uint32),
('seq_no', ctypes.c_uint32),
('time_delta', ctypes.c_uint64),
('sd', ctypes.c_uint32),
('sock_res', ctypes.c_uint32),
('err_num', ctypes.c_uint32)
]
class tftp_connection_info(ctypes.Structure):
_pack_ = 1
_fields_ = [
('bootup_timetick', ctypes.c_uint64),
('seq_no', ctypes.c_uint32),
('dbg_evt', tftp_events_debug_info * tftp_hash_defines.dbg_evt_array_size),
('dbg_evt_idx', ctypes.c_uint32),
('tx_pkts', tftp_pkt_debug_info * tftp_hash_defines.pkt_array_size),
('tx_evt_idx', ctypes.c_uint32),
('rx_pkts', tftp_pkt_debug_info * tftp_hash_defines.pkt_array_size),
('rx_evt_idx', ctypes.c_uint32)
]
class tftp_debug_item(object):
def __init__(self):
self.seqno = None
self.time_delta = None
self.adj_time_in_ms = None
self.sd = None
self.event = None
self.sock_res = None
self.err_num = None
self.payload = None
class tftp_debug(object):
def __init__(self):
self.out_fd = None
self.raw_buf = None
self.tftpd = None
self.dbg_list = {}
self.sanity_checks()
def print_usage(self):
print("usage:", file=self.out_fd)
print("tftp_debug.py <filename>", file=self.out_fd)
def sanity_checks(self):
assert(ctypes.sizeof(tftp_events_debug_info) == 28)
assert(ctypes.sizeof(tftp_sock_addr) == 32)
assert(ctypes.sizeof(tftp_pkt_debug_info) == 174)
assert(ctypes.sizeof(tftp_connection_info) == 20224)
def process_dbg_array(self):
print("", file=self.out_fd)
print("Events list:", file=self.out_fd)
print("-" * 85, file=self.out_fd)
line_str = "{0:<4s} | {1:<16s} | {2:<6s} | {3:<12s} | {4:<12s} | {5:<10s} | {6:<8s}".format(
"idx", "time", "seqno", "sd", "event", "result", "errno")
print(line_str, file=self.out_fd)
print("-" * 85, file=self.out_fd)
for i in range(tftp_hash_defines.dbg_evt_array_size):
dbg_evt = self.tftpd.dbg_evt[i]
if dbg_evt.id == 0:
continue
assert(dbg_evt.id > tftp_connection_debug_id.invalid and dbg_evt.id < tftp_connection_debug_id.max)
evt_id_str = tftp_connection_debug_id_string.str[dbg_evt.id]
sock_res_str = tftp_sock_result_string.str[dbg_evt.sock_res]
line_str = "{0:<4d} | {1:<16d} | {2:<6d} | {3:<12d} | {4:<12s} | {5:<10s} | {6:<8d}".format(
i, dbg_evt.time_delta, dbg_evt.seq_no, dbg_evt.sd, evt_id_str, sock_res_str, dbg_evt.err_num)
print(line_str, file=self.out_fd)
dbg_item = tftp_debug_item()
dbg_item.seqno = dbg_evt.seq_no
dbg_item.time_delta = dbg_evt.time_delta
dbg_item.sd = dbg_evt.sd
dbg_item.event = evt_id_str
dbg_item.sock_res = sock_res_str
dbg_item.err_num = dbg_evt.err_num
self.dbg_list[dbg_evt.seq_no] = dbg_item
print("-" * 80, file=self.out_fd)
def process_pkt_array(self, pkt_array, name_str, event_id):
print('', file=self.out_fd)
print(name_str, file=self.out_fd)
print("-" * 110, file=self.out_fd)
line_str = "{0:<4s} | {1:<16s} | {2:<6s} | {3:<12s} | {4:<10s} | {5:<8s} | {6:<8s}".format(
"idx", "time", "seqno", "sd", "result", "errno", "payload")
print(line_str, file=self.out_fd)
print("-" * 110, file=self.out_fd)
for i in range(tftp_hash_defines.pkt_array_size):
pkt_evt = pkt_array[i]
if pkt_evt.id == 0:
continue
assert(pkt_evt.id == tftp_connection_debug_id.tx or pkt_evt.id == tftp_connection_debug_id.rx)
assert(pkt_evt.id == event_id)
sock_res_str = tftp_sock_result_string.str[pkt_evt.sock_res]
line_str = "{0:<4d} | {1:<16d} | {2:<6d} | {3:<12d} | {4:<10s} | {5:<8d} | ".format(
i, pkt_evt.time_delta, pkt_evt.seq_no, pkt_evt.sd, sock_res_str, pkt_evt.err_num)
payload_str = ""
pkt = pkt_evt.pkt_payload
if sys.version_info > (3,):
mv = memoryview(pkt)
pkt_buf = mv.tobytes()
else:
pkt_buf = buffer(pkt)[:]
(opcode,) = struct.unpack('>H', pkt_buf[:2])
opcode_str = tftp_pkt_opcode_string.str[opcode]
payload_str += opcode_str
payload_str += " : "
if opcode == 1 or opcode == 2:
path_buf = str(pkt_buf[2:])
path_buf = path_buf.split('\0', 1)[0]
for i in range(tftp_hash_defines.pkt_payload_size):
b = pkt_buf[i]
if (b > 32 and b < 128):
payload_str += chr(b)
else:
payload_str += ' '
#payload_str += path_buf
elif opcode == 3:
(block_no,) = struct.unpack('>H', pkt_buf[2:4])
payload_str += str(block_no)
payload_str += " : "
data_buf = str(pkt_buf[4:])
#pdb.set_trace()
data_buf = data_buf.split('\0', 1)[0]
#payload_str += data_buf
elif opcode == 4:
(block_no,) = struct.unpack('>H', pkt_buf[2:4])
payload_str += str(block_no)
elif opcode == 5:
(err_code,) = struct.unpack('>H', pkt_buf[2:4])
payload_str += str(err_code)
payload_str += ","
for i in range(tftp_hash_defines.pkt_payload_size):
b = pkt_buf[i]
if (b > 32 and b < 128):
payload_str += chr(b)
elif opcode == 6:
for i in range(1,tftp_hash_defines.pkt_payload_size):
b = pkt_buf[i]
if (b > 32 and b < 128):
payload_str += chr(b)
line_str += payload_str
print(line_str, file=self.out_fd)
dbg_item = tftp_debug_item()
dbg_item.seqno = pkt_evt.seq_no
dbg_item.time_delta = pkt_evt.time_delta
dbg_item.sd = pkt_evt.sd
dbg_item.event = tftp_connection_debug_id_string.str[pkt_evt.id]
dbg_item.sock_res = sock_res_str
dbg_item.err_num = pkt_evt.err_num
dbg_item.payload = payload_str
self.dbg_list[pkt_evt.seq_no] = dbg_item
print("-" * 80, file=self.out_fd)
def print_consolidated(self):
print('', file=self.out_fd)
print("combined list:", file=self.out_fd)
print("-" * 110, file=self.out_fd)
line_str = "{:<6s} | {:<12s} | {:<12s} | {:<12s} | {:<10s} | {:<8s} | {:<8s}".format(
"seqno", "adj-time(ms)", "sd", "event", "result", "errno", "payload")
print(line_str, file=self.out_fd)
print("-" * 110, file=self.out_fd)
first_time = None
for seqno in sorted(self.dbg_list.keys()):
if first_time == None:
first_time = self.dbg_list[seqno].time_delta
self.dbg_list[seqno].adj_time_in_ms = (self.dbg_list[seqno].time_delta - first_time) / 1000
payload = ""
if (self.dbg_list[seqno].payload != None):
payload = self.dbg_list[seqno].payload
line_str = "{:<6d} | {:<12d} | {:<12d} | {:<12s} | {:<10s} | {:<8d} | {:<s}".format(
seqno, int(self.dbg_list[seqno].adj_time_in_ms), self.dbg_list[seqno].sd,
self.dbg_list[seqno].event, self.dbg_list[seqno].sock_res, self.dbg_list[seqno].err_num, payload)
print(line_str, file=self.out_fd)
print("-" * 80, file=self.out_fd)
def open_file(self, filename):
fd = open(filename, "rb")
filesize = os.stat(filename).st_size
print("opened file [%d]bytes" % filesize, file=self.out_fd)
raw_buf = fd.read()
self.open_buf(raw_buf)
def open_buf(self, buf):
self.raw_buf = buf
self.tftpd = tftp_connection_info.from_buffer_copy(self.raw_buf, 0)
print("", file=self.out_fd)
print("-" * 25, file=self.out_fd)
print('seq_no : ', self.tftpd.seq_no, file=self.out_fd)
print('dbg_evt_idx : ', self.tftpd.dbg_evt_idx, file=self.out_fd)
print('tx_evt_idx : ', self.tftpd.tx_evt_idx, file=self.out_fd)
print('rx_evt_idx : ', self.tftpd.rx_evt_idx, file=self.out_fd)
print("-" * 25, file=self.out_fd)
self.process_dbg_array()
self.process_pkt_array(self.tftpd.tx_pkts, "tx_pkts:", tftp_connection_debug_id.tx)
self.process_pkt_array(self.tftpd.rx_pkts, "rx_pkts:", tftp_connection_debug_id.rx)
self.print_consolidated()
@register_parser('--tftp-server', 'TFTP Server report', optional=True)
class tftp_server_class(RamParser):
def __init__(self, *args):
super(tftp_server_class, self).__init__(*args)
self.tftp_debug = tftp_debug()
self.bss_start = None
self.pgdp = None
self.tftp_mmu = None
self.tftp_bss_region = None
def output(self, str_val):
self.output_file.write(str_val)
def parse(self):
self.output_file = self.ramdump.open_file('tftp-server-report.txt')
self.tftp_debug.out_fd = self.output_file
print_out_str("tftp-server-parser:start")
s1 = self.ramdump.get_kernel_version()
s2 = 'Kernel version : [{0:d}.{0:d}.{0:d}]\n'.format(s1[0], s1[1], s1[2])
self.output(s2)
self.locate_tftp_server_mmap()
self.read_tftp_bss_region()
self.tftp_debug.open_buf(self.tftp_bss_region)
#self.dump_tftp_bss_region('tftp_bss_region.bin')
print_out_str("tftp-server-parser:end")
self.output_file.close()
def locate_tftp_server_mmap(self):
addr_length = 8 if self.ramdump.arm64 else 4
offset_comm = self.ramdump.field_offset('struct task_struct', 'comm')
mm_offset = self.ramdump.field_offset('struct task_struct', 'mm')
bss_start = 0
for task in self.ramdump.for_each_process():
task_name = task + offset_comm
task_name = cleanupString(self.ramdump.read_cstring(task_name, 16))
if task_name == 'tftp_server':
mm_addr = self.ramdump.read_word(task + mm_offset)
mmap = self.ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'mmap')
pgd = self.ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'pgd')
self.pgdp = self.ramdump.virt_to_phys(pgd)
start_data = self.ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'start_data')
end_data = self.ramdump.read_structure_field(mm_addr, 'struct mm_struct', 'end_data')
mmap_next = mmap
# bss section is after data section
map_count = 0
while mmap_next != 0:
tmpstartVm = self.ramdump.read_structure_field(mmap_next, 'struct vm_area_struct', 'vm_start')
tmpendVm = self.ramdump.read_structure_field(mmap_next, 'struct vm_area_struct', 'vm_end')
map_count = map_count + 1
if (end_data > tmpstartVm) and (end_data < tmpendVm):
# android P and older : 3 logd vma, bss section is just after end_data
if map_count < 3:
# data section is 4bytes align while bss section is 8 bytes align
bss_start = (end_data + 7) & 0x000000fffffffff8
else:
# android Q: 2 code vma + 2 data vma + 1bss, bss section is individual vma after end_data
if (start_data < tmpstartVm):
mmap_next = self.ramdump.read_structure_field(mmap_next, 'struct vm_area_struct', 'vm_next')
bss_start = self.ramdump.read_structure_field(mmap_next, 'struct vm_area_struct', 'vm_start')
else:
# android R: 3 code vma and 1 data+bss vma, bss section is just after end_data, data section is addr_length align and bss section is 8 bytes align
if self.ramdump.arm64:
bss_start = end_data
else:
bss_start = (end_data + 7) & 0xfffffff8
print_out_str("bss_start:2 0x%x\n" %(bss_start))
break
mmap_next = self.ramdump.read_structure_field(mmap_next, 'struct vm_area_struct', 'vm_next')
break
self.bss_start = bss_start
if self.ramdump.arm64:
self.tftp_mmu = Armv8MMU(self.ramdump, self.pgdp)
else:
self.tftp_mmu = Armv7MMU(self.ramdump, self.pgdp)
def read_tftp_bss_region(self):
va = self.bss_start
cur_size = 0
self.tftp_bss_region = bytes()
while cur_size < 32768:
pa = self.tftp_mmu.virt_to_phys(va)
tftp_buf = self.ramdump.read_physical(pa, 1)
if (tftp_buf is None) or (tftp_buf == ''):
break
self.tftp_bss_region += tftp_buf
va = va + 1
cur_size += 1
def dump_tftp_bss_region(self, filename):
fd = self.ramdump.open_file(filename, mode='wb')
fd.write(self.tftp_bss_region)
fd.close()
if __name__ == '__main__':
tftp_inst = tftp_debug()
tftp_inst.out_fd = sys.stdout
if len(sys.argv) != 2:
tftp_inst.print_usage()
die
tftp_inst.open_file(sys.argv[1])

View File

@@ -0,0 +1,538 @@
# Copyright (c) 2015, 2020-2021 The Linux Foundation. All rights reserved.
# Copyright (c) 2022 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
import linux_list as llist
import traceback
TSENS_MAX_SENSORS = 16
DEBUG_SIZE = 10
THERMAL_MAX_TRIPS = 12
THERMAL_MAX_CDEVS = 150
@register_parser(
'--thermal-info', 'Useful information from thermal data structures')
class Thermal_info(RamParser):
def __init__(self, dump):
super(Thermal_info, self).__init__(dump)
# List of all sub-parsers as (func, info, outfile, mode) tuples.
self.parser_list_5_10 = [
(self.parse_thremal_zone_data, "ThermalZone", 'thermal_zone.txt', "w"),
(self.parse_cooling_device_data, "CoolingDevice", 'cooling_device.txt', "w"),
]
self.parser_list_5_4 = [
(self.parse_thremal_zone_data, "ThermalZone", 'thermal_zone.txt', "w"),
(self.parse_cooling_device_data, "CoolingDevice", 'cooling_device.txt', "w"),
(self.parse_tsen_device_data, "Tsensors", 'tsens_info.txt', "w"),
]
self.parser_list_v1 = [
(self.tmdev_data, "TsensDbgData", 'tsens_dbg_info.txt', "w"),
]
def print_thermal_info(
self, sensor_dbg_info_start_address, ram_dump,
time_stamp, sensor_mapping):
for ncpu in ram_dump.iter_cpus():
self.writeln(
"------------------------------------------------")
self.writeln(
" TEMPERATURE ENTRIES FOR CPU:{0}".format(
int(ncpu)))
self.writeln(
"------------------------------------------------")
cpu_sensor_addr = sensor_dbg_info_start_address + \
sensor_mapping[ncpu]
for i in range(0, 10):
temp = self.ramdump.read_word(cpu_sensor_addr + (i * 8), True)
time = self.ramdump.read_word(
cpu_sensor_addr + time_stamp + (i * 8), True)
self.writeln(
"Temperature reading - {0} ".format(int(temp)))
self.writeln("TimeStamp - {0}\n".format(int(time)))
def tmdev_data(self, ram_dump):
sensor_mapping = []
self.writeln("Thermal sensor data \n")
tmdev = self.ramdump.address_of('tmdev')
tmdev_address = self.ramdump.read_word(tmdev, True)
sensor_dbg_info_size = ram_dump.sizeof('struct tsens_sensor_dbg_info')
sensor_dbg_info = self.ramdump.field_offset(
'struct tsens_tm_device',
'sensor_dbg_info')
time_stamp = self.ramdump.field_offset(
'struct tsens_sensor_dbg_info',
'time_stmp')
cpus_sensor = self.ramdump.address_of('cpus')
cpus_sensor_size = ram_dump.sizeof('struct cpu_info')
sensor_id_offset = self.ramdump.field_offset(
'struct cpu_info',
'sensor_id')
if not all((tmdev, sensor_dbg_info_size, sensor_dbg_info,
time_stamp, cpus_sensor, cpus_sensor_size,
sensor_id_offset)):
self.writeln("Not supported for this target yet :-( \n")
return
for i in ram_dump.iter_cpus():
cpu_sensor_id_address = cpus_sensor + sensor_id_offset
sensor_id = self.ramdump.read_u32(cpu_sensor_id_address, True)
cpus_sensor = cpus_sensor + cpus_sensor_size
sensor_mapping.append((sensor_id - 1) * sensor_dbg_info_size)
self.print_thermal_info(
(tmdev_address + sensor_dbg_info),
ram_dump,
time_stamp,
sensor_mapping)
def tsens_dbg_parse_fields(self, tsens_device_p):
dev_o = self.ramdump.field_offset('struct tsens_device', 'dev')
dev = self.ramdump.read_word(dev_o + tsens_device_p)
kobj_o = self.ramdump.field_offset('struct device', ' kobj')
kobj = (dev + kobj_o)
name_o = self.ramdump.field_offset('struct kobject', 'name')
name_addr = self.ramdump.read_word(name_o + kobj)
name = self.ramdump.read_cstring(name_addr)
if name is not None:
self.writeln("%s" % (name))
tsens_dbg_o = self.ramdump.field_offset('struct tsens_device', 'tsens_dbg')
tsens_dbg = tsens_device_p + tsens_dbg_o
sensor_dbg_info_o = self.ramdump.field_offset('struct tsens_dbg_context', 'sensor_dbg_info')
sensor_dbg_info = sensor_dbg_info_o + tsens_dbg
self.writeln('v.v (struct tsens_device)0x{:8x} 0x{:8x}\n'.format(tsens_device_p,
sensor_dbg_info))
for i in range(0, TSENS_MAX_SENSORS):
idx = self.ramdump.read_u32(self.ramdump.array_index(sensor_dbg_info, 'struct tsens_dbg', i))
tsens_dbg_addr = self.ramdump.array_index(sensor_dbg_info, 'struct tsens_dbg', i)
self.writeln(" idx: %d tsens_dbg_addr 0x%x" % (idx, tsens_dbg_addr))
time_stmp_o = self.ramdump.field_offset('struct tsens_dbg', 'time_stmp')
temp_o = self.ramdump.field_offset('struct tsens_dbg', 'temp')
self.writeln(" time_stmp temp ")
for j in range(0, DEBUG_SIZE):
time_stmp = self.ramdump.read_word(self.ramdump.array_index(time_stmp_o + tsens_dbg_addr,
'unsigned long long', j))
temp = self.ramdump.read_u64(
self.ramdump.array_index(temp_o + tsens_dbg_addr, 'unsigned long', j))
self.writeln(" %d %d" % (time_stmp, temp))
def parse_cooling_device_fields(self, cdev_struct_addr, thermal_cdev_dict: dict):
cdev_data_struct = {}
cdev_id = None
try:
if not cdev_struct_addr:
return
cdev_data_struct["cdev_struct_addr"] = cdev_struct_addr
cdev_id = cdev_data_struct["cdev_id"] = self.ramdump.read_s32(cdev_struct_addr +
self.ramdump.field_offset(
'struct thermal_cooling_device',
'id'))
if cdev_id > THERMAL_MAX_CDEVS:
return
cdev_data_struct["cdev_type"] = self.ramdump.read_structure_cstring(cdev_struct_addr,
'struct thermal_cooling_device', 'type')
if not cdev_data_struct["cdev_type"]:
return
cdev_data_struct["updated"] = self.ramdump.read_bool(
self.ramdump.struct_field_addr(cdev_struct_addr, 'struct thermal_cooling_device', 'updated'))
if cdev_data_struct["updated"] in [0, "0"]:
cdev_data_struct["updated"] = "False"
elif cdev_data_struct["updated"] in [1, "1"]:
cdev_data_struct["updated"] = "True"
stats_addr = cdev_data_struct["stats_addr"] = self.ramdump.read_structure_field(cdev_struct_addr,
'struct thermal_cooling_device',
'stats')
cdev_data_struct["devdata"] = self.ramdump.struct_field_addr(cdev_struct_addr,
'struct thermal_cooling_device', 'devdata')
if stats_addr:
cdev_data_struct["stats_state"] = self.ramdump.read_structure_field(stats_addr,
'struct cooling_dev_stats', 'state')
cdev_data_struct["stats_total_trans"] = self.ramdump.read_structure_field(stats_addr,
'struct cooling_dev_stats',
'total_trans')
cdev_data_struct["stats_last_time"] = self.ramdump.read_structure_field(stats_addr,
'struct cooling_dev_stats',
'last_time')
except Exception as e:
cdev_data_struct["exception"] = str(e)
if cdev_data_struct and cdev_id is not None:
thermal_cdev_dict[cdev_id] = cdev_data_struct
return
def parse_thermal_zone_fields(self, tz_device_addr, thermal_tzone_dict: list, triggered_zones: list):
tzone_data_dict = {}
tzone_id = None
try:
if not tz_device_addr:
return
tzone_data_dict["tz_device_addr"] = hex(tz_device_addr)
tzone_id = tzone_data_dict["tzone_id"] = self.ramdump.read_s32(tz_device_addr + self.ramdump.field_offset(
'struct thermal_zone_device', 'id'))
tzone_data_dict["mode"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device', 'mode')
if tzone_data_dict["mode"] not in [0, 1]:
return
type_addr = self.ramdump.struct_field_addr(tz_device_addr, 'struct thermal_zone_device', 'type')
tzone_data_dict["type"] = self.ramdump.read_cstring(type_addr)
algo_type_off = self.ramdump.field_offset('struct thermal_zone_device', 'governor')
tzone_data_dict["algo_type"] = self.ramdump.read_structure_cstring(tz_device_addr + algo_type_off,
'struct thermal_governor', 'name')
filed_offset = self.ramdump.field_offset('struct thermal_zone_device', 'polling_delay')
if filed_offset:
tzone_data_dict["polling_delay"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'polling_delay')
tzone_data_dict["passive_delay"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'passive_delay')
else:
tzone_data_dict["polling_delay"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'polling_delay_jiffies')
tzone_data_dict["passive_delay"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'passive_delay_jiffies')
tzone_data_dict["temperature"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device', 'temperature'))
tzone_data_dict["last_temperature"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device',
'last_temperature'))
tzone_data_dict["emul_temperature"] = self.ramdump.read_s32(tz_device_addr + self.ramdump.field_offset(
'struct thermal_zone_device',
'emul_temperature'))
tzone_data_dict["passive"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset('struct thermal_zone_device',
'passive'))
tzone_data_dict["prev_low_trip"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device', 'prev_low_trip'))
tzone_data_dict["prev_high_trip"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device',
'prev_high_trip'))
tzone_data_dict[" need_update"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'need_update')
filed_offset = self.ramdump.field_offset('struct thermal_zone_device', 'num_trips')
if filed_offset:
tzone_data_dict["trip_count"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device', 'num_trips'))
else:
tzone_data_dict["trip_count"] = self.ramdump.read_s32(tz_device_addr +
self.ramdump.field_offset(
'struct thermal_zone_device', 'trips'))
tzone_data_dict["trips_disabled"] = self.ramdump.read_structure_field(tz_device_addr,
'struct thermal_zone_device',
'trips_disabled')
tzone_data_dict["suspended"] = self.ramdump.read_bool(self.ramdump.struct_field_addr(tz_device_addr,
'struct thermal_zone_device',
'suspended'))
if tzone_data_dict["suspended"] in [0, "0"]:
tzone_data_dict["suspended"] = "False"
elif tzone_data_dict["suspended"] in [1, "1"]:
tzone_data_dict["suspended"] = "True"
devdata_off = self.ramdump.field_offset('struct thermal_zone_device', 'devdata')
if devdata_off:
devdata_off = hex(tz_device_addr + devdata_off)
tzone_data_dict["devdata"] = devdata_off
node_addr = self.ramdump.struct_field_addr(tz_device_addr,
"struct thermal_zone_device",
"thermal_instances")
list_offset = self.ramdump.field_offset('struct thermal_instance', 'tz_node')
device_list_walker = llist.ListWalker(self.ramdump, node_addr, list_offset)
tzone_data_dict["trips_data"] = trips_data = {}
trip_triggered = False
last_node = None
trip_number = 0
tzone_data_dict["trip_thresholds"] = {}
if device_list_walker.is_empty():
pass
else:
while not device_list_walker.is_empty():
try:
thermal_instance_addr = device_list_walker.next()
except Exception as e:
break
_trip_data = {}
kv = self.ramdump.kernel_version
if (kv[0], kv[1]) > (5, 10):
trip_number += 1
if trip_number not in trips_data:
trips_data[trip_number] = []
else:
trip_number = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'trip')
if trip_number > THERMAL_MAX_TRIPS:
return
if trip_number not in trips_data:
trips_data[trip_number] = []
_trip_data["id"] = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance',
'id')
instance_name_addr = self.ramdump.struct_field_addr(thermal_instance_addr,
'struct thermal_instance', 'name')
_trip_data["name"] = self.ramdump.read_cstring(instance_name_addr)
_trip_data["initialized"] = self.ramdump.read_bool(
self.ramdump.struct_field_addr(thermal_instance_addr,
'struct thermal_instance', 'initialized'))
if _trip_data["initialized"] in [0, "0"]:
_trip_data["initialized"] = "False"
elif _trip_data["initialized"] in [1, "1"]:
_trip_data["initialized"] = "True"
_trip_data["lower"] = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'lower')
_trip_data["upper"] = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'upper')
_trip_data["target"] = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'target')
_trip_data["weight"] = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'weight')
_trip_data["upper_no_limit"] = self.ramdump.read_bool(
self.ramdump.struct_field_addr(thermal_instance_addr,
'struct thermal_instance', 'upper_no_limit'))
if _trip_data["upper_no_limit"] in [0, "0"]:
_trip_data["upper_no_limit"] = "False"
elif _trip_data["upper_no_limit"] in [1, "0"]:
_trip_data["upper_no_limit"] = "True"
if _trip_data["target"] > 0xFFFFFF:
_trip_data["trip_status"] = "Not Triggered"
elif _trip_data["target"] == 0:
_trip_data["trip_status"] = "In Clear State"
else:
trip_triggered = True
_trip_data["trip_status"] = "In Trigger State"
_trip_data["cdev"] = {}
cdev_addr = self.ramdump.read_structure_field(thermal_instance_addr,
'struct thermal_instance', 'cdev')
self.parse_cooling_device_fields(cdev_addr, _trip_data["cdev"])
trips_data[trip_number].append(_trip_data)
if trip_triggered:
triggered_zones.append(tzone_data_dict["type"])
except Exception as e:
tzone_data_dict["exception"] = str(e)
print_out_str(traceback.format_exc())
if tzone_data_dict and tzone_id is not None:
thermal_tzone_dict.append(tzone_data_dict)
return
def parse_thremal_zone_data(self, dump):
self.tzone_struct_list = []
self.triggered_zones = []
# thermal_zone data
thermal_tz_list = self.ramdump.read('thermal_tz_list.next')
list_offset = self.ramdump.field_offset('struct thermal_zone_device', 'node')
list_walker = llist.ListWalker(self.ramdump, thermal_tz_list, list_offset)
list_walker.walk(thermal_tz_list, self.parse_thermal_zone_fields,
self.tzone_struct_list, self.triggered_zones)
if len(self.tzone_struct_list) == 0:
self.writeln("No thermal Zones defined")
return
self.writeln("")
self.writeln("# Total Tzones: {0}".format(len(self.tzone_struct_list)))
self.writeln("# violated Tzones: {0}".format(",".join(self.triggered_zones)))
self.writeln("")
format_str = "{0:<35} {1}"
self.tzone_struct_list.sort(key=lambda x: float(x["temperature"]), reverse=True)
for tzone_struct in self.tzone_struct_list:
self.writeln("")
self.writeln("[THERMAL_ZONE_{0}]".format(tzone_struct["tzone_id"]))
self.writeln(format_str.format("sensor", tzone_struct.get("type")))
self.writeln(format_str.format("algo_type", tzone_struct.get("algo_type")))
if "exception" in tzone_struct.keys():
self.writeln(format_str.format("Exception", tzone_struct.get("exception")))
continue
self.writeln(format_str.format("mode", "enabled" if (tzone_struct.get("mode") == 1) else "disabled"))
self.writeln(format_str.format("polling_delay", tzone_struct.get("polling_delay")))
self.writeln(format_str.format("passive_delay", tzone_struct.get("passive_delay")))
self.writeln(format_str.format("temperature", tzone_struct.get("temperature")))
self.writeln(format_str.format("last_temperature", tzone_struct.get("last_temperature")))
self.writeln(format_str.format("emul_temperature", tzone_struct.get("emul_temperature")))
self.writeln(format_str.format("prev_high_trip", tzone_struct.get("prev_high_trip")))
self.writeln(format_str.format("prev_low_trip", tzone_struct.get("prev_low_trip")))
self.writeln(format_str.format("passive", tzone_struct.get("passive")))
if tzone_struct.get("need_update") is not None:
self.writeln(format_str.format("tzone_registered", tzone_struct.get("need_update")))
if tzone_struct.get("suspended") is not None:
self.writeln(format_str.format("suspended", tzone_struct.get("suspended")))
self.writeln(format_str.format("trip_count", tzone_struct.get("trip_count")))
if tzone_struct.get("trips_disabled") is not None:
self.writeln(format_str.format("trips_disabled", tzone_struct.get("trips_disabled")))
self.writeln(format_str.format("tzone_data_struct",
"v.v ((struct thermal_zone_device *){0}".format(
tzone_struct.get("tz_device_addr"))))
self.writeln(format_str.format("tzone devdata ",
"v.v ((struct __thermal_zone *){0}".format(
tzone_struct.get("devdata"))))
# print trip data
self.writeln("Devices:")
cdev_format_str = "\t\t{0:<35} {1}"
trips_data = tzone_struct.get("trips_data")
if not trips_data:
self.writeln("\tNo Cooling Devices")
continue
trip_nums = sorted(trips_data.keys())
for trip_num in trip_nums:
trip_cdevs_info = trips_data[trip_num]
self.writeln(" Trip{0}:".format(trip_num))
for trip_info in trip_cdevs_info:
if trip_info.get("trip_status") is not None:
self.writeln("\t {0:<35} {1}".format("trip_status", trip_info.get("trip_status")))
cdev_dict = trip_info.get("cdev")
if not cdev_dict or len(cdev_dict) > 1:
self.writeln("Invalid cdev for trip instance")
continue
cdev_id = list(cdev_dict.keys())[0]
cdev_dict = cdev_dict[cdev_id]
self.writeln("\t {0}".format(cdev_dict.get("cdev_type")))
self.writeln(cdev_format_str.format("id", cdev_id))
self.writeln(cdev_format_str.format("initialized", trip_info.get("initialized")))
if trip_info.get("upper_no_limit") is not None:
self.writeln(cdev_format_str.format("upper_no_limit", trip_info.get("upper_no_limit")))
if trip_info.get("weight"):
self.writeln(cdev_format_str.format("weight", trip_info.get("weight")))
self.writeln(cdev_format_str.format("trip_status", trip_info.get("status")))
self.writeln(cdev_format_str.format("states(lower, upper, cur_state)",
"({0}, {1}, {2})".format(
trip_info.get("lower"),
trip_info.get("upper"),
trip_info.get("target"))))
stats_data = cdev_dict.get("stats_addr")
stats_state = "NA"
if stats_data:
stats_state = cdev_dict.get("stats_state")
self.writeln(cdev_format_str.format("cdev status(updated, stats_state)",
"({0}, {1})".format(cdev_dict.get("updated"), stats_state)))
if stats_data:
self.writeln(cdev_format_str.format("stats_total_trans",
cdev_dict.get("stats_total_trans")))
self.writeln(cdev_format_str.format("stats_last_time",
hex(cdev_dict.get("stats_last_time"))))
self.writeln("")
return
def parse_cooling_device_data(self, dump):
self.cdev_struct_list = {}
# thermal_zone data
thermal_cdev_list = self.ramdump.read('thermal_cdev_list.next')
list_offset = self.ramdump.field_offset('struct thermal_cooling_device', 'node')
list_walker = llist.ListWalker(self.ramdump, thermal_cdev_list, list_offset)
list_walker.walk(thermal_cdev_list, self.parse_cooling_device_fields, self.cdev_struct_list)
cdev_ids_list = self.cdev_struct_list.keys()
if not cdev_ids_list:
print_out_str("No cooling Devices or exception in parsing")
return
format_str = "{0:<35} {1}"
cdev_ids_list = sorted(cdev_ids_list)
for cdev_id in cdev_ids_list:
cdev_struct = self.cdev_struct_list[cdev_id]
self.writeln("")
self.writeln("[COOLING_DEVICE_{0}]".format(cdev_id))
self.writeln(format_str.format("type", cdev_struct.get("cdev_type")))
self.writeln(format_str.format("updated", cdev_struct.get("updated")))
stats_addr = cdev_struct.get("stats_addr")
if stats_addr:
self.writeln(format_str.format("state", cdev_struct.get("stats_state")))
self.writeln(format_str.format("total_trans", cdev_struct.get("stats_total_trans")))
self.writeln(format_str.format("last_time", hex(cdev_struct.get("stats_last_time"))))
self.writeln(format_str.format("cdev_max_states", cdev_struct.get("max_states")))
self.writeln(format_str.format("cdev_struct",
"v.v (struct thermal_cooling_device*){0}".format(
hex(cdev_struct.get("cdev_struct_addr")))))
self.writeln(format_str.format("cdev_devdata",
"v.v (struct *){0}".format(hex(cdev_struct.get("devdata")))))
self.writeln(format_str.format("cdev_stats_struct",
"v.v (struct cooling_dev_stats*){0}".format(hex(stats_addr))))
if "exception" in cdev_struct.keys():
self.writeln(format_str.format("Exception", cdev_struct.get("exception")))
return
def parse_tsen_device_data(self, dump):
tsens_device_list = self.ramdump.address_of('tsens_device_list')
list_offset = self.ramdump.field_offset('struct tsens_device', 'list')
list_walker = llist.ListWalker(self.ramdump, tsens_device_list, list_offset)
list_walker.walk(tsens_device_list, self.tsens_dbg_parse_fields)
def write(self, string):
self.out.write(string)
def writeln(self, string=""):
self.out.write(string + '\n')
def parse(self):
print_out_str("Started thermal parsing")
kv = self.ramdump.kernel_version
if (kv[0], kv[1]) == (5, 4):
self.parser_list = self.parser_list_5_4
elif (kv[0], kv[1]) > (5, 4):
self.parser_list = self.parser_list_5_10
else:
self.parser_list = self.parser_list_v1
for subparser in self.parser_list:
try:
self.out = self.ramdump.open_file('thermal_info/' + subparser[2], subparser[3])
self.write(subparser[1].center(90, '-') + '\n')
subparser[0](self.ramdump)
self.writeln()
self.out.close()
except Exception as e:
if self.out:
self.writeln(str(e))
self.out.close()
print_out_str("Thermal info: Parsing failed in "
+ subparser[0].__name__)
print_out_str(traceback.format_exc())
print_out_str("Done thermal parsing")

View File

@@ -0,0 +1,58 @@
# Copyright (c) 2022 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
MAX_DATA_NUM = 50
@register_parser('--thermal-temp', 'Print the tsensor/adc temp')
class ThermalTemp(RamParser):
def __init__(self, *args):
super(ThermalTemp, self).__init__(*args)
def parse(self):
def parse_thermal_minidump(name):
data_addr = self.ramdump.read_word('md')
if data_addr is None and self.ramdump.minidump:
seg = next((s for s in self.ramdump.elffile.iter_sections() if s.name == name), None)
if seg is None:
print_out_str('--md_{0} does not exist--'.format(name))
if seg is not None:
data_addr = seg['sh_addr']
print_out_str('------{0} data------'.format(name))
count = self.ramdump.read_u32(data_addr + MAX_DATA_NUM * 24)
last_type = self.ramdump.read_cstring(data_addr + (MAX_DATA_NUM - 1) * 20)
if last_type == '':
for i in range(count):
sensor_type = self.ramdump.read_cstring(data_addr + i * 20)
sensor_temp = self.ramdump.read_u32(data_addr + MAX_DATA_NUM * 20 + i * 4)
print_out_str('sensor_type : {0}\nsensor_temp : {1}\n'.format(sensor_type, sensor_temp))
if last_type != '':
for i in range(count, MAX_DATA_NUM):
sensor_type = self.ramdump.read_cstring(data_addr + i * 20)
sensor_temp = self.ramdump.read_u32(data_addr + MAX_DATA_NUM * 20 + i * 4)
print_out_str('sensor_type : {0}\nsensor_temp : {1}\n'.format(sensor_type, sensor_temp))
for i in range(count):
sensor_type = self.ramdump.read_cstring(data_addr + i * 20)
sensor_temp = self.ramdump.read_u32(data_addr + MAX_DATA_NUM * 20 + i * 4)
print_out_str('sensor_type : {0}\nsensor_temp : {1}\n'.format(sensor_type, sensor_temp))
minidump_table_name = ['adc5_gen3', 'tsens0', 'tsens1', 'tsens2']
for i in range(len(minidump_table_name)):
parse_thermal_minidump(minidump_table_name[i])

View File

@@ -0,0 +1,283 @@
# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
# Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 sys
import os
import linux_list
from print_out import print_out_str
from parser_util import register_parser, RamParser
import rb_tree
@register_parser('--timer-list', 'Print all the linux timers')
class TimerList(RamParser) :
def __init__(self, *args):
super(TimerList, self).__init__(*args)
self.vectors = {'tv1': 256, 'tv2': 64, 'tv3': 64, 'tv4': 64, 'tv5': 64}
self.output = []
major, minor, patch = self.ramdump.kernel_version
self.timer_42 = False
self.timer_has_data = True
self.timer_jiffies = 'timer_jiffies'
self.tvec_base = 'struct tvec_base'
self.tvec_bases = 'tvec_bases'
self.next_timer = 'next_timer'
self.global_deferrable = 'tvec_base_deferrable'
# As of kernel 4.15, timer list structure no longer has data field
if (major, minor) >= (4, 15):
self.timer_has_data = False
HZ = self.ramdump.get_config_val("CONFIG_HZ")
if HZ != None:
self.HZ = float(HZ)
else:
self.HZ = 100.0
if (major, minor) >= (4, 9):
# the wheel size is defined in kernel/time/timer.c:
# the WHEEL_SIZE is LVL_SIZE * LVL_DEPTH
# LVL_SIZE is 64
# if HZ > 100
# define LVL_DEPTH 9
# else
# define LVL_DEPTH 8
# endif
self.vectors = {'vectors': 9 * 64 if self.HZ > 100.0 else 8 * 64}
self.timer_jiffies = 'clk'
self.tvec_base = 'struct timer_base'
self.tvec_bases = 'timer_bases'
self.next_timer = 'next_expiry'
self.global_deferrable = 'timer_base_deferrable'
# Timerlist structure changed in kernel 4.2
# Requires separate processing
if (major, minor) >= (4, 2):
self.timer_42 = True
def timer_list_walker(self, node, type, index, base):
if node == self.head:
return
remarks = ''
function_addr = node + self.ramdump.field_offset('struct timer_list', 'function')
expires_addr = node + self.ramdump.field_offset('struct timer_list', 'expires')
try:
function = self.ramdump.unwind_lookup(self.ramdump.read_word(function_addr))[0]
except TypeError:
function = "<dynamic module>"
expires = self.ramdump.read_word(expires_addr)
if self.timer_has_data:
data_addr = node + self.ramdump.field_offset('struct timer_list', 'data')
try:
data = hex(self.ramdump.read_word(data_addr)).rstrip('L')
except TypeError:
self.output_file.write("+ Corruption detected at index {0} in {1} list, found corrupted value: {2:x}\n".format(index, type, data_addr))
return
else:
data = ""
if function.split('[')[0] == "delayed_work_timer_fn":
timer_list_offset = self.ramdump.field_offset('struct delayed_work', 'timer')
work_addr = node - timer_list_offset
func_addr = work_addr + self.ramdump.field_offset('struct work_struct', 'func')
try:
work_func = self.ramdump.unwind_lookup(self.ramdump.read_word(func_addr))[0]
if self.timer_has_data:
data += " / " + work_func
else:
data = work_func
except TypeError:
if self.timer_has_data:
data += " / " + hex(self.ramdump.read_word(func_addr)) + "<MODULE>"
else:
data = hex(self.ramdump.read_word(func_addr)) + "<MODULE>"
if not self.timer_42:
timer_base_addr = node + self.ramdump.field_offset(
'struct timer_list', 'base')
timer_base = self.ramdump.read_word(timer_base_addr) & ~3
if timer_base != base:
remarks += "Timer Base Mismatch detected"
expires_s = (expires-(0xFFFFFFFF - 300 * int(self.HZ)) )/(self.HZ)
output = "\t{0:<6} {1:<18x} {2:<14} {3:<14} {4:<40} {5:<52} {6}\n".format(index, node, expires, str(expires_s) + 's', function, data, remarks)
self.output.append(output)
def iterate_vec(self, type, base):
vec_addr = base + self.ramdump.field_offset(self.tvec_base, type)
for i in range(0, self.vectors[type]):
index = self.ramdump.array_index(vec_addr, 'struct list_head', i)
self.head = index
node_offset = self.ramdump.field_offset('struct list_head', 'next')
timer_list_walker = linux_list.ListWalker(self.ramdump, index, node_offset)
timer_list_walker.walk(index, self.timer_list_walker, type, i, base)
def iterate_vec_v2(self, type, base):
vec_addr = base + self.ramdump.field_offset(self.tvec_base, type)
for i in range(0, self.vectors[type]):
index = self.ramdump.array_index(vec_addr, 'struct hlist_head', i)
self.head = index
index = self.ramdump.read_word(index)
node_offset = self.ramdump.field_offset(
'struct hlist_node', 'next')
timer_list_walker = linux_list.ListWalker(self.ramdump, index,
node_offset)
timer_list_walker.walk(index, self.timer_list_walker, type, i,
base)
def print_vec(self, type):
headers = ['INDEX', 'TIMER_LIST_ADDR', 'EXPIRES', 'FUNCTION', 'DATA/WORK', 'REMARKS']
if not self.timer_has_data:
headers[4] = 'WORK'
if len(self.output):
self.output_file.write("+ {0} Timers ({1})\n\n".format(type, len(self.output)))
self.output_file.write("\t{0:6} {1:18} {2:14} {3:14} {4:40} {5:52} {6}\n".format(headers[0], headers[1], headers[2], 'EXPIRES(s)', headers[3], headers[4], headers[5]))
for out in self.output:
self.output_file.write(out)
self.output_file.write("\n")
else:
self.output_file.write("+ No {0} Timers found\n\n".format(type))
def get_timer_list(self):
self.output_file.write("Timer List Dump\n\n")
tvec_base_deferral_addr = self.ramdump.address_of(self.global_deferrable)
if tvec_base_deferral_addr:
timer_jiffies_addr = tvec_base_deferral_addr + self.ramdump.field_offset(self.tvec_base, self.timer_jiffies)
next_timer_addr = tvec_base_deferral_addr + self.ramdump.field_offset(self.tvec_base, self.next_timer)
timer_jiffies = self.ramdump.read_word(timer_jiffies_addr)
next_timer = self.ramdump.read_word(next_timer_addr)
active_timers_offset = self.ramdump.field_offset(self.tvec_base, 'active_timers')
if active_timers_offset is not None:
active_timers_addr = tvec_base_deferral_addr + self.ramdump.field_offset(self.tvec_base, 'active_timers')
active_timers = self.ramdump.read_word(active_timers_addr)
else:
active_timers = "NA"
title = "(deferrable_base: {0:x} ".format(tvec_base_deferral_addr)
title += "timer_jiffies: {0} next_timer: {1} active_timers: {2})\n".format(timer_jiffies, next_timer, active_timers)
self.output_file.write("-" * len(title) + "\n")
self.output_file.write(title)
self.output_file.write("-" * len(title) + "\n\n")
for vec in sorted(self.vectors):
self.output = []
if self.timer_42:
self.iterate_vec_v2(vec, tvec_base_deferral_addr)
else:
self.iterate_vec(vec, tvec_base_deferral_addr)
self.print_vec(vec)
tvec_bases_addr = self.ramdump.address_of(self.tvec_bases)
for cpu in self.ramdump.iter_cpus():
title = "CPU {0}".format(cpu)
base_addr = tvec_bases_addr + self.ramdump.per_cpu_offset(cpu)
if self.timer_42:
base = base_addr
else:
base = self.ramdump.read_word(base_addr)
title += "(tvec_base: {0:x} ".format(base)
timer_jiffies_addr = base + self.ramdump.field_offset(self.tvec_base, self.timer_jiffies)
next_timer_addr = base + self.ramdump.field_offset(self.tvec_base, self.next_timer)
timer_jiffies = self.ramdump.read_word(timer_jiffies_addr)
next_timer = self.ramdump.read_word(next_timer_addr)
active_timers_offset = self.ramdump.field_offset(self.tvec_base, 'active_timers')
if active_timers_offset is not None:
active_timers_addr = base + self.ramdump.field_offset(self.tvec_base, 'active_timers')
active_timers = self.ramdump.read_word(active_timers_addr)
else:
active_timers = "NA"
timer_jiffies_s = (timer_jiffies-(0xFFFFFFFF - 300 * int(self.HZ)) )/(self.HZ)
next_timer_s = (next_timer-(0xFFFFFFFF - 300 * int(self.HZ)) )/(self.HZ)
title += "timer_jiffies: {0}({1}s) next_timer: {2}({3}s) active_timers: {4})\n".format(timer_jiffies, timer_jiffies_s, next_timer, next_timer_s, active_timers)
self.output_file.write("-" * len(title) + "\n")
self.output_file.write(title)
self.output_file.write("-" * len(title) + "\n\n")
for vec in sorted(self.vectors):
self.output = []
if self.timer_42:
self.iterate_vec_v2(vec, base)
else:
self.iterate_vec(vec, base)
self.print_vec(vec)
tick_do_timer_cpu_addr = self.ramdump.address_of('tick_do_timer_cpu')
tick_do_timer_cpu_val = "tick_do_timer_cpu: {0}\n".format(self.ramdump.read_int(tick_do_timer_cpu_addr))
self.output_file.write("=" * len(tick_do_timer_cpu_val) + "\n")
self.output_file.write(tick_do_timer_cpu_val)
self.output_file.write("=" * len(tick_do_timer_cpu_val) + "\n")
def hrtimer_walker(self, hrtimer_base, extra):
if hrtimer_base == None:
print(" %s " % ("\n rbtree corrupted \n"), file=self.output_file)
return
node = self.ramdump.struct_field_addr(hrtimer_base , 'struct hrtimer', 'node')
expires = self.ramdump.read_structure_field(node, 'struct timerqueue_node', 'expires')
function = self.ramdump.read_structure_field(hrtimer_base, 'struct hrtimer', 'function')
function_name = self.ramdump.unwind_lookup(function)
if function_name == None:
function_name = 'n/a'
_softexpires = self.ramdump.read_structure_field(hrtimer_base, 'struct hrtimer', '_softexpires')
'''
in some case the rb tree is corrupt, the rt_node could be a valid pointer but the member value is invalid.
'''
if function != None and _softexpires != None and expires != None:
self.hrtimer_list.append([hrtimer_base, function, function_name, _softexpires, expires])
def get_hrtimer(self):
print(" %s " % ("\nhrtimer info: \n"), file=self.output_file)
hrtimer_bases_addr = self.ramdump.address_of('hrtimer_bases')
clock_base_offset = self.ramdump.field_offset('struct hrtimer_cpu_base', 'clock_base')
for i in self.ramdump.iter_cpus():
hrtimer_bases = hrtimer_bases_addr + self.ramdump.per_cpu_offset(i)
clock_base = (hrtimer_bases + clock_base_offset)
print(" CPU %d hrtimer_bases v.v (struct hrtimer_cpu_base)0x%x " % (i, hrtimer_bases), file = self.output_file)
num_of_HRTIMER_MAX_CLOCK_BASES = self.ramdump.gdbmi.get_value_of('HRTIMER_MAX_CLOCK_BASES')
self.hrtimer_list = []
for j in range(0, num_of_HRTIMER_MAX_CLOCK_BASES):
hrtimer_cpu_base_index = self.ramdump.array_index(clock_base, 'struct hrtimer_clock_base', j)
if hrtimer_cpu_base_index != None and hrtimer_cpu_base_index != 0:
print(" hrtimer_cpu_base 0x%x " %(hrtimer_cpu_base_index), file = self.output_file)
active_offset = self.ramdump.field_offset('struct hrtimer_clock_base', 'active')
active = hrtimer_cpu_base_index + active_offset
rb_node = self.ramdump.read_pointer(active)
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
rb_walker.walk(rb_node, self.hrtimer_walker)
self.hrtimer_list = sorted(self.hrtimer_list, key=lambda l: l[4])
print(" hrtimer function _softexpires _softexpires" , file=self.output_file)
for item in self.hrtimer_list:
hrtimer_base = item[0]
function = item[1]
function_name = item[2]
_softexpires = item[3]
expires = item[4]
print(" v.v (struct hrtimer *)0x%x 0x%-16x %-64s %-32ld %-32ld" % (
hrtimer_base, function, function_name, _softexpires, expires), file = self.output_file)
def parse(self):
self.output_file= open(self.ramdump.outdir + "/timerlist.txt", "w")
self.get_timer_list()
self.get_hrtimer()
self.output_file.close()
print_out_str("--- Wrote the output to timerlist.txt")

View File

@@ -0,0 +1,141 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
import linux_list
import enum
from print_out import print_out_str
from parser_util import RamParser, cleanupString, register_parser
from prettytable import PrettyTable
@register_parser('--ubwcp', 'Dump UBWC-P')
class Ubwcp(RamParser):
def __init__(self, *args):
super(Ubwcp, self).__init__(*args)
def handle_dmabuf(self, dmabuf_addr, target_dict):
file_addr = self.ramdump.read_structure_field(dmabuf_addr, 'struct dma_buf', 'file')
target_dict['ino'] = "Null"
if file_addr != 0:
file_ino_addr = self.ramdump.read_structure_field(file_addr, 'struct file', 'f_inode')
if file_ino_addr != 0:
file_ino = self.ramdump.read_structure_field(file_ino_addr, 'struct inode', 'i_ino')
target_dict['ino'] = "0x{0:x}".format(file_ino)
return
def store_data(self, target_dict, struct_obj, members=None):
for k,v in struct_obj.__dict__.items():
if members and k not in members:
continue
if "unused" in k:
continue
if isinstance(v, bool):
target_dict[k] = v
elif isinstance(v, int):
target_dict[k] = "0x{0:x}".format(v)
if "dma_buf" in k and v != 0:
self.handle_dmabuf(v, target_dict)
elif isinstance(v, enum.Enum):
target_dict[k] = v.name
elif '__dict__' in dir(v):
member_dict = {}
self.store_data(member_dict, v)
target_dict[k] = member_dict
else:
target_dict[k] = v
return
def parse_ubwcp_buffer(self, node, key, buffer_list:list, output_fd, members=None):
buffer_dict = {}
buf_addr = self.ramdump.container_of(node, 'struct ubwcp_buf', 'hnode')
buf_members = ['address', 'key', 'buf_attr', 'perm', 'buf_attr_set', 'locked', 'lock_dir', 'lock_count', 'dma_buf', 'ula_pa', 'ula_size', 'mmdata']
buffer_dict['hash_key'] = "0x{0:x}".format(key)
buffer_dict['address'] = "0x{0:x}".format(buf_addr)
ubwcp_buf_struct = self.ramdump.read_datatype(buf_addr, 'struct ubwcp_buf')
self.store_data(buffer_dict, ubwcp_buf_struct, members=buf_members)
buffer_list.append(buffer_dict)
return
def print_buf_dict(self, buf_dict, output_dict, parent=None):
for key, value in buf_dict.items():
if parent:
key = "{}->{}".format(parent, key)
if isinstance(value, dict):
self.print_buf_dict(value, output_dict, parent=key)
else:
output_dict[key] = value
return
def print_column(self, table_ptr, column_list):
table_ptr.field_names = column_list
return
def print_output(self, buffer_list, output_fd):
output_fd.write("\n[Buffers]\n")
if len(buffer_list) == 0:
print_out_str('no buffer is found')
return
buf_print = PrettyTable()
for i in range(0, len(buffer_list)):
buf = buffer_list[i]
output_dict = {}
self.print_buf_dict(buf, output_dict)
if i == 0:
self.print_column(buf_print, output_dict.keys())
buf_print.add_row(output_dict.values())
output_fd.write(str(buf_print) + "\n")
return
def traverse_ubwcp_buffers(self, ubwcp_ptr, output_fd):
buffer_list = []
buf_table_offset = self.ramdump.field_offset('struct ubwcp_driver', 'buf_table')
entry_size = self.ramdump.sizeof('((struct ubwcp_driver *)0x0)->buf_table[0]')
hash_size = int(self.ramdump.sizeof('((struct ubwcp_driver *)0x0)->buf_table') / entry_size)
next_offset = self.ramdump.field_offset('struct hlist_node', 'next')
for i in range(0, hash_size):
entry_addr = self.ramdump.array_index(ubwcp_ptr + buf_table_offset, 'struct hlist_head', i)
first_entry_addr = self.ramdump.read_word(entry_addr)
if first_entry_addr != 0:
hash_list = linux_list.ListWalker(self.ramdump, first_entry_addr, next_offset)
hash_list.walk(first_entry_addr, self.parse_ubwcp_buffer, i, buffer_list, output_fd)
self.print_output(buffer_list, output_fd)
return
def ubwcp_info(self, ubwcp_ptr, output_fd):
info_dict = {}
info_members = ["address", "hw_ver_major", "hw_ver_minor", "ula_pool_base", "ula_pool_size"]
info_dict['address'] = "0x{0:x}".format(ubwcp_ptr)
ubwcp_driver_struct = self.ramdump.read_datatype(ubwcp_ptr, 'struct ubwcp_driver')
self.store_data(info_dict, ubwcp_driver_struct, members=info_members)
output_fd.write("[UBWC-P information]\n")
info_print = PrettyTable()
info_print.field_names = info_dict.keys()
info_print.add_row(info_dict.values())
output_fd.write(str(info_print) + "\n")
return
def parse_ubwcp(self, output_filename):
ubwcp = self.ramdump.read_pointer('me')
if ubwcp is None:
print_out_str('ubwcp is not found')
return
output_fd = self.ramdump.open_file(output_filename)
self.ubwcp_info(ubwcp, output_fd)
self.traverse_ubwcp_buffers(ubwcp, output_fd)
output_fd.close()
return
def parse(self):
output_filename = "ubwcp.txt"
self.parse_ubwcp(output_filename)
print_out_str('---wrote the output to {}'.format(output_filename))

View File

@@ -0,0 +1,131 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
import linux_list
from print_out import print_out_str
from parser_util import RamParser, cleanupString, register_parser
@register_parser('--uevent', 'Dump uevent socket')
class Uevent(RamParser):
def __init__(self, *args):
super(Uevent, self).__init__(*args)
def parse_sock(self, node, head, data_list:list, output_fd):
data_dict = {}
data_head = self.ramdump.read_structure_field(node, 'struct sk_buff', 'head')
if node != head or data_head != 0:
data_dict['head'] = data_head
data_len = self.ramdump.read_structure_field(node, 'struct sk_buff', 'len')
data_len = data_len if data_len < 1000 else 1000
read_pos = 0
data = ""
while read_pos < data_len:
read_str = self.ramdump.read_cstring(data_head + read_pos, data_len - read_pos)
read_len = len(read_str)
if read_len == 0:
read_str = " "
read_len = 1
else:
read_str = read_str.strip()
data = "{}{}".format(data, read_str)
read_pos = read_pos + read_len
data_dict['msg'] = data
data_list.append(data_dict)
return
def parse_netlink(self, netlink_node, output_fd):
liner = "-" * 133
sk = self.ramdump.struct_field_addr(netlink_node, 'struct netlink_sock', 'sk')
eth = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_protocol')
portid = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'portid')
groups = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'groups')
groups = self.ramdump.read_pointer(groups)
flags = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'flags')
state = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'state')
sk_rmem = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_backlog.rmem_alloc.counter')
sk_wmem = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_wmem_alloc.refs.counter') - 1
cbrun = self.ramdump.struct_field_addr(netlink_node, 'struct netlink_sock', 'cb_running')
cbrun = self.ramdump.read_bool(cbrun)
sk_refcnt = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.__sk_common.skc_refcnt.refs.counter')
sk_drops = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_drops.counter')
sk_socket = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_socket')
sk_vfsino = 0
if sk_socket != 0:
sk_vfsino_addr = self.ramdump.container_of(sk_socket, 'struct socket_alloc', 'socket')
sk_vfsino = self.ramdump.read_structure_field(sk_vfsino_addr, 'struct socket_alloc', 'vfs_inode.i_ino')
output_fd.write("|{0:^20x}|{1:^5d}|{2:^8d}|{3:^10x}|{4:^10d}|{5:^10d}|{6:^7d}|{7:^10d}|{8:^10d}|{9:^10d}|{10:^10x}|{11:^10x}|\n".format( \
sk, eth, portid, groups, sk_rmem, sk_wmem, cbrun, sk_refcnt, sk_drops, sk_vfsino, flags, state))
output_fd.write("{}\n".format(liner))
queue_lenth = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_receive_queue.qlen')
output_fd.write("* pending receive buffers : {0:d}\n".format(queue_lenth))
data_list = []
if queue_lenth > 0:
entry_head = self.ramdump.struct_field_addr(netlink_node, 'struct netlink_sock', 'sk.sk_receive_queue')
entry_addr = self.ramdump.read_structure_field(netlink_node, 'struct netlink_sock', 'sk.sk_receive_queue.next')
node_offset = self.ramdump.field_offset('struct sk_buff', 'next')
netlink_list = linux_list.ListWalker(self.ramdump, entry_addr, node_offset)
netlink_list.walk(entry_addr, self.parse_sock, entry_head, data_list, output_fd)
for i in range(0, len(data_list)):
data_dict = data_list[i]
output_fd.write(" [{0:03d}] data:0x{1:x} msg: {2}\n".format(i, data_dict['head'], data_dict['msg']))
return
def parse_bind_node(self, bind_node, output_fd):
header = ["sk", "Eth", "PortId", "Groups", "Rmem", "Wmem", "Dump", "Locks", "Drops", "Inode", "Flags", "State"]
header_liner = "=" * 133
output_fd.write("{}\n".format(header_liner))
output_fd.write("|{0:^20s}|{1:^5s}|{2:^8s}|{3:^10s}|{4:^10s}|{5:^10s}|{6:^7s}|{7:^10s}|{8:^10s}|{9:^10s}|{10:^10s}|{11:^10s}|\n".format( \
header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7], header[8], header[9], header[10], header[11]))
output_fd.write("{}\n".format(header_liner))
self.parse_netlink(self.ramdump.container_of(bind_node, 'struct netlink_sock', 'sk'), output_fd)
return
def iterate_protocol_node(self, protocol, output_fd):
table_entry_addr = self.ramdump.array_index(self.ramdump.read_pointer('nl_table'), 'struct netlink_table', protocol)
node_entry_addr = self.ramdump.read_structure_field(table_entry_addr, 'struct netlink_table', 'mc_list.first')
if node_entry_addr != 0:
node_offset = self.ramdump.field_offset('struct sock', '__sk_common.skc_bind_node')
netlink_list = linux_list.ListWalker(self.ramdump, node_entry_addr, node_offset)
netlink_list.walk(node_entry_addr, self.parse_bind_node, output_fd)
return
def parse_uevent(self, node, head, output_fd):
if node == head:
return
uevent_addr = self.ramdump.container_of(node, 'struct uevent_sock', 'list')
sk_addr = self.ramdump.read_structure_field(uevent_addr, 'struct uevent_sock', 'sk')
sk_type = self.ramdump.read_structure_field(sk_addr, 'struct sock', 'sk_type')
sk_protocol = self.ramdump.read_structure_field(sk_addr, 'struct sock', 'sk_protocol')
self.iterate_protocol_node(sk_protocol, output_fd)
return
def traverse_uevent_sock(self, output_filename):
uevent_list_head = self.ramdump.address_of('uevent_sock_list')
if uevent_list_head is None:
print_out_str('uevent_sock_list is not found')
return
output_fd = self.ramdump.open_file(output_filename)
next_offset = self.ramdump.field_offset('struct list_head', 'next')
if uevent_list_head != 0:
uevent_list = linux_list.ListWalker(self.ramdump, uevent_list_head, next_offset)
uevent_list.walk(uevent_list_head, self.parse_uevent, uevent_list_head, output_fd)
output_fd.close()
return
def parse(self):
output_filename = "uevent.txt"
self.traverse_uevent_sock(output_filename)
print_out_str('---wrote the output to {}'.format(output_filename))

View File

@@ -0,0 +1,958 @@
# Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
# Copyright (c) 2022-2024 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 os
import csv
from parser_util import register_parser, RamParser
from parsers.irqstate import IrqParse
from dmesglib import DmesgLib
from print_out import print_out_str
from linux_list import ListWalker
# Refer to the Header before changing this
VERSION = "1.0"
F_UFSIPC = "ipc_logging/ufs-qcom.txt"
F_UFSDEBUG = "ufsreport.txt"
UFS_HEADER = "__UFS_Dumps__"
def setup_out_file(path, self):
global out_file
try:
out_file = self.ramdump.open_file(path, 'wb')
out_file.write(UFS_HEADER + '__v' + VERSION + '__' + '\n\n' + '\t\t\t\t' + '-- Begin --' + '\n\n')
return out_file
except:
print_out_str("could not open path {0}".format(path))
print_out_str("Do you have write/read permissions on the path?")
def print_out_ufs(string, newline=True):
if newline:
out_file.write((string + '\n').encode('ascii', 'ignore'))
return
out_file.write((string).encode('ascii', 'ignore'))
class UfsHba():
ufs_dev_pwr_mode_l = ['INVALID_MODE', 'UFS_ACTIVE_PWR_MODE', 'UFS_SLEEP_PWR_MODE', 'UFS_POWERDOWN_PWR_MODE']
ufs_link_state_l = ['UIC_LINK_OFF_STATE', 'UIC_LINK_ACTIVE_STATE', 'UIC_LINK_HIBERN8_STATE']
ufs_pm_level_l = ['UFS_PM_LVL_0', 'UFS_PM_LVL_1', 'UFS_PM_LVL_2', 'UFS_PM_LVL_3', 'UFS_PM_LVL_4', 'UFS_PM_LVL_5']
ufs_ref_clk_l = ['REF_CLK_FREQ_19_2_MHZ', 'REF_CLK_FREQ_26_MHZ', 'REF_CLK_FREQ_38_4_MHZ', 'REF_CLK_FREQ_52_MHZ']
ufs_clk_gating_l = ['CLKS_OFF', 'CLKS_ON', 'REQ_CLKS_OFF', 'REQ_CLKS_ON']
ufs_bkops_status_l = ['BKOPS_STATUS_NO_OP', 'BKOPS_STATUS_NON_CRITICAL', 'BKOPS_STATUS_PERF_IMPACT',
'BKOPS_STATUS_CRITICAL']
ufs_sdev_state_l = ['INVALID_MODE', 'SDEV_CREATED', 'SDEV_RUNNING', 'SDEV_CANCEL', 'SDEV_DEL', 'SDEV_QUIESCE',
'SDEV_TRANSPORT_OFFLINE', 'SDEV_TRANSPORT_OFFLINE', 'SDEV_BLOCK', 'SDEV_CREATED_BLOCK']
ufs_rpm_request_l = ['RPM_REQ_NONE', 'RPM_REQ_IDLE', 'RPM_REQ_SUSPEND', 'RPM_REQ_AUTOSUSPEND', 'RPM_REQ_RESUME']
ufs_rpm_status_l = ['RPM_ACTIVE', 'RPM_RESUMING', 'RPM_SUSPENDED', 'RPM_SUSPENDING']
ufs_evt_bf_k515_l = ['UFS_EVT_PA_ERR', 'UFS_EVT_DL_ERR', 'UFS_EVT_NL_ERR', 'UFS_EVT_TL_ERR', 'UFS_EVT_DME_ERR',
'UFS_EVT_AUTO_HIBERN8_ERR', 'UFS_EVT_FATAL_ERR', 'UFS_EVT_LINK_STARTUP_FAIL', 'UFS_EVT_RESUME_ERR',
'UFS_EVT_SUSPEND_ERR', 'UFS_EVT_DEV_RESET','UFS_EVT_HOST_RESET', 'UFS_EVT_ABORT']
ufs_evt_af_k515_l = ['UFS_EVT_PA_ERR', 'UFS_EVT_DL_ERR', 'UFS_EVT_NL_ERR', 'UFS_EVT_TL_ERR', 'UFS_EVT_DME_ERR',
'UFS_EVT_AUTO_HIBERN8_ERR', 'UFS_EVT_FATAL_ERR', 'UFS_EVT_LINK_STARTUP_FAIL', 'UFS_EVT_RESUME_ERR',
'UFS_EVT_SUSPEND_ERR', 'UFS_EVT_WL_SUSP_ERR' , 'UFS_EVT_WL_RES_ERR' , 'UFS_EVT_DEV_RESET',
'UFS_EVT_HOST_RESET', 'UFS_EVT_ABORT']
def __init__(self, ramdump, ufs_hba_addr):
self.ramdump = ramdump
self.ufs_hba_addr = ufs_hba_addr
def dump_ufs_hba_params(self):
pass
def get_scsi_host(self):
scsi_host_offset = self.ramdump.field_offset('struct ufs_hba', 'host')
return self.ramdump.read_pointer(self.ufs_hba_addr + scsi_host_offset)
def get_scsi_device(self):
if self.ramdump.get_kernel_version() < (6, 1, 0):
scsi_dev_offset = self.ramdump.field_offset('struct ufs_hba', 'sdev_ufs_device')
else:
scsi_dev_offset = self.ramdump.field_offset('struct ufs_hba', 'ufs_device_wlun')
return self.ramdump.read_pointer(self.ufs_hba_addr + scsi_dev_offset)
def dump_ufs_lrbs(self):
pass
def dump_uic_cmd(self):
active_uic_cmd_addr = self.ramdump.read_pointer(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'active_uic_cmd'))
print_out_ufs('\tstruct uic_command = 0x%x {' %(active_uic_cmd_addr))
try:
print_out_ufs('\t\tcommand = 0x%x' %(self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'command'))))
print_out_ufs('\t\targument1 = 0x%x' % (self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'argument1'))))
print_out_ufs('\t\targument2 = 0x%x' % (self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'argument2'))))
print_out_ufs('\t\targument3 = 0x%x' % (self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'argument3'))))
print_out_ufs('\t\tcmd_active = %d' % (self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'cmd_active'))))
print_out_ufs('\t\tresult = %d' % (self.ramdump.read_int(
active_uic_cmd_addr + self.ramdump.field_offset('struct uic_command', 'result'))))
print_out_ufs('\t}')
except:
print_out_ufs('\t\tactive_uic_cmd = 0x0\n\t}')
def dump_ufs_event_bfk515(self, ufs_stats_addr):
print_out_ufs("\t\tufs_event {")
ufs_evt_base = ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'event')
evt_sz = self.ramdump.sizeof('struct ufs_event_hist')
ts_sz = self.ramdump.sizeof('ktime_t')
val_sz = self.ramdump.sizeof('u32')
for x in range(13):
ufs_evt_addr = ufs_evt_base + (x * evt_sz)
ufs_evt_cnt = self.ramdump.read_int(ufs_evt_addr + self.ramdump.field_offset(
'struct ufs_event_hist', 'cnt'))
print_out_ufs("\t\t\t%s:" %self.ufs_evt_bf_k515_l[x], False)
print_out_ufs("\tCnt: %d" %ufs_evt_cnt)
if ufs_evt_cnt != 0:
ts_base = ufs_evt_addr + self.ramdump.field_offset('struct ufs_event_hist', 'tstamp')
val_base = ufs_evt_addr + self.ramdump.field_offset('struct ufs_event_hist', 'val')
for y in range(8):
ts_addr = ts_base + (y * ts_sz)
if self.ramdump.read_s64(ts_addr) != 0:
print_out_ufs("\t\t\t\tval: 0x%x" %self.ramdump.read_u32(val_base + (y * val_sz)), False)
print_out_ufs("\tts: %d" %self.ramdump.read_s64(ts_addr))
print_out_ufs("\t\t}")
def dump_ufs_event_afk515(self, ufs_stats_addr):
print_out_ufs("\t\tufs_event {")
ufs_evt_base = ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'event')
evt_sz = self.ramdump.sizeof('struct ufs_event_hist')
ts_sz = self.ramdump.sizeof('ktime_t')
val_sz = self.ramdump.sizeof('u32')
for x in range(15):
ufs_evt_addr = ufs_evt_base + (x * evt_sz)
ufs_evt_cnt = self.ramdump.read_int(ufs_evt_addr + self.ramdump.field_offset(
'struct ufs_event_hist', 'cnt'))
print_out_ufs("\t\t\t%s:" %self.ufs_evt_af_k515_l[x], False)
print_out_ufs("\tCnt: %d" %ufs_evt_cnt)
if ufs_evt_cnt != 0:
ts_base = ufs_evt_addr + self.ramdump.field_offset('struct ufs_event_hist', 'tstamp')
val_base = ufs_evt_addr + self.ramdump.field_offset('struct ufs_event_hist', 'val')
for y in range(8):
ts_addr = ts_base + (y * ts_sz)
if self.ramdump.read_s64(ts_addr) != 0:
print_out_ufs("\t\t\t\tval: 0x%x" %self.ramdump.read_u32(val_base + (y * val_sz)), False)
print_out_ufs("\tts: %d" %self.ramdump.read_s64(ts_addr))
print_out_ufs("\t\t}")
def dump_ufs_stats(self):
ufs_stats_addr = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ufs_stats')
print_out_ufs('\tstruct ufs_stats = 0x%x {' %(ufs_stats_addr))
print_out_ufs('\t\tlast_intr_status = %d' %(self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'last_intr_status'))))
print_out_ufs('\t\tlast_intr_ts = %d' %(self.ramdump.read_u64(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'last_intr_ts'))))
print_out_ufs('\t\thibern8_exit_cnt = %d' %(self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'hibern8_exit_cnt'))))
print_out_ufs('\t\tlast_hibern8_exit_tstamp = %d' % (self.ramdump.read_u64(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'last_hibern8_exit_tstamp'))))
try:
# GKI doesn't have these
print_out_ufs('\t\tenabled = %d' % (self.ramdump.read_bool(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'enabled'))))
print_out_ufs('\t\tpa_err_cnt_total = %d' %(self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'pa_err_cnt_total'))))
print_out_ufs('\t\tdl_err_cnt_total = %d' % (self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'dl_err_cnt_total'))))
print_out_ufs('\t\tdme_err_cnt = %d' % (self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'dme_err_cnt'))))
print_out_ufs('\t\tlast_intr_status = 0x%x' % (self.ramdump.read_int(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'last_intr_status'))))
print_out_ufs('\t\tlast_intr_ts = %d' % (self.ramdump.read_s64(
ufs_stats_addr + self.ramdump.field_offset('struct ufs_stats', 'last_intr_ts'))))
except:
print_out_ufs('\t\tenabled = 0')
if self.ramdump.get_kernel_version() < (5, 14, 0):
self.dump_ufs_event_bfk515(ufs_stats_addr)
else:
self.dump_ufs_event_afk515(ufs_stats_addr)
print_out_ufs("\t}")
def dump_ufs_dev_info(self):
dev_info_addr = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'dev_info')
print_out_ufs('\tstruct ufs_dev_info = 0x%x {' %(dev_info_addr))
print_out_ufs('\t\tf_power_on_wp_en = %d' % (self.ramdump.read_bool(
dev_info_addr + self.ramdump.field_offset('struct ufs_dev_info', 'f_power_on_wp_en'))))
print_out_ufs('\t\tis_lu_power_on_wp = %d' % (self.ramdump.read_bool(
dev_info_addr + self.ramdump.field_offset('struct ufs_dev_info', 'is_lu_power_on_wp'))))
print_out_ufs('\t\tmodel = %s' % (self.ramdump.read_structure_cstring(
dev_info_addr, 'struct ufs_dev_info', 'model')))
print_out_ufs('\t\twspecversion = %d' % (self.ramdump.read_int(
dev_info_addr + self.ramdump.field_offset('struct ufs_dev_info', 'wspecversion'))))
print_out_ufs('\t\tclk_gating_wait_us = %d' % (self.ramdump.read_int(
dev_info_addr + self.ramdump.field_offset('struct ufs_dev_info', 'clk_gating_wait_us'))))
print_out_ufs("\t}")
def dump_ufs_pa_layer_attr(self, drp_addr):
print_out_ufs("\t\tgear_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_rx'))))
print_out_ufs("\t\tgear_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_tx'))))
print_out_ufs("\t\tlane_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_rx'))))
print_out_ufs("\t\tlane_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_tx'))))
print_out_ufs("\t\tpwr_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_rx'))))
print_out_ufs("\t\tpwr_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_tx'))))
print_out_ufs("\t\ths_rate = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'hs_rate'))))
return
def get_vreg_info(self, vreg_addr, vreg):
vreg_p = self.ramdump.read_pointer(vreg_addr + self.ramdump.field_offset('struct ufs_vreg_info', vreg))
if vreg_p == 0:
return
print_out_ufs("\t\t[%s]{"%vreg)
print_out_ufs("\t\t\taddr = 0x%x" %vreg_p)
print_out_ufs("\t\t\tname = %s" %(self.ramdump.read_structure_cstring(
vreg_p, 'struct ufs_vreg', 'name')))
print_out_ufs("\t\t\talways_on = %d" %(self.ramdump.read_bool(vreg_p +
self.ramdump.field_offset(
'struct ufs_vreg',
'always_on'))))
print_out_ufs("\t\t\tenabled = %d" %(self.ramdump.read_bool(vreg_p +
self.ramdump.field_offset(
'struct ufs_vreg',
'enabled'))))
reg_p = self.ramdump.read_pointer(vreg_p + self.ramdump.field_offset('struct ufs_vreg', 'reg'))
rdev_p = self.ramdump.read_pointer(reg_p + self.ramdump.field_offset('struct regulator', 'rdev'))
rdesc_p = self.ramdump.read_pointer(rdev_p + self.ramdump.field_offset('struct regulator_dev', 'desc'))
print_out_ufs("\t\t\tregulator: %s" %(self.ramdump.read_structure_cstring(
rdesc_p, 'struct regulator_desc', 'name')))
print_out_ufs("\t\t\tsupply: %s" %(self.ramdump.read_structure_cstring(
rdesc_p, 'struct regulator_desc', 'supply_name')))
print_out_ufs("\t\t}")
def dump_ufs_vreg(self):
print_out_ufs("\tstruct ufs_vreg {")
vreg_addr = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'vreg_info')
self.get_vreg_info(vreg_addr, "vcc")
self.get_vreg_info(vreg_addr, "vccq")
self.get_vreg_info(vreg_addr, "vccq2")
self.get_vreg_info(vreg_addr, "vdd_hba")
print_out_ufs("\t}")
def get_clk_info(self, clk):
print_out_ufs("\t\t[%s]" %(self.ramdump.read_structure_cstring(
clk, 'struct ufs_clk_info', 'name')))
print_out_ufs("\t\t\taddr = 0x%x" %clk)
print_out_ufs("\t\t\tmax_freq = %d" % (self.ramdump.read_u32(clk +
self.ramdump.field_offset(
'struct ufs_clk_info',
'max_freq'))))
print_out_ufs("\t\t\tmin_freq = %d" % (self.ramdump.read_u32(clk +
self.ramdump.field_offset(
'struct ufs_clk_info',
'min_freq'))))
print_out_ufs("\t\t\tcurr_freq = %d" % (self.ramdump.read_u32(clk +
self.ramdump.field_offset(
'struct ufs_clk_info',
'curr_freq'))))
print_out_ufs("\t\t\tkeep_link_active = %d" % (self.ramdump.read_bool(clk +
self.ramdump.field_offset(
'struct ufs_clk_info',
'keep_link_active'))))
print_out_ufs("\t\t\tenabled = %d" % (self.ramdump.read_bool(clk +
self.ramdump.field_offset(
'struct ufs_clk_info',
'enabled'))))
print_out_ufs("\t\t}")
def dump_ufs_clk(self):
print_out_ufs("\tstruct ufs_clk_inifo {")
clk_list_h = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'clk_list_head')
clk_base_p = self.ramdump.read_pointer(clk_list_h + self.ramdump.field_offset('struct list_head', 'next'))
clk_next_p = self.ramdump.read_pointer(clk_base_p + self.ramdump.field_offset('struct list_head', 'next'))
self.get_clk_info(clk_base_p)
while clk_next_p != clk_list_h:
self.get_clk_info(clk_next_p)
clk_next_p = self.ramdump.read_pointer(clk_next_p + self.ramdump.field_offset('struct list_head', 'next'))
print_out_ufs("\t}")
def get_scsi_cmd(self, lrbp):
try:
scsi_cmd_offset = self.ramdump.field_offset('struct ufshcd_lrb', 'cmd')
scsi_cmd_addr = self.ramdump.read_pointer(scsi_cmd_offset + lrbp)
print_out_ufs("\t\t\tcmd = %s" %(hex(ord(self.ramdump.read_structure_cstring(
scsi_cmd_addr, 'struct scsi_cmnd', 'cmnd')))))
except:
print_out_ufs("\t\t\tcmd = None")
return
def dump_ufs_lrb(self):
lrb_p = self.ramdump.read_pointer(self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'lrb'))
lrb_sz = self.ramdump.sizeof('struct ufshcd_lrb')
print_out_ufs("\tstruct ufshcd_lrb {")
for x in range(32):
print_out_ufs("\t\t[%d] {" %(x))
lrb_addr = lrb_p + (x * lrb_sz)
self.get_scsi_cmd(lrb_addr)
if self.ramdump.get_kernel_version() < (6, 1, 0):
print_out_ufs("\t\t\tsense_bufflen = %d" %(self.ramdump.read_int(lrb_addr +
self.ramdump.field_offset('struct ufshcd_lrb', 'sense_bufflen'))))
print_out_ufs("\t\t\tscsi_status = %d" % (self.ramdump.read_int(lrb_addr +
self.ramdump.field_offset('struct ufshcd_lrb', 'scsi_status'))))
print_out_ufs("\t\t\tcommand_type = %d" % (self.ramdump.read_int(lrb_addr +
self.ramdump.field_offset('struct ufshcd_lrb',
'command_type'))))
print_out_ufs("\t\t\ttask_tag = %d" % (self.ramdump.read_int(lrb_addr +
self.ramdump.field_offset(
'struct ufshcd_lrb',
'task_tag'))))
print_out_ufs("\t\t\tlun = %d" % (self.ramdump.read_byte(lrb_addr +
self.ramdump.field_offset(
'struct ufshcd_lrb',
'lun'))))
print_out_ufs("\t\t\tintr_cmd = %d" % (self.ramdump.read_int(lrb_addr +
self.ramdump.field_offset(
'struct ufshcd_lrb',
'intr_cmd'))))
print_out_ufs("\t\t\tissue_time_stamp = %d" % (self.ramdump.read_u64(lrb_addr +
self.ramdump.field_offset(
'struct ufshcd_lrb',
'issue_time_stamp'))))
print_out_ufs("\t\t\tcompl_time_stamp = %d" % (self.ramdump.read_u64(lrb_addr +
self.ramdump.field_offset(
'struct ufshcd_lrb',
'compl_time_stamp'))))
print_out_ufs("\t\t}")
print_out_ufs("\t}")
def get_pm_info(self, power):
print_out_ufs("\t\t\tusage_count = %d" %(self.ramdump.read_int(power + self.ramdump.field_offset(
'struct dev_pm_info', 'usage_count'))))
print_out_ufs("\t\t\tchild_count = %d" %(self.ramdump.read_int(power + self.ramdump.field_offset(
'struct dev_pm_info', 'child_count'))))
print_out_ufs("\t\t\tautosuspend_delay = %d" %(self.ramdump.read_int(power + self.ramdump.field_offset(
'struct dev_pm_info', 'autosuspend_delay'))))
print_out_ufs("\t\t\trequest = %s" %self.ufs_rpm_request_l[(self.ramdump.read_int(
power + self.ramdump.field_offset(
'struct dev_pm_info',
'request')))])
print_out_ufs("\t\t\truntime_status = %s" %self.ufs_rpm_status_l[(self.ramdump.read_int(
power + self.ramdump.field_offset(
'struct dev_pm_info',
'runtime_status')))])
def get_queue_info(self, queue):
print_out_ufs("\t\t\taddr = 0x%x" %queue)
print_out_ufs("\t\t\tqueue_flags = 0x%x" %(self.ramdump.read_u64(queue + self.ramdump.field_offset(
'struct request_queue', 'queue_flags'))))
print_out_ufs("\t\t\tpm_only = %d" %(self.ramdump.read_int(queue + self.ramdump.field_offset(
'struct request_queue', 'pm_only'))))
try:
print_out_ufs("\t\t\tnr_pending = %d" %(self.ramdump.read_int(queue + self.ramdump.field_offset(
'struct request_queue', 'nr_pending'))))
print_out_ufs("\t\t\trpm_status = %s" %self.ufs_rpm_status_l[(self.ramdump.read_int(
queue + self.ramdump.field_offset(
'struct request_queue',
'rpm_status')))])
except:
print_out_ufs("\t\t\tNo CONFIG_PM")
if self.ramdump.get_kernel_version() > (4, 20, 0):
hw_ctx_p = self.ramdump.read_pointer(self.ramdump.read_pointer(queue + self.ramdump.field_offset(
'struct request_queue',
'queue_hw_ctx')))
print_out_ufs("\t\t\tblk_mq_hw_ctx = 0x%x" %hw_ctx_p)
print_out_ufs("\t\t\tnr_active = %d" %(self.ramdump.read_int(queue + self.ramdump.field_offset(
'struct blk_mq_hw_ctx', 'nr_active'))))
def get_sdev_info(self, sdev):
print_out_ufs("\tsdev [%d] {" %(self.ramdump.read_u64(sdev + self.ramdump.field_offset(
'struct scsi_device', 'lun'))))
print_out_ufs("\t\taddr = 0x%x" %sdev)
if self.ramdump.get_kernel_version() < (5, 14, 0):
print_out_ufs("\t\tdevice_busy = %d" %(self.ramdump.read_int(sdev + self.ramdump.field_offset(
'struct scsi_device', 'device_busy'))))
print_out_ufs("\t\tdevice_blocked = %d" %(self.ramdump.read_int(sdev + self.ramdump.field_offset(
'struct scsi_device', 'device_blocked'))))
print_out_ufs("\t\tsdev_state = %s" %self.ufs_sdev_state_l[(self.ramdump.read_int(
sdev + self.ramdump.field_offset(
'struct scsi_device',
'sdev_state')))])
try:
sdev_gendev_addr = sdev + self.ramdump.field_offset(
'struct scsi_device', 'sdev_gendev')
print_out_ufs("\t\tname: %s" %(self.ramdump.read_structure_cstring(
sdev_gendev_addr+self.ramdump.field_offset(
'struct device', 'kobj'),
'struct kobject', 'name')))
power_addr = sdev_gendev_addr + self.ramdump.field_offset(
'struct device', 'power')
print_out_ufs("\t\tpm{")
self.get_pm_info(power_addr)
except:
print_out_ufs("\t\t\tdevice or power = NULL")
print_out_ufs("\t\t}")
print_out_ufs("\t\trequest_queue{")
try:
queue_p = self.ramdump.read_pointer(sdev + self.ramdump.field_offset(
'struct scsi_device', 'request_queue'))
self.get_queue_info(queue_p)
except:
print_out_ufs("\t\trequest_queue = NULL")
print_out_ufs("\t\t}")
print_out_ufs("\t}")
def dump_sdev_info(self):
shost_p = self.ramdump.read_pointer(self.ufs_hba_addr + self.ramdump.field_offset(
'struct ufs_hba', 'host'))
sdev_list_addr = shost_p + self.ramdump.field_offset(
'struct Scsi_Host', '__devices')
sdev_list_first = self.ramdump.read_pointer(sdev_list_addr + self.ramdump.field_offset(
'struct list_head', 'next'))
sdev_list_next = self.ramdump.read_pointer(sdev_list_first + self.ramdump.field_offset(
'struct list_head', 'next'))
sdev_base_p = self.get_scsi_device()
sdev_offest = sdev_list_first - sdev_base_p
self.get_sdev_info(sdev_base_p)
while sdev_list_next != sdev_list_addr:
self.get_sdev_info(sdev_list_next - sdev_offest)
sdev_list_next = self.ramdump.read_pointer(sdev_list_next + self.ramdump.field_offset(
'struct list_head', 'next'))
def dump_ufs_hba_params(self):
print_out_ufs("struct ufs_hba = 0x%x {" % (self.ufs_hba_addr))
if self.ramdump.get_kernel_version() < (6, 1, 0):
ufs_scsi_device_addr = self.ramdump.read_pointer(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'sdev_ufs_device'))
else:
ufs_scsi_device_addr = self.ramdump.read_pointer(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ufs_device_wlun'))
print_out_ufs("\tvendor = %s" %(self.ramdump.read_structure_cstring(
ufs_scsi_device_addr,'struct scsi_device', 'vendor')))
print_out_ufs("\tmodel = %s" %(self.ramdump.read_structure_cstring(
ufs_scsi_device_addr, 'struct scsi_device', 'model')))
print_out_ufs("\trev = %s" %(self.ramdump.read_structure_cstring(
ufs_scsi_device_addr, 'struct scsi_device', 'rev')))
ufs_dev_pwr_mode = self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'curr_dev_pwr_mode'))
print_out_ufs("\tcurr_dev_pwr_mode = %s" %(self.ufs_dev_pwr_mode_l[ufs_dev_pwr_mode]))
ufs_link_state = self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'uic_link_state'))
print_out_ufs("\tuic_link_state = %s" % (self.ufs_link_state_l[ufs_link_state]))
ufs_pm_level = self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'rpm_lvl'))
print_out_ufs("\trpm_lvl = %s" % (self.ufs_pm_level_l[ufs_pm_level]))
ufs_pm_level = self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'spm_lvl'))
print_out_ufs("\tspm_lvl = %s" % (self.ufs_pm_level_l[ufs_pm_level]))
print_out_ufs("\tpm_op_in_progress = %d" %(self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'pm_op_in_progress'))))
print_out_ufs("\tahit = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ahit'))))
if self.ramdump.get_kernel_version() < (5, 10, 0):
print_out_ufs("\tlrb_in_use = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'lrb_in_use'))))
print_out_ufs("\toutstanding_tasks = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'outstanding_tasks'))))
print_out_ufs("\toutstanding_reqs = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'outstanding_reqs'))))
print_out_ufs("\tcapabilities = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'capabilities'))))
print_out_ufs("\tnutrs = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'nutrs'))))
print_out_ufs("\tnutmrs = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'nutmrs'))))
print_out_ufs("\tufs_version = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ufs_version'))))
print_out_ufs("\tsg_entry_size = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'sg_entry_size'))))
print_out_ufs("\tirq = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'irq'))))
print_out_ufs("\tis_irq_enabled = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'is_irq_enabled'))))
dev_ref_clk_freq = self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'dev_ref_clk_freq'))
print_out_ufs("\tdev_ref_clk_freq = %s" % (self.ufs_ref_clk_l[dev_ref_clk_freq]))
print_out_ufs("\tquirks = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'quirks'))))
print_out_ufs("\tdev_quirks = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'dev_quirks'))))
if self.ramdump.get_kernel_version() < (5, 10, 0):
print_out_ufs("\ttm_condition = 0x%x" % (self.ramdump.read_ulong(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'tm_condition'))))
print_out_ufs("\ttm_slots_in_use = 0x%x" % (self.ramdump.read_ulong(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'tm_slots_in_use'))))
self.dump_uic_cmd()
print_out_ufs("\tufshcd_state = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ufshcd_state'))))
print_out_ufs("\teh_flags = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'eh_flags'))))
print_out_ufs("\tintr_mask = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'intr_mask'))))
print_out_ufs("\tee_ctrl_mask = 0x%x" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'ee_ctrl_mask'))))
print_out_ufs("\tis_powered = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'is_powered'))))
print_out_ufs("\terrors = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'errors'))))
print_out_ufs("\tuic_error = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'uic_error'))))
print_out_ufs("\tsaved_err = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'saved_err'))))
print_out_ufs("\tsaved_uic_err = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'saved_uic_err'))))
self.dump_ufs_stats()
print_out_ufs("\tsilence_err_logs = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'silence_err_logs'))))
print_out_ufs("\tlast_dme_cmd_tstamp = %d" % (self.ramdump.read_u64(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'last_dme_cmd_tstamp'))))
self.dump_ufs_dev_info()
print_out_ufs("\tauto_bkops_enabled = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'auto_bkops_enabled'))))
if self.ramdump.get_kernel_version() < (5, 14, 0):
print_out_ufs("\twlun_dev_clr_ua = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'wlun_dev_clr_ua'))))
print_out_ufs("\treq_abort_count = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'req_abort_count'))))
print_out_ufs("\tlanes_per_direction = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'lanes_per_direction'))))
# PA Layer Attributes
drp_offset = self.ramdump.field_offset('struct ufs_hba', 'pwr_info')
drp_addr = self.ufs_hba_addr + drp_offset
print_out_ufs("\tpwr_info = 0x%x {" %(drp_addr))
self.dump_ufs_pa_layer_attr(drp_addr)
print_out_ufs("\t}")
# Power Mode Info
mpi_offset = self.ramdump.field_offset('struct ufs_hba', 'max_pwr_info')
mpi_addr = self.ufs_hba_addr + mpi_offset
is_valid = self.ramdump.read_bool(mpi_addr + self.ramdump.field_offset('struct ufs_pwr_mode_info', 'is_valid'))
if is_valid == 1:
print_out_ufs("\tmax_pwr_info = 0x%x {" %(mpi_addr))
print_out_ufs("\t\tis_valid = %d" %(is_valid))
self.dump_ufs_pa_layer_attr(mpi_addr + self.ramdump.field_offset('struct ufs_pwr_mode_info', 'info'))
print_out_ufs("\t}")
# Clock Gating
cg_addr = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'clk_gating')
print_out_ufs("\tufs_clk_gating = 0x%x {" %(cg_addr))
print_out_ufs("\t\tstate = %s" % (self.ufs_clk_gating_l[(self.ramdump.read_int(
cg_addr + self.ramdump.field_offset('struct ufs_clk_gating', 'state')))]))
print_out_ufs("\t\tdelay_ms = %d" % (self.ramdump.read_ulong(
cg_addr + self.ramdump.field_offset('struct ufs_clk_gating', 'delay_ms'))))
print_out_ufs("\t\tis_suspended = %d" % (self.ramdump.read_bool(
cg_addr + self.ramdump.field_offset('struct ufs_clk_gating', 'is_suspended'))))
print_out_ufs("\t\tis_enabled = %d" % (self.ramdump.read_bool(
cg_addr + self.ramdump.field_offset('struct ufs_clk_gating', 'is_enabled'))))
print_out_ufs("\t\tactive_reqs = %d" % (self.ramdump.read_int(
cg_addr + self.ramdump.field_offset('struct ufs_clk_gating', 'active_reqs'))))
print_out_ufs("\t}")
# Clock Scaling
cs_addr = self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'clk_scaling')
print_out_ufs("\tufs_clk_scaling = 0x%x {" % (cs_addr))
print_out_ufs("\t\tactive_reqs = %d" % (self.ramdump.read_int(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'active_reqs'))))
print_out_ufs("\t\ttot_busy_t = %d" % (self.ramdump.read_ulong(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'tot_busy_t'))))
print_out_ufs("\t\twindow_start_t = %d" % (self.ramdump.read_ulong(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'window_start_t'))))
print_out_ufs("\t\tbusy_start_t = %d" % (self.ramdump.read_u64(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'busy_start_t'))))
spi_addr = cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'saved_pwr_info')
print_out_ufs("\t\tstruct ufs_saved_pwr_info = 0x%x {" %(spi_addr))
if self.ramdump.get_kernel_version() < (6, 5, 0):
drp_addr = spi_addr + self.ramdump.field_offset('struct ufs_saved_pwr_info', 'info')
print_out_ufs("\t\t\tgear_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_rx'))))
print_out_ufs("\t\t\tgear_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_tx'))))
print_out_ufs("\t\t\tlane_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_rx'))))
print_out_ufs("\t\t\tlane_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_tx'))))
print_out_ufs("\t\t\tpwr_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_rx'))))
print_out_ufs("\t\t\tpwr_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_tx'))))
print_out_ufs("\t\t\ths_rate = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'hs_rate'))))
print_out_ufs("\t\t}")
print_out_ufs("\t\tis_valid = %d" % (self.ramdump.read_bool(
cs_addr + self.ramdump.field_offset('struct ufs_saved_pwr_info', 'is_valid'))))
else:
print_out_ufs("\t\t\tgear_rx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_rx'))))
print_out_ufs("\t\t\tgear_tx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_tx'))))
print_out_ufs("\t\t\tlane_rx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_rx'))))
print_out_ufs("\t\t\tlane_tx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_tx'))))
print_out_ufs("\t\t\tpwr_rx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_rx'))))
print_out_ufs("\t\t\tpwr_tx = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_tx'))))
print_out_ufs("\t\t\ths_rate = %d" % (self.ramdump.read_int(
spi_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'hs_rate'))))
print_out_ufs("\t\t}")
print_out_ufs("\t\tis_allowed = %d" % (self.ramdump.read_bool(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'is_allowed'))))
print_out_ufs("\t\tis_busy_started = %d" % (self.ramdump.read_bool(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'is_busy_started'))))
print_out_ufs("\t\tis_suspended = %d" % (self.ramdump.read_bool(
cs_addr + self.ramdump.field_offset('struct ufs_clk_scaling', 'is_suspended'))))
print_out_ufs("\t}")
print_out_ufs("\tis_sys_suspended = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'is_sys_suspended'))))
print_out_ufs("\turgent_bkops_lvl = %s" % (self.ufs_bkops_status_l[(self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'urgent_bkops_lvl')))]))
print_out_ufs("\tis_urgent_bkops_lvl_checked = %d" % (self.ramdump.read_bool(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'is_urgent_bkops_lvl_checked'))))
print_out_ufs("\tscsi_block_reqs_cnt = %d" % (self.ramdump.read_int(
self.ufs_hba_addr + self.ramdump.field_offset('struct ufs_hba', 'scsi_block_reqs_cnt'))))
self.dump_ufs_vreg()
self.dump_ufs_clk()
self.dump_ufs_lrb()
self.dump_sdev_info()
print_out_ufs("}")
return
class UfsQcHost():
def __init__(self, ramdump, ufs_qc_host_addr):
self.ramdump = ramdump
self.ufs_qc_host_addr = ufs_qc_host_addr
def get_qphy_vreg_info(self, qphy_vreg_addr):
if qphy_vreg_addr == 0:
return
reg_p = self.ramdump.read_pointer(qphy_vreg_addr + self.ramdump.field_offset(
'struct ufs_qcom_phy_vreg', 'reg'))
if reg_p == 0:
return
print_out_ufs("\t\tphy-vreg-%s{" %(self.ramdump.read_structure_cstring(
qphy_vreg_addr,
'struct ufs_qcom_phy_vreg',
'name')))
rdev_p = self.ramdump.read_pointer(reg_p + self.ramdump.field_offset(
'struct regulator', 'rdev'))
rdesc_p = self.ramdump.read_pointer(rdev_p + self.ramdump.field_offset(
'struct regulator_dev','desc'))
print_out_ufs("\t\t\tregulator: %s" %(self.ramdump.read_structure_cstring(
rdesc_p, 'struct regulator_desc', 'name')))
print_out_ufs("\t\t\tsupply: %s" %(self.ramdump.read_structure_cstring(
rdesc_p,
'struct regulator_desc',
'supply_name')))
print_out_ufs("\t\t\tenabled = %d" % (self.ramdump.read_bool(qphy_vreg_addr +
self.ramdump.field_offset(
'struct ufs_qcom_phy_vreg',
'enabled'))))
print_out_ufs("\t\t}")
def dump_ufs_phy(self):
gphy_p = self.ramdump.read_pointer(self.ufs_qc_host_addr + self.ramdump.field_offset(
'struct ufs_qcom_host',
'generic_phy'))
dev_addr = gphy_p + self.ramdump.field_offset('struct phy', 'dev')
qphy_p = self.ramdump.read_pointer(dev_addr + self.ramdump.field_offset(
'struct device',
'driver_data'))
print_out_ufs("\tufs_phy: %s {" %(self.ramdump.read_cstring(
qphy_p + self.ramdump.field_offset(
'struct ufs_qcom_phy',
'name'))))
print_out_ufs("\t\tiface_clk_enabled = %d" % (self.ramdump.read_bool(qphy_p +
self.ramdump.field_offset(
'struct ufs_qcom_phy',
'is_iface_clk_enabled'))))
print_out_ufs("\t\tref_clk_enabled = %d" % (self.ramdump.read_bool(qphy_p +
self.ramdump.field_offset(
'struct ufs_qcom_phy',
'is_ref_clk_enabled'))))
print_out_ufs("\t\tdev_ref_clk_enabled = %d" % (self.ramdump.read_bool(qphy_p +
self.ramdump.field_offset(
'struct ufs_qcom_phy',
'is_dev_ref_clk_enabled'))))
print_out_ufs("\t\tquirks = 0x%x" % (self.ramdump.read_s64(qphy_p +
self.ramdump.field_offset(
'struct ufs_qcom_phy',
'quirks'))))
print_out_ufs("\t\tlanes_per_direction = %d" % (self.ramdump.read_u32(qphy_p +
self.ramdump.field_offset(
'struct ufs_qcom_phy',
'lanes_per_direction'))))
vdda_pll_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'vdda_pll')
vdda_phy_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'vdda_phy')
vddp_ref_clk_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'vddp_ref_clk')
self.get_qphy_vreg_info(vdda_pll_addr)
self.get_qphy_vreg_info(vdda_phy_addr)
self.get_qphy_vreg_info(vddp_ref_clk_addr)
if self.ramdump.get_kernel_version() > (5, 11, 0):
vdd_phy_gdsc_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'vdd_phy_gdsc')
vdda_qref_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'vdda_qref')
self.get_qphy_vreg_info(vdd_phy_gdsc_addr)
self.get_qphy_vreg_info(vdda_qref_addr)
print_out_ufs("\t}")
def dump_ufs_qc_params(self):
print_out_ufs("struct ufs_qc_host = 0x%x {" % (self.ufs_qc_host_addr))
caps_offset = self.ramdump.field_offset('struct ufs_qcom_host', 'caps')
caps_addr = self.ufs_qc_host_addr + caps_offset
print_out_ufs("\tcaps = %d" %(self.ramdump.read_int(caps_addr)))
# Bus Vote
bus_vote_offset = self.ramdump.field_offset('struct ufs_qcom_host', 'bus_vote')
bus_vote_addr = self.ufs_qc_host_addr + bus_vote_offset
print_out_ufs("\tbus_vote = {")
print_out_ufs("\t\tcurr_vote = %d" %(self.ramdump.read_int(
bus_vote_addr + self.ramdump.field_offset('struct ufs_qcom_bus_vote', 'curr_vote'))))
print_out_ufs("\t\tmin_vote = %d" %(self.ramdump.read_int(
bus_vote_addr + self.ramdump.field_offset('struct ufs_qcom_bus_vote', 'min_bw_vote'))))
print_out_ufs("\t\tmax_vote = %d" % (self.ramdump.read_int(
bus_vote_addr + self.ramdump.field_offset('struct ufs_qcom_bus_vote', 'max_bw_vote'))))
print_out_ufs("\t\tsaved_vote = %d" % (self.ramdump.read_int(
bus_vote_addr + self.ramdump.field_offset('struct ufs_qcom_bus_vote', 'saved_vote'))))
print_out_ufs("\t\tis_max_bw_needed = %d" % (self.ramdump.read_int(
bus_vote_addr + self.ramdump.field_offset('struct ufs_qcom_bus_vote', 'is_max_bw_needed'))))
print_out_ufs("\t}")
# PA Layer Attributes
drp_offset = self.ramdump.field_offset('struct ufs_qcom_host', 'dev_req_params')
drp_addr = self.ufs_qc_host_addr + drp_offset
print_out_ufs("\tdev_req_params = {")
print_out_ufs("\t\tgear_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_rx'))))
print_out_ufs("\t\tgear_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'gear_tx'))))
print_out_ufs("\t\tlane_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_rx'))))
print_out_ufs("\t\tlane_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'lane_tx'))))
print_out_ufs("\t\tpwr_rx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_rx'))))
print_out_ufs("\t\tpwr_tx = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'pwr_tx'))))
print_out_ufs("\t\ths_rate = %d" % (self.ramdump.read_int(
drp_addr + self.ramdump.field_offset('struct ufs_pa_layer_attr', 'hs_rate'))))
print_out_ufs("\t}")
print_out_ufs("\tis_lane_clks_enabled = %d" %(self.ramdump.read_bool(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'is_lane_clks_enabled'))))
# HW Version
print_out_ufs("\thw_ver = {")
hw_ver_offset = self.ramdump.field_offset('struct ufs_qcom_host', 'hw_ver')
hw_ver_addr = self.ufs_qc_host_addr + hw_ver_offset
print_out_ufs("\t\tstep = %d" %(self.ramdump.read_byte(
hw_ver_addr + self.ramdump.field_offset('struct ufs_hw_version', 'step'))))
print_out_ufs("\t\tminor = %d" % (self.ramdump.read_byte(
hw_ver_addr + self.ramdump.field_offset('struct ufs_hw_version', 'minor'))))
print_out_ufs("\t\tmajor = %d" % (self.ramdump.read_byte(
hw_ver_addr + self.ramdump.field_offset('struct ufs_hw_version', 'major'))))
print_out_ufs("\t}")
print_out_ufs("\tdev_ref_clk_en_mask = 0x%x" %(self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'dev_ref_clk_en_mask'))))
print_out_ufs("\tdbg_print_en = 0x%x" %(self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'dbg_print_en'))))
if self.ramdump.get_kernel_version() < (6, 5, 0):
print_out_ufs("\tlimit_tx_hs_gear = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_tx_hs_gear'))))
print_out_ufs("\tlimit_rx_hs_gear = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_rx_hs_gear'))))
print_out_ufs("\tlimit_tx_pwm_gear = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_tx_pwm_gear'))))
print_out_ufs("\tlimit_rx_pwm_gear = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_rx_pwm_gear'))))
print_out_ufs("\tlimit_rate = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_rate'))))
print_out_ufs("\tlimit_phy_submode = %d" % (self.ramdump.read_int(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'limit_phy_submode'))))
else:
pwr_cap_addr = self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'host_pwr_cap')
print_out_ufs("\tlimit_tx_hs_gear = %d" % (self.ramdump.read_u32(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'hs_tx_gear'))))
print_out_ufs("\tlimit_rx_hs_gear = %d" % (self.ramdump.read_u32(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'hs_rx_gear'))))
print_out_ufs("\tlimit_tx_pwm_gear = %d" % (self.ramdump.read_u32(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'pwm_tx_gear'))))
print_out_ufs("\tlimit_rx_pwm_gear = %d" % (self.ramdump.read_u32(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'pwm_rx_gear'))))
print_out_ufs("\tlimit_rate = %d" % (self.ramdump.read_int(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'hs_rate'))))
print_out_ufs("\tlimit_phy_submode = %d" % (self.ramdump.read_int(
pwr_cap_addr + self.ramdump.field_offset('struct ufs_qcom_dev_params', 'phy_submode'))))
print_out_ufs("\tdisable_lpm = %d" % (self.ramdump.read_bool(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'disable_lpm'))))
print_out_ufs("\twork_pending = %d" % (self.ramdump.read_bool(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'work_pending'))))
print_out_ufs("\tis_phy_pwr_on = %d" % (self.ramdump.read_bool(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'is_phy_pwr_on'))))
print_out_ufs("\terr_occurred = %d" % (self.ramdump.read_bool(
self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'err_occurred'))))
#QoS
print_out_ufs("\tqos = {\n\t\tNot Implemented\n\t}")
self.dump_ufs_phy()
print_out_ufs("}")
return
def get_ufs_hba(self):
hba_offset = self.ramdump.field_offset('struct ufs_qcom_host', 'hba')
return self.ramdump.read_pointer(self.ufs_qc_host_addr + hba_offset)
class UfsIpc():
phase_l = ['PRE ', 'POST ']
state_l = ['CLK_OFF ', 'CLK_ON ']
pmop_l = ['RT_PM ', 'SYS_PM', 'SHUTDOWN_PM ']
link_l = ['OFF ', 'ACTIVE', 'HIBERN8 ', 'BROKEN ']
pwrmod_l = ['NONE ', 'ACTIVE ', 'SLEEP ', 'PWRDOWN ', 'DPSLEEP ']
def __init__(self, ramdump, ufs_ipc_file):
self.ramdump = ramdump
self.ipc_file = ufs_ipc_file
def ufsipcdict(self, line2, line):
return{
'<': lambda: ' [Send cmd] ' + str(line[3]) + ' tag:' + str(line[4]) + ' DBR/HWQ:' + str(line[5]) + ' size=' + str(line[6]),
'>': lambda: ' [Done cmd] ' + str(line[3]) + ' tag:' + str(line[4]) + ' DBR/HWQ:' + str(line[5]) + ' size=' + str(line[6]),
'(': lambda: ' [Send uic] ' + str(line[3]) + ' arg1=' + str(line[4]) + ' arg2=' + str(line[5]) + ' arg3=' + str(line[6]),
')': lambda: ' [Done uic] ' +str(line[3]) + ' arg1=' + str(line[4]) + ' arg2=' + str(line[5]) + ' arg3=' + str(line[6]),
'#': lambda: ' [CLK GATE] ' + self.phase_l[int(line[3])] + self.state_l[int(line[4])] + 'err=' + str(line[5]),
'^': lambda: ' [CLK SCALE UP] ' + self.phase_l[int(line[3])] + 'err=' + str(line[4]),
'v': lambda: ' [CLK SCALE DOWN] ' + self.phase_l[int(line[3])] + 'err =' + str(line[4]),
'@': lambda: ' [PWR CHANGE] ' + self.phase_l[int(line[3])] + 'GEAR' + str(line[4]) + ' cur_dev_pwr_mode=' + str(line[5]) + ' rate=' + str(line[6]) + ' err=' + str(line[7]),
'&': lambda: ' [SUSPEND] ' + self.pmop_l[int(line[3])] + 'rpm_lvl=' + str(line[4]) + ' spm_lvl=' + str(line[5]) + ' link_state: ' + self.link_l[int(line[6])]
+ 'cur_dev_pwr_mode: ' + self.pwrmod_l[int(line[7])] + 'err=' + str(line[8]),
'$': lambda: ' [RESUME] ' + self.pmop_l[int(line[3])] + 'rpm_lvl=' + str(line[4]) + ' spm_lvl=' + str(line[5]) + ' link_state: ' + self.link_l[int(line[6])]
+ 'cur_dev_pwr_mode: ' + self.pwrmod_l[int(line[7])] + 'err = ' + str(line[8]),
'-': lambda: ' [HCE] ' + self.phase_l[int(line[3])] + 'err= ' + str(line[4]),
'*': lambda: ' [LSS] ' + self.phase_l[int(line[3])] + 'err= ' + str(line[4]),
'_': lambda: ' [ERROR] ' + ' hba_err=' + str(line[3]) + ' uic_err=' + str(line[4]),
'!': lambda: ' [ESI] ' + ' irq=' + str(line[3]) + ' msi_index=' + str(line[4]),
'0xdead': lambda: ' [SHUTDOWN] ',
}.get(line2, None)()
def parse_ufs_ipc(self):
print_out_ufs('\n\n==================Parsed UFS IPC Log===========================')
file_path = os.path.join(self.ramdump.outdir, self.ipc_file)
if not os.path.exists(file_path):
print_out_ufs("\tThe file %s dose not exist!!!" %file_path)
return
with open(file_path) as csv_file:
csv_reader = csv.reader(csv_file)
for line in csv_reader:
if len(line) < 3:
continue
try:
result = str(line[0]) + ' cpu' + str(line[1])
result += self.ufsipcdict(line[2], line)
except:
print_out_ufs("\texception occurred during parsing '%s'" %str(line))
continue
print_out_ufs("\t%s" %result)
class UfsRegs():
def __init__(self, ramdump, ufs_qc_host_addr):
self.ramdump = ramdump
self.ufs_qc_host_addr = ufs_qc_host_addr
def dump_ufs_regs(self, buf, word, prefix):
for i in range(word):
if i%4 == 0:
offest = (i//4)*0x10
print_out_ufs('\t%s:' %prefix, False)
print_out_ufs('\t%04x:' %offest, False)
if i%4 == 3 or i == word-1:
print_out_ufs('\t%08x' %self.ramdump.read_u32(buf + i*4))
else:
print_out_ufs('\t%08x' %self.ramdump.read_u32(buf + i*4), False)
def get_ufs_regs(self, regs_list_addr):
regs_list_next = self.ramdump.read_pointer(regs_list_addr + self.ramdump.field_offset('struct list_head', 'next'))
while regs_list_next != regs_list_addr:
prefix = self.ramdump.read_structure_cstring(regs_list_next, 'struct ufs_qcom_regs', 'prefix')
len = self.ramdump.read_u32(regs_list_next + self.ramdump.field_offset('struct ufs_qcom_regs', 'len'))
buf = self.ramdump.read_pointer(regs_list_next + self.ramdump.field_offset('struct ufs_qcom_regs', 'ptr'))
self.dump_ufs_regs(buf, len//4, prefix)
regs_list_next = self.ramdump.read_pointer(regs_list_next + self.ramdump.field_offset(
'struct list_head', 'next'))
def parse_ufs_regs(self):
gphy_p = self.ramdump.read_pointer(self.ufs_qc_host_addr + self.ramdump.field_offset(
'struct ufs_qcom_host',
'generic_phy'))
dev_addr = gphy_p + self.ramdump.field_offset('struct phy', 'dev')
qphy_p = self.ramdump.read_pointer(dev_addr + self.ramdump.field_offset(
'struct device',
'driver_data'))
host_regs_list_addr = self.ufs_qc_host_addr + self.ramdump.field_offset('struct ufs_qcom_host', 'regs_list_head')
phy_regs_list_addr = qphy_p + self.ramdump.field_offset('struct ufs_qcom_phy', 'regs_list_head')
print_out_ufs('\n\n===========================Parsed UFS Registers====================================')
self.get_ufs_regs(host_regs_list_addr)
self.get_ufs_regs(phy_regs_list_addr)
@register_parser('--ufs-parser', 'Generate UFS diagnose report', optional=True)
class UfsParser(RamParser):
def parse(self):
try:
if self.ramdump.get_kernel_version() < (4, 20, 0):
return
except:
return
setup_out_file(F_UFSDEBUG, self)
print_out_ufs("Kernel Version - %d.%d.%d" %(self.ramdump.get_kernel_version()))
# struct ufs_qcom dump
ufs_qc_hosts_ptr = self.ramdump.read_pointer('ufs_qcom_hosts')
ufs_qc_host_addr = self.ramdump.array_index(ufs_qc_hosts_ptr, 'struct ufs_qcom_host', 0)
ufsqc_0 = UfsQcHost(self.ramdump, ufs_qc_host_addr)
ufsqc_0.dump_ufs_qc_params()
# struct ufs_hba dump
ufs_hba_0 = UfsHba(self.ramdump, ufsqc_0.get_ufs_hba())
ufs_hba_0.dump_ufs_hba_params()
# ufs ipc log parser
ufs_ipc_0 = UfsIpc(self.ramdump, F_UFSIPC)
ufs_ipc_0.parse_ufs_ipc()
# ufs register parser
ufs_regs_0 = UfsRegs(self.ramdump, ufs_qc_host_addr)
ufs_regs_0.parse_ufs_regs()
return

View File

@@ -0,0 +1,953 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
import re
import parser_util
import print_out
from parser_util import register_parser, RamParser
gsi_set = {'rmnet', 'rndis', 'ecm', 'mbim'}
dwc3_cmd_types = ["Disconnected", "USB Reset", "U/L State Change", "Wakeup/Resume",
"Hibernation Req", "Suspend Entry", "Microframe Start", "L1 Suspend",
"Erratic Error", "Command Complete", "EV Buffer Overflow",
"Vendor DevTest Received", "Stopped on Disconnect", "L1 Resume Detected",
"LDM Response Received"]
# maps driver names to struct names
hsphy_name_map = {'msm-qusb-phy': 'qusb_phy', 'msm_eusb2_phy': 'msm_eusb2_phy',
'msm_usb_hsphy': 'msm_hsphy', 'msm-qusb-phy-v2': 'qusb_phy'}
phy_clk_enabled_map = {'struct msm_hsphy': 'clocks_enabled',
'struct msm_eusb2_phy': 'clocks_enabled',
'struct qusb_phy': 'None', 'struct msm_ssphy_qmp': 'clk_enabled'}
connected_mask = 0b01
softconnect_mask = 0b10
def parse_dwc3_ep_event(evt):
"""
Parses dwc3 endpoint related event from the event buffer
EP events will end with a 0 bit
:param evt: 4 byte event
:return: String command interpreting the event
"""
ep_num = "EP " + str((evt & (0x1f << 1)) >> 1)
evt_type = (evt & (0xf << 6)) >> 6
evt_status = (evt & (0xf << 12)) >> 12
upper_bits = (evt & (0xffff << 16)) >> 16
if evt_type == 1:
# XferComplete
trb_ioc = (evt_status & 0b100) >> 2
return f"{ep_num} Xfer Complete, TRB IOC = {trb_ioc}"
elif evt_type == 2:
# Xfer In Progress
trb_ioc = (evt_status & 0b100) >> 2
return f"{ep_num} Xfer In Progress, TRB IOC = {trb_ioc}"
elif evt_type == 3:
# Xfer Not Ready
xfer_active = (evt_status & 0b1000) >> 3
req_type = evt_status & 0b11
xfer_reason = "Requested transfer not present in hardware"
if xfer_active == 1:
xfer_reason = "No valid TRBs available"
ctrl_req_type = "Ctrl Data Req"
if req_type == 0b10:
ctrl_req_type = "Ctrl Status Req"
return f"{ep_num} Xfer Not Ready, Type: {ctrl_req_type}. {xfer_reason}"
elif evt_type == 6:
# Stream Event
s = "ERROR"
if evt_status == 1:
s = "StreamFound"
elif evt_status == 2:
s = "StreamNotFound"
return f"{ep_num} Stream Event. {s}"
elif evt_type == 7:
# EP Command Complete
cmd_type = (upper_bits & 0xf00) >> 8
xfer_rsc_idx = upper_bits & 0x7f
return f"{ep_num} Command Complete, Type: {cmd_type}, Xfer Resource Idx: " \
f"{xfer_rsc_idx}, Status: {evt_status}"
else:
return f"UNKNOWN EVENT TYPE: {evt_type}"
def parse_dwc3_dvc_event(evt):
"""
Parses a dwc3 device event from the event buffer
Device events will end with 0x01
:param evt: 4 byte event
:return: String command interpreting the event
"""
evt_type = (evt & (0xf << 8)) >> 8
return dwc3_cmd_types[evt_type]
def trb_status(num_active, name, is_tx=False):
"""
Returns a message about how full the given trb is based on num_active
:param num_active: Number of active (non-empty, in use) TRBs in the ring
:param name: Ring name
:param is_tx: Transfer or receive ring (used for case where num_active == 0)
:return: String message
"""
if num_active == 0:
s = "Host PC has no data to send"
if is_tx:
s = "No data received from IPA GSI. Please check with IPA GSI team"
return f"{name} is EMPTY. {s}"
elif num_active == 256:
return f"{name} is Full with pending data to be picked up by GSI"
elif num_active >= 256 * 0.9:
return f"{name} is almost Full with pending data to be picked up by GSI"
else:
return f"{name} has {num_active} buffers to be picked up by GSI"
def read_endpoint_mask(mask):
"""
Given a bitmask of endpoints (such as from struct usb_function), returns a string of all
endpoints used by the bitmask in ascending order, with outbound eps before inbound eps
:param mask: 32 bit mask representing
:return: String of format 'name1 name2 name3 ...'
"""
ret = ""
for i in range(32):
if mask & (1 << i) == (1 << i):
io = "out "
if i >= 16:
io = "in "
ret += "ep" + str(i % 16) + io
return ret
# Optional = True here because if --everything is called we only want --usbv to be run
@register_parser('--usb', 'Usb device info', optional=True)
class UsbReader(RamParser):
def __init__(self, dump):
super(UsbReader, self).__init__(dump)
self.outfile = None # initialized in parse so that usbv can have a separate output file
self.outfile_name = 'usb_output.txt'
# Lets us know if we are on 64 or 32 bit architecture
self.ptr_size = self.ramdump.sizeof("int*")
self.device_driver_data_offset = self.ramdump.field_offset('struct device', 'driver_data')
self.device_driver_offset = self.ramdump.field_offset('struct device', 'driver')
self.device_parent_offset = self.ramdump.field_offset('struct device', 'parent')
self.device_kobj_offset = self.ramdump.field_offset('struct device', 'kobj')
self.kobj_entry_offset = self.ramdump.field_offset('struct kobject', 'entry')
self.kobj_name_offset = self.ramdump.field_offset('struct kobject', 'name')
self.dwc3_ep_trb_pool_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_pool')
def display_ring(self, addr, enqueue=-1, dequeue=-1):
"""
Creates a string that displays the ring contents at addr
:param addr: Ring address
:param enqueue: Position of the enqueue pointer, -1 if not in this segment
:param dequeue: Position of the dequeue pointer, -1 if not in this segment
:return: String containing ring contents
"""
hdump = self.ramdump.hexdump(addr, 256 * 16, little_endian=False)
if enqueue >= 0:
split = hdump.splitlines()
split[enqueue] += "<- Enqueue pointer "
hdump = "\n".join(split)
if dequeue >= 0:
split = hdump.splitlines()
split[dequeue] += "<- Dequeue pointer"
hdump = "\n".join(split)
pattern = re.compile(
"(([a-fA-F0-9]{" + str(self.ptr_size * 2) + "}: (0000 ){6}([0-9]{4} ){2} \\.+\n){2,})")
# TRBs are skipped if BPL, BPH, and CTRL are 0
for match in pattern.findall(hdump):
num_lines = match[0].count("\n")
hdump = re.sub(pattern, "Skipping " + str(num_lines) + " empty TRBs\n", hdump, count=1)
return hdump
def count_active_trbs(self, trb_ptr):
"""
Finds the number of active (HWO bit == 1) trbs in the given endpoint
:param trb_ptr: Address of the first trb in the endpoint
:return: Number of active trbs
"""
num_active = 0
for i in range(256):
ctrl = self.ramdump.read_u32(
trb_ptr + (i * 16) + self.ramdump.field_offset('struct dwc3_trb', 'ctrl'))
if ctrl % 2 == 1:
num_active += 1
return num_active
def find_faulty_request(self, ep, fault_addr):
"""
Helper method for self.findCrashAddr()
Examines each trb in ep to see if it tried to read memory from faultaddr
Prints
:param ep: Device endpoint containing TRB that caused the fault
:param fault_addr: TRB address that caused the arm-smmu fault
:return: Address of the faulty request if found, or None
"""
# offsets
list_head_next_offset = self.ramdump.field_offset('struct list_head', 'next')
dwc_req_num_trbs_offset = self.ramdump.field_offset('struct dwc3_request', 'num_trbs')
dwc_req_trb_offset = self.ramdump.field_offset('struct dwc3_request', 'trb')
dwc3_req_list_offset = self.ramdump.field_offset('struct dwc3_request', 'list')
req_list_h = self.ramdump.struct_field_addr(ep, 'struct dwc3_ep', 'started_list')
curr_req_node = self.ramdump.read_pointer(req_list_h)
while curr_req_node != req_list_h:
curr_req = curr_req_node - dwc3_req_list_offset
num_trbs = self.ramdump.read_int(curr_req + dwc_req_num_trbs_offset)
trbs = curr_req + dwc_req_trb_offset
for i in range(num_trbs):
curr_trb = self.ramdump.read_pointer(trbs + (i * self.ptr_size))
if curr_trb == fault_addr:
return curr_req
curr_req_node = self.ramdump.read_pointer(curr_req_node + list_head_next_offset)
return None
def find_crash_addr(self, eps, fault_addr):
"""
Finds the TRB that caused an arm-smmu context fault, if possible
Prints the address of the context fault and the address of the TRB causing it,
or that none was found
:param eps: Address of the device endpoints
:param fault_addr: Address that was accessed to cause the fault
:return: Tuple: (Endpoint addr holding trb that caused the fault,
TRB addr that caused the fault)"""
# offsets
bpl_offset = self.ramdump.field_offset('struct dwc3_trb', 'bpl')
bph_offset = self.ramdump.field_offset('struct dwc3_trb', 'bph')
size_offset = self.ramdump.field_offset('struct dwc3_trb', 'size')
ctrl_offset = self.ramdump.field_offset('struct dwc3_trb', 'ctrl')
for ep in eps:
trb_pool = self.ramdump.read_pointer(ep + self.dwc3_ep_trb_pool_offset)
for i in range(256):
trb = trb_pool + (i * 16)
bpl = self.ramdump.read_u32(trb + bpl_offset)
bph = self.ramdump.read_u32(trb + bph_offset)
bpl += (bph << 32)
size = self.ramdump.read_u32(trb + size_offset)
hwo = self.ramdump.read_byte(trb + ctrl_offset)
if bpl <= fault_addr < bpl + size and (hwo & 0x01) == 1:
return ep, trb
return None, None
def read_xhci_ring(self, xhci_ring, name):
"""
Returns a string containing the name of the xhci ring and the locations of its enqueue and
dequeue pointers and its first segment
:param xhci_ring: Address of the xhci_ring struct
:param name: Name of the xhci_ring
:return: String containing the details of the xhci ring
"""
first_seg_addr = self.ramdump.struct_field_addr(xhci_ring, 'struct xhci_ring', 'first_seg')
eq = self.ramdump.read_structure_field(xhci_ring, 'struct xhci_ring', 'enqueue')
dq = self.ramdump.read_structure_field(xhci_ring, 'struct xhci_ring', 'dequeue')
ring_struct = f"{name}\n\tEnqueue pointer located at: {hex(eq)}" \
f"\n\tDequeue pointer located at: {hex(dq)}\n" \
f"\tFirst segment located at: {hex(first_seg_addr)}\n"
return ring_struct
def print_ev_buf(self, buf, num_evts, lpos):
"""
Prints the last num_evts events in the event buffer, interpreted as human-readable commands
:param buf: Event Buffer address
:param num_evts: Number of events to print
:param lpos: Position of the last event in the event buffer
:return: None
"""
print(f"\nEvent Buffer {hex(buf)}, last {num_evts} events:", file=self.outfile)
for i in reversed(range(lpos - (num_evts * 4) + 4, lpos + 4, 4)):
evt = self.ramdump.read_u32(buf + i)
# events can be device-specific (final 8 bits = 0x01) or ep-specific (final bit = 0)
if evt is not None:
s = ""
if evt & 0xff == 1:
s = parse_dwc3_dvc_event(evt)
elif evt & 1 == 0:
s = parse_dwc3_ep_event(evt)
print(f"\t{hex(buf + i)}: {s}", file=self.outfile)
def clock_is_enabled(self, phy_addr, phy_name, clk_name):
"""
Returns whether the phy's given clock is enabled or not
:param phy_addr: Phy address
:param phy_name: Phy name
:param clk_name: Clock name
:return: "Disabled" if clk->core.enabled_count == 0, "Enabled" otherwise
"""
clk = self.ramdump.read_pointer(self.ramdump.struct_field_addr(phy_addr, phy_name,
clk_name))
if clk is not None:
core = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(clk, 'struct clk', 'core'))
enabled = self.ramdump.read_u32(
self.ramdump.struct_field_addr(core, 'struct clk_core', 'enabled_count'))
return "disabled" if enabled == 0 else "enabled"
def regulator_is_enabled(self, phy_addr, phy_name, reg_name):
"""
Returns whether the phy's given regulator is enabled or not
:param phy_addr: Phy address
:param phy_name: Phy name
:param reg_name: Regulator name
:return: "Disabled" if regulator.enabled_count == 0, "Enabled" otherwise
"""
reg = self.ramdump.read_pointer(self.ramdump.struct_field_addr(phy_addr, phy_name,
reg_name))
if reg is not None:
enabled = self.ramdump.read_u32(
self.ramdump.struct_field_addr(reg, 'struct regulator', 'enabled_count'))
return "disabled" if enabled == 0 else "enabled"
def find_device_fields(self, device, field_names):
"""
Returns the fields of all properties in field_names
:param device: Device
:param field_names: Names of properties to investigate
:return: 2D-Array containing property values in the order of their corresponding field_names
"""
ret_fields = [[]] * len(field_names)
of_node = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(device, 'struct device', 'of_node'))
properties = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(of_node, 'struct device_node', 'properties'))
while properties != 0:
name = self.ramdump.read_cstring(self.ramdump.read_pointer(properties))
if name in field_names:
length = self.ramdump.read_int(
self.ramdump.struct_field_addr(properties, 'struct property', 'length'))
val = self.ramdump.read_binarystring(self.ramdump.read_pointer(
self.ramdump.struct_field_addr(properties, 'struct property', 'value')),
length).decode()
ret_fields[field_names.index(name)] = val.split('\x00')[:-1]
properties = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(properties, 'struct property', 'next'))
return ret_fields
def find_usb_devs(self):
"""
Searches usb_bus_type's klist for all usb devices
:return: A list of all usb devices found
"""
usb_bus = self.ramdump.read('usb_bus_type')
usb_type = self.ramdump.read('usb_device_type')
p = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(usb_bus, 'struct bus_type', 'p'))
list_h = self.ramdump.struct_field_addr(self.ramdump.struct_field_addr(
p, 'struct subsys_private', 'klist_devices'), 'struct klist', 'k_list')
next_dv = self.ramdump.read_pointer(list_h)
ret = []
klist_nnode_offset = self.ramdump.field_offset('struct klist_node', 'n_node')
dev_priv_knode_bus_offset = self.ramdump.field_offset('struct device_private', 'knode_bus')
dev_priv_device_offset = self.ramdump.field_offset('struct device_private', 'device')
dev_type_offset = self.ramdump.field_offset('struct device', 'type')
while next_dv != list_h:
knode_bus = next_dv - klist_nnode_offset
dev_priv = knode_bus - dev_priv_knode_bus_offset
dev = self.ramdump.read_pointer(dev_priv + dev_priv_device_offset)
dev_type = self.ramdump.read_pointer(dev + dev_type_offset)
if dev_type and dev_type == usb_type:
ret.append(dev)
next_dv = self.ramdump.read_pointer(next_dv)
return ret
def cleanup(self):
"""
Closes self.outfile and writes a short message to ramparse's outfile
:return: None
"""
print_out.print_out_str('--- Wrote the output to ' + self.outfile_name)
self.outfile.close()
def search_udc_list(self, pattern):
"""
Searches udc_list for the first device whose name matches the given regex
:param pattern: Compiled regex
:return: The address of the matching device, or None
"""
usb_udc_list_offset = self.ramdump.field_offset('struct usb_udc', 'list')
usb_udc_dev_offset = self.ramdump.field_offset('struct usb_udc', 'dev')
list_h = self.ramdump.address_of('udc_list')
next_dv = self.ramdump.read_pointer(list_h)
while next_dv != list_h:
dvc_addr = next_dv - usb_udc_list_offset
dev = dvc_addr + usb_udc_dev_offset
kobj = dev + self.device_kobj_offset
name = self.ramdump.read_cstring(
self.ramdump.read_pointer(kobj + self.kobj_name_offset))
if name and pattern.match(name) is not None:
return self.ramdump.read_pointer(dev + self.device_parent_offset)
next_dv = self.ramdump.read_pointer(next_dv)
return None
def search_device_kset(self, name_regexes):
"""
Search devices_kset for all devices whose names match the input regexes
:param name_regexes: List of compiled regexes
:return: List of the last devices found to match each regex,
in the same index as the matching regex
"""
devices_kset = self.ramdump.read('devices_kset')
list_h = self.ramdump.struct_field_addr(devices_kset, 'struct kset', 'list')
next_dv = self.ramdump.read_pointer(list_h)
devs = [0] * len(name_regexes)
while next_dv != list_h and next_dv is not None:
dvc_addr = next_dv - (self.device_kobj_offset + self.kobj_entry_offset)
name = self.ramdump.read_cstring(self.ramdump.read_pointer(dvc_addr))
if name:
for i in range(len(name_regexes)):
if name_regexes[i].search(name) and self.ramdump.read_pointer(
dvc_addr + self.device_driver_data_offset) != 0:
devs[i] = dvc_addr
next_dv = self.ramdump.read_pointer(next_dv)
return devs
def read_substream_formats(self, substream):
"""
Given a snd_usb_substream, print all of its formats
:param substream: Substream address
:return: Formatted string containing substream format details
"""
audio_fmt_list_offset = self.ramdump.field_offset('struct audioformat', 'list')
audio_fmt_channels_offset = self.ramdump.field_offset('struct audioformat', 'channels')
audio_fmt_frame_size_offset = self.ramdump.field_offset('struct audioformat',
'frame_size')
audio_fmt_implicit_fb_offset = self.ramdump.field_offset('struct audioformat',
'implicit_fb')
audio_fmt_rate_table_offset = self.ramdump.field_offset('struct audioformat',
'rate_table')
audio_fmt_nr_rates_offset = self.ramdump.field_offset('struct audioformat', 'nr_rates')
audio_fmt_protocol_offset = self.ramdump.field_offset('struct audioformat', 'protocol')
audio_fmt_formats_offset = self.ramdump.field_offset('struct audioformat', 'formats')
audio_fmt_max_ps_offset = self.ramdump.field_offset('struct audioformat', 'maxpacksize')
int_size = self.ramdump.sizeof('int')
direction = self.ramdump.read_int(
self.ramdump.struct_field_addr(substream, 'struct snd_usb_substream', 'direction'))
fmt_list = self.ramdump.struct_field_addr(substream, 'struct snd_usb_substream', 'fmt_list')
num_formats = self.ramdump.read_int(
self.ramdump.struct_field_addr(substream, 'struct snd_usb_substream', 'num_formats'))
audio_fmt = self.ramdump.read_pointer(fmt_list) - audio_fmt_list_offset
d = "Playback" if direction == 0 else "Recording" if direction == 1 else "INVALID"
ret = f"\tSubstream {hex(substream)}:\n" \
f"\t\tDirection: {d}\n"
for i in range(num_formats):
channels = self.ramdump.read_int(audio_fmt + audio_fmt_channels_offset)
frame_size = self.ramdump.read_int(audio_fmt + audio_fmt_frame_size_offset)
implicit_fb = self.ramdump.read_bool(audio_fmt + audio_fmt_implicit_fb_offset)
rate_table = audio_fmt + audio_fmt_rate_table_offset
nr_rates = self.ramdump.read_int(audio_fmt + audio_fmt_nr_rates_offset)
formats = self.ramdump.read_int(audio_fmt + audio_fmt_formats_offset)
protocol = self.ramdump.read_byte(audio_fmt + audio_fmt_protocol_offset)
maxpacksize = self.ramdump.read_int(audio_fmt + audio_fmt_max_ps_offset)
rate_str = ""
for j in range(nr_rates):
rate_str += str(self.ramdump.read_int(self.ramdump.read_pointer(rate_table)
+ j * int_size)) + ", "
ret += f'\t\tFormat {i+1} ({hex(audio_fmt)}):\n' \
f'\t\t\tChannels: {channels}\n' \
f'\t\t\tFrame Size: {frame_size}\n' \
f'\t\t\tImplicit Feedback: {implicit_fb}\n' \
f'\t\t\tPossible rates: {rate_str[:-2]}\n' \
f'\t\t\tFormats: {bin(formats)}\n' \
f'\t\t\tProtocol: {hex(protocol)}\n' \
f'\t\t\tMax packet size: {maxpacksize}\n'
audio_fmt = self.ramdump.read_pointer(audio_fmt)
return ret
def usb_audio_details(self, snd_usb_audio):
"""
Finds the various parameters of the snd_audio driver passed in
:param snd_usb_audio: Address of the snd_usb_audio driver
:return: String with audio summary
"""
pcm_list = self.ramdump.struct_field_addr(snd_usb_audio, 'struct snd_usb_audio', 'pcm_list')
next_pcm = self.ramdump.read_pointer(pcm_list)
ret = ""
if next_pcm is not None:
# offsets
snd_stream_list_offset = self.ramdump.field_offset('struct snd_usb_stream', 'list')
snd_stream_substream_offset = self.ramdump.field_offset('struct snd_usb_stream',
'substream')
visited_pcm = [pcm_list]
while next_pcm not in visited_pcm:
snd_stream = next_pcm - snd_stream_list_offset
substream = snd_stream + snd_stream_substream_offset
substream2 = substream + self.ramdump.sizeof('struct snd_usb_substream')
ret += self.read_substream_formats(substream)
ret += self.read_substream_formats(substream2)
visited_pcm.append(next_pcm)
next_pcm = self.ramdump.read_pointer(next_pcm)
return ret
def print_clocks_and_regs(self, addr, phy_type):
"""
Prints the clocks and regulators associated with phy 'addr'
:param addr: Phy address
:param phy_type: Phy struct name ('struct {type}')
:return:
"""
phy = self.ramdump.read_pointer(addr + self.device_driver_data_offset)
clk_enabled = self.ramdump.read_bool(
self.ramdump.struct_field_addr(phy, phy_type, phy_clk_enabled_map[phy_type]))
power_enabled = self.ramdump.read_bool(
self.ramdump.struct_field_addr(phy, phy_type, 'power_enabled'))
print(
f"\t{phy_type}:\n\t\tClock Enabled: {clk_enabled}\n\t\tPower Enabled: "
f"{power_enabled}", file=self.outfile)
fields = self.find_device_fields(addr, ['clock-names', 'reg-names'])
clocks, regs = fields[0], fields[1]
for clk in clocks:
is_enabled = self.clock_is_enabled(phy, phy_type, clk)
if is_enabled is not None:
print(f"\t\tClock {clk}: {is_enabled}", file=self.outfile)
for reg in regs:
is_enabled = self.regulator_is_enabled(phy, phy_type, reg)
if is_enabled is not None:
print(f"\t\tRegulator {reg}: {is_enabled}", file=self.outfile)
def print_function_drivers(self, cdev):
"""
Prints all configs and the associated function drivers to self.outfile
:param cdev: usb_composite_dev associated with dwc3
:return:
"""
config_list_offset = self.ramdump.field_offset('struct usb_configuration', 'list')
config_label_offset = self.ramdump.field_offset('struct usb_configuration', 'label')
config_functions_offset = self.ramdump.field_offset('struct usb_configuration',
'functions')
function_list_offset = self.ramdump.field_offset('struct usb_function', 'list')
function_ep_offset = self.ramdump.field_offset('struct usb_function', 'endpoints')
function_name_offset = self.ramdump.field_offset('struct usb_function', 'name')
active_config = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(cdev, 'struct usb_composite_dev', 'config'))
configs_h = self.ramdump.struct_field_addr(cdev, 'struct usb_composite_dev',
'configs')
configs = self.ramdump.read_pointer(configs_h)
while configs != configs_h:
cfg = configs - config_list_offset
label = self.ramdump.read_cstring(
self.ramdump.read_pointer(cfg + config_label_offset))
is_active = " (Active Cfg)" if cfg == active_config else ""
print(f"Configuration {label}{is_active} Function Drivers:", file=self.outfile)
functions_h = cfg + config_functions_offset
functions = self.ramdump.read_pointer(functions_h)
while functions != functions_h:
fun = functions - function_list_offset
endpoints = self.ramdump.read_pointer(fun + function_ep_offset)
name = self.ramdump.read_cstring(
self.ramdump.read_pointer(fun + function_name_offset))
print(f"{name}\n\tEndpoints: " + read_endpoint_mask(endpoints),
file=self.outfile)
if gsi_set.intersection([name]): # if this function is part of the gsi set
d_port = self.ramdump.struct_field_addr(fun, 'struct f_gsi', 'd_port')
in_ep = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(d_port, 'struct gsi_data_port',
'in_ep'))
out_ep = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(d_port, 'struct gsi_data_port',
'out_ep'))
active_in = self.count_active_trbs(
self.ramdump.read_pointer(in_ep + self.dwc3_ep_trb_pool_offset)) - 1
active_out = self.count_active_trbs(
self.ramdump.read_pointer(out_ep + self.dwc3_ep_trb_pool_offset)) - 1
print(f"\t{trb_status(max(active_in, 0), 'TxRing', is_tx=True)}",
file=self.outfile)
print(f"\t{trb_status(max(active_out, 0), 'RxRing')}", file=self.outfile)
functions = self.ramdump.read_pointer(functions)
configs = self.ramdump.read_pointer(configs)
def get_drivers(self, interfaces, length):
"""
For a host mode device, returns all drivers associated with its interfaces
:param interfaces: Address of the interface list
:param length: Number of interfaces
:return: Names of all drivers, audio details
"""
usb_interface_condition_offset = self.ramdump.field_offset('struct usb_interface',
'condition')
usb_interface_dev_offset = self.ramdump.field_offset('struct usb_interface', 'dev')
driver_names = []
audio = ""
for j in range(length):
iface = self.ramdump.read_pointer(interfaces + (j * self.ptr_size))
condition = self.ramdump.read_byte(
iface + usb_interface_condition_offset)
if condition == 2:
dev = iface + usb_interface_dev_offset
driver = self.ramdump.read_pointer(dev + self.device_driver_offset)
driver_data = self.ramdump.read_pointer(dev + self.device_driver_data_offset)
name = self.ramdump.read_cstring(
self.ramdump.read_pointer(driver))
if name == 'snd-usb-audio':
details = self.usb_audio_details(driver_data)
if details != "":
audio += f'Driver {j}:\n' + details
if name not in driver_names:
driver_names.append(name)
driver_name = "Driver(s):"
if not driver_names: # empty
driver_name += " None,"
for name in driver_names:
driver_name += f" {name},"
return driver_name[:-1], audio
def find_xhci_addrs(self):
"""
Returns all usb devices that have the xhci driver
:return: List of all address of 'struct usb_device's whose driver == xhci_plat_hc_driver
"""
usb_device_dev_offset = self.ramdump.field_offset('struct usb_device', 'dev')
usb_device_parent_offset = self.ramdump.field_offset('struct usb_device', 'parent')
usb_device_bus_offset = self.ramdump.field_offset('struct usb_device', 'bus')
usb_hcd_bus_offset = self.ramdump.field_offset('struct usb_hcd', 'self')
usb_hcd_driver_offset = self.ramdump.field_offset('struct usb_hcd', 'driver')
usb_hcd_priv_offset = self.ramdump.field_offset('struct usb_hcd', 'hcd_priv')
xhci_plat_hc_drv = self.ramdump.read('xhci_plat_hc_driver')
xhci_addrs = []
for dev in self.find_usb_devs():
usb_dev = dev - usb_device_dev_offset
parent = self.ramdump.read_pointer(usb_dev + usb_device_parent_offset)
bus = self.ramdump.read_pointer(usb_dev + usb_device_bus_offset)
if parent == 0:
dev_hcd = bus - usb_hcd_bus_offset
hv_hcd = self.ramdump.read_pointer(dev_hcd + usb_hcd_driver_offset)
if hv_hcd == xhci_plat_hc_drv:
dev_xhci = dev_hcd + usb_hcd_priv_offset
xhci_addrs.append(dev_xhci)
return xhci_addrs
def print_device_endpoints(self, eps):
"""
Prints out a summary of all endpoints given
:param eps: List of addresses pointing to 'struct dwc3_ep's
:return: None
"""
ep_trb_pool_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_pool')
ep_name_offset = self.ramdump.field_offset('struct dwc3_ep', 'name')
ep_eq_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_enqueue')
ep_dq_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_dequeue')
ring_summaries = "\nActive endpoint summaries:\n"
for ep in eps:
trb_pool = self.ramdump.read_pointer(ep + ep_trb_pool_offset)
name = self.ramdump.read_cstring(ep + ep_name_offset)
trb_eq = self.ramdump.read_byte(ep + ep_eq_offset)
trb_dq = self.ramdump.read_byte(ep + ep_dq_offset)
ring_summaries += f"{name}:\n\tAddress: {hex(ep)}\n\tActive trbs: " \
f"{self.count_active_trbs(trb_pool)}\n\tEnqueue Pointer: " \
f"Trb {trb_eq}\n\tDequeue Pointer: Trb {trb_dq}\n"
print(f"{ring_summaries}\n", file=self.outfile)
def parse(self):
"""
Main method for UsbReader. Determines which mode the ram dump is in (device, host,
disconnected), then processes it accordingly. All outputs are written to self.outfile
:return: None
"""
self.outfile = self.ramdump.open_file(self.outfile_name)
# find dwc3 for processing host mode or device mode
# find hsphy and ssphy in case we crashed during init
pat_hsphy = re.compile('^[0-9a-fA-F]{1,16}\\.hsphy$')
pat_ssphy = re.compile('^[0-9a-fA-F]{1,16}\\.ssphy$')
pat_dwc3 = re.compile('^[0-9a-fA-F]{1,16}\\.dwc3$')
name_regexs = [pat_dwc3, pat_hsphy, pat_ssphy]
devs = self.search_device_kset(name_regexs)
dwc3_dev, hsphy, ssphy = devs[0], devs[1], devs[2]
# in case we couldn't find dwc3 this way, try backups through udc_list,
# usb_bus_type, and android_device
if dwc3_dev == 0:
print("dwc3 is not on the platform bus", file=self.outfile)
if self.ramdump.read('udc_list.next') != self.ramdump.read('udc_list.prev'):
# Device mode backup access to dwc3_msm
print('Accessing dwc3 through udc_list\n', file=self.outfile)
pattern = re.compile('[0-9a-fA-F]{1,16}\\.dwc3')
dwc3_dev = self.search_udc_list(pattern)
else:
# Host mode backup access to dwc3_msm
udev = self.find_usb_devs()
if udev: # non-empty
print("accessing dwc3 through usb_bus_type\n", file=self.outfile)
xhci = self.ramdump.read_pointer(udev[0] + self.device_parent_offset)
dwc3_dev = self.ramdump.read_pointer(xhci + self.device_parent_offset)
else:
android_device = self.ramdump.read('android_device')
if android_device != 0:
gadget_info = self.ramdump.read_pointer(
android_device + self.device_driver_data_offset)
cdev = self.ramdump.struct_field_addr(gadget_info, 'struct gadget_info',
'cdev')
gadget = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(cdev, 'struct usb_composite_dev',
'gadget'))
if gadget != 0:
print("accessing dwc3 through android_device\n", file=self.outfile)
dev = self.ramdump.struct_field_addr(gadget, 'struct usb_gadget', 'dev')
dwc3_dev = self.ramdump.read_pointer(dev + self.device_parent_offset)
else:
print("dwc3 is not enumerated. Backup access to dwc3 also failed.\n"
"The device likely crashed during init\n", file=self.outfile)
hsphy_drv = self.ramdump.read_pointer(hsphy + self.device_driver_offset)
hsphy_drv_name = self.ramdump.read_cstring(self.ramdump.read_pointer(
self.ramdump.struct_field_addr(hsphy_drv, 'struct device_driver',
'name')))
hsphy_type = hsphy_name_map.get(
hsphy_drv_name) or hsphy_drv_name # default to drv_name
print(f'Device Summary:', file=self.outfile)
if ssphy != 0:
self.print_clocks_and_regs(ssphy, 'struct msm_ssphy_qmp')
self.print_clocks_and_regs(hsphy, f'struct {hsphy_type}')
print("", file=self.outfile)
self.cleanup()
return
# derive dwc3_msm from dwc3 so that we can see what mode we're in
dwc3_msm_dev = self.ramdump.read_pointer(dwc3_dev + self.device_parent_offset)
dwc3 = self.ramdump.read_pointer(dwc3_dev + self.device_driver_data_offset)
dwc3_msm = self.ramdump.read_pointer(dwc3_msm_dev + self.device_driver_data_offset)
is_host = self.ramdump.read_bool(
self.ramdump.struct_field_addr(dwc3_msm, 'struct dwc3_msm', 'in_host_mode'))
is_dev = self.ramdump.read_bool(
self.ramdump.struct_field_addr(dwc3_msm, 'struct dwc3_msm', 'in_device_mode'))
# Device mode parsing
if is_dev:
print("Reading device mode crash dump\n", file=self.outfile)
eps = self.ramdump.struct_field_addr(dwc3, 'struct dwc3', 'eps')
# Create list of active endpoints
active_eps = []
for i in range(2, 32):
ep = self.ramdump.read_pointer(eps + (i * self.ptr_size))
if ep != 0:
flags = self.ramdump.read_u32(
self.ramdump.struct_field_addr(ep, 'struct dwc3_ep', 'flags'))
if (flags & 0x1) != 0:
active_eps.append(ep)
# Print device summary
connected = self.ramdump.read_byte(
self.ramdump.struct_field_addr(dwc3, 'struct dwc3', 'connected'))
connected = connected & connected_mask
soft_connect = connected & softconnect_mask
vbus_active = self.ramdump.read_bool(
self.ramdump.struct_field_addr(dwc3_msm, 'struct dwc3_msm',
'vbus_active'))
eud_active = self.ramdump.read_bool(
self.ramdump.struct_field_addr(dwc3_msm, 'struct dwc3_msm',
'eud_active'))
print(f"Device Summary:\n-- Active Endpoints: {len(active_eps)}\n-- Connected: "
f"{connected}\n-- SoftConnect: {soft_connect}\n-- VBus Active: {vbus_active}\n"
f"-- EUD Active: {eud_active}\n", file=self.outfile)
# Function drivers
gadget = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(dwc3, 'struct dwc3', 'gadget'))
dev = self.ramdump.struct_field_addr(gadget, 'struct usb_gadget', 'dev')
cdev = self.ramdump.read_pointer(dev + self.device_driver_data_offset)
if cdev != 0:
self.print_function_drivers(cdev)
print("", file=self.outfile)
else:
print("Cannot display function drivers", file=self.outfile)
if len(active_eps) != 0:
# Device specific offsets
# Find smmu fault and trb that caused it, if applicable
with self.ramdump.open_file('dmesg_TZ.txt', mode='r') as f:
pattern = "Unhandled arm-smmu context fault from .+\\.dwc3!\\n.+FAR" \
"\\s+= 0x([0-9a-f]{16})"
match = re.search(pattern, f.read())
if match is not None:
print(f"Arm-smmu context fault detected at FAR: {match.group(1)}, "
f"searching for the TRB where it occurred.", file=self.outfile)
fault_addr = int(match.group(1), base=16)
ep, trb = self.find_crash_addr(active_eps, fault_addr)
if ep is not None and trb is not None:
name = self.ramdump.read_cstring(
self.ramdump.struct_field_addr(ep, 'struct dwc3_ep', 'name'))
print(f"Fault occurred in ring {name} at trb memory address "
f"{self.ramdump.hexdump(trb, 16, little_endian=False)}",
file=self.outfile)
request = self.find_faulty_request(ep, trb)
if request is not None:
print(f"Exception thrown by request {hex(request)}",
file=self.outfile)
else:
print("TRB that caused the fault not found. It may have been unmapped",
file=self.outfile)
else:
print("No arm-smmu fault detected", file=self.outfile)
# Print last num_events in the dwc3 event buffer
num_evts = 20
ev_buf = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(dwc3, 'struct dwc3', 'ev_buf'))
lpos = self.ramdump.read_int(
self.ramdump.struct_field_addr(ev_buf, 'struct dwc3_event_buffer', 'lpos'))
buf = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(ev_buf, 'struct dwc3_event_buffer', 'buf'))
self.print_ev_buf(buf, num_evts, lpos)
# Dump endpoint summaries. usbv (verbose mode) will print rings in detail
self.print_device_endpoints(active_eps)
else:
print("No active endpoints to display", file=self.outfile)
# Host mode
elif is_host:
# Host specific offsets
usb_dev_product_offset = self.ramdump.field_offset('struct usb_device', 'product')
usb_dev_manufacturer_offset = self.ramdump.field_offset('struct usb_device',
'manufacturer')
usb_dev_config_offset = self.ramdump.field_offset('struct usb_device', 'config')
usb_hconfig_interface_offset = self.ramdump.field_offset('struct usb_host_config',
'interface')
usb_hconfig_desc_offset = self.ramdump.field_offset('struct usb_host_config', 'desc')
usb_cfg_desc_length_offset = self.ramdump.field_offset('struct usb_config_descriptor',
'wTotalLength')
xhci_cmd_ring_offset = self.ramdump.field_offset('struct xhci_hcd', 'cmd_ring')
xhci_evt_ring_offset = self.ramdump.field_offset('struct xhci_hcd', 'event_ring')
xhci_devs_offset = self.ramdump.field_offset('struct xhci_hcd', 'devs')
xhci_virt_eps_offset = self.ramdump.field_offset('struct xhci_virt_device', 'eps')
xhci_virt_udev_offset = self.ramdump.field_offset('struct xhci_virt_device', 'udev')
xhci_virt_ep_ring_offset = self.ramdump.field_offset('struct xhci_virt_ep', 'ring')
ep_size = self.ramdump.sizeof('struct xhci_virt_ep')
print("Reading host mode crash dump\n", file=self.outfile)
# Find xhci ports
xhci_addrs = self.find_xhci_addrs()
for xhci in xhci_addrs:
# Command Ring and Event Ring summaries. If in verbose mode, also print the rings
cmd_ring = self.ramdump.read_pointer(xhci + xhci_cmd_ring_offset)
event_ring = self.ramdump.read_pointer(xhci + xhci_evt_ring_offset)
print(f"xchi_hcd: {hex(xhci)}\n", file=self.outfile)
if cmd_ring != 0:
print(self.read_xhci_ring(cmd_ring, "Command Ring") + "\n", file=self.outfile)
else:
print("Command Ring is NULL", file=self.outfile)
if event_ring != 0:
print(self.read_xhci_ring(event_ring, "Event Ring") + "\n", file=self.outfile)
else:
print("Event Ring is NULL", file=self.outfile)
# Dump device info. If in verbose mode, also print xhci rings
devs = xhci + xhci_devs_offset
for i in range(256):
dev = self.ramdump.read_pointer(devs + i * self.ptr_size)
if dev != 0:
eps = dev + xhci_virt_eps_offset
udev = self.ramdump.read_pointer(dev + xhci_virt_udev_offset)
product = self.ramdump.read_cstring(
self.ramdump.read_pointer(udev + usb_dev_product_offset))
manufacturer = self.ramdump.read_cstring(self.ramdump.read_pointer(
udev + usb_dev_manufacturer_offset))
config = self.ramdump.read_pointer(udev + usb_dev_config_offset)
desc = config + usb_hconfig_desc_offset
length = self.ramdump.read_u16(desc + usb_cfg_desc_length_offset)
interfaces = config + usb_hconfig_interface_offset
driver_names, audio = self.get_drivers(interfaces, length)
if audio != "":
audio = "Audio Details:\n" + audio + "\n"
print(f"Device {i}:\nProduct: {product}\nManufacturer: {manufacturer}\n"
f"{driver_names}\n{audio}", file=self.outfile)
for j in range(31):
ep = eps + j * ep_size
ring = self.ramdump.read_pointer(ep + xhci_virt_ep_ring_offset)
if ring != 0:
print(self.read_xhci_ring(ring, f'Endpoint {j}'), file=self.outfile)
else:
print("Reading disconnected mode crash dump\n", file=self.outfile)
in_lpm = self.ramdump.struct_field_addr(dwc3_msm, 'struct dwc3_msm', 'in_lpm')
if in_lpm == 0:
power = self.ramdump.struct_field_addr(dwc3_dev, 'struct device', 'power')
runtime_status = self.ramdump.read_u32(
self.ramdump.struct_field_addr(power, 'struct dev_pm_info', 'runtime_status'))
usage_count = self.ramdump.read_int(
self.ramdump.struct_field_addr(power, 'struct dev_pm_info', 'usage_count'))
print(f"DWC Power:\nRuntime Status: {runtime_status}\nUsage Count: {usage_count}",
file=self.outfile)
else:
print("Cable disconnected, device is in Low Power Mode", file=self.outfile)
self.cleanup()

View File

@@ -0,0 +1,309 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
from parsers.usb import UsbReader
from parser_util import register_parser
trb_completion_codes = ["invalid", "success", "data buffer err", "babble detected",
"USB transaction err", "trb err", "stall", "resource err", "bandwidth err",
"no slots avail", "invalid stream type", "device slot disabled",
"ep disabled", "short packet", "ring underrun", "ring overrun",
"vf event ring full", "param err", "bandwidth overrun", "context state err",
"no ping response", "event ring full", "incompatible device",
"missed service", "command ring stopped", "command aborted", "stopped",
"stopped - length invalid", "stopped - short packet",
"max exit latency too large", "rzvd", "isoch buffer overrun", "event lost",
"undefined err", "invalid stream id", "secondary bandwidth err",
"split transaction"]
trb_mask = 0x3f << 10
@register_parser('--usbv', 'Detailed usb device info')
class UsbVerboseReader(UsbReader):
"""
A UsbReader subclass that prints more verbose debugging information, such as entire TRB rings or
human-readable interpretations of command and event rings.
"""
def __init__(self, dump):
super(UsbVerboseReader, self).__init__(dump)
self.outfile_name = 'usbv_output.txt'
def get_trb_cmds(self, trb, length):
"""
Interprets the commands in the given TRB, up to length. Each command is 16 bytes long.
:param trb: TRB address
:param length: TRB length
:return: List of strings containing the interpreted commands
"""
cmds = []
for i in range(length):
curr = trb + (i * 16)
ctrl = self.ramdump.read_u32(curr + 12)
if ctrl != 0:
cmd = (ctrl & trb_mask) >> 10
if cmd == 1:
# Normal Transfer
status = self.ramdump.read_u32(curr + 8)
transfer_length = status & 0x1ffff
td_size = (status & (0x1f << 17)) >> 17
ioc = (ctrl & (1 << 5)) >> 5
cmds.append(
f"READ {transfer_length} bytes. {td_size} packets remain. IOC = {ioc}")
elif cmd == 2:
# Setup Stage Transfer
transfer_length = (self.ramdump.read_u32(curr + 4) & 0xffff0000) >> 16
ioc = (ctrl & (1 << 5)) >> 5
trt = (ctrl & (1 << 17)) >> 17
read_write = "ERROR"
types = ['no data transfer of', 'reserve', 'write', 'read']
if trt <= 3:
read_write = types[trt]
cmds.append(f"SETUP {read_write} {transfer_length} bytes. IOC = {ioc}")
elif cmd == 3:
# Data Stage Transfer
status = self.ramdump.read_u32(curr + 8)
transfer_length = status & 0x1ffff
td_size = (status & (0x1f << 17)) >> 17
direction = (ctrl & (1 << 15)) >> 15
ioc = (ctrl & (1 << 5)) >> 5
read_write = "read" if direction == 1 else "write"
cmds.append(
f"DATA STAGE {read_write} {transfer_length} bytes. "
f"{td_size} packets remain. IOC = {ioc}")
elif cmd == 4:
# Status Stage Transfer
ioc = (ctrl & (1 << 5)) >> 5
cmds.append(f"STATUS. IOC = {ioc}")
elif cmd == 5:
# Isoch Transfer
status = self.ramdump.read_u32(curr + 8)
transfer_length = status & 0xffff
td_size = (status & (0x1f << 17)) >> 17
ioc = (ctrl & (1 << 5)) >> 5
cmds.append(
f"ISOCHronous transfer length {transfer_length}. "
f"{td_size} packets remain. IOC = {ioc}")
elif cmd == 6:
# Link
seg_ptr = self.ramdump.read_u64(curr) >> 4
cmds.append(f"LINK, return to {hex(seg_ptr)}")
elif cmd == 7:
# Event Data
event_data = self.ramdump.read_u64(curr)
ioc = (ctrl & (1 << 5)) >> 5
cmds.append(f"EVENT DATA, sending {hex(event_data)}. IOC = {ioc}")
elif cmd == 8:
# No-op
cmds.append("NOP")
elif cmd == 9:
# Enable Slot
slot_type = (ctrl & (0x1f << 16)) >> 16
cmds.append(f"ENABLE SLOT, Type {slot_type}")
elif cmd == 10:
# Disable Slot
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(f"DISABLE SLOT {slot_id}")
elif cmd == 11:
# Address Device
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(f"ADDRESS DEVICE {slot_id}")
elif cmd == 12:
# Configure Endpoint
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(f"CONFIGURE EP, Device {slot_id}")
elif cmd == 13:
# Evaluate Context
ctxt_addr = self.ramdump.read_u64(curr)
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(
f"EVALUATE CONTEXT {hex(ctxt_addr)} which interacts with device "
f"{slot_id}")
elif cmd == 14:
# Reset Endpoint
slot_id = (ctrl & (0xff << 24)) >> 24
ep_id = (ctrl & (0x1f << 16)) >> 16
tsp = (ctrl & (1 << 9)) >> 1
cmds.append(f"RESET EP {ep_id}, Device {slot_id}")
elif cmd == 15:
# Stop Endpoint
dvc_id = (ctrl & (0xff << 24)) >> 24
ep_id = (ctrl & (0x1f << 16)) >> 16
suspend = (ctrl & (1 << 23)) >> 23
s = f"STOP EP {ep_id}, Device {dvc_id}"
if suspend == 1:
s += " about to be suspended"
cmds.append(s)
elif cmd == 16:
# Set TR Dequeue Pointer
dq_ptr = self.ramdump.read_u64(curr) >> 4
endpoint_id = (ctrl & (0x1f << 16)) >> 16
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(
f"SET TR DEQUEUE POINTER to {hex(dq_ptr)}, Endpoint {endpoint_id}, "
f"Device {slot_id}")
elif cmd == 17:
# Reset Device
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(f"RESET DEVICE {slot_id}")
elif cmd == 22:
# Force Header
data_lower = self.ramdump.read_u64(curr)
packet_type = data_lower & 0x1f
type_name = "RESERVED"
if packet_type == 0:
type_name = "link management"
elif packet_type == 4:
type_name = "transaction"
elif packet_type == 8:
type_name = "data"
elif packet_type == 12:
type_name = "isochronous timestamp"
header_mid_low = data_lower >> 5
header_hi = self.ramdump.read_u32(curr + 8)
header = (header_hi << 59) + header_mid_low
root_hub = (ctrl & (0xff << 24)) >> 24
cmds.append(
f"FORCE HEADER {hex(header)} of type {type_name}, "
f"Root hub port {root_hub}")
elif cmd == 23:
# Nop
cmds.append("NOP Command")
elif cmd == 32:
# Transfer Event
status = self.ramdump.read_u32(curr + 8)
transfer_length = status & 0xffffff
completion_code = (status & (0xff << 24)) >> 24
endpoint_id = (ctrl & (0x1f << 16)) >> 16
complete = "Failed"
if 37 <= completion_code <= 191:
complete += ": (reserved)"
elif completion_code > 191:
complete += ": vendor defined"
elif completion_code == 1 or (
completion_code == 13 and endpoint_id % 2 == 1):
complete = "Successful"
else:
complete += ": " + trb_completion_codes[completion_code]
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(
f"TRANSFER EVENT {complete} of {transfer_length} bytes, "
f"Endpoint {endpoint_id}, Device {slot_id}")
elif cmd == 33:
# Command Completion Event
status = self.ramdump.read_u32(curr + 8)
completion_code = (status & (0xff << 24)) >> 24
complete = "COMPLETE" if completion_code == 1 else "FAILED"
slot_id = (ctrl & (0xff << 24)) >> 24
cmds.append(f"COMMAND {complete} for device {slot_id}")
elif cmd == 34:
# Port Status Change
port_id = (self.ramdump.read_u32(curr) & (0xff << 24)) >> 24
completion_code = (self.ramdump.read_u32(curr + 8) & (0xff << 24)) >> 24
if completion_code == 1:
cmds.append(f"PORT {port_id} STATUS CHANGE")
else:
cmds.append("ERROR during Port Status Change command")
elif cmd == 37:
# Host Controller
cmds.append("xHC STATUS CHANGE")
elif cmd == 38:
# Device Notification
cmd_trb = self.ramdump.read_u64(curr)
notif_type = (cmd_trb & (0xf << 4)) >> 4
notif_data = cmd_trb >> 8
completion_code = self.ramdump.read_u32(curr + 8)
complete = "SUCCESS" if completion_code == 1 else "FAILED"
slot_id = (ctrl & (0xff << 8)) >> 8
cmds.append(f"NOTIFICATION of type {notif_type} {complete} to device "
f"{slot_id}. Notification data: {notif_data}")
elif cmd == 39:
# MFINDEX Wrap
cmds.append("MFINDEX register wrapping from 0x3FFFh to 0")
else:
cmds.append("RESERVED")
return cmds
def read_xhci_ring(self, xhci_ring, name):
"""
Returns a summary and the structure of the xhci ring given
:param xhci_ring: Address of the xhci ring
:param name: Name of the xhci ring
:return: String containing the summary and structure
"""
first_seg_addr = self.ramdump.struct_field_addr(xhci_ring, 'struct xhci_ring', 'first_seg')
eq = self.ramdump.read_structure_field(xhci_ring, 'struct xhci_ring', 'enqueue')
dq = self.ramdump.read_structure_field(xhci_ring, 'struct xhci_ring', 'dequeue')
ring_struct = f"{name}\n\tEnqueue pointer located at: {hex(eq)}" \
f"\n\tDequeue pointer located at: {hex(dq)}\n"
xhci_seg_trb_offset = self.ramdump.field_offset('struct xhci_segment', 'trbs')
xhci_seg_next_offset = self.ramdump.field_offset('struct xhci_segment', 'next')
first_seg = self.ramdump.read_pointer(first_seg_addr)
eq_seg = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(xhci_ring, 'struct xhci_ring', 'enq_seg'))
dq_seg = self.ramdump.read_pointer(
self.ramdump.struct_field_addr(xhci_ring, 'struct xhci_ring', 'deq_seg'))
num_segs = self.ramdump.read_u32(
self.ramdump.struct_field_addr(xhci_ring, 'struct xhci_ring', 'num_segs'))
curr_seg = first_seg
ring_header = "Ring Structure:\n" + " " * (10 + self.ptr_size * 2) + "0" + " " * 9 + \
"4" + " " * 9 + "8" + " " * 8 + "12"
for i in range(num_segs):
trb = self.ramdump.read_pointer(curr_seg + xhci_seg_trb_offset)
trb_ids = self.get_trb_cmds(trb, 256)
trb_cmds = "\nTRB Commands:\n"
for j in range(len(trb_ids)):
trb_cmds += f"\t{j}: {trb_ids[j]}\n"
eq_idx = -1
if curr_seg == eq_seg:
eq_idx = int((eq - trb) / 16)
dq_idx = -1
if curr_seg == dq_seg:
dq_idx = int((dq - trb) / 16)
ring_struct += f"{trb_cmds} \nSegment {i}:\n{ring_header}\n" \
f"{self.display_ring(trb, eq_idx, dq_idx)}\n\n"
curr_seg = self.ramdump.read_pointer(curr_seg + xhci_seg_next_offset)
return ring_struct
def print_device_endpoints(self, eps):
"""
Prints all endpoints given and their ring structures
:param eps: Endpoints to be printed
:return: None
"""
ep_trb_pool_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_pool')
ep_name_offset = self.ramdump.field_offset('struct dwc3_ep', 'name')
ep_eq_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_enqueue')
ep_dq_offset = self.ramdump.field_offset('struct dwc3_ep', 'trb_dequeue')
ring_structs = "Detailed Ring Structures:\n"
ring_summaries = "\nActive endpoint summaries:\n"
for ep in eps:
trb_pool = self.ramdump.read_pointer(ep + ep_trb_pool_offset)
name = self.ramdump.read_cstring(ep + ep_name_offset)
trb_eq = self.ramdump.read_byte(ep + ep_eq_offset)
trb_dq = self.ramdump.read_byte(ep + ep_dq_offset)
ring_summaries += f"{name}:\n\tAddress: {hex(ep)}\n\tActive trbs: " \
f"{self.count_active_trbs(trb_pool)}\n\tEnqueue Pointer: " \
f"Trb {trb_eq}\n\tDequeue Pointer: Trb {trb_dq}\n"
ring_struct = f'Ring {name}\n{" " * (10 + self.ptr_size * 2)}0{" " * 9}4' \
f'{" " * 9}8{" " * 8}12\n' \
f'{self.display_ring(trb_pool, trb_eq, trb_dq)}\n\n'
ring_structs += ring_struct
print(f"{ring_summaries}\n{ring_structs}\n", file=self.outfile)
def parse(self):
# The methods above will be called instead of the superclass methods they override
super().parse()

View File

@@ -0,0 +1,383 @@
# Copyright (c) 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 os
import rb_tree
import sys
import string
import maple_tree
import re
import time
from mmu import Armv8MMU
from mm import pfn_to_page, page_buddy, page_count, for_each_pfn, page_to_pfn, phys_to_virt
from print_out import print_out_str
from parser_util import register_parser, RamParser, cleanupString
PAGE_SHIFT = 12
PAGE_SIZE = 4096
PGDIR_SHIFT_L3_4K = 30
PTRS_PER_PGD_L3_4K = 512
PMD_SHIFT_L3_4K = (21)
PMD_SIZE_L3_4K = (1 << PMD_SHIFT_L3_4K)
PHYS_MASK_SHIFT = (48)
PHYS_MASK = ((1 << PHYS_MASK_SHIFT) - 1)
PTRS_PER_PMD_L3_4K = (512)
PTRS_PER_PTE_L3_4K = 512
pagemask = 0xfffffffffffff000
pageoffset = 0xfff
#help -m userspace_top: 0000008000000000
def arm64_is_uvaddr(addr):
return (addr < 0x8000000000)
def PAGEOFFSET(x):
return x & pageoffset
def phys_to_pfn(paddr):
x = ((paddr) >> PAGE_SHIFT)
return x
def ARM64_HW_PGTABLE_LEVEL_SHIFT(n):
return ((PAGE_SHIFT - 3) * (4 - (n)) + 3)
VA_BITS = 39
CONFIG_PGTABLE_LEVELS = 3
PGDIR_SHIFT = ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS)
#define PGDIR_SIZE (_AC(1, UL) << PGDIR_SHIFT)
#define PGDIR_MASK (~(PGDIR_SIZE-1))
PTRS_PER_PGD = (1 << (VA_BITS - PGDIR_SHIFT))
VM_NONE = 0x00000000
VM_READ = 0x00000001
VM_WRITE = 0x00000002
VM_EXEC = 0x00000004
VM_SHARED = 0x00000008
def pgd_index(addr):
return (addr >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)
def pgd_offset_raw(pgd, addr):
return pgd + pgd_index(addr) * 8
def pgd_offset(pgd, addr):
return pgd_offset_raw(pgd, addr)
PTRS_PER_PTE = (1 << (PAGE_SHIFT - 3))
PTRS_PER_PMD = PTRS_PER_PTE
PMD_SHIFT = ARM64_HW_PGTABLE_LEVEL_SHIFT(2)
def pmd_index(addr):
return (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
def pte_index(addr):
return (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE_L3_4K - 1))
def pte_show(pte):
aList = []
pte_dict = {'PTE_VALID': 1 << 0, 'PTE_WRITE': 1 << 51, 'PTE_DIRTY': 1 << 55, 'PTE_SPECIAL': 1 << 56,
'PTE_PROT_NONE': 1 << 58, 'PTE_USER': 1 << 6, 'PTE_RDONLY': 1 << 7, 'PTE_SHARED': 1 << 8,
'PTE_AF': 1 << 10, 'PTE_NG': 1 << 11, 'PTE_CONT': 1 << 52, 'PTE_PXN': 1 << 53, 'PTE_UXN': 1 << 54}
for (d, x) in pte_dict.items():
if pte & x:
aList.append(d)
return aList
def remove_non_ascii(a_str):
ascii_chars = set(string.printable)
return ''.join(
filter(lambda x: x in ascii_chars, a_str)
)
PTE_VALID = 1 << 0
PTE_PROT_NONE = 58 << 1
def do_get_task_info(ramdump, task):
offset_comm = ramdump.field_offset('struct task_struct', 'comm')
offset_pid = ramdump.field_offset('struct task_struct', 'pid')
tgid_offset = ramdump.field_offset('struct task_struct', 'tgid')
tgid = ramdump.read_int(task + tgid_offset)
thread_task_name = cleanupString(
ramdump.read_cstring(task + offset_comm, 16))
if thread_task_name is None or thread_task_name == "":
return thread_task_pid, None, None
thread_task_pid = ramdump.read_int(task + offset_pid)
mm = ramdump.read_structure_field(task, 'struct task_struct', 'mm')
return thread_task_pid, thread_task_name, mm
@register_parser('--print-uvtop', 'Print task virtual memory to page info', optional=True)
class uvtop(RamParser):
def __init__(self, *args):
super(uvtop, self).__init__(*args)
self.task_list = []
self.flags_offset = self.ramdump.field_offset('struct page', 'flags')
self.page_flags = {
'PG_locked ':0x000001 ,
'PG_referenced ':0x000002 ,
'PG_uptodate ':0x000004 ,
'PG_dirty ':0x000008 ,
'PG_lru ':0x000010 ,
'PG_active ':0x000020 ,
'PG_workingset ':0x000040 ,
'PG_waiters ':0x000080 ,
'PG_error ':0x000100 ,
'PG_slab ':0x000200 ,
'PG_owner_priv_1 ':0x000400 ,
'PG_arch_1 ':0x000800 ,
'PG_reserved ':0x001000 ,
'PG_private ':0x002000 ,
'PG_private_2 ':0x004000 ,
'PG_writeback ':0x008000 ,
'PG_head ':0x010000 ,
'PG_mappedtodisk ':0x020000 ,
'PG_reclaim ':0x040000 ,
'PG_swapbacked ':0x080000 ,
'PG_unevictable ':0x100000 ,
'PG_mlocked ':0x200000 ,
'PG_checked ':0x000400 ,
'PG_swapcache ':0x000400 ,
'PG_fscache ':0x004000 ,
'PG_pinned ':0x000400 ,
'PG_savepinned ':0x000008 ,
'PG_foreign ':0x000400 ,
'PG_slob_free ':0x002000 ,
'PG_double_map ':0x004000 ,
'PG_isolated ':0x040000 ,
}
def uvtop(self, mm_struct, vaddr):
if mm_struct == 0:
return 0, PAGE_SIZE
"""
arch/arm64/include/asm/pgtable-prot.h
"""
pgd = self.ramdump.read_structure_field(mm_struct, 'struct mm_struct', 'pgd')
pgd_ptr = pgd_offset(pgd, vaddr)
pgd_val = self.ramdump.read_word(pgd_ptr)
if pgd_val == 0 or pgd_val is None:
return 0, 0x40000000 # 1G
pmd_addr_phy = pgd_val & PHYS_MASK & pagemask
pmd_base = phys_to_virt(self.ramdump, pmd_addr_phy)
pmd_ptr = pmd_base + pmd_index(vaddr) * 8
pmd_val = self.ramdump.read_word(pmd_ptr)
if pmd_val == 0 or pmd_val is None:
return 0, (512 * PAGE_SIZE) # 2M
pte_base_phy = pmd_val & PHYS_MASK & pagemask
pte_base = phys_to_virt(self.ramdump, pte_base_phy)
pte_index_value = pte_index(vaddr)
pte_ptr = pte_base + (pte_index_value << 3)
pte_val = self.ramdump.read_word(pte_ptr)
if pte_val == 0 or pte_val is None:
return 0, PAGE_SIZE
# check if it is swap_pte
if (pte_val & PTE_VALID) == 0:
return 0, PAGE_SIZE
if pte_val & PTE_PROT_NONE == 1:
return 0, PAGE_SIZE
page_addr_phy = pte_val & pagemask & PHYS_MASK + PAGEOFFSET(vaddr) #arm64_vtop_3level_4k
pfn = phys_to_pfn(page_addr_phy)
page = pfn_to_page(self.ramdump, pfn)
self.fout.write(
" start 0x{0:x} -> 0x{1:x} \n"
.format(vaddr, vaddr + 0x1000))
phy = self.mmu.virt_to_phys(vaddr)
if phy != None:
self.fout.write(" phy: 0x{0:x}\n".format(phy))
self.fout.write(" PGD: 0x{0:16x} => {1:16x} \n".format(pgd_ptr, pgd_val))
self.fout.write(" PMD: 0x{0:16x} => {1:16x} \n".format(pmd_ptr, pmd_val))
self.fout.write(" PTE: 0x{0:16x} => {1:16x} \n".format(pte_ptr, pte_val))
self.fout.write(" {0} \n".format(pte_show(pte_val)))
self.fout.write(" page: 0x{0:16x}\n".format(page))
self.fout.write(" pfn: 0x{0:x}\n".format(pfn))
self.fout.write(" phy: 0x{0:x}\n".format(page_addr_phy))
return page, PAGE_SIZE
def get_dname_of_dentry(self, dentry):
ramdump = self.ramdump
dentry_name_offset = ramdump.field_offset(
'struct dentry', 'd_name')
len_offset = ramdump.field_offset(
'struct qstr', 'len')
qst_name_offset = ramdump.field_offset(
'struct qstr', 'name')
if dentry == None or dentry_name_offset == None or qst_name_offset == None:
return None
name_address = ramdump.read_word(dentry + dentry_name_offset + qst_name_offset)
len_address = dentry + dentry_name_offset + len_offset
len = ramdump.read_u32(len_address)
name = cleanupString(ramdump.read_cstring(
name_address, len))
return name
def dump_page(self, page):
flags = self.ramdump.read_word(page + self.flags_offset)
if flags is None:
flags = 0
aList= []
for (d,x) in self.page_flags.items():
if flags & x:
aList.append(d)
self.fout.write(" flags: {0} \n".format(aList))
return flags
def vm_area_struct_dump(self, vm_area_struct, mm):
vm_start = self.ramdump.read_structure_field(vm_area_struct, 'struct vm_area_struct', 'vm_start')
vm_end = self.ramdump.read_structure_field(vm_area_struct, 'struct vm_area_struct', 'vm_end')
vm_flags = self.ramdump.read_structure_field(vm_area_struct, 'struct vm_area_struct', 'vm_flags')
vm_file = self.ramdump.read_structure_field(vm_area_struct, 'struct vm_area_struct', 'vm_file')
vm_page_prot = self.ramdump.read_structure_field(vm_area_struct, 'struct vm_area_struct', 'vm_page_prot')
if vm_area_struct == None:
return
if vm_start == None or vm_end == None:
return
mount_dir_full_name = ''
'''
anonymous memory/page, vm_file could NULL.
'''
if vm_file != None and vm_file != 0:
f_path = self.ramdump.struct_field_addr(vm_file, 'struct file', 'f_path')
dentry = self.ramdump.read_structure_field(
f_path, 'struct path', 'dentry')
d_parent_offset = self.ramdump.field_offset(
'struct dentry', 'd_parent')
d_parent = dentry
d_parent_pre = 0
names = []
while d_parent_pre != d_parent and d_parent != None:
d_parent_pre = d_parent
name = self.get_dname_of_dentry(d_parent)
d_parent = self.ramdump.read_word(d_parent + d_parent_offset)
if name == None or name == '/':
break
names.append(name)
if d_parent == 0:
break
names.reverse()
mount_dir_full_name = ''
for item in names:
mount_dir_full_name += '/' + item
if mount_dir_full_name == None:
mount_dir_full_name = ''
self.fout.write(
" 0x{0:x} -> 0x{1:x} 0x{2:x} {3:8x} {4:x} {5:48s}\n"
.format(vm_start, vm_end, vm_area_struct, vm_flags, vm_page_prot, mount_dir_full_name))
start = vm_start
while start < vm_end:
if arm64_is_uvaddr(start) == 0:
break
page, step_size = self.uvtop(mm, start)
if page != 0:
flags = self.dump_page(page)
if flags != 0:
self.page_count += 1
start += step_size
'''
5.15 kernel
'''
def vm_area_struct_walker_5_1(self, rb_node, mm):
vm_area_struct = self.ramdump.container_of(rb_node, 'struct vm_area_struct', 'vm_rb')
self.vm_area_struct_dump(vm_area_struct, mm)
def dump_vma_5_15(self, mm, pid, comm):
if mm != 0:
file_name = "{0}_{1}.txt".format(pid, comm)
f_path = os.path.join(self.output_dir, file_name)
self.fout = open(f_path, "w")
self.fout.write(
"------------comm {0:16s} pid {1:6d}, mm 0x{2:x} ---------------- \n ".format(
comm, pid, mm))
self.fout.write("VMA START END FLAGS FILE\n")
mm_rb = self.ramdump.read_structure_field(mm, 'struct mm_struct', 'mm_rb')
rb_walker = rb_tree.RbTreeWalker(self.ramdump)
if mm_rb == None or mm_rb == 0:
return
pgd = self.ramdump.read_structure_field(mm, 'struct mm_struct', 'pgd')
pgdp = self.ramdump.virt_to_phys(pgd)
self.mmu = Armv8MMU(self.ramdump, pgdp)
rb_walker.walk(mm_rb, self.vm_area_struct_walker_5_1, mm)
self.fout.close()
'''
6.1 kernel
'''
def vm_area_struct_walker_6_1(self, node, mm):
if node !=0 and node != None:
self.vm_area_struct_dump(node, mm)
def dump_vma_6_1(self, mm, pid, comm):
file_name = "{0}_{1}.txt".format(pid, comm)
f_path = os.path.join(self.output_dir, file_name)
self.fout = open(f_path, "w")
self.fout.write(
"------------comm {0:16s} pid {1:6d}, mm 0x{2:x} ---------------- \n ".format(
comm, pid, mm))
self.fout.write("VMA START END FLAGS FILE\n")
pgd = self.ramdump.read_structure_field(mm, 'struct mm_struct', 'pgd')
pgdp = self.ramdump.virt_to_phys(pgd)
self.mmu = Armv8MMU(self.ramdump, pgdp)
mt_walk = maple_tree.MapleTreeWalker(self.ramdump)
mm_mt = self.ramdump.struct_field_addr(mm, 'struct mm_struct', 'mm_mt')
mt_walk.walk(mm_mt, self.vm_area_struct_walker_6_1, mm)
self.fout.close()
def do_one_task(self, task, given_pid = -1):
pid, thread_task_name, mm = do_get_task_info(self.ramdump, task)
if given_pid != -1:
if pid != given_pid:
return
comm = remove_non_ascii(thread_task_name)
comm = re.sub(r'[^a-zA-Z0-9]', '', comm)
if mm != 0:
starttime = time.time()
print_out_str('start {0} {1}'.format(pid, comm))
if (self.ramdump.kernel_version) < (6, 1, 0):
self.dump_vma_5_15(mm, pid, comm)
else:
self.dump_vma_6_1(mm, pid, comm)
endtime = time.time()
print_out_str('done {0} {1}'.format(pid, comm))
print_out_str(" Time taken to complete : {}".format(endtime - starttime))
def parse(self):
'''
--print-uvtop pid=1 # dump a task by given the pid, for example 1
--print-uvtop # dump all tasks
'''
pid_vm = None
for arg in sys.argv:
if "pid=" in arg:
k, pid_vm = arg.split('=')
pid_vm = int(pid_vm, 16)
break
if pid_vm == None:
print_out_str("invalid PID, will to dump all pid")
pid_vm = -1
else:
print_out_str("PID {0}".format(pid_vm))
print_out_str("PGDIR_SHIFT {0}".format(PGDIR_SHIFT))
print_out_str("PTRS_PER_PGD {0}".format(PTRS_PER_PGD))
self.page_count = 0
self.output_dir = os.path.join(os.path.abspath(self.ramdump.outdir), "uvtop")
if os.path.exists(self.output_dir) is False:
os.makedirs(self.output_dir)
for task in self.ramdump.for_each_process():
self.do_one_task(task, pid_vm)

View File

@@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0-only
# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
from parser_util import register_parser, RamParser, cleanupString
def extract_va_minidump(ramdump):
try:
kva_out = ramdump.open_file('kva_output.txt')
kva_out.write("\n************** msm_drm_priv **************\n")
msm_drm_priv_addr = ramdump.get_section_address("msm_drm_priv")[0][0]
msm_drm_priv = ramdump.read_datatype(msm_drm_priv_addr,
"struct msm_drm_private")
ramdump.print_struct(msm_drm_priv, kva_out)
kva_out.write("\n************** sde_evtlog **************\n")
sde_evtlog_addr = ramdump.get_section_address("sde_evtlog")[0][0]
sde_evtlog = ramdump.read_datatype(sde_evtlog_addr,
"sde_dbg_base_evtlog")
kva_out.write(str(sde_evtlog))
kva_out.write("\n************** sde_reglog **************\n")
sde_reglog_addr = ramdump.get_section_address("sde_reglog")[0][0]
sde_reglog = ramdump.read_datatype(sde_reglog_addr,
"struct sde_dbg_reglog")
ramdump.print_struct(sde_reglog, kva_out)
kva_out.write("\n************** sde_kms **************\n")
sde_kms_addr = ramdump.get_section_address("sde_kms")[0][0]
sde_kms = ramdump.read_datatype(sde_kms_addr, "struct sde_kms")
ramdump.print_struct(sde_kms, kva_out)
kva_out.close()
except Exception as err:
traceback.print_exc()
@register_parser('--vm-minidump-sample', 'Print reserved memory info ')
class VaMinidump(RamParser):
def parse(self):
extract_va_minidump(self.ramdump)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
# Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved.
# Copyright (c) 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
from print_out import print_out_str
from parser_util import register_parser, RamParser
import linux_list as llist
VM_IOREMAP = 0x00000001
VM_ALLOC = 0x00000002
VM_MAP = 0x00000004
VM_USERMAP = 0x00000008
VM_VPAGES = 0x00000010
VM_UNLIST = 0x00000020
@register_parser('--print-vmalloc', 'print vmalloc information')
class Vmalloc(RamParser):
def print_vm(self, vm):
if vm == 0 or vm is None:
return
addr = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'addr')
if addr is None:
return
caller = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'caller')
nr_pages = size = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'nr_pages')
phys_addr = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'phys_addr')
flags = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'flags')
size = self.ramdump.read_structure_field(vm, 'struct vm_struct', 'size')
flags_str = ''
if (flags & VM_IOREMAP) != 0:
flags_str = ' ioremap'
if (flags & VM_ALLOC) != 0:
flags_str = ' vmalloc'
if (flags & VM_MAP) != 0:
flags_str = ' vmap'
if (flags & VM_USERMAP) != 0:
flags_str = ' user'
if (flags & VM_VPAGES) != 0:
flags_str = ' vpages'
line = ''
func = ''
if (caller != 0):
line = self.ramdump.gdbmi.get_func_info(caller)
if line == None:
line = 'n/a'
l = self.ramdump.unwind_lookup(caller)
if l is not None:
func, offset = l
else:
func = 'Unknown function'
print("v.v (struct vm_struct)0x%x 0x%x-0x%x 0x%-16x 0x%-16x %-10s %-8d %32s %-s"
%(vm, addr, addr + size, size , phys_addr, flags_str, nr_pages, func, line), file = self.vmalloc_out)
def list_func(self, vmlist):
vm_offset = self.ramdump.field_offset('struct vmap_area', 'vm')
vm = self.ramdump.read_word(vmlist + vm_offset)
self.print_vm(vm)
def print_vmalloc_info_v2(self, out_path):
vmalloc_out = self.ramdump.open_file('vmalloc.txt')
next_offset = self.ramdump.field_offset('struct vmap_area', 'list')
vmlist = self.ramdump.read_word('vmap_area_list')
list_walker = llist.ListWalker(self.ramdump, vmlist, next_offset)
self.vmalloc_out = vmalloc_out
print("VM_STRUCT ADDRESS_RANGE SIZE PHYS_ADDR Flag PAGES CALLER ", file = self.vmalloc_out)
list_walker.walk(vmlist, self.list_func)
print_out_str('---wrote vmalloc to vmalloc.txt')
vmalloc_out.close()
def print_vmalloc_info(self, out_path):
vmlist = self.ramdump.read_word('vmlist')
next_offset = self.ramdump.field_offset('struct vm_struct', 'next')
vmalloc_out = self.ramdump.open_file('vmalloc.txt')
self.vmalloc_out = vmalloc_out
while (vmlist is not None) and (vmlist != 0):
self.print_vm(vmlist)
vmlist = self.ramdump.read_word(vmlist + next_offset)
print_out_str('---wrote vmalloc to vmalloc.txt')
vmalloc_out.close()
def parse(self):
out_path = self.ramdump.outdir
major, minor, patch = self.ramdump.kernel_version
if (major, minor) >= (3, 10):
self.print_vmalloc_info_v2(out_path)
else:
self.print_vmalloc_info(out_path)

View File

@@ -0,0 +1,154 @@
# Copyright (c) 2013-2015, 2017, 2019-2020 The Linux Foundation. All rights reserved.
# Copyright (c) 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.
from print_out import print_out_str
from parser_util import register_parser, RamParser
@register_parser('--print-vmstats', 'Print the information similar to /proc/zoneinfo and /proc/vmstat')
class ZoneInfo(RamParser):
def print_atomic_long_counters(self, output_file, counter_name, addr, num):
for i in range(0, num):
val = 0
if self.ramdump.arm64:
val = self.ramdump.read_s64(self.ramdump.array_index(addr,
'atomic_long_t', i))
else:
val = self.ramdump.read_s32(self.ramdump.array_index(addr,
'atomic_long_t', i))
output_file.write(('{0:30}: {1:10} {2:10}MB\n'.format(counter_name[i], val, val * 4 // 1024)))
def print_zone_stats(self, zone, vmstat_names, max_zone_stats, output_file):
nr_watermark = self.ramdump.gdbmi.get_value_of('NR_WMARK')
wmark_names = self.ramdump.gdbmi.get_enum_lookup_table(
'zone_watermarks', nr_watermark)
zone_name_offset = self.ramdump.field_offset('struct zone', 'name')
zname_addr = self.ramdump.read_word(zone + zone_name_offset)
zname = self.ramdump.read_cstring(zname_addr, 12)
zstats_addr = zone + \
self.ramdump.field_offset('struct zone', 'vm_stat')
if (self.ramdump.kernel_version < (4, 19, 0)):
zwatermark_addr = zone + \
self.ramdump.field_offset('struct zone', 'watermark')
else:
zwatermark_addr = zone + \
self.ramdump.field_offset('struct zone', '_watermark')
output_file.write('\nZone {0:8}\n'.format(zname))
present_pages_addr = zone + self.ramdump.field_offset('struct zone', 'present_pages')
present_pages = self.ramdump.read_word(present_pages_addr)
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format("present_pages",
present_pages, present_pages * 4 // 1024))
spanned_pages_addr = zone + self.ramdump.field_offset('struct zone', 'spanned_pages')
spanned_pages = self.ramdump.read_word(spanned_pages_addr)
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format("spanned_pages",
spanned_pages, spanned_pages * 4 // 1024))
managed_pages_addr = zone + self.ramdump.field_offset('struct zone', 'managed_pages')
managed_pages = self.ramdump.read_word(managed_pages_addr)
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format("managed_pages",
managed_pages, managed_pages * 4 // 1024))
cma_pages_offset = self.ramdump.field_offset('struct zone', 'cma_pages')
if cma_pages_offset != None:
cma_pages_addr = zone + cma_pages_offset
cma_pages = self.ramdump.read_word(cma_pages_addr)
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format("cma_pages",
cma_pages, cma_pages * 4 // 1024))
for i in range(0, nr_watermark):
val = self.ramdump.read_word(
self.ramdump.array_index(zwatermark_addr,
'unsigned long', i))
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format(wmark_names[i],
val, val * 4 // 1024))
self.print_atomic_long_counters(output_file, vmstat_names, zstats_addr,
max_zone_stats)
def print_vm_node_states(self, output_file):
vm_node_stats = self.ramdump.address_of('vm_node_stat')
max_node_stat_item = self.ramdump.gdbmi.get_value_of('NR_VM_NODE_STAT_ITEMS')
vmstat_names = self.ramdump.gdbmi.get_enum_lookup_table('node_stat_item', max_node_stat_item)
self.print_atomic_long_counters(output_file, vmstat_names, vm_node_stats,
max_node_stat_item)
def print_vm_event_states(self, output_file):
vm_event_states = self.ramdump.address_of('vm_event_states')
max_node_stat_item = self.ramdump.gdbmi.get_value_of('NR_VM_EVENT_ITEMS')
vmstat_names = self.ramdump.gdbmi.get_enum_lookup_table(
'vm_event_item', max_node_stat_item)
rq_addr=[0,0,0,0,0,0,0,0]
for i in range(0, max_node_stat_item):
val = 0
for j in self.ramdump.iter_cpus():
rq_addr[j] = vm_event_states + self.ramdump.per_cpu_offset(j)
if self.ramdump.arm64:
val += self.ramdump.read_word(self.ramdump.array_index(rq_addr[j],
'unsigned long', i))
else:
val += self.ramdump.read_word(self.ramdump.array_index(rq_addr[j],
'unsigned long', i))
if self.ramdump.arm64:
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format(vmstat_names[i],
val, val * 4 // 1024))
else:
output_file.write('{0:30}: {1:10} {2:10}MB\n'.format(vmstat_names[i],
val, val * 4 // 1024))
def parse(self):
output_file = self.ramdump.open_file("vmstats.txt", "w")
max_zone_stats = self.ramdump.gdbmi.get_value_of(
'NR_VM_ZONE_STAT_ITEMS')
vmstat_names = self.ramdump.gdbmi.get_enum_lookup_table(
'zone_stat_item', max_zone_stats)
max_nr_zones = self.ramdump.gdbmi.get_value_of('__MAX_NR_ZONES')
contig_page_data = self.ramdump.address_of('contig_page_data')
node_zones_offset = self.ramdump.field_offset(
'struct pglist_data', 'node_zones')
present_pages_offset = self.ramdump.field_offset(
'struct zone', 'present_pages')
sizeofzone = self.ramdump.sizeof('struct zone')
zone = contig_page_data + node_zones_offset
while zone < (contig_page_data + node_zones_offset + max_nr_zones * sizeofzone):
present_pages = self.ramdump.read_word(zone + present_pages_offset)
if not not present_pages:
self.print_zone_stats(zone, vmstat_names, max_zone_stats,
output_file)
zone = zone + sizeofzone
output_file.write("\nGlobal Stats\n")
if self.ramdump.kernel_version < (4,9,0):
vmstats_addr = self.ramdump.address_of('vm_stat')
else:
vmstats_addr = self.ramdump.address_of('vm_zone_stat')
self.print_atomic_long_counters(output_file, vmstat_names,
vmstats_addr,
max_zone_stats)
output_file.write('\nNode Stats\n')
self.print_vm_node_states(output_file)
output_file.write('\nVM EVENT Stats\n')
self.print_vm_event_states(output_file)
output_file.write('Total system pages:{0}\n'.format(self.ramdump.read_word(self.ramdump.address_of('totalram_pages'))))
output_file.close()

View File

@@ -0,0 +1,130 @@
"""
Copyright (c) 2020 The Linux Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of The Linux Foundation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Changes from Qualcomm Innovation Center are provided under the following license:
Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause-Clear
"""
import os,sys
import struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
wakeup_log = "wakeup_resource.txt"
def setup_out_file(path, self):
global out_file
try:
out_file = self.ramdump.open_file(path, 'wb')
return out_file
except:
print_out_str("could not open path {0}".format(path))
print_out_str("Do you have write/read permissions on the path?")
def print_out_ip(string):
out_file.write((string + '\n').encode('ascii', 'ignore'))
@register_parser('--wakeup', 'print wakelock wakeup resource information')
class wakeup_logging(RamParser):
def __init__(self, *args):
super(wakeup_logging, self).__init__(*args)
def wakeup_parse(self, ram_dump):
curr_chan=0
ep_idx=0
wakeup_sources_addr = ram_dump.address_of('wakeup_sources')
next = ram_dump.read_pointer(wakeup_sources_addr)
entry_offset = ram_dump.field_offset('struct wakeup_source','entry')
name_offset = ram_dump.field_offset('struct wakeup_source','name')
active_count_offset = ram_dump.field_offset('struct wakeup_source','active_count')
event_count_offset = ram_dump.field_offset('struct wakeup_source','event_count')
wakeup_count_offset = ram_dump.field_offset('struct wakeup_source','wakeup_count')
expire_count_offset = ram_dump.field_offset('struct wakeup_source','expire_count')
active_offset = ram_dump.field_offset('struct wakeup_source','active')
total_time_offset = ram_dump.field_offset('struct wakeup_source','total_time')
last_time_offset = ram_dump.field_offset('struct wakeup_source','last_time')
start_prevent_time_offset = ram_dump.field_offset('struct wakeup_source','start_prevent_time')
prevent_sleep_time_offset = ram_dump.field_offset('struct wakeup_source','prevent_sleep_time')
header_str = ('{0:11} {1:10} {2:10} {3:13} {4:14} {5:11} {6:13} {7:16} {8:16} {9:10}'
.format('active_count', 'event_count', 'wakeup_count',
'expire_count', 'total_time', 'last_time', 'start_prevent_time', 'prevent_suspend_time', 'active', 'name'))
print_out_ip(header_str)
while wakeup_sources_addr != next:
idx = 0
next = next - entry_offset
name_addr = ram_dump.read_pointer(next + name_offset)
name = ram_dump.read_cstring(name_addr)
active_count = ram_dump.read_ulong(next + active_count_offset)
event_count = ram_dump.read_ulong(next + event_count_offset)
wakeup_count = ram_dump.read_ulong(next + wakeup_count_offset)
expire_count = ram_dump.read_ulong(next + expire_count_offset)
active = ram_dump.read_bool(next + active_offset)
total_time_addr = ram_dump.read_s64(next + total_time_offset)
last_time_addr = ram_dump.read_s64(next + last_time_offset)
start_prevent_time_addr = ram_dump.read_s64(next + start_prevent_time_offset)
prevent_sleep_time_addr = ram_dump.read_s64(next + prevent_sleep_time_offset)
total_time0 = total_time_addr
total_time1 = ram_dump.read_u32(next + total_time_offset +0x4)
total_time1= (total_time1<<0x20)
total_time = (total_time0 | total_time1)
last_time0 = last_time_addr
last_time1 = ram_dump.read_u32(next + last_time_offset +0x4)
last_time1 = (last_time1<<0x20)
last_time = (last_time0 | last_time1)
start_prevent_time0 = start_prevent_time_addr
start_prevent_time1 = ram_dump.read_u32(next + start_prevent_time_offset +0x4)
start_prevent_time1= (start_prevent_time1<<0x20)
start_prevent_time = (start_prevent_time0 | start_prevent_time1)
prevent_sleep_time0 = prevent_sleep_time_addr
prevent_sleep_time1 = ram_dump.read_u32(next + prevent_sleep_time_offset +0x4)
prevent_sleep_time1= (prevent_sleep_time1<<0x20)
prevent_sleep_time = (prevent_sleep_time0 | prevent_sleep_time1)
data_str = ('{0:11} {1:10} {2:10} {3:13} {4:14} {5:11} {6:13} {7:16} {8:13} \t\t{9:10}'
.format(int(active_count), int(event_count), int(wakeup_count),
int(expire_count), total_time, last_time, start_prevent_time, prevent_sleep_time, active, name))
print_out_ip(data_str)
next = ram_dump.read_pointer(next + entry_offset)
def parse(self):
setup_out_file(wakeup_log, self)
self.wakeup_parse(self.ramdump)

View File

@@ -0,0 +1,330 @@
# Copyright (c) 2012-2015, 2020 The Linux Foundation. All rights reserved.
# Copyright (c) 2024 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 struct
from print_out import print_out_str
from parser_util import register_parser, RamParser
# (name from tz dump, corresponding T32 register, whether or not to print_out_str (the function name))
tzbsp_register_names = [
('mon_lr', 'pc', True),
('mon_spsr', None, False),
('usr_r0', 'r0', False),
('usr_r1', 'r1', False),
('usr_r2', 'r2', False),
('usr_r3', 'r3', False),
('usr_r4', 'r4', False),
('usr_r5', 'r5', False),
('usr_r6', 'r6', False),
('usr_r7', 'r7', False),
('usr_r8', 'r8', False),
('usr_r9', 'r9', False),
('usr_r10', 'r10', False),
('usr_r11', 'r11', False),
('usr_r12', 'r12', False),
('usr_r13', 'r13_usr', False),
('usr_r14', 'r14_usr', False),
('irq_spsr', 'spsr_irq', False),
('irq_r13', 'r13_irq', False),
('irq_r14', 'r14_irq', True),
('svc_spsr', 'spsr', False),
('svc_r13', 'r13_svc', False),
('svc_r14', 'r14_svc', True),
('abt_spsr', 'spsr_abt', False),
('abt_r13', 'r13_abt', False),
('abt_r14', 'r14_abt', True),
('und_spsr', 'spsr_und', False),
('und_r13', 'r13_und', False),
('und_r14', 'r14_und', True),
('fiq_spsr', 'spsr_fiq', False),
('fiq_r8', 'r8_fiq', False),
('fiq_r9', 'r9_fiq', False),
('fiq_r10', 'r10_fiq', False),
('fiq_r11', 'r11_fiq', False),
('fiq_r12', 'r12_fiq', False),
('fiq_r13', 'r13_fiq', False),
('fiq_r14', 'r14_fiq', False),
]
tzbsp_dump_cpu_ctx_t = ''.join([
'I', # mon_lr
'I', # mon_spsr
'I', # usr_r0
'I', # usr_r1
'I', # usr_r2
'I', # usr_r3
'I', # usr_r4
'I', # usr_r5
'I', # usr_r6
'I', # usr_r7
'I', # usr_r8
'I', # usr_r9
'I', # usr_r10
'I', # usr_r11
'I', # usr_r12
'I', # usr_r13
'I', # usr_r14
'I', # irq_spsr
'I', # irq_r13
'I', # irq_r14
'I', # svc_spsr
'I', # svc_r13
'I', # svc_r14
'I', # abt_spsr
'I', # abt_r13
'I', # abt_r14
'I', # und_spsr
'I', # und_r13
'I', # und_r14
'I', # fiq_spsr
'I', # fiq_r8
'I', # fiq_r9
'I', # fiq_r10
'I', # fiq_r11
'I', # fiq_r12
'I', # fiq_r13
'I', # fiq_r14
])
tzbsp_dump_buf_t_v2 = ''.join([
'I', # magic
'I', # version
'I', # cpu count
])
tz_sc_status_ns = 1
tz_sc_status_wdt = 2
tz_sc_status_sgi = 4
v2tzbsp_sc_status_ns_bit = 1
v2tzbsp_sc_status_wdt = 2
v2tzbsp_sc_status_sgi = 4
v2tzbsp_sc_status_warm_boot = 8
tz_sc_ignore_status = 0x10
v3tzbsp_cpu_dump_status = 0x20
v3sdi_cpu_dump_status = 0x10
class TZCpuCtx():
def __init__(self, regs_t, version, alt_pc):
i = 0
self.regs = {}
for r in regs_t:
# in version 2, mon_lr is not actually useful for HLOS
# debugging. Swap it with the alternate if necessary
if (version > 1) and i == 0 and alt_pc is not None:
r = alt_pc
self.regs[tzbsp_register_names[i][0]] = r
i += 1
def print_regs(self, outfile, ramdump):
for reg_name, t32_name, print_pc in tzbsp_register_names:
if print_pc:
a = ramdump.unwind_lookup(self.regs[reg_name])
if a is not None:
symname, offset = ramdump.unwind_lookup(
self.regs[reg_name])
pc_string = '[{0}+0x{1:x}]'.format(symname, offset)
else:
pc_string = ''
else:
pc_string = ''
print_out_str(' {0} = 0x{1:08x} {2}'.format(
reg_name, self.regs[reg_name], pc_string))
if t32_name is not None:
outfile.write(
'r.s {0} 0x{1:x}\n'.format(t32_name, self.regs[reg_name]))
@register_parser('--check-for-watchdog', 'Check for an FIQ watchdog', shortopt='-w')
class TZRegDump(RamParser):
def __init__(self, *args):
super(TZRegDump, self).__init__(*args)
self.sc_status = []
self.core_regs = []
self.wdt0_status = []
self.core_regs = []
self.sec_regs = None
self.ncores = 0
self.version = 0
self.mon_sp = []
self.wdog_pc = []
self.sc_regs = []
def dump_all_regs(self):
for i in range(0, self.ncores):
coren_regs = self.ramdump.open_file('core{0}_regs.cmm'.format(i))
if (self.sc_status[i] & tz_sc_ignore_status) == 0:
if self.version == 0:
if (self.sc_status[i] & tz_sc_status_ns):
print_out_str(
'Core {0} was in the non-secure world'.format(i))
if (self.sc_status[i] & tz_sc_status_wdt):
print_out_str(
'Core {0} experienced a watchdog timeout'.format(i))
if (self.sc_status[i] & tz_sc_status_sgi):
print_out_str(
'Core {0} did not experience a watchdog timeout but some other core did'.format(i))
else:
if (self.sc_status[i] & v2tzbsp_sc_status_ns_bit):
print_out_str(
'Core {0} was in the non-secure world'.format(i))
else:
print_out_str(
'Core {0} was in the secure world'.format(i))
if (self.sc_status[i] & v2tzbsp_sc_status_sgi):
print_out_str(
'Core {0} received an SGI interrupt'.format(i))
if (self.sc_status[i] & v2tzbsp_sc_status_wdt):
print_out_str(
'Core {0} recieved the watchdog interrupt'.format(i))
if (self.sc_status[i] & v2tzbsp_sc_status_warm_boot):
print_out_str(
'core {0} has the warm boot flag set'.format(i))
print_out_str(
'Core {0} WDT status: {1:x}'.format(i, self.wdt0_status[i]))
if (self.version >= 3):
print_out_str('status:{0}'.format(hex(self.sc_status[i])))
if (self.sc_status[i] & v3sdi_cpu_dump_status):
print_out_str(
'SDI dumped CPU context for core {0}'.format(i))
elif (self.sc_status[i] & v3tzbsp_cpu_dump_status):
print_out_str(
'TZ dumped CPU context for core {0}'.format(i))
print_out_str('core{0} regs:'.format(i))
self.core_regs[i].print_regs(coren_regs, self.ramdump)
coren_regs.close()
secure_regs = self.ramdump.open_file('secure_world_regs.cmm')
print_out_str('\n=============== secure contex ===========')
self.sec_regs.print_regs(secure_regs, self.ramdump)
print_out_str('============ end secure context ===========')
secure_regs.close()
def dump_core_pc(self, core):
if self.version > 1:
pc = self.wdog_pc[core]
else:
pc = self.core_regs[core].regs['mon_lr']
lr = self.core_regs[core].regs['svc_r14']
a = self.ramdump.unwind_lookup(pc)
if a is not None:
symname, offset = a
else:
symname = 'UNKNOWN'
offset = 0
print_out_str(
'Core {3} PC: {0}+{1:x} <{2:x}>'.format(symname, offset, pc, core))
a = self.ramdump.unwind_lookup(lr)
if a is not None:
symname, offset = a
else:
symname = 'UNKNOWN'
offset = 0
print_out_str(
'Core {3} LR: {0}+{1:x} <{2:x}>'.format(symname, offset, lr, core))
print_out_str('')
self.ramdump.unwind.unwind_backtrace(
self.core_regs[core].regs['svc_r13'], 0, pc, lr, '')
print_out_str('')
def init_regs(self, ebi_addr):
cpu_count = 0
status = self.ramdump.read_string(ebi_addr, tzbsp_dump_buf_t_v2, False)
if status is None:
print_out_str("!!! Couldn't read from {0:x}!".format(ebi_addr))
print_out_str('!!! No FIQ information will be parsed')
print_out_str(
'!!! Do you need to specify a different offset for TZ?')
return False
if status[0] == 0x44434151:
cpu_count = status[2]
version = status[1]
ebi_addr += struct.calcsize(tzbsp_dump_buf_t_v2)
else:
cpu_count = 2
version = 0
print_out_str('running dump version {0}'.format(version))
for i in range(0, cpu_count):
self.sc_status.append(self.ramdump.read_word(ebi_addr, False))
ebi_addr += 4
for i in range(0, cpu_count):
self.sc_regs.append(
self.ramdump.read_string(ebi_addr, tzbsp_dump_cpu_ctx_t, False))
ebi_addr += struct.calcsize(tzbsp_dump_cpu_ctx_t)
# new versions have extra data
if version > 1:
self.mon_sp.append(self.ramdump.read_word(ebi_addr, False))
self.wdog_pc.append(
self.ramdump.read_word(ebi_addr + 4, False))
ebi_addr += 8
sc_secure = self.ramdump.read_string(
ebi_addr, tzbsp_dump_cpu_ctx_t, False)
ebi_addr += struct.calcsize(tzbsp_dump_cpu_ctx_t)
for i in range(0, cpu_count):
self.wdt0_status.append(self.ramdump.read_word(ebi_addr, False))
ebi_addr += 4
if version > 1:
for regs, p in zip(self.sc_regs, self.wdog_pc):
self.core_regs.append(TZCpuCtx(regs, version, p))
else:
for regs in self.sc_regs:
self.core_regs.append(TZCpuCtx(regs, version, None))
self.sec_regs = TZCpuCtx(sc_secure, version, None)
self.ncores = cpu_count
self.version = version
return True
def parse(self):
if self.ramdump.address_of('memdump'):
print_out_str(
'!!! No memdump')
return None
ebi_addr = self.ramdump.read_tz_offset()
if ebi_addr is None:
print_out_str(
'!!! Could not read from IMEM at address {0:x}'.format(self.ramdump.tz_addr))
return None
if (ebi_addr == 0):
print_out_str(
'!!! No EBI address at IMEM location {0:x}.'.format(self.ramdump.tz_addr))
print_out_str('!!! No FIQ occured on this system')
return None
print_out_str(
'[!!!!] Read {0:x} from IMEM successfully!'.format(ebi_addr))
print_out_str('[!!!!] An FIQ occured on the system!')

View File

@@ -0,0 +1,548 @@
# Copyright (c) 2012-2015, 2017 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 linux_list
from parser_util import register_parser, RamParser, cleanupString
@register_parser('--print-workqueues', 'Print the state of the workqueues', shortopt='-q')
class Workqueues(RamParser):
def get_caller(self, caller):
return self.ramdump.gdbmi.get_func_info(caller)
def print_workqueue_state_3_0(self, ram_dump):
per_cpu_offset_addr = ram_dump.address_of('__per_cpu_offset')
global_cwq_sym_addr = ram_dump.address_of('global_cwq')
idle_list_offset = ram_dump.field_offset(
'struct global_cwq', 'idle_list')
worklist_offset = ram_dump.field_offset(
'struct global_cwq', 'worklist')
busy_hash_offset = ram_dump.field_offset(
'struct global_cwq', 'busy_hash')
scheduled_offset = ram_dump.field_offset('struct worker', 'scheduled')
worker_task_offset = ram_dump.field_offset('struct worker', 'task')
worker_entry_offset = ram_dump.field_offset('struct worker', 'entry')
offset_comm = ram_dump.field_offset('struct task_struct', 'comm')
work_entry_offset = ram_dump.field_offset(
'struct work_struct', 'entry')
work_hentry_offset = ram_dump.field_offset('struct worker', 'hentry')
work_func_offset = ram_dump.field_offset('struct work_struct', 'func')
current_work_offset = ram_dump.field_offset(
'struct worker', 'current_work')
cpu_wq_offset = ram_dump.field_offset(
'struct workqueue_struct', 'cpu_wq')
unbound_gcwq_addr = ram_dump.address_of('unbound_global_cwq')
if per_cpu_offset_addr is None:
per_cpu_offset0 = 0
per_cpu_offset1 = 0
else:
per_cpu_offset0 = ram_dump.read_word(per_cpu_offset_addr)
per_cpu_offset1 = ram_dump.read_word(per_cpu_offset_addr + 4)
global_cwq_cpu0_addr = global_cwq_sym_addr + per_cpu_offset0
idle_list_addr0 = ram_dump.read_word(
global_cwq_cpu0_addr + idle_list_offset)
idle_list_addr1 = ram_dump.read_word(
unbound_gcwq_addr + idle_list_offset)
worklist_addr0 = ram_dump.read_word(
global_cwq_cpu0_addr + worklist_offset)
worklist_addr1 = ram_dump.read_word(
unbound_gcwq_addr + worklist_offset)
s = '<'
for a in range(0, 64):
s = s + 'I'
busy_hash0 = ram_dump.read_string(
global_cwq_cpu0_addr + busy_hash_offset, s)
busy_hash1 = ram_dump.read_string(
unbound_gcwq_addr + busy_hash_offset, s)
busy_hash = []
for a in busy_hash0:
busy_hash.append(a)
for a in busy_hash1:
busy_hash.append(a)
for k in range(0, 128):
next_busy_worker = busy_hash[k]
if busy_hash[k] != 0:
cnt = 0
while True:
worker_addr = next_busy_worker - work_hentry_offset
worker_task_addr = ram_dump.read_word(
worker_addr + worker_task_offset)
if worker_task_addr is None or worker_task_addr == 0:
break
taskname = ram_dump.read_cstring(
worker_task_addr + offset_comm, 16)
scheduled_addr = ram_dump.read_word(
worker_addr + scheduled_offset)
current_work_addr = ram_dump.read_word(
worker_addr + current_work_offset)
current_work_func = ram_dump.read_word(
current_work_addr + work_func_offset)
wname = ram_dump.unwind_lookup(current_work_func)
if wname is not None:
worker_name, a = wname
else:
worker_name = 'Worker at 0x{0:x}'.format(
current_work_func)
self.f.write(
'BUSY Workqueue worker: {0} current_work: {1}\n'.format(taskname, worker_name))
if cnt > 200:
break
cnt += 1
next_busy_worker = ram_dump.read_word(
worker_addr + work_hentry_offset)
if next_busy_worker == 0:
break
for i in (0, 1):
if i == 0:
idle_list_addr = idle_list_addr0
else:
idle_list_addr = idle_list_addr1
next_entry = idle_list_addr
while True:
worker_addr = next_entry - worker_entry_offset
worker_task_addr = ram_dump.read_word(
next_entry - worker_entry_offset + worker_task_offset)
if worker_task_addr is None or worker_task_addr == 0:
break
taskname = ram_dump.read_cstring(
(worker_task_addr + offset_comm), 16)
scheduled_addr = ram_dump.read_word(
worker_addr + scheduled_offset)
current_work_addr = ram_dump.read_word(
worker_addr + current_work_offset)
next_entry = ram_dump.read_word(next_entry)
if current_work_addr != 0:
current_work_func = ram_dump.read_word(
current_work_addr + work_func_offset)
wname = ram_dump.unwind_lookup(current_work_func)
if wname is not None:
current_work_name, foo = wname
else:
current_work_name = 'worker at 0x{0:x}'.format(
current_work_func)
else:
current_work_func = 0
current_work_name = '(null)'
if next_entry == idle_list_addr:
break
self.f.write('IDLE Workqueue worker: {0} current_work: {1}\n'.format(
taskname, current_work_name))
if scheduled_addr == (worker_addr + scheduled_offset):
continue
if (next_entry == idle_list_addr):
break
self.f.write('Pending workqueue info\n')
for i in (0, 1):
if i == 0:
worklist_addr = worklist_addr0
else:
worklist_addr = worklist_addr1
next_work_entry = worklist_addr
while True:
work_func_addr = ram_dump.read_word(
next_work_entry - work_entry_offset + work_func_offset)
next_work_temp = ram_dump.read_word(next_work_entry)
if next_work_temp == next_work_entry:
self.f.write('!!! Cycle in workqueue!\n')
break
next_work_entry = next_work_temp
if ram_dump.virt_to_phys(work_func_addr) != 0:
wname = ram_dump.unwind_lookup(work_func_addr)
if wname is not None:
work_func_name, foo = wname
else:
work_func_name = 'worker at 0x{0:x}'.format(
work_func_addr)
if i == 0:
self.f.write(
'Pending unbound entry: {0}\n'.format(work_func_name))
else:
self.f.write(
'Pending bound entry: {0}\n'.format(work_func_name))
if next_work_entry == worklist_addr:
break
def print_workqueue_state_3_7(self, ram_dump):
per_cpu_offset_addr = ram_dump.address_of('__per_cpu_offset')
global_cwq_sym_addr = ram_dump.address_of('global_cwq')
pools_offset = ram_dump.field_offset('struct global_cwq', 'pools')
worklist_offset = ram_dump.field_offset(
'struct global_cwq', 'worklist')
busy_hash_offset = ram_dump.field_offset(
'struct global_cwq', 'busy_hash')
scheduled_offset = ram_dump.field_offset('struct worker', 'scheduled')
worker_task_offset = ram_dump.field_offset('struct worker', 'task')
worker_entry_offset = ram_dump.field_offset('struct worker', 'entry')
offset_comm = ram_dump.field_offset('struct task_struct', 'comm')
work_entry_offset = ram_dump.field_offset(
'struct work_struct', 'entry')
work_hentry_offset = ram_dump.field_offset('struct worker', 'hentry')
work_func_offset = ram_dump.field_offset('struct work_struct', 'func')
current_work_offset = ram_dump.field_offset(
'struct worker', 'current_work')
cpu_wq_offset = ram_dump.field_offset(
'struct workqueue_struct', 'cpu_wq')
pool_idle_offset = ram_dump.field_offset(
'struct worker_pool', 'idle_list')
worker_pool_size = ram_dump.sizeof('struct worker_pool')
pending_work_offset = ram_dump.field_offset(
'struct worker_pool', 'worklist')
cpus = ram_dump.get_num_cpus()
s = '<'
for a in range(0, 64):
s = s + 'I'
for i in ram_dump.iter_cpus():
busy_hash = []
if per_cpu_offset_addr is None:
offset = 0
else:
offset = ram_dump.read_word(per_cpu_offset_addr + 4 * i)
workqueue_i = global_cwq_sym_addr + offset
busy_hashi = ram_dump.read_string(
workqueue_i + busy_hash_offset, s)
for a in busy_hashi:
busy_hash.append(a)
for k in range(0, 64):
next_busy_worker = busy_hash[k]
if busy_hash[k] != 0:
cnt = 0
while True:
worker_addr = next_busy_worker - work_hentry_offset
worker_task_addr = ram_dump.read_word(
worker_addr + worker_task_offset)
if worker_task_addr is None or worker_task_addr == 0:
break
taskname = ram_dump.read_cstring(
worker_task_addr + offset_comm, 16)
scheduled_addr = ram_dump.read_word(
worker_addr + scheduled_offset)
current_work_addr = ram_dump.read_word(
worker_addr + current_work_offset)
current_work_func = ram_dump.read_word(
current_work_addr + work_func_offset)
wname = ram_dump.unwind_lookup(current_work_func)
if wname is not None:
worker_name, a = wname
else:
worker_name = 'Worker at 0x{0:x}'.format(
current_work_func)
self.f.write(
'BUSY Workqueue worker: {0} current_work: {1}\n'.format(taskname, worker_name))
if cnt > 200:
break
cnt += 1
next_busy_worker = ram_dump.read_word(
worker_addr + work_hentry_offset)
if next_busy_worker == 0:
break
worker_pool = workqueue_i + pools_offset
seen = []
# Need better way to ge the number of pools...
for k in range(0, 2):
worker_pool_i = worker_pool + k * worker_pool_size
idle_list_addr = worker_pool_i + pool_idle_offset
next_entry = ram_dump.read_word(idle_list_addr)
while True:
worker_addr = next_entry - worker_entry_offset
worker_task_addr = ram_dump.read_word(
next_entry - worker_entry_offset + worker_task_offset)
if worker_task_addr is None or worker_task_addr == 0 or worker_task_addr in seen:
break
seen.append(worker_task_addr)
taskname = ram_dump.read_cstring(
(worker_task_addr + offset_comm), 16)
scheduled_addr = ram_dump.read_word(
worker_addr + scheduled_offset)
current_work_addr = ram_dump.read_word(
worker_addr + current_work_offset)
next_entry = ram_dump.read_word(next_entry)
if current_work_addr != 0:
current_work_func = ram_dump.read_word(
current_work_addr + work_func_offset)
wname = ram_dump.unwind_lookup(current_work_func)
if wname is not None:
current_work_name, foo = wname
else:
current_work_name = 'worker at 0x{0:x}'.format(
current_work_func)
else:
current_work_func = 0
current_work_name = '(null)'
if next_entry == idle_list_addr:
break
self.f.write(
'IDLE Workqueue worker: {0} current_work: {1}\n'.format(taskname, current_work_name))
if scheduled_addr == (worker_addr + scheduled_offset):
continue
if (next_entry == idle_list_addr):
break
worklist_addr = worker_pool_i + pending_work_offset
next_work_entry = worklist_addr
while ram_dump.read_word(next_work_entry) != next_work_entry:
work_func_addr = ram_dump.read_word(
next_work_entry - work_entry_offset + work_func_offset)
next_work_temp = ram_dump.read_word(next_work_entry)
if next_work_temp == next_work_entry:
self.f.write('!!! Cycle in workqueue!\n')
break
next_work_entry = next_work_temp
if ram_dump.virt_to_phys(work_func_addr) != 0:
work_func_name, foo = ram_dump.unwind_lookup(
work_func_addr)
if i == 0:
self.f.write(
'Pending unbound entry: {0}\n'.format(work_func_name))
else:
self.f.write(
'Pending bound entry: {0}\n'.format(work_func_name))
if next_work_entry == worklist_addr:
break
def walk_workers(self, worker_addr, state):
worker_task_offset = self.ramdump.field_offset('struct worker', 'task')
offset_comm = self.ramdump.field_offset('struct task_struct', 'comm')
current_work_offset = self.ramdump.field_offset(
'struct worker', 'current_work')
work_func_offset = self.ramdump.field_offset('struct work_struct', 'func')
try:
last_func_offset = self.ramdump.field_offset('struct worker', 'last_func')
last_work = self.ramdump.read_word(worker_addr + last_func_offset)
last_func = self.ramdump.unwind_lookup(last_work)
except Exception as e:
last_func='(Unknown)'
worker_task_addr = self.ramdump.read_word(
worker_addr + worker_task_offset)
taskname = self.ramdump.read_cstring(
worker_task_addr + offset_comm, 16)
current_work_addr = self.ramdump.read_word(
worker_addr + current_work_offset)
current_func_addr = self.ramdump.read_word(
current_work_addr + work_func_offset)
try:
phys = self.ramdump.virt_to_phys(current_func_addr)
current_work_func = self.ramdump.read_word(
current_work_addr + work_func_offset)
wname = self.ramdump.unwind_lookup(current_work_func)
if wname is not None:
worker_name, a = wname
else:
worker_name = 'Worker at 0x{0:x}'.format(
current_work_func)
except:
worker_name = '(None)'
self.f.write(
'{2} Workqueue worker: {0} current_work: {1} last_func: {3}\n'.format(taskname, worker_name, state, last_func))
def pending_list_walk(self, work):
work_func_offset = self.ramdump.field_offset('struct work_struct', 'func')
work_func_addr = self.ramdump.read_word(work + work_func_offset)
try:
# virt to phys may throw an exception if the virtual address is bad
# if that happens, just skip any printing
work_func_name, foo = self.ramdump.unwind_lookup(work_func_addr)
line = self.get_caller(work_func_addr)
self.f.write(
' Pending entry: v.v (struct work_struct)0x{0:x} {1} {2}\n '.format(work, work_func_name, line))
except:
pass
def print_workqueue_state_3_10(self, ram_dump):
cpu_worker_pools_addr = ram_dump.address_of('cpu_worker_pools')
busy_hash_offset = ram_dump.field_offset(
'struct worker_pool', 'busy_hash')
worker_entry_offset = ram_dump.field_offset('struct worker', 'entry')
work_entry_offset = ram_dump.field_offset(
'struct work_struct', 'entry')
work_hentry_offset = ram_dump.field_offset('struct worker', 'hentry')
pool_idle_offset = ram_dump.field_offset(
'struct worker_pool', 'idle_list')
worker_pool_size = ram_dump.sizeof('struct worker_pool')
pending_work_offset = ram_dump.field_offset(
'struct worker_pool', 'worklist')
hash_size = 2 ** self.ramdump.gdbmi.get_value_of('BUSY_WORKER_HASH_ORDER')
s = '<'
if ram_dump.arm64:
pool_char = 'Q'
else:
pool_char = 'I'
for a in range(0, hash_size):
s = s + pool_char
for i in ram_dump.iter_cpus():
busy_hash = []
worker_pool = cpu_worker_pools_addr + ram_dump.per_cpu_offset(i)
self.f.write('\nCPU {0}\n'.format(i))
n_pools = self.ramdump.gdbmi.get_value_of('NR_STD_WORKER_POOLS')
for k in range(0, n_pools):
self.f.write('pool {0}\n'.format(k))
worker_pool_i = worker_pool + k * worker_pool_size
busy_hashi = ram_dump.read_string(
worker_pool_i + busy_hash_offset, s)
for a in busy_hashi:
busy_hash.append(a)
for k in range(0, hash_size):
next_busy_worker = busy_hash[k]
if busy_hash[k] != 0:
busy_list_walker = linux_list.ListWalker(ram_dump, next_busy_worker, work_hentry_offset)
busy_list_walker.walk(next_busy_worker, self.walk_workers, 'BUSY')
idle_list_addr = worker_pool_i + pool_idle_offset
idle_list_walker = linux_list.ListWalker(ram_dump, idle_list_addr, worker_entry_offset)
idle_list_walker.walk(self.ramdump.read_word(idle_list_addr), self.walk_workers, 'IDLE')
worklist_addr = worker_pool_i + pending_work_offset
pending_list = linux_list.ListWalker(ram_dump, worklist_addr, work_entry_offset)
pending_list.walk(self.ramdump.read_word(worklist_addr), self.pending_list_walk)
def get_workqueues_func(self, workqueue_struct_base):
name_offset = self.ramdump.field_offset('struct workqueue_struct', 'name')
flags_offset = self.ramdump.field_offset('struct workqueue_struct', 'flags')
name = self.ramdump.read_cstring(workqueue_struct_base + name_offset)
flags = self.ramdump.read_int(workqueue_struct_base + flags_offset)
aList = []
for (d, x) in self.flags_array.items():
if flags & x:
aList.append(d)
if name is None:
name =''
print(" v.v (struct workqueue_struct *)0x%x %-32s flags 0x%-8x %-64s "
% (workqueue_struct_base, name, flags, aList), file=self.f)
def get_workqueues_list(self):
print("\n\n", file = self.f)
self.flags_array = {'WQ_UNBOUND':1<<1,'WQ_FREEZABLE':1 << 2,'WQ_MEM_RECLAIM':1 << 3,
'WQ_HIGHPRI':1 << 4,'WQ_CPU_INTENSIVE':1 << 5,'WQ_SYSFS':1 << 6}
workqueues = self.ramdump.address_of('workqueues')
list_offset = self.ramdump.field_offset('struct workqueue_struct', 'list')
list_walker = linux_list.ListWalker(self.ramdump, workqueues, list_offset)
list_walker.walk(workqueues, self.get_workqueues_func)
def get_busy_hash(self, worker_pool_addr):
busy_hash_offset = self.ramdump.field_offset('struct worker_pool', 'busy_hash')
nr_busy_hash_entries = 64
busy_hash_base_addr = worker_pool_addr + busy_hash_offset
busy_hash_entry_size = self.ramdump.sizeof('struct hlist_head')
busy_hash_index = 0
base_addr = busy_hash_base_addr
busy_hash_entry = base_addr
first_offset = self.ramdump.field_offset('struct hlist_head', 'first')
current_work_offset = self.ramdump.field_offset('struct worker', 'current_work')
func_offset = self.ramdump.field_offset('struct work_struct', 'func')
while busy_hash_index < nr_busy_hash_entries:
first_worker_pool = self.ramdump.read_pointer(busy_hash_entry + first_offset)
if first_worker_pool != 0:
next_busy_worker = first_worker_pool
current_work = self.ramdump.read_pointer(next_busy_worker + current_work_offset)
func = self.ramdump.read_pointer(current_work + func_offset)
wname = self.ramdump.unwind_lookup(func)
print(" v.v (struct worker*)0x%x v.v (struct work_struct*)0x%x %s"
% (next_busy_worker, current_work, wname), file=self.f)
busy_hash_index = busy_hash_index + 1
busy_hash_entry = base_addr + busy_hash_entry_size * busy_hash_index
def get_unbound_pool_hash(self):
unbound_pool_hash = self.ramdump.address_of('unbound_pool_hash')
unbound_pool_hash_base = unbound_pool_hash
hash_entry_size = self.ramdump.sizeof('struct hlist_head')
worker_pool_hlist_offset = self.ramdump.field_offset('struct worker_pool', 'hash_node')
hash_index = 0
nr_busy_hash_entries = 64
first_offset = self.ramdump.field_offset('struct hlist_head', 'first')
next_offset = self.ramdump.field_offset('struct hlist_node', 'next')
worklist_offset = self.ramdump.field_offset('struct worker_pool', 'worklist')
print("==========>Unbound wqs", file = self.f)
while (hash_index < nr_busy_hash_entries):
first_worker_pool = self.ramdump.read_pointer(unbound_pool_hash + first_offset)
if first_worker_pool != 0:
next_worker_pool = first_worker_pool
while next_worker_pool != 0:
worker_pool_addr = next_worker_pool - worker_pool_hlist_offset
print ( "v.v (struct worker_pool*)0x%x" %(worker_pool_addr), file=self.f)
worklist = (worker_pool_addr + worklist_offset)
list_offset = self.ramdump.field_offset('struct work_struct', 'entry')
list_walker = linux_list.ListWalker(self.ramdump, worklist, list_offset)
list_walker.walk(worklist, self.pending_list_walk)
'''
walk the busy_hash
'''
self.get_busy_hash(worker_pool_addr)
next_worker_pool = self.ramdump.read_pointer(next_worker_pool + next_offset)
hash_index = hash_index + 1
unbound_pool_hash = unbound_pool_hash_base + hash_entry_size * hash_index
def parse(self):
self.f = open(self.ramdump.outdir + "/workqueue.txt", "w")
major, minor, patch = self.ramdump.kernel_version
if (major, minor) == (3, 0):
print_workqueue_state_3_0(self.ramdump)
elif (major, minor) == (3, 4):
# somebody did a backport of 3.7 workqueue patches to msm so
# need to detect new vs. old versions
idle_list_offset = self.ramdump.field_offset(
'struct global_cwq', 'idle_list')
if idle_list_offset is None:
self.print_workqueue_state_3_7(self.ramdump)
else:
self.print_workqueue_state_3_0(self.ramdump)
elif (major, minor) == (3, 7):
self.print_workqueue_state_3_7(self.ramdump)
elif (major, minor) >= (3, 10):
self.print_workqueue_state_3_10(self.ramdump)
else:
self.f.write('Kernel version {0}.{1} is not yet supported for parsing workqueues\n'.format(major, minor))
self.get_unbound_pool_hash()
self.get_workqueues_list()
self.f.close()

View File

@@ -0,0 +1,406 @@
#SPDX-License-Identifier: GPL-2.0-only
#Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
import linux_radix_tree
from mm import page_address
from mm import pfn_to_page
import os
from parser_util import register_parser, RamParser, cleanupString
from lzo1xlib import Lzo1xParser
from print_out import print_out_str
import struct
import traceback
import linux_list as llist
@register_parser('--print-zram', 'Extract data from zram')
class Zram(RamParser):
OBJ_TAG_BITS = 1
BITS_PER_LONG = 64
ZSPAGE_MAGIC = 0x58
FULLNESS_BITS = 2
CLASS_BITS = 8
ISOLATED_BITS = 3
MAGIC_VAL_BITS = 8
CRYPTO_MAX_ALG_NAME = 128
MAX_POSSIBLE_PHYSMEM_BITS = 48
CRYPTO_LZO = "lzo"
CRYPTO_LZO_RLE = "lzo-rle"
def __init__(self, *args):
super(Zram, self).__init__(*args)
# struct zram
self.zram_mem_pool_oft = self.ramdump.field_offset('struct zram', 'mem_pool')
self.zram_table_oft = self.ramdump.field_offset('struct zram', 'table')
self.zram_comp_oft = self.ramdump.field_offset('struct zram', 'comp')
self.zram_disksize_oft = self.ramdump.field_offset('struct zram', 'disksize')
self.zram_compressor_oft = self.ramdump.field_offset('struct zram', 'compressor')
# struct zram_table_entry
self.zram_table_entry_flags_oft = self.ramdump.field_offset('struct zram_table_entry', 'flags')
self.zram_table_entry_handle_oft = self.ramdump.field_offset('struct zram_table_entry', 'handle')
self.sizeof_zram_table_entry = self.ramdump.sizeof("struct zram_table_entry")
# struct page
self.page_private_oft = self.ramdump.field_offset('struct page', 'private')
self.page_freelist_oft = self.ramdump.field_offset('struct page', 'freelist')
if not self.page_freelist_oft:
self.page_freelist_oft = self.ramdump.field_offset('struct page', 'index')
self.zspage_magic_oft = self.ramdump.field_offset('struct zspage', 'magic')
# struct size_class
self.size_class_size_oft = self.ramdump.field_offset('struct size_class', 'size')
self.size_class_objs_per_zspage_offset = self.ramdump.field_offset('struct size_class', 'objs_per_zspage')
self.size_class_pages_per_zspage_offset = self.ramdump.field_offset('struct size_class', 'pages_per_zspage')
self.zs_pool_size_class_oft = self.ramdump.field_offset('struct zs_pool', 'size_class')
self.lzo_parser = Lzo1xParser()
self.init_config()
def init_config(self):
if (self.ramdump.kernel_version) < (6, 1, 0):
self.__SWP_TYPE_SHIFT = 2
self.__SWP_TYPE_BITS = 6
if self.ramdump.is_config_defined('CONFIG_ARM'):
self.__SWP_TYPE_BITS = 5
self.ZRAM_FLAG_SHIFT = 24
self.HUGE_BITS = 0
else:
self.__SWP_TYPE_SHIFT = 3
self.__SWP_TYPE_BITS = 5
self.ZRAM_FLAG_SHIFT = self.ramdump.page_shift + 1
self.HUGE_BITS = 1
self.__SWP_OFFSET_BITS = 50
if self.ramdump.is_config_defined('CONFIG_ARM'):
self.__SWP_OFFSET_BITS = 0
self.__SWP_TYPE_MASK = ((1 << self.__SWP_TYPE_BITS) - 1)
self.__SWP_OFFSET_SHIFT = (self.__SWP_TYPE_BITS + self.__SWP_TYPE_SHIFT)
self.__SWP_OFFSET_MASK = ((1 << self.__SWP_OFFSET_BITS) - 1)
self.ZRAM_SAME = self.ZRAM_FLAG_SHIFT + 1
self.ZRAM_WB = self.ZRAM_FLAG_SHIFT + 2
self.sizeof_long = self.ramdump.sizeof("unsigned long")
self.ZS_HANDLE_SIZE = self.sizeof_long
self.PAGE_SIZE = 1 << self.ramdump.page_shift
try:
self.pgtable_levels = int(self.ramdump.get_config_val("CONFIG_PGTABLE_LEVELS"))
except:
self.pgtable_levels = 3
if self.pgtable_levels == 2 and self.ramdump.is_config_defined('CONFIG_ARM'):
self.MAX_POSSIBLE_PHYSMEM_BITS = 32
if self.pgtable_levels == 3 and self.ramdump.is_config_defined('CONFIG_ARM'):
self.MAX_POSSIBLE_PHYSMEM_BITS = 40
self._PFN_BITS = (self.MAX_POSSIBLE_PHYSMEM_BITS - self.ramdump.page_shift)
if not self.ramdump.is_config_defined('CONFIG_64BIT'):
self.BITS_PER_LONG = 32
# 64-36-1 = 27 for 64bit and 32-20-1 = 11 for 32bit(pgtable_levels = 2)
self.OBJ_INDEX_BITS = (self.BITS_PER_LONG - self._PFN_BITS - self.OBJ_TAG_BITS)
self.OBJ_INDEX_MASK = (1 << self.OBJ_INDEX_BITS) -1
self.fullness_group = ['ZS_EMPTY', 'ZS_ALMOST_EMPTY','ZS_ALMOST_FULL','ZS_FULL']
def read_binary(self, addr, length):
"""Reads binary data of specified length from addr_or_name."""
min = 0
msg = b''
size = length
while length > 0:
addr = addr + min
# msg located in the same page
if length < (0x1000 - addr % 0x1000):
min = length
# msg separated in two pages
else:
min = 0x1000 - addr % 0x1000
length = length - min
addr_phys = self.ramdump.virt_to_phys(addr)
msg_binary = self.ramdump.read_physical(addr_phys, min)
if msg_binary is None or msg_binary == '':
return msg
msg = msg + msg_binary
return msg
def save_to_file(self, zram_addr, zdata, index):
with self.ramdump.open_file(os.path.join("zram", \
"zram_%x_index_0x%x.bin" % (zram_addr, index)), 'wb') as out_file:
out_file.write(self.zdata)
def process_zram(self, zram_addr, index, need_save_to_file=True):
table_addr = self.ramdump.read_word(zram_addr + self.zram_table_oft)
table_entry_addr = self.ramdump.array_index(table_addr, "struct zram_table_entry", index)
entry_flags = self.ramdump.read_word(table_entry_addr + self.zram_table_entry_flags_oft)
handle_addr = self.ramdump.read_word(table_entry_addr + self.zram_table_entry_handle_oft)
if handle_addr is None or entry_flags is None :
return False
elif handle_addr == 0:
self.zdata = bytearray(self.PAGE_SIZE)
return True
elif (entry_flags & (1<< self.ZRAM_SAME)) != 0 :
element = handle_addr
self.zdata = bytearray(self.PAGE_SIZE)
e = struct.unpack("<Q", struct.pack(">Q", element))[0]
e_s = "{0:016x}".format(e)
e_a = bytearray.fromhex(e_s)
idx = 0
while idx < self.PAGE_SIZE:
self.zdata[idx : idx + self.sizeof_long] = e_a[0 : self.sizeof_long]
idx += self.sizeof_long
if need_save_to_file:
self.save_to_file(zram_addr, self.zdata, index)
#print_out_str("Zram: parse success due to ZRAM_SAME, element=0x%x, index=0x%x" % (element, index))
return True
elif (entry_flags & (1<< self.ZRAM_WB)) != 0 :
self.zdata = None
#print_out_str("Zram: parse failed due to ZRAM_WB not support, index=0x%x" % index)
return True
handle = self.ramdump.read_word(handle_addr)
size = entry_flags & ((1 << self.ZRAM_FLAG_SHIFT) - 1)
mem_pool_addr = self.ramdump.read_word(zram_addr + self.zram_mem_pool_oft)
data = self.zs_map_object(mem_pool_addr, handle);
if (size == self.PAGE_SIZE):
sl = len(data)
if sl > self.PAGE_SIZE:
sl = self.PAGE_SIZE
self.zdata = bytearray(self.PAGE_SIZE)
idx = 0
while idx < sl:
self.zdata[idx] = data[idx]
idx += 1
if need_save_to_file:
self.save_to_file(zram_addr, self.zdata, index)
#print_out_str("Zram: parse success due to size == self.PAGE_SIZE index=0x%x" % index)
else:
if data[0] != 17 or (data[1] != 1 and data[1] !=0):
raise Exception("invalid lzo-rel header")
oudata = bytearray(self.PAGE_SIZE)
try:
self.lzo_parser.lzo1x_decompress_safe(data[0 : size], size, oudata, self.PAGE_SIZE)
except Exception as e:
if self.lzo_parser.error != self.lzo_parser.LZO_E_OK:
traceback.print_exc()
print_out_str(traceback.format_exc())
raise Exception("lzo1x_decompress_safe error happen!! error=%s, index=0x%x" \
% (self.lzo_parser.error_to_str(self.lzo_parser.error), index))
else:
#print_out_str("Zram: parse success due to lzo1x_decompress_safe, decompressed data len=0x%x, index=0x%x"\
# % (self.lzo_parser.ou_len, index))
self.zdata = self.lzo_parser.oudata[0 : self.lzo_parser.ou_len]
if need_save_to_file:
self.save_to_file(zram_addr, self.zdata, index)
return True
def zs_map_object(self, mem_pool_addr, handle):
pfn = handle >> (self.OBJ_TAG_BITS + self.OBJ_INDEX_BITS)
page_addr = pfn_to_page(self.ramdump, pfn)
if page_addr == None:
return
zspage = self.ramdump.read_word(page_addr + self.page_private_oft)
if zspage == None:
return
inuse, freeobj, magic, class_idx, fullness, isolated = self.get_zspage_meta(zspage)
if magic != self.ZSPAGE_MAGIC:
print_out_str("Zram: Page is not a zram page")
raise Exception("(struct page*)=0x%x page->private (struct zspage*)0x%x is not a zram page, \
magic=0x%x expect magic=0x%x)" % \
(page_addr, page_private_addr, magic, self.ZSPAGE_MAGIC))
size_class_addr = mem_pool_addr + self.zs_pool_size_class_oft
class_addr = self.ramdump.read_word(self.ramdump.array_index( \
size_class_addr, "struct size_class *", class_idx))
size = self.ramdump.read_s32(class_addr + self.size_class_size_oft)
obj_idx = (handle >> self.OBJ_TAG_BITS) & self.OBJ_INDEX_MASK
off = (size * obj_idx) & (self.PAGE_SIZE -1)
vm_addr = page_address(self.ramdump, page_addr) + off
data = b''
if off + size <= self.PAGE_SIZE:
data = self.read_binary(vm_addr, size)
else:
page0_size = self.PAGE_SIZE - off
data = self.read_binary(vm_addr, page0_size)
page_freelist_addr = self.ramdump.read_word(page_addr + self.page_freelist_oft)
page1_vm_addr = page_address(self.ramdump, page_freelist_addr)
data += self.read_binary(page1_vm_addr, size - page0_size)
# is huge page?
objs_per_zspage = self.ramdump.read_s32(class_addr + self.size_class_objs_per_zspage_offset)
pages_per_zspage = self.ramdump.read_s32(class_addr + self.size_class_pages_per_zspage_offset)
is_huge_page = (objs_per_zspage == 1) and (pages_per_zspage == 1)
if is_huge_page:
return data
else:
return data[self.ZS_HANDLE_SIZE:len(data)]
def read_data(self, addr, pte):
try:
if addr:
page_offset = addr & 0xFFF
type = ((pte >> self.__SWP_TYPE_SHIFT) & self.__SWP_TYPE_MASK)
offset = (pte >> self.__SWP_OFFSET_SHIFT) & self.__SWP_OFFSET_MASK
if type == 0x0:
self.zdata = None
zram_index_idr = self.ramdump.address_of('zram_index_idr')
zram_dev_rtw = linux_radix_tree.RadixTreeWalker(self.ramdump)
zram_dev_rtw.walk_radix_tree(zram_index_idr,
self.process_zram, offset, False)
if self.zdata != None:
return self.zdata[page_offset : len(self.zdata)]
except:
pass
return None
def zram_exact(self, zram_addr):
compressor = cleanupString(self.ramdump.read_cstring( \
zram_addr + self.zram_compressor_oft, self.CRYPTO_MAX_ALG_NAME))
disk_size = self.ramdump.read_word(zram_addr + self.zram_disksize_oft)
disk_size_pages = disk_size >> 12
print_out_str("zram disk_size %d Mb, compressor %s v.v (struct zram*)0x%x" % (disk_size/1024/1024, compressor, zram_addr))
self.zram_zs_pool_dump(zram_addr)
index = 0
if compressor != self.CRYPTO_LZO and compressor != self.CRYPTO_LZO_RLE:
print_out_str("Zram: not support compressoFr %s" % compressor)
return
while index < disk_size_pages:
try:
ret = self.process_zram(zram_addr, index * self.sizeof_zram_table_entry)
if ret is False:
break
except Exception as e:
traceback.print_exc()
print_out_str(traceback.format_exc())
index += 1
def get_zspage_meta(self, zspage):
freeobj_offset = self.ramdump.field_offset('struct zspage', 'freeobj')
inuse_offset = self.ramdump.field_offset('struct zspage', 'inuse')
inuse = self.ramdump.read_u32(zspage + inuse_offset)
freeobj = self.ramdump.read_u32(zspage + freeobj_offset)
magic_int = self.ramdump.read_u32(zspage + self.zspage_magic_oft)
magic = (magic_int >> (self.HUGE_BITS + self.FULLNESS_BITS + self.CLASS_BITS + 1 + self.ISOLATED_BITS)) \
& ((1 << self.MAGIC_VAL_BITS) - 1)
class_idx = (magic_int >> (self.HUGE_BITS + self.FULLNESS_BITS)) \
& ((1 << (self.CLASS_BITS + 1)) - 1)
fullness = (magic_int >> (self.HUGE_BITS)) \
& ((1 << (self.FULLNESS_BITS)) - 1)
isolated = (magic_int >> (self.HUGE_BITS + self.FULLNESS_BITS + self.CLASS_BITS + 1)) \
& ((1 << self.ISOLATED_BITS) - 1)
return inuse, freeobj, magic, class_idx, fullness, isolated
def list_call_back(self, list):
list_offset = self.ramdump.field_offset('struct zspage', 'list')
zspage = list - list_offset
inuse, freeobj, magic, class_idx, fullness, isolated = self.get_zspage_meta(zspage)
if magic == self.ZSPAGE_MAGIC:
print(" v.v (struct zspage *)0x%x " % (zspage), file=self.fout)
print(" inuse %-16d freeobj 0x%-32x magic 0x%-2x class %-2d fullness %-32s isolated %-8d" % (inuse, freeobj, magic, class_idx, self.fullness_group[fullness], isolated), file=self.fout)
if self.curret_size_class_fullness_group != fullness:
print(" possible something wrong here: v.v (struct zspage *)0x%x " % (zspage), file=self.fout)
def list_head_start(self, head):
list_walker = llist.ListWalker(self.ramdump, head, 0)
list_walker.walk(head, self.list_call_back)
def print_unsigned_long_stat(self, output_file, counter_name, addr, num):
for i in range(0, num):
val = self.ramdump.read_ulong(self.ramdump.array_index(addr, 'long', i))
print(" %s %d " % (counter_name[i], val), file=self.fout)
def print_obj_stats(self, size_class_value, output_file):
stats_offset = self.ramdump.field_offset('struct size_class', 'stats')
objs_address = size_class_value + stats_offset
max_node_stat_item = self.ramdump.gdbmi.get_value_of('NR_ZS_STAT_TYPE')
try:
class_stat_type_names = self.ramdump.gdbmi.get_enum_lookup_table('class_stat_type', max_node_stat_item)
except:
class_stat_type_names = self.ramdump.gdbmi.get_enum_lookup_table('zs_stat_type', max_node_stat_item)
self.print_unsigned_long_stat(output_file, class_stat_type_names, objs_address,
max_node_stat_item)
def zs_pool_dump(self, zs_pool_addr):
mem_pool = zs_pool_addr
print("v.v (struct zs_pool)0x%x " %(mem_pool), file = self.fout)
size_class_offset = self.ramdump.field_offset('struct zs_pool', 'size_class')
size_class = (mem_pool + size_class_offset)
print(" size_class base 0x%x " % (size_class), file=self.fout)
size_class_list = []
ZS_SIZE_CLASSES = 255
# pointer array member offset for struct size_class *size_class[ZS_SIZE_CLASSES]
offset_size = 8
if not self.ramdump.is_config_defined('CONFIG_64BIT'):
offset_size = 4
for i in range(0, ZS_SIZE_CLASSES):
size_class_index = size_class + i * offset_size
size_class_value = self.ramdump.read_pointer(size_class_index)
if size_class_value in size_class_list:
continue
size_class_list.append(size_class_value)
print("v.v (struct size_class)0x%x " % (size_class_value), file = self.fout, end=" ")
size = self.ramdump.read_s32(size_class_value + self.size_class_size_oft)
objs_per_zspage = self.ramdump.read_s32(size_class_value + self.size_class_objs_per_zspage_offset)
pages_per_zspage = self.ramdump.read_s32(size_class_value + self.size_class_pages_per_zspage_offset)
print(" size %-8d objs_per_zspage %-8d pages_per_zspage %-8d" % (
size, objs_per_zspage, pages_per_zspage), file=self.fout)
self.print_obj_stats(size_class_value, self.fout)
fullness_list_offset = self.ramdump.field_offset('struct size_class', 'fullness_list')
fullness_list = (size_class_value + fullness_list_offset)
NR_ZS_FULLNESS = 4
for i in range(0, NR_ZS_FULLNESS):
print(" fullness_group %s " % (self.fullness_group[i]), file=self.fout)
list_head = self.ramdump.array_index(fullness_list, 'struct list_head', i)
self.curret_size_class_fullness_group = i
self.list_head_start(list_head)
return 0
def zram_zs_pool_dump(self, zram_addr):
file_name = "zram_{0:x}.txt".format(zram_addr)
f_path = os.path.join(self.output_dir, file_name)
self.fout = open(f_path, "w")
mem_pool_offset = self.ramdump.field_offset('struct zram', 'mem_pool')
mem_pool = self.ramdump.read_pointer(zram_addr + mem_pool_offset)
self.zs_pool_dump(mem_pool)
self.fout.close()
def parse(self):
self.output_dir = os.path.join(os.path.abspath(
self.ramdump.outdir), "zram")
if os.path.exists(self.output_dir) is False:
os.makedirs(self.output_dir)
if self.ramdump.get_config_val("CONFIG_ZRAM") == 'm' and 'zram' not in self.ramdump.ko_file_names:
print_out_str("zram is not set")
return
if not self.ramdump.is_config_defined('CONFIG_ZRAM'):
print_out_str("zram is not set")
return
if self.ramdump.kernel_version >= (5, 4):
zram_index_idr = self.ramdump.address_of('zram_index_idr')
zram_dev_rtw = linux_radix_tree.RadixTreeWalker(self.ramdump)
try:
zram_dev_rtw.walk_radix_tree(zram_index_idr,
self.zram_exact)
except Exception as e:
traceback.print_exc()
print_out_str(traceback.format_exc())