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

525 lines
23 KiB
Python
Executable File

#!/usr/bin/env python3
# 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.
# this script requires python2. However, we'd like to be able to print
# an informative message to a user who might be unknowingly running
# python3 so we can't allow any python2 print statements to sneak in
# since they result in syntax errors in python3. By importing
# print_function we are requiring ourselves to use the python3 syntax.
from __future__ import print_function
import sys
import os
import re
import time
import platform
import textwrap
from optparse import OptionParser
import parser_util
from ramdump import RamDump
from print_out import print_out_str, set_outfile, print_out_section, print_out_exception, flush_outfile
from sched_info import verify_active_cpus
# Please update version when something is changed!'
VERSION = '2.0'
# Requires Python 3.5 or newer.
LRDP_PYTHON_VERSION_MAJOR = 3
LRDP_PYTHON_VERSION_MINOR = 5
# quick check of system requirements:
major, minor = sys.version_info[:2]
if (major, minor) < (LRDP_PYTHON_VERSION_MAJOR, LRDP_PYTHON_VERSION_MINOR):
print("This script requires python >=3.5 to run!\n")
print("You seem to be running: " + sys.version)
print()
sys.exit(1)
def parse_ram_file(option, opt_str, value, parser):
a = getattr(parser.values, option.dest)
if a is None:
a = []
temp = []
for arg in parser.rargs:
if arg[:2] == '--':
break
if arg[:1] == '-' and len(arg) > 1:
break
temp.append(arg)
if len(temp) != 3:
raise OptionValueError(
"Ram files must be specified in 'name, start, end' format")
a.append((temp[0], int(temp[1], 16), int(temp[2], 16)))
setattr(parser.values, option.dest, a)
def get_ftrace_args(option, opt, value, parser):
setattr(parser.values, option.dest, value.split(','))
if __name__ == '__main__':
starttime = time.time()
usage = 'usage: %prog [options to print]. Run with --help for more details'
parser = OptionParser(usage)
parser.add_option('', '--logcat_limit_time_sec',
dest='logcat_limit_time', type='int', default=0,
help='Defined the max time logcat parse running')
parser.add_option('', '--ftrace_limit_time_sec',
dest='ftrace_limit_time', type='int', default=0,
help='Defined the max time ftrace parse running')
parser.add_option('-e', '--ram-file', dest='ram_addr',
help='List of ram files (name, start, end)', action='callback', callback=parse_ram_file)
parser.add_option('-v', '--vmlinux', dest='vmlinux', help='vmlinux path')
parser.add_option('-n', '--nm-path', dest='nm', help='nm path')
parser.add_option('-g', '--gdb-path', dest='gdb', help='gdb path')
parser.add_option('-j', '--objdump-path', dest='objdump', help='objdump path')
parser.add_option('-a', '--auto-dump', dest='autodump',
help='Auto find ram dumps from the path')
parser.add_option('-o', '--outdir', dest='outdir', help='Output directory')
parser.add_option('-s', '--t32launcher', action='store_true',
dest='t32launcher', help='Create T32 simulator launcher', default=False)
parser.add_option('', '--t32-host-system', dest='t32_host_system',
metavar='HOST', choices=('Linux', 'Windows'),
help='T32 host system (for launcher script generation). Supported choices: "Linux", "Windows". Defaults to the system ramparse.py is running on.')
parser.add_option('-x', '--everything', action='store_true',
dest='everything', help='Output everything (may be slow')
parser.add_option('-f', '--output-file', dest='outfile',
help='Name of file to save output')
parser.add_option('', '--stdout', action='store_true',
dest='stdout', help='Dump to stdout instead of the file')
parser.add_option('', '--phys-offset', type='int',
dest='phys_offset', help='use custom phys offset')
parser.add_option('', '--kaslr-offset', type='int',
dest='kaslr_offset',
help='Offset for address space layout randomization')
parser.add_option('', '--page-offset', type='int',
dest='page_offset', help='use custom page offset')
parser.add_option('', '--force-hardware',
dest='force_hardware', help='Force the hardware detection')
parser.add_option(
'', '--force-version', type='int', dest='force_hardware_version',
help='Force the hardware detection to a specific hardware version')
parser.add_option('-l', '--kernel-exception-level', type='int',
dest='currentEL', help='Current exception level for kernel')
parser.add_option('', '--parse-qdss', action='store_true',
dest='qdss', help='Parse QDSS (deprecated)')
parser.add_option('', '--64-bit', action='store_true', dest='arm64',
help='Parse dumps as 64-bit dumps (default)')
parser.add_option('', '--32-bit', action='store_true', dest='arm32',
help='Parse dumps as 32-bit dumps')
parser.add_option('', '--shell', action='store_true',
help='Run an interactive python interpreter with the ramdump loaded')
parser.add_option('', '--classic-shell', action='store_true',
help='Like --shell, but forces the use of the classic python shell, even if ipython is installed')
parser.add_option('', '--qtf', action='store_true', dest='ftrace_format',
help='Use QTF tool to parse and save QDSS trace data')
parser.add_option('', '--ftrace-formats', action='store_true', dest='ftrace_format',
help='Extracts formats.txt used for generating ftrace')
parser.add_option('', '--qtf-path', dest='qtf_path',
help='QTF tool executable')
parser.add_option('', '--skip-qdss-bin', action='store_true',
dest='skip_qdss_bin', help='Skip QDSS ETF and ETR '
'binary data parsing from debug image (may save time '
'if large ETM and ETR buffers are present)')
parser.add_option('', '--eval',
help='Evaluate some python code directly, or from stdin if "-" is passed. The "dump" variable will be available, as it is with the --shell option.') # noqa
parser.add_option('', '--wlan', dest='wlan', help='wlan.ko path')
parser.add_option('', '--minidump', action='store_true', dest='minidump',
help='Parse minidump')
# Adding option for reduced dump
parser.add_option('', '--reduceddump', action='store_true', dest='reduceddump',
help='Parse reduceddump')
parser.add_option('', '--svm', default='', dest='svm',action='store',type="string",
help='Parse svm')
parser.add_option('', '--ram-elf', dest='ram_elf_addr',
help='pass ap_minidump.elf generated by crashscope')
parser.add_option('-m', '--mod_path', action='append', dest='mod_path_list', help='Symbol path to all loadable module ".ko.unstripped" files. Files will be searched for recursively in sub-directories. Multiple paths can be provided using -m <path> -m <path> ...')
parser.add_option('', '--timeout', dest='timeout', help='symbol path to all loadable modules')
parser.add_option('', '--dump_mod_sym_tbl', action='store_true', dest='dump_module_symbol_table',
help='dump all symbols within a module whose symbol file is available.', default=False)
parser.add_option('', '--dump_krnl_sym_tbl', action='store_true', dest='dump_kernel_symbol_table',
help='dump all symbols within the Linux kernel', default=False)
parser.add_option('', '--dump_mod_kallsyms', action='store_true', dest='dump_module_kallsyms',
help='dump all symbols retrieved from kallsyms of a specific module', default=False)
parser.add_option('', '--dump_glb_sym_tbl', action='store_true', dest='dump_global_symbol_table',
help='dump all symbols within the global symbol lookup table', default=False)
parser.add_option('', '--hyp', dest='hyp', help='elf file containing hypervisor symbol information. Required for --svm')
parser.add_option('--dbg', '--debug', action='store_true', dest='debug',
help=textwrap.dedent("""\
Enable debug.
Stop ignoring exceptions from running a parser. Intended to be used with:
python -m pdb ramparse.py <args for ramparse.py>
"""))
parser.add_option('', '--ftrace-args', type='string', action='callback',
callback=get_ftrace_args, dest = 'ftrace_args',
help="""
Comma separated trace subsystems to capture. Works with --dump-ftrace parser option
trace_names = [
"binder", "bootreceiver", "clock_reg",
"kgsl-fence", "memory", "mmc", "rproc_qcom",
"suspend", "ufs", "usb", "wifi"
]
""",
default=[])
parser.add_option('--fs', '--ftrace_buffer_size_kb', type='int', dest='ftrace_max_size',
help="""
This option indicates that ftrace trace buffer max size in KB.
It will be passed to ensure an early bailout from ftrace parser if size goes
beyond this specified value.
Example: --fs 4096
This specifies that max size is 4096 KB.
""")
parser.add_option('', '--skip_TLB_Cache_parse', action='store_true', help='Skip parsing TLB Cache Dumps in parse_debug_image')
parser.add_option('--iommu-pg-table-format', action='store', choices=['fastrpc', 'default'],
default='default')
for p in parser_util.get_parsers():
parser.add_option(p.shortopt or '',
p.longopt,
dest=p.cls.__name__,
help=p.desc,
action='store_true')
(options, args) = parser.parse_args()
if options.reduceddump:
try:
import ramreduction_util as elfutil
except ImportError as ierr:
print("Missing PyElftools library. Check README")
sys.exit(1)
except Exception as err:
print("{}".format(str(err)))
sys.exit(1)
everything_exclusion_list = []
if options.minidump:
default_list = []
default_list.append("Schedinfo")
default_list.append("Dmesg")
default_list.append("RTB")
default_list.append("DebugImage")
default_list.append("Watchdog")
default_list.append("KBootLog")
default_list.append("PageTracking")
default_list.append("Slabinfo")
default_list.append("RunQueues")
default_list.append("PStore")
default_list.append("Kconfig")
default_list.append("ThermalTemp")
default_list.append("ipc_logging_cn")
if options.everything:
everything_exclusion_list.append("ROData")
if options.outdir:
if not os.path.exists(options.outdir):
print ('!!! Out directory does not exist. Creating...')
try:
os.makedirs(options.outdir)
except:
print ("Failed to create %s. You probably don't have permissions there. Bailing." % options.outdir)
sys.exit(1)
else:
options.outdir = '.'
if options.outfile is None:
# dmesg_TZ is a very non-descriptive name and should be changed
# sometime in the future
options.outfile = 'dmesg_TZ.txt'
if not options.stdout:
set_outfile(options.outdir + '/' + options.outfile)
print_out_str('Linux Ram Dump Parser Version %s' % VERSION)
args = ''
for arg in sys.argv:
args = args + arg + ' '
print_out_str('Arguments: {0}'.format(args))
system_type = parser_util.get_system_type()
if options.autodump is not None:
if os.path.exists(options.autodump):
print_out_str(
'Looking for Ram dumps in {0}'.format(options.autodump))
else:
print_out_str(
'Path {0} does not exist for Ram dumps. Exiting...'.format(options.autodump))
sys.exit(1)
if options.vmlinux is None:
if options.autodump is None:
print_out_str("No vmlinux or autodump dir given. I can't proceed!")
parser.print_usage()
sys.exit(1)
autovm = os.path.join(options.autodump, 'vmlinux')
if os.path.isfile(autovm):
options.vmlinux = autovm
else:
print_out_str("No vmlinux given or found in autodump dir. I can't proceed!")
parser.print_usage()
sys.exit(1)
if not os.path.exists(options.vmlinux):
print_out_str(
'{0} does not exist. Cannot proceed without vmlinux. Exiting...'.format(options.vmlinux))
sys.exit(1)
elif not os.path.isfile(options.vmlinux):
print_out_str(
'{0} is not a file. Did you pass the ram file directory instead of the vmlinux?'.format(options.vmlinux))
sys.exit(1)
print_out_str('using vmlinux file {0}'.format(options.vmlinux))
if options.ram_addr is None and options.autodump is None and not options.minidump and not options.reduceddump:
print_out_str('Need one of --auto-dump or at least one --ram-file')
sys.exit(1)
if options.reduceddump and not options.autodump:
print_out_str('Need autodump with --reduceddump option')
sys.exit(1)
if options.ram_addr is not None:
for a in options.ram_addr:
if os.path.exists(a[0]):
print_out_str(
'Loading Ram file {0} from {1:x}--{2:x}'.format(a[0], a[1], a[2]))
else:
print_out_str(
'Ram file {0} does not exist. Exiting...'.format(a[0]))
sys.exit(1)
# offset 4 of vmlinux indcates it's 32 or 64 bits
# for 64 bits it is 0x2, for 32 bits it is 0x1
vm_file = open(options.vmlinux, "rb")
vm_file.seek(4, 0)
bin_bits = vm_file.read(1)
vm_file.close()
options.arm64 = (bin_bits == b'\x02')
if options.wlan is None:
options.wlan = "INTEGRATED"
else:
if not os.path.exists(options.wlan):
print_out_str('{} does not exist.'.format(options.wlan))
print_out_str('Cannot proceed without wlan.ko Exiting')
sys.exit(1)
gdb_path = options.gdb
nm_path = options.nm
objdump_path = options.objdump
try:
import local_settings
try:
gdb_ndk_path = None
if options.arm64:
gdb_path = gdb_path or local_settings.gdb64_path
if hasattr(local_settings, 'gdb64_ndk_path'):
gdb_ndk_path = local_settings.gdb64_ndk_path
nm_path = nm_path or local_settings.nm64_path
objdump_path = objdump_path or local_settings.objdump64_path
else:
gdb_path = gdb_path or local_settings.gdb_path
nm_path = nm_path or local_settings.nm_path
objdump_path = objdump_path or local_settings.objdump_path
except AttributeError as e:
print_out_str("local_settings.py looks bogus. Please see README.txt")
missing_attr = re.sub(".*has no attribute '(.*)'", '\\1', e.message)
print_out_str("Specifically, looks like you're missing `%s'\n" % missing_attr)
print_out_str("Full message: %s" % e.message)
sys.exit(1)
except ImportError:
cross_compile = os.environ.get('CROSS_COMPILE')
if cross_compile is not None:
gdb_path = gdb_path or cross_compile+"gdb"
nm_path = nm_path or cross_compile+"nm"
if gdb_path is None or nm_path is None:
print_out_str("!!! Incorrect path for toolchain specified.")
print_out_str("!!! Please see the README for instructions on setting up local_settings.py or CROSS_COMPILE")
sys.exit(1)
print_out_str("Using gdb path {0}".format(gdb_path))
print_out_str("Using nm path {0}".format(nm_path))
if not os.path.exists(gdb_path):
print_out_str("!!! gdb_path {0} does not exist! Check your settings!".format(gdb_path))
sys.exit(1)
if not os.access(gdb_path, os.X_OK):
print_out_str("!!! No execute permissions on gdb path {0}".format(gdb_path))
print_out_str("!!! Please check the path settings")
print_out_str("!!! If this tool is being run from a shared location, contact the maintainer")
sys.exit(1)
if not os.path.exists(nm_path):
print_out_str("!!! nm_path {0} does not exist! Check your settings!".format(nm_path))
sys.exit(1)
if not os.access(nm_path, os.X_OK):
print_out_str("!!! No execute permissions on nm path {0}".format(nm_path))
print_out_str("!!! Please check the path settings")
print_out_str("!!! If this tool is being run from a shared location, contact the maintainer")
sys.exit(1)
if options.everything:
options.qtf = True
dump = RamDump(options, nm_path, gdb_path, objdump_path,gdb_ndk_path)
if options.eval:
if options.eval == '-':
code = sys.stdin.read()
else:
code = options.eval
exec(code)
sys.exit(0)
if options.shell or options.classic_shell:
print("Entering interactive shell mode.")
print("The RamDump instance is available in the `dump' variable\n")
do_fallback = options.classic_shell
if not do_fallback:
try:
from IPython import embed
embed()
except ImportError:
do_fallback = True
if do_fallback:
import code
vars = globals()
vars.update(locals())
#readline library not available as a standard python libary on windows
if platform.system() == 'Linux':
import readline
import rlcompleter
readline.set_completer(rlcompleter.Completer(vars).complete)
readline.parse_and_bind("tab: complete")
shell = code.InteractiveConsole(vars)
shell.interact()
sys.exit(0)
if not options.minidump:
if not dump.print_command_line():
print_out_str('!!! Error printing saved command line.')
print_out_str('!!! The vmlinux is probably wrong for the ramdumps')
print_out_str('!!! Exiting now...')
sys.exit(1)
if options.minidump:
try:
if not dump.print_socinfo_minidump():
print_out_str('!!! No serial number information '
'available for this minidump.')
except Exception as err:
print_out_str('Unable to extract serial number information')
else:
if not dump.print_socinfo():
print_out_str('!!! No serial number information available.')
try:
epoch_ns = dump.read_u64('cd.read_data[0].epoch_ns')
epoch_cyc = dump.read_u64('cd.read_data[0].epoch_cyc')
print_out_str('\nepoch_ns: {0}ns epoch_cyc: {1}\n'.format(epoch_ns,epoch_cyc))
except Exception as e:
print_out_str(str(e))
pass
if options.qdss:
print_out_str('!!! --parse-qdss is now deprecated')
print_out_str(
'!!! Please just use --parse-debug-image to get QDSS information')
# Always verify Scheduler requirement for active_cpus on 64-bit platforms.
if options.arm64:
try:
verify_active_cpus(dump)
except Exception as err:
print_out_str('Unable to extract active cpus info')
# we called parser.add_option with dest=p.cls.__name__ above,
# so if the user passed that option then `options' will have a
# p.cls.__name__ attribute.
if options.minidump:
parsers_to_run = [p for p in parser_util.get_parsers()
if getattr(options, p.cls.__name__)
or (options.everything and p.cls.__name__ in default_list)]
else:
parsers_to_run = [p for p in parser_util.get_parsers()
if getattr(options, p.cls.__name__)
or (options.everything and not p.optional)]
if options.timeout:
from func_timeout import func_timeout, FunctionTimedOut
print_out_str("Time taken to setup the subparsers run : {}".format(time.time()-starttime))
starttime = time.time()
for i,p in enumerate(parsers_to_run):
if options.everything:
if p.cls.__name__ in everything_exclusion_list:
continue
if i == 0:
sys.stderr.write("\n")
if options.minidump:
if p.cls.__name__ not in default_list:
print(" [%d/%d] %s ... not supported in minidump \n" %
(i + 1, len(parsers_to_run), p.longopt))
continue
print(" [%d/%d] %s ... " %
(i + 1, len(parsers_to_run), p.longopt), end='', flush=True)
before = time.time()
print_out_str("start time {0}".format(before))
with print_out_section(p.cls.__name__):
try:
if options.timeout:
try:
func_timeout(int(options.timeout), p.cls(dump).parse)
except Exception as e:
print_out_str(e)
else:
p.cls(dump).parse()
except:
# log exceptions and continue by default
if not options.debug:
print_out_str('!!! Exception while running {0}'.format(p.cls.__name__))
print_out_exception()
print("FAILED! ")
else:
raise
after = time.time()
print_out_str("end time {0} time cost {1} for {2}".format(after, (after - before), p.cls.__name__))
print("%fs" % (after - before), flush=True)
flush_outfile()
sys.stderr.write("\n")
if options.t32launcher or options.everything or options.minidump:
dump.create_t32_launcher()
dump.gdbmi.close()
print_out_str("Time taken to complete ramparser subscripts : {}".format(time.time()-starttime))
if options.reduceddump:
print_out_str("Number of cache hits on full cache : {}".format(elfutil.cachehits))
print_out_str("Number of cache misses on full cache : {}".format(elfutil.cachemiss))