summaryrefslogtreecommitdiffstats
path: root/scripts/gdb/linux
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/gdb/linux')
-rw-r--r--scripts/gdb/linux/.gitignore4
-rw-r--r--scripts/gdb/linux/Makefile25
-rw-r--r--scripts/gdb/linux/__init__.py1
-rw-r--r--scripts/gdb/linux/clk.py78
-rw-r--r--scripts/gdb/linux/config.py44
-rw-r--r--scripts/gdb/linux/constants.py.in79
-rw-r--r--scripts/gdb/linux/cpus.py193
-rw-r--r--scripts/gdb/linux/device.py182
-rw-r--r--scripts/gdb/linux/dmesg.py152
-rw-r--r--scripts/gdb/linux/genpd.py85
-rw-r--r--scripts/gdb/linux/lists.py136
-rw-r--r--scripts/gdb/linux/modules.py95
-rw-r--r--scripts/gdb/linux/proc.py275
-rw-r--r--scripts/gdb/linux/rbtree.py177
-rw-r--r--scripts/gdb/linux/symbols.py189
-rw-r--r--scripts/gdb/linux/tasks.py140
-rw-r--r--scripts/gdb/linux/timerlist.py221
-rw-r--r--scripts/gdb/linux/utils.py206
18 files changed, 2282 insertions, 0 deletions
diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore
new file mode 100644
index 000000000..43234cbcb
--- /dev/null
+++ b/scripts/gdb/linux/.gitignore
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+*.pyc
+*.pyo
+constants.py
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
new file mode 100644
index 000000000..48941faa6
--- /dev/null
+++ b/scripts/gdb/linux/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ifdef building_out_of_srctree
+
+symlinks := $(patsubst $(srctree)/$(src)/%,%,$(wildcard $(srctree)/$(src)/*.py))
+
+quiet_cmd_symlink = SYMLINK $@
+ cmd_symlink = ln -fsn $(patsubst $(obj)/%,$(abspath $(srctree))/$(src)/%,$@) $@
+
+always-y += $(symlinks)
+$(addprefix $(obj)/, $(symlinks)): FORCE
+ $(call if_changed,symlink)
+
+endif
+
+quiet_cmd_gen_constants_py = GEN $@
+ cmd_gen_constants_py = \
+ $(CPP) -E -x c -P $(c_flags) $< > $@ ;\
+ sed -i '1,/<!-- end-c-headers -->/d;' $@
+
+always-y += constants.py
+$(obj)/constants.py: $(src)/constants.py.in FORCE
+ $(call if_changed_dep,gen_constants_py)
+
+clean-files := *.pyc *.pyo
diff --git a/scripts/gdb/linux/__init__.py b/scripts/gdb/linux/__init__.py
new file mode 100644
index 000000000..4680fb176
--- /dev/null
+++ b/scripts/gdb/linux/__init__.py
@@ -0,0 +1 @@
+# nothing to do for the initialization of this package
diff --git a/scripts/gdb/linux/clk.py b/scripts/gdb/linux/clk.py
new file mode 100644
index 000000000..7a01fdc3e
--- /dev/null
+++ b/scripts/gdb/linux/clk.py
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) NXP 2019
+
+import gdb
+import sys
+
+from linux import utils, lists, constants
+
+clk_core_type = utils.CachedType("struct clk_core")
+
+
+def clk_core_for_each_child(hlist_head):
+ return lists.hlist_for_each_entry(hlist_head,
+ clk_core_type.get_type().pointer(), "child_node")
+
+
+class LxClkSummary(gdb.Command):
+ """Print clk tree summary
+
+Output is a subset of /sys/kernel/debug/clk/clk_summary
+
+No calls are made during printing, instead a (c) if printed after values which
+are cached and potentially out of date"""
+
+ def __init__(self):
+ super(LxClkSummary, self).__init__("lx-clk-summary", gdb.COMMAND_DATA)
+
+ def show_subtree(self, clk, level):
+ gdb.write("%*s%-*s %7d %8d %8d %11lu%s\n" % (
+ level * 3 + 1, "",
+ 30 - level * 3,
+ clk['name'].string(),
+ clk['enable_count'],
+ clk['prepare_count'],
+ clk['protect_count'],
+ clk['rate'],
+ '(c)' if clk['flags'] & constants.LX_CLK_GET_RATE_NOCACHE else ' '))
+
+ for child in clk_core_for_each_child(clk['children']):
+ self.show_subtree(child, level + 1)
+
+ def invoke(self, arg, from_tty):
+ if utils.gdb_eval_or_none("clk_root_list") is None:
+ raise gdb.GdbError("No clocks registered")
+ gdb.write(" enable prepare protect \n")
+ gdb.write(" clock count count count rate \n")
+ gdb.write("------------------------------------------------------------------------\n")
+ for clk in clk_core_for_each_child(gdb.parse_and_eval("clk_root_list")):
+ self.show_subtree(clk, 0)
+ for clk in clk_core_for_each_child(gdb.parse_and_eval("clk_orphan_list")):
+ self.show_subtree(clk, 0)
+
+
+LxClkSummary()
+
+
+class LxClkCoreLookup(gdb.Function):
+ """Find struct clk_core by name"""
+
+ def __init__(self):
+ super(LxClkCoreLookup, self).__init__("lx_clk_core_lookup")
+
+ def lookup_hlist(self, hlist_head, name):
+ for child in clk_core_for_each_child(hlist_head):
+ if child['name'].string() == name:
+ return child
+ result = self.lookup_hlist(child['children'], name)
+ if result:
+ return result
+
+ def invoke(self, name):
+ name = name.string()
+ return (self.lookup_hlist(gdb.parse_and_eval("clk_root_list"), name) or
+ self.lookup_hlist(gdb.parse_and_eval("clk_orphan_list"), name))
+
+
+LxClkCoreLookup()
diff --git a/scripts/gdb/linux/config.py b/scripts/gdb/linux/config.py
new file mode 100644
index 000000000..8843ab3cb
--- /dev/null
+++ b/scripts/gdb/linux/config.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2019 Google LLC.
+
+import gdb
+import zlib
+
+from linux import utils
+
+
+class LxConfigDump(gdb.Command):
+ """Output kernel config to the filename specified as the command
+ argument. Equivalent to 'zcat /proc/config.gz > config.txt' on
+ a running target"""
+
+ def __init__(self):
+ super(LxConfigDump, self).__init__("lx-configdump", gdb.COMMAND_DATA,
+ gdb.COMPLETE_FILENAME)
+
+ def invoke(self, arg, from_tty):
+ if len(arg) == 0:
+ filename = "config.txt"
+ else:
+ filename = arg
+
+ try:
+ py_config_ptr = gdb.parse_and_eval("&kernel_config_data")
+ py_config_ptr_end = gdb.parse_and_eval("&kernel_config_data_end")
+ py_config_size = py_config_ptr_end - py_config_ptr
+ except gdb.error as e:
+ raise gdb.GdbError("Can't find config, enable CONFIG_IKCONFIG?")
+
+ inf = gdb.inferiors()[0]
+ zconfig_buf = utils.read_memoryview(inf, py_config_ptr,
+ py_config_size).tobytes()
+
+ config_buf = zlib.decompress(zconfig_buf, 16)
+ with open(filename, 'wb') as f:
+ f.write(config_buf)
+
+ gdb.write("Dumped config to " + filename + "\n")
+
+
+LxConfigDump()
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
new file mode 100644
index 000000000..08f0587d1
--- /dev/null
+++ b/scripts/gdb/linux/constants.py.in
@@ -0,0 +1,79 @@
+/*
+ * gdb helper commands and functions for Linux kernel debugging
+ *
+ * Kernel constants derived from include files.
+ *
+ * Copyright (c) 2016 Linaro Ltd
+ *
+ * Authors:
+ * Kieran Bingham <kieran.bingham@linaro.org>
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/mount.h>
+#include <linux/of_fdt.h>
+#include <linux/threads.h>
+
+/* We need to stringify expanded macros so that they can be parsed */
+
+#define STRING(x) #x
+#define XSTRING(x) STRING(x)
+
+#define LX_VALUE(x) LX_##x = x
+#define LX_GDBPARSED(x) LX_##x = gdb.parse_and_eval(XSTRING(x))
+
+/*
+ * IS_ENABLED generates (a || b) which is not compatible with python
+ * We can only switch on configuration items we know are available
+ * Therefore - IS_BUILTIN() is more appropriate
+ */
+#define LX_CONFIG(x) LX_##x = IS_BUILTIN(x)
+
+/* The build system will take care of deleting everything above this marker */
+<!-- end-c-headers -->
+
+import gdb
+
+LX_CONFIG(CONFIG_DEBUG_INFO_REDUCED)
+
+/* linux/clk-provider.h */
+if IS_BUILTIN(CONFIG_COMMON_CLK):
+ LX_GDBPARSED(CLK_GET_RATE_NOCACHE)
+
+/* linux/fs.h */
+LX_VALUE(SB_RDONLY)
+LX_VALUE(SB_SYNCHRONOUS)
+LX_VALUE(SB_MANDLOCK)
+LX_VALUE(SB_DIRSYNC)
+LX_VALUE(SB_NOATIME)
+LX_VALUE(SB_NODIRATIME)
+
+/* linux/htimer.h */
+LX_GDBPARSED(hrtimer_resolution)
+
+/* linux/mount.h */
+LX_VALUE(MNT_NOSUID)
+LX_VALUE(MNT_NODEV)
+LX_VALUE(MNT_NOEXEC)
+LX_VALUE(MNT_NOATIME)
+LX_VALUE(MNT_NODIRATIME)
+LX_VALUE(MNT_RELATIME)
+
+/* linux/threads.h */
+LX_VALUE(NR_CPUS)
+
+/* linux/of_fdt.h> */
+LX_VALUE(OF_DT_HEADER)
+
+/* Kernel Configs */
+LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS)
+LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+LX_CONFIG(CONFIG_HIGH_RES_TIMERS)
+LX_CONFIG(CONFIG_NR_CPUS)
+LX_CONFIG(CONFIG_OF)
+LX_CONFIG(CONFIG_TICK_ONESHOT)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
new file mode 100644
index 000000000..15fc4626d
--- /dev/null
+++ b/scripts/gdb/linux/cpus.py
@@ -0,0 +1,193 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# per-cpu tools
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import tasks, utils
+
+
+task_type = utils.CachedType("struct task_struct")
+
+
+MAX_CPUS = 4096
+
+
+def get_current_cpu():
+ if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU:
+ return gdb.selected_thread().num - 1
+ elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB:
+ tid = gdb.selected_thread().ptid[2]
+ if tid > (0x100000000 - MAX_CPUS - 2):
+ return 0x100000000 - tid - 2
+ else:
+ return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu']
+ else:
+ raise gdb.GdbError("Sorry, obtaining the current CPU is not yet "
+ "supported with this gdb server.")
+
+
+def per_cpu(var_ptr, cpu):
+ if cpu == -1:
+ cpu = get_current_cpu()
+ if utils.is_target_arch("sparc:v9"):
+ offset = gdb.parse_and_eval(
+ "trap_block[{0}].__per_cpu_base".format(str(cpu)))
+ else:
+ try:
+ offset = gdb.parse_and_eval(
+ "__per_cpu_offset[{0}]".format(str(cpu)))
+ except gdb.error:
+ # !CONFIG_SMP case
+ offset = 0
+ pointer = var_ptr.cast(utils.get_long_type()) + offset
+ return pointer.cast(var_ptr.type).dereference()
+
+
+cpu_mask = {}
+
+
+def cpu_mask_invalidate(event):
+ global cpu_mask
+ cpu_mask = {}
+ gdb.events.stop.disconnect(cpu_mask_invalidate)
+ if hasattr(gdb.events, 'new_objfile'):
+ gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
+
+
+def cpu_list(mask_name):
+ global cpu_mask
+ mask = None
+ if mask_name in cpu_mask:
+ mask = cpu_mask[mask_name]
+ if mask is None:
+ mask = gdb.parse_and_eval(mask_name + ".bits")
+ if hasattr(gdb, 'events'):
+ cpu_mask[mask_name] = mask
+ gdb.events.stop.connect(cpu_mask_invalidate)
+ if hasattr(gdb.events, 'new_objfile'):
+ gdb.events.new_objfile.connect(cpu_mask_invalidate)
+ bits_per_entry = mask[0].type.sizeof * 8
+ num_entries = mask.type.sizeof * 8 / bits_per_entry
+ entry = -1
+ bits = 0
+
+ while True:
+ while bits == 0:
+ entry += 1
+ if entry == num_entries:
+ return
+ bits = mask[entry]
+ if bits != 0:
+ bit = 0
+ break
+
+ while bits & 1 == 0:
+ bits >>= 1
+ bit += 1
+
+ cpu = entry * bits_per_entry + bit
+
+ bits >>= 1
+ bit += 1
+
+ yield int(cpu)
+
+
+def each_online_cpu():
+ for cpu in cpu_list("__cpu_online_mask"):
+ yield cpu
+
+
+def each_present_cpu():
+ for cpu in cpu_list("__cpu_present_mask"):
+ yield cpu
+
+
+def each_possible_cpu():
+ for cpu in cpu_list("__cpu_possible_mask"):
+ yield cpu
+
+
+def each_active_cpu():
+ for cpu in cpu_list("__cpu_active_mask"):
+ yield cpu
+
+
+class LxCpus(gdb.Command):
+ """List CPU status arrays
+
+Displays the known state of each CPU based on the kernel masks
+and can help identify the state of hotplugged CPUs"""
+
+ def __init__(self):
+ super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu())))
+ gdb.write("Present CPUs : {}\n".format(list(each_present_cpu())))
+ gdb.write("Online CPUs : {}\n".format(list(each_online_cpu())))
+ gdb.write("Active CPUs : {}\n".format(list(each_active_cpu())))
+
+
+LxCpus()
+
+
+class PerCpu(gdb.Function):
+ """Return per-cpu variable.
+
+$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
+given CPU number. If CPU is omitted, the CPU of the current context is used.
+Note that VAR has to be quoted as string."""
+
+ def __init__(self):
+ super(PerCpu, self).__init__("lx_per_cpu")
+
+ def invoke(self, var_name, cpu=-1):
+ var_ptr = gdb.parse_and_eval("&" + var_name.string())
+ return per_cpu(var_ptr, cpu)
+
+
+PerCpu()
+
+def get_current_task(cpu):
+ task_ptr_type = task_type.get_type().pointer()
+
+ if utils.is_target_arch("x86"):
+ var_ptr = gdb.parse_and_eval("&current_task")
+ return per_cpu(var_ptr, cpu).dereference()
+ elif utils.is_target_arch("aarch64"):
+ current_task_addr = gdb.parse_and_eval("$SP_EL0")
+ if((current_task_addr >> 63) != 0):
+ current_task = current_task_addr.cast(task_ptr_type)
+ return current_task.dereference()
+ else:
+ raise gdb.GdbError("Sorry, obtaining the current task is not allowed "
+ "while running in userspace(EL0)")
+ else:
+ raise gdb.GdbError("Sorry, obtaining the current task is not yet "
+ "supported with this arch")
+
+class LxCurrentFunc(gdb.Function):
+ """Return current task.
+
+$lx_current([CPU]): Return the per-cpu task variable for the given CPU
+number. If CPU is omitted, the CPU of the current context is used."""
+
+ def __init__(self):
+ super(LxCurrentFunc, self).__init__("lx_current")
+
+ def invoke(self, cpu=-1):
+ return get_current_task(cpu)
+
+
+LxCurrentFunc()
diff --git a/scripts/gdb/linux/device.py b/scripts/gdb/linux/device.py
new file mode 100644
index 000000000..16376c5cf
--- /dev/null
+++ b/scripts/gdb/linux/device.py
@@ -0,0 +1,182 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) NXP 2019
+
+import gdb
+
+from linux.utils import CachedType
+from linux.utils import container_of
+from linux.lists import list_for_each_entry
+
+
+device_private_type = CachedType('struct device_private')
+device_type = CachedType('struct device')
+
+subsys_private_type = CachedType('struct subsys_private')
+kobject_type = CachedType('struct kobject')
+kset_type = CachedType('struct kset')
+
+bus_type = CachedType('struct bus_type')
+class_type = CachedType('struct class')
+
+
+def dev_name(dev):
+ dev_init_name = dev['init_name']
+ if dev_init_name:
+ return dev_init_name.string()
+ return dev['kobj']['name'].string()
+
+
+def kset_for_each_object(kset):
+ return list_for_each_entry(kset['list'],
+ kobject_type.get_type().pointer(), "entry")
+
+
+def for_each_bus():
+ for kobj in kset_for_each_object(gdb.parse_and_eval('bus_kset')):
+ subsys = container_of(kobj, kset_type.get_type().pointer(), 'kobj')
+ subsys_priv = container_of(subsys, subsys_private_type.get_type().pointer(), 'subsys')
+ yield subsys_priv['bus']
+
+
+def for_each_class():
+ for kobj in kset_for_each_object(gdb.parse_and_eval('class_kset')):
+ subsys = container_of(kobj, kset_type.get_type().pointer(), 'kobj')
+ subsys_priv = container_of(subsys, subsys_private_type.get_type().pointer(), 'subsys')
+ yield subsys_priv['class']
+
+
+def get_bus_by_name(name):
+ for item in for_each_bus():
+ if item['name'].string() == name:
+ return item
+ raise gdb.GdbError("Can't find bus type {!r}".format(name))
+
+
+def get_class_by_name(name):
+ for item in for_each_class():
+ if item['name'].string() == name:
+ return item
+ raise gdb.GdbError("Can't find device class {!r}".format(name))
+
+
+klist_type = CachedType('struct klist')
+klist_node_type = CachedType('struct klist_node')
+
+
+def klist_for_each(klist):
+ return list_for_each_entry(klist['k_list'],
+ klist_node_type.get_type().pointer(), 'n_node')
+
+
+def bus_for_each_device(bus):
+ for kn in klist_for_each(bus['p']['klist_devices']):
+ dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_bus')
+ yield dp['device']
+
+
+def class_for_each_device(cls):
+ for kn in klist_for_each(cls['p']['klist_devices']):
+ dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_class')
+ yield dp['device']
+
+
+def device_for_each_child(dev):
+ for kn in klist_for_each(dev['p']['klist_children']):
+ dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_parent')
+ yield dp['device']
+
+
+def _show_device(dev, level=0, recursive=False):
+ gdb.write('{}dev {}:\t{}\n'.format('\t' * level, dev_name(dev), dev))
+ if recursive:
+ for child in device_for_each_child(dev):
+ _show_device(child, level + 1, recursive)
+
+
+class LxDeviceListBus(gdb.Command):
+ '''Print devices on a bus (or all buses if not specified)'''
+
+ def __init__(self):
+ super(LxDeviceListBus, self).__init__('lx-device-list-bus', gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ if not arg:
+ for bus in for_each_bus():
+ gdb.write('bus {}:\t{}\n'.format(bus['name'].string(), bus))
+ for dev in bus_for_each_device(bus):
+ _show_device(dev, level=1)
+ else:
+ bus = get_bus_by_name(arg)
+ if not bus:
+ raise gdb.GdbError("Can't find bus {!r}".format(arg))
+ for dev in bus_for_each_device(bus):
+ _show_device(dev)
+
+
+class LxDeviceListClass(gdb.Command):
+ '''Print devices in a class (or all classes if not specified)'''
+
+ def __init__(self):
+ super(LxDeviceListClass, self).__init__('lx-device-list-class', gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ if not arg:
+ for cls in for_each_class():
+ gdb.write("class {}:\t{}\n".format(cls['name'].string(), cls))
+ for dev in class_for_each_device(cls):
+ _show_device(dev, level=1)
+ else:
+ cls = get_class_by_name(arg)
+ for dev in class_for_each_device(cls):
+ _show_device(dev)
+
+
+class LxDeviceListTree(gdb.Command):
+ '''Print a device and its children recursively'''
+
+ def __init__(self):
+ super(LxDeviceListTree, self).__init__('lx-device-list-tree', gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ if not arg:
+ raise gdb.GdbError('Please provide pointer to struct device')
+ dev = gdb.parse_and_eval(arg)
+ if dev.type != device_type.get_type().pointer():
+ raise gdb.GdbError('Please provide pointer to struct device')
+ _show_device(dev, level=0, recursive=True)
+
+
+class LxDeviceFindByBusName(gdb.Function):
+ '''Find struct device by bus and name (both strings)'''
+
+ def __init__(self):
+ super(LxDeviceFindByBusName, self).__init__('lx_device_find_by_bus_name')
+
+ def invoke(self, bus, name):
+ name = name.string()
+ bus = get_bus_by_name(bus.string())
+ for dev in bus_for_each_device(bus):
+ if dev_name(dev) == name:
+ return dev
+
+
+class LxDeviceFindByClassName(gdb.Function):
+ '''Find struct device by class and name (both strings)'''
+
+ def __init__(self):
+ super(LxDeviceFindByClassName, self).__init__('lx_device_find_by_class_name')
+
+ def invoke(self, cls, name):
+ name = name.string()
+ cls = get_class_by_name(cls.string())
+ for dev in class_for_each_device(cls):
+ if dev_name(dev) == name:
+ return dev
+
+
+LxDeviceListBus()
+LxDeviceListClass()
+LxDeviceListTree()
+LxDeviceFindByBusName()
+LxDeviceFindByClassName()
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py
new file mode 100644
index 000000000..c771831eb
--- /dev/null
+++ b/scripts/gdb/linux/dmesg.py
@@ -0,0 +1,152 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# kernel log buffer dump
+#
+# Copyright (c) Siemens AG, 2011, 2012
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+import sys
+
+from linux import utils
+
+printk_info_type = utils.CachedType("struct printk_info")
+prb_data_blk_lpos_type = utils.CachedType("struct prb_data_blk_lpos")
+prb_desc_type = utils.CachedType("struct prb_desc")
+prb_desc_ring_type = utils.CachedType("struct prb_desc_ring")
+prb_data_ring_type = utils.CachedType("struct prb_data_ring")
+printk_ringbuffer_type = utils.CachedType("struct printk_ringbuffer")
+
+class LxDmesg(gdb.Command):
+ """Print Linux kernel log buffer."""
+
+ def __init__(self):
+ super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ inf = gdb.inferiors()[0]
+
+ # read in prb structure
+ prb_addr = int(str(gdb.parse_and_eval("(void *)'printk.c'::prb")).split()[0], 16)
+ sz = printk_ringbuffer_type.get_type().sizeof
+ prb = utils.read_memoryview(inf, prb_addr, sz).tobytes()
+
+ # read in descriptor ring structure
+ off = printk_ringbuffer_type.get_type()['desc_ring'].bitpos // 8
+ addr = prb_addr + off
+ sz = prb_desc_ring_type.get_type().sizeof
+ desc_ring = utils.read_memoryview(inf, addr, sz).tobytes()
+
+ # read in descriptor count, size, and address
+ off = prb_desc_ring_type.get_type()['count_bits'].bitpos // 8
+ desc_ring_count = 1 << utils.read_u32(desc_ring, off)
+ desc_sz = prb_desc_type.get_type().sizeof
+ off = prb_desc_ring_type.get_type()['descs'].bitpos // 8
+ desc_addr = utils.read_ulong(desc_ring, off)
+
+ # read in info size and address
+ info_sz = printk_info_type.get_type().sizeof
+ off = prb_desc_ring_type.get_type()['infos'].bitpos // 8
+ info_addr = utils.read_ulong(desc_ring, off)
+
+ # read in text data ring structure
+ off = printk_ringbuffer_type.get_type()['text_data_ring'].bitpos // 8
+ addr = prb_addr + off
+ sz = prb_data_ring_type.get_type().sizeof
+ text_data_ring = utils.read_memoryview(inf, addr, sz).tobytes()
+
+ # read in text data size and address
+ off = prb_data_ring_type.get_type()['size_bits'].bitpos // 8
+ text_data_sz = 1 << utils.read_u32(text_data_ring, off)
+ off = prb_data_ring_type.get_type()['data'].bitpos // 8
+ text_data_addr = utils.read_ulong(text_data_ring, off)
+
+ sv_off = prb_desc_type.get_type()['state_var'].bitpos // 8
+
+ off = prb_desc_type.get_type()['text_blk_lpos'].bitpos // 8
+ begin_off = off + (prb_data_blk_lpos_type.get_type()['begin'].bitpos // 8)
+ next_off = off + (prb_data_blk_lpos_type.get_type()['next'].bitpos // 8)
+
+ ts_off = printk_info_type.get_type()['ts_nsec'].bitpos // 8
+ len_off = printk_info_type.get_type()['text_len'].bitpos // 8
+
+ # definitions from kernel/printk/printk_ringbuffer.h
+ desc_committed = 1
+ desc_finalized = 2
+ desc_sv_bits = utils.get_long_type().sizeof * 8
+ desc_flags_shift = desc_sv_bits - 2
+ desc_flags_mask = 3 << desc_flags_shift
+ desc_id_mask = ~desc_flags_mask
+
+ # read in tail and head descriptor ids
+ off = prb_desc_ring_type.get_type()['tail_id'].bitpos // 8
+ tail_id = utils.read_atomic_long(desc_ring, off)
+ off = prb_desc_ring_type.get_type()['head_id'].bitpos // 8
+ head_id = utils.read_atomic_long(desc_ring, off)
+
+ did = tail_id
+ while True:
+ ind = did % desc_ring_count
+ desc_off = desc_sz * ind
+ info_off = info_sz * ind
+
+ desc = utils.read_memoryview(inf, desc_addr + desc_off, desc_sz).tobytes()
+
+ # skip non-committed record
+ state = 3 & (utils.read_atomic_long(desc, sv_off) >> desc_flags_shift)
+ if state != desc_committed and state != desc_finalized:
+ if did == head_id:
+ break
+ did = (did + 1) & desc_id_mask
+ continue
+
+ begin = utils.read_ulong(desc, begin_off) % text_data_sz
+ end = utils.read_ulong(desc, next_off) % text_data_sz
+
+ info = utils.read_memoryview(inf, info_addr + info_off, info_sz).tobytes()
+
+ # handle data-less record
+ if begin & 1 == 1:
+ text = ""
+ else:
+ # handle wrapping data block
+ if begin > end:
+ begin = 0
+
+ # skip over descriptor id
+ text_start = begin + utils.get_long_type().sizeof
+
+ text_len = utils.read_u16(info, len_off)
+
+ # handle truncated message
+ if end - text_start < text_len:
+ text_len = end - text_start
+
+ text_data = utils.read_memoryview(inf, text_data_addr + text_start,
+ text_len).tobytes()
+ text = text_data[0:text_len].decode(encoding='utf8', errors='replace')
+
+ time_stamp = utils.read_u64(info, ts_off)
+
+ for line in text.splitlines():
+ msg = u"[{time:12.6f}] {line}\n".format(
+ time=time_stamp / 1000000000.0,
+ line=line)
+ # With python2 gdb.write will attempt to convert unicode to
+ # ascii and might fail so pass an utf8-encoded str instead.
+ if sys.hexversion < 0x03000000:
+ msg = msg.encode(encoding='utf8', errors='replace')
+ gdb.write(msg)
+
+ if did == head_id:
+ break
+ did = (did + 1) & desc_id_mask
+
+
+LxDmesg()
diff --git a/scripts/gdb/linux/genpd.py b/scripts/gdb/linux/genpd.py
new file mode 100644
index 000000000..b53649c0a
--- /dev/null
+++ b/scripts/gdb/linux/genpd.py
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) NXP 2019
+
+import gdb
+import sys
+
+from linux.utils import CachedType, gdb_eval_or_none
+from linux.lists import list_for_each_entry
+
+generic_pm_domain_type = CachedType('struct generic_pm_domain')
+pm_domain_data_type = CachedType('struct pm_domain_data')
+device_link_type = CachedType('struct device_link')
+
+
+def kobject_get_path(kobj):
+ path = kobj['name'].string()
+ parent = kobj['parent']
+ if parent:
+ path = kobject_get_path(parent) + '/' + path
+ return path
+
+
+def rtpm_status_str(dev):
+ if dev['power']['runtime_error']:
+ return 'error'
+ if dev['power']['disable_depth']:
+ return 'unsupported'
+ _RPM_STATUS_LOOKUP = [
+ "active",
+ "resuming",
+ "suspended",
+ "suspending"
+ ]
+ return _RPM_STATUS_LOOKUP[dev['power']['runtime_status']]
+
+
+class LxGenPDSummary(gdb.Command):
+ '''Print genpd summary
+
+Output is similar to /sys/kernel/debug/pm_genpd/pm_genpd_summary'''
+
+ def __init__(self):
+ super(LxGenPDSummary, self).__init__('lx-genpd-summary', gdb.COMMAND_DATA)
+
+ def summary_one(self, genpd):
+ if genpd['status'] == 0:
+ status_string = 'on'
+ else:
+ status_string = 'off-{}'.format(genpd['state_idx'])
+
+ child_names = []
+ for link in list_for_each_entry(
+ genpd['parent_links'],
+ device_link_type.get_type().pointer(),
+ 'parent_node'):
+ child_names.append(link['child']['name'])
+
+ gdb.write('%-30s %-15s %s\n' % (
+ genpd['name'].string(),
+ status_string,
+ ', '.join(child_names)))
+
+ # Print devices in domain
+ for pm_data in list_for_each_entry(genpd['dev_list'],
+ pm_domain_data_type.get_type().pointer(),
+ 'list_node'):
+ dev = pm_data['dev']
+ kobj_path = kobject_get_path(dev['kobj'])
+ gdb.write(' %-50s %s\n' % (kobj_path, rtpm_status_str(dev)))
+
+ def invoke(self, arg, from_tty):
+ if gdb_eval_or_none("&gpd_list") is None:
+ raise gdb.GdbError("No power domain(s) registered")
+ gdb.write('domain status children\n');
+ gdb.write(' /device runtime status\n');
+ gdb.write('----------------------------------------------------------------------\n');
+ for genpd in list_for_each_entry(
+ gdb.parse_and_eval('&gpd_list'),
+ generic_pm_domain_type.get_type().pointer(),
+ 'gpd_list_node'):
+ self.summary_one(genpd)
+
+
+LxGenPDSummary()
diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
new file mode 100644
index 000000000..bae4d70b7
--- /dev/null
+++ b/scripts/gdb/linux/lists.py
@@ -0,0 +1,136 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# list tools
+#
+# Copyright (c) Thiebaud Weksteen, 2015
+#
+# Authors:
+# Thiebaud Weksteen <thiebaud@weksteen.fr>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import utils
+
+list_head = utils.CachedType("struct list_head")
+hlist_head = utils.CachedType("struct hlist_head")
+hlist_node = utils.CachedType("struct hlist_node")
+
+
+def list_for_each(head):
+ if head.type == list_head.get_type().pointer():
+ head = head.dereference()
+ elif head.type != list_head.get_type():
+ raise TypeError("Must be struct list_head not {}"
+ .format(head.type))
+
+ if head['next'] == 0:
+ gdb.write("list_for_each: Uninitialized list '{}' treated as empty\n"
+ .format(head.address))
+ return
+
+ node = head['next'].dereference()
+ while node.address != head.address:
+ yield node.address
+ node = node['next'].dereference()
+
+
+def list_for_each_entry(head, gdbtype, member):
+ for node in list_for_each(head):
+ yield utils.container_of(node, gdbtype, member)
+
+
+def hlist_for_each(head):
+ if head.type == hlist_head.get_type().pointer():
+ head = head.dereference()
+ elif head.type != hlist_head.get_type():
+ raise TypeError("Must be struct hlist_head not {}"
+ .format(head.type))
+
+ node = head['first'].dereference()
+ while node.address:
+ yield node.address
+ node = node['next'].dereference()
+
+
+def hlist_for_each_entry(head, gdbtype, member):
+ for node in hlist_for_each(head):
+ yield utils.container_of(node, gdbtype, member)
+
+
+def list_check(head):
+ nb = 0
+ if (head.type == list_head.get_type().pointer()):
+ head = head.dereference()
+ elif (head.type != list_head.get_type()):
+ raise gdb.GdbError('argument must be of type (struct list_head [*])')
+ c = head
+ try:
+ gdb.write("Starting with: {}\n".format(c))
+ except gdb.MemoryError:
+ gdb.write('head is not accessible\n')
+ return
+ while True:
+ p = c['prev'].dereference()
+ n = c['next'].dereference()
+ try:
+ if p['next'] != c.address:
+ gdb.write('prev.next != current: '
+ 'current@{current_addr}={current} '
+ 'prev@{p_addr}={p}\n'.format(
+ current_addr=c.address,
+ current=c,
+ p_addr=p.address,
+ p=p,
+ ))
+ return
+ except gdb.MemoryError:
+ gdb.write('prev is not accessible: '
+ 'current@{current_addr}={current}\n'.format(
+ current_addr=c.address,
+ current=c
+ ))
+ return
+ try:
+ if n['prev'] != c.address:
+ gdb.write('next.prev != current: '
+ 'current@{current_addr}={current} '
+ 'next@{n_addr}={n}\n'.format(
+ current_addr=c.address,
+ current=c,
+ n_addr=n.address,
+ n=n,
+ ))
+ return
+ except gdb.MemoryError:
+ gdb.write('next is not accessible: '
+ 'current@{current_addr}={current}\n'.format(
+ current_addr=c.address,
+ current=c
+ ))
+ return
+ c = n
+ nb += 1
+ if c == head:
+ gdb.write("list is consistent: {} node(s)\n".format(nb))
+ return
+
+
+class LxListChk(gdb.Command):
+ """Verify a list consistency"""
+
+ def __init__(self):
+ super(LxListChk, self).__init__("lx-list-check", gdb.COMMAND_DATA,
+ gdb.COMPLETE_EXPRESSION)
+
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ if len(argv) != 1:
+ raise gdb.GdbError("lx-list-check takes one argument")
+ list_check(gdb.parse_and_eval(argv[0]))
+
+
+LxListChk()
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
new file mode 100644
index 000000000..441b23239
--- /dev/null
+++ b/scripts/gdb/linux/modules.py
@@ -0,0 +1,95 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# module tools
+#
+# Copyright (c) Siemens AG, 2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import cpus, utils, lists
+
+
+module_type = utils.CachedType("struct module")
+
+
+def module_list():
+ global module_type
+ modules = utils.gdb_eval_or_none("modules")
+ if modules is None:
+ return
+
+ module_ptr_type = module_type.get_type().pointer()
+
+ for module in lists.list_for_each_entry(modules, module_ptr_type, "list"):
+ yield module
+
+
+def find_module_by_name(name):
+ for module in module_list():
+ if module['name'].string() == name:
+ return module
+ return None
+
+
+class LxModule(gdb.Function):
+ """Find module by name and return the module variable.
+
+$lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules
+of the target and return that module variable which MODULE matches."""
+
+ def __init__(self):
+ super(LxModule, self).__init__("lx_module")
+
+ def invoke(self, mod_name):
+ mod_name = mod_name.string()
+ module = find_module_by_name(mod_name)
+ if module:
+ return module.dereference()
+ else:
+ raise gdb.GdbError("Unable to find MODULE " + mod_name)
+
+
+LxModule()
+
+
+class LxLsmod(gdb.Command):
+ """List currently loaded modules."""
+
+ _module_use_type = utils.CachedType("struct module_use")
+
+ def __init__(self):
+ super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ gdb.write(
+ "Address{0} Module Size Used by\n".format(
+ " " if utils.get_long_type().sizeof == 8 else ""))
+
+ for module in module_list():
+ layout = module['core_layout']
+ gdb.write("{address} {name:<19} {size:>8} {ref}".format(
+ address=str(layout['base']).split()[0],
+ name=module['name'].string(),
+ size=str(layout['size']),
+ ref=str(module['refcnt']['counter'] - 1)))
+
+ t = self._module_use_type.get_type().pointer()
+ first = True
+ sources = module['source_list']
+ for use in lists.list_for_each_entry(sources, t, "source_list"):
+ gdb.write("{separator}{name}".format(
+ separator=" " if first else ",",
+ name=use['source']['name'].string()))
+ first = False
+
+ gdb.write("\n")
+
+
+LxLsmod()
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
new file mode 100644
index 000000000..09cd87192
--- /dev/null
+++ b/scripts/gdb/linux/proc.py
@@ -0,0 +1,275 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# Kernel proc information reader
+#
+# Copyright (c) 2016 Linaro Ltd
+#
+# Authors:
+# Kieran Bingham <kieran.bingham@linaro.org>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+from linux import constants
+from linux import utils
+from linux import tasks
+from linux import lists
+from struct import *
+
+
+class LxCmdLine(gdb.Command):
+ """ Report the Linux Commandline used in the current kernel.
+ Equivalent to cat /proc/cmdline on a running target"""
+
+ def __init__(self):
+ super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
+
+
+LxCmdLine()
+
+
+class LxVersion(gdb.Command):
+ """ Report the Linux Version of the current kernel.
+ Equivalent to cat /proc/version on a running target"""
+
+ def __init__(self):
+ super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ # linux_banner should contain a newline
+ gdb.write(gdb.parse_and_eval("(char *)linux_banner").string())
+
+
+LxVersion()
+
+
+# Resource Structure Printers
+# /proc/iomem
+# /proc/ioports
+
+def get_resources(resource, depth):
+ while resource:
+ yield resource, depth
+
+ child = resource['child']
+ if child:
+ for res, deep in get_resources(child, depth + 1):
+ yield res, deep
+
+ resource = resource['sibling']
+
+
+def show_lx_resources(resource_str):
+ resource = gdb.parse_and_eval(resource_str)
+ width = 4 if resource['end'] < 0x10000 else 8
+ # Iterate straight to the first child
+ for res, depth in get_resources(resource['child'], 0):
+ start = int(res['start'])
+ end = int(res['end'])
+ gdb.write(" " * depth * 2 +
+ "{0:0{1}x}-".format(start, width) +
+ "{0:0{1}x} : ".format(end, width) +
+ res['name'].string() + "\n")
+
+
+class LxIOMem(gdb.Command):
+ """Identify the IO memory resource locations defined by the kernel
+
+Equivalent to cat /proc/iomem on a running target"""
+
+ def __init__(self):
+ super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ return show_lx_resources("iomem_resource")
+
+
+LxIOMem()
+
+
+class LxIOPorts(gdb.Command):
+ """Identify the IO port resource locations defined by the kernel
+
+Equivalent to cat /proc/ioports on a running target"""
+
+ def __init__(self):
+ super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ return show_lx_resources("ioport_resource")
+
+
+LxIOPorts()
+
+
+# Mount namespace viewer
+# /proc/mounts
+
+def info_opts(lst, opt):
+ opts = ""
+ for key, string in lst.items():
+ if opt & key:
+ opts += string
+ return opts
+
+
+FS_INFO = {constants.LX_SB_SYNCHRONOUS: ",sync",
+ constants.LX_SB_MANDLOCK: ",mand",
+ constants.LX_SB_DIRSYNC: ",dirsync",
+ constants.LX_SB_NOATIME: ",noatime",
+ constants.LX_SB_NODIRATIME: ",nodiratime"}
+
+MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
+ constants.LX_MNT_NODEV: ",nodev",
+ constants.LX_MNT_NOEXEC: ",noexec",
+ constants.LX_MNT_NOATIME: ",noatime",
+ constants.LX_MNT_NODIRATIME: ",nodiratime",
+ constants.LX_MNT_RELATIME: ",relatime"}
+
+mount_type = utils.CachedType("struct mount")
+mount_ptr_type = mount_type.get_type().pointer()
+
+
+class LxMounts(gdb.Command):
+ """Report the VFS mounts of the current process namespace.
+
+Equivalent to cat /proc/mounts on a running target
+An integer value can be supplied to display the mount
+values of that process namespace"""
+
+ def __init__(self):
+ super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
+
+ # Equivalent to proc_namespace.c:show_vfsmnt
+ # However, that has the ability to call into s_op functions
+ # whereas we cannot and must make do with the information we can obtain.
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ if len(argv) >= 1:
+ try:
+ pid = int(argv[0])
+ except gdb.error:
+ raise gdb.GdbError("Provide a PID as integer value")
+ else:
+ pid = 1
+
+ task = tasks.get_task_by_pid(pid)
+ if not task:
+ raise gdb.GdbError("Couldn't find a process with PID {}"
+ .format(pid))
+
+ namespace = task['nsproxy']['mnt_ns']
+ if not namespace:
+ raise gdb.GdbError("No namespace for current process")
+
+ gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format(
+ "mount", "super_block", "devname", "pathname", "fstype"))
+
+ for vfs in lists.list_for_each_entry(namespace['list'],
+ mount_ptr_type, "mnt_list"):
+ devname = vfs['mnt_devname'].string()
+ devname = devname if devname else "none"
+
+ pathname = ""
+ parent = vfs
+ while True:
+ mntpoint = parent['mnt_mountpoint']
+ pathname = utils.dentry_name(mntpoint) + pathname
+ if (parent == parent['mnt_parent']):
+ break
+ parent = parent['mnt_parent']
+
+ if (pathname == ""):
+ pathname = "/"
+
+ superblock = vfs['mnt']['mnt_sb']
+ fstype = superblock['s_type']['name'].string()
+ s_flags = int(superblock['s_flags'])
+ m_flags = int(vfs['mnt']['mnt_flags'])
+ rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw"
+
+ gdb.write("{} {} {} {} {} {}{}{} 0 0\n".format(
+ vfs.format_string(), superblock.format_string(), devname,
+ pathname, fstype, rd, info_opts(FS_INFO, s_flags),
+ info_opts(MNT_INFO, m_flags)))
+
+
+LxMounts()
+
+
+class LxFdtDump(gdb.Command):
+ """Output Flattened Device Tree header and dump FDT blob to the filename
+ specified as the command argument. Equivalent to
+ 'cat /proc/fdt > fdtdump.dtb' on a running target"""
+
+ def __init__(self):
+ super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA,
+ gdb.COMPLETE_FILENAME)
+
+ def fdthdr_to_cpu(self, fdt_header):
+
+ fdt_header_be = ">IIIIIII"
+ fdt_header_le = "<IIIIIII"
+
+ if utils.get_target_endianness() == 1:
+ output_fmt = fdt_header_le
+ else:
+ output_fmt = fdt_header_be
+
+ return unpack(output_fmt, pack(fdt_header_be,
+ fdt_header['magic'],
+ fdt_header['totalsize'],
+ fdt_header['off_dt_struct'],
+ fdt_header['off_dt_strings'],
+ fdt_header['off_mem_rsvmap'],
+ fdt_header['version'],
+ fdt_header['last_comp_version']))
+
+ def invoke(self, arg, from_tty):
+
+ if not constants.LX_CONFIG_OF:
+ raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n")
+
+ if len(arg) == 0:
+ filename = "fdtdump.dtb"
+ else:
+ filename = arg
+
+ py_fdt_header_ptr = gdb.parse_and_eval(
+ "(const struct fdt_header *) initial_boot_params")
+ py_fdt_header = py_fdt_header_ptr.dereference()
+
+ fdt_header = self.fdthdr_to_cpu(py_fdt_header)
+
+ if fdt_header[0] != constants.LX_OF_DT_HEADER:
+ raise gdb.GdbError("No flattened device tree magic found\n")
+
+ gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0]))
+ gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1]))
+ gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2]))
+ gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3]))
+ gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4]))
+ gdb.write("version: {}\n".format(fdt_header[5]))
+ gdb.write("last_comp_version: {}\n".format(fdt_header[6]))
+
+ inf = gdb.inferiors()[0]
+ fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr,
+ fdt_header[1]).tobytes()
+
+ try:
+ f = open(filename, 'wb')
+ except gdb.error:
+ raise gdb.GdbError("Could not open file to dump fdt")
+
+ f.write(fdt_buf)
+ f.close()
+
+ gdb.write("Dumped fdt blob to " + filename + "\n")
+
+
+LxFdtDump()
diff --git a/scripts/gdb/linux/rbtree.py b/scripts/gdb/linux/rbtree.py
new file mode 100644
index 000000000..fe462855e
--- /dev/null
+++ b/scripts/gdb/linux/rbtree.py
@@ -0,0 +1,177 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2019 Google LLC.
+
+import gdb
+
+from linux import utils
+
+rb_root_type = utils.CachedType("struct rb_root")
+rb_node_type = utils.CachedType("struct rb_node")
+
+
+def rb_first(root):
+ if root.type == rb_root_type.get_type():
+ node = root.address.cast(rb_root_type.get_type().pointer())
+ elif root.type != rb_root_type.get_type().pointer():
+ raise gdb.GdbError("Must be struct rb_root not {}".format(root.type))
+
+ node = root['rb_node']
+ if node == 0:
+ return None
+
+ while node['rb_left']:
+ node = node['rb_left']
+
+ return node
+
+
+def rb_last(root):
+ if root.type == rb_root_type.get_type():
+ node = root.address.cast(rb_root_type.get_type().pointer())
+ elif root.type != rb_root_type.get_type().pointer():
+ raise gdb.GdbError("Must be struct rb_root not {}".format(root.type))
+
+ node = root['rb_node']
+ if node == 0:
+ return None
+
+ while node['rb_right']:
+ node = node['rb_right']
+
+ return node
+
+
+def rb_parent(node):
+ parent = gdb.Value(node['__rb_parent_color'] & ~3)
+ return parent.cast(rb_node_type.get_type().pointer())
+
+
+def rb_empty_node(node):
+ return node['__rb_parent_color'] == node.address
+
+
+def rb_next(node):
+ if node.type == rb_node_type.get_type():
+ node = node.address.cast(rb_node_type.get_type().pointer())
+ elif node.type != rb_node_type.get_type().pointer():
+ raise gdb.GdbError("Must be struct rb_node not {}".format(node.type))
+
+ if rb_empty_node(node):
+ return None
+
+ if node['rb_right']:
+ node = node['rb_right']
+ while node['rb_left']:
+ node = node['rb_left']
+ return node
+
+ parent = rb_parent(node)
+ while parent and node == parent['rb_right']:
+ node = parent
+ parent = rb_parent(node)
+
+ return parent
+
+
+def rb_prev(node):
+ if node.type == rb_node_type.get_type():
+ node = node.address.cast(rb_node_type.get_type().pointer())
+ elif node.type != rb_node_type.get_type().pointer():
+ raise gdb.GdbError("Must be struct rb_node not {}".format(node.type))
+
+ if rb_empty_node(node):
+ return None
+
+ if node['rb_left']:
+ node = node['rb_left']
+ while node['rb_right']:
+ node = node['rb_right']
+ return node.dereference()
+
+ parent = rb_parent(node)
+ while parent and node == parent['rb_left'].dereference():
+ node = parent
+ parent = rb_parent(node)
+
+ return parent
+
+
+class LxRbFirst(gdb.Function):
+ """Lookup and return a node from an RBTree
+
+$lx_rb_first(root): Return the node at the given index.
+If index is omitted, the root node is dereferenced and returned."""
+
+ def __init__(self):
+ super(LxRbFirst, self).__init__("lx_rb_first")
+
+ def invoke(self, root):
+ result = rb_first(root)
+ if result is None:
+ raise gdb.GdbError("No entry in tree")
+
+ return result
+
+
+LxRbFirst()
+
+
+class LxRbLast(gdb.Function):
+ """Lookup and return a node from an RBTree.
+
+$lx_rb_last(root): Return the node at the given index.
+If index is omitted, the root node is dereferenced and returned."""
+
+ def __init__(self):
+ super(LxRbLast, self).__init__("lx_rb_last")
+
+ def invoke(self, root):
+ result = rb_last(root)
+ if result is None:
+ raise gdb.GdbError("No entry in tree")
+
+ return result
+
+
+LxRbLast()
+
+
+class LxRbNext(gdb.Function):
+ """Lookup and return a node from an RBTree.
+
+$lx_rb_next(node): Return the node at the given index.
+If index is omitted, the root node is dereferenced and returned."""
+
+ def __init__(self):
+ super(LxRbNext, self).__init__("lx_rb_next")
+
+ def invoke(self, node):
+ result = rb_next(node)
+ if result is None:
+ raise gdb.GdbError("No entry in tree")
+
+ return result
+
+
+LxRbNext()
+
+
+class LxRbPrev(gdb.Function):
+ """Lookup and return a node from an RBTree.
+
+$lx_rb_prev(node): Return the node at the given index.
+If index is omitted, the root node is dereferenced and returned."""
+
+ def __init__(self):
+ super(LxRbPrev, self).__init__("lx_rb_prev")
+
+ def invoke(self, node):
+ result = rb_prev(node)
+ if result is None:
+ raise gdb.GdbError("No entry in tree")
+
+ return result
+
+
+LxRbPrev()
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
new file mode 100644
index 000000000..dc07b6d12
--- /dev/null
+++ b/scripts/gdb/linux/symbols.py
@@ -0,0 +1,189 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# load kernel and module symbols
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+import os
+import re
+
+from linux import modules, utils
+
+
+if hasattr(gdb, 'Breakpoint'):
+ class LoadModuleBreakpoint(gdb.Breakpoint):
+ def __init__(self, spec, gdb_command):
+ super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
+ self.silent = True
+ self.gdb_command = gdb_command
+
+ def stop(self):
+ module = gdb.parse_and_eval("mod")
+ module_name = module['name'].string()
+ cmd = self.gdb_command
+
+ # enforce update if object file is not found
+ cmd.module_files_updated = False
+
+ # Disable pagination while reporting symbol (re-)loading.
+ # The console input is blocked in this context so that we would
+ # get stuck waiting for the user to acknowledge paged output.
+ show_pagination = gdb.execute("show pagination", to_string=True)
+ pagination = show_pagination.endswith("on.\n")
+ gdb.execute("set pagination off")
+
+ if module_name in cmd.loaded_modules:
+ gdb.write("refreshing all symbols to reload module "
+ "'{0}'\n".format(module_name))
+ cmd.load_all_symbols()
+ else:
+ cmd.load_module_symbols(module)
+
+ # restore pagination state
+ gdb.execute("set pagination %s" % ("on" if pagination else "off"))
+
+ return False
+
+
+class LxSymbols(gdb.Command):
+ """(Re-)load symbols of Linux kernel and currently loaded modules.
+
+The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
+are scanned recursively, starting in the same directory. Optionally, the module
+search path can be extended by a space separated list of paths passed to the
+lx-symbols command."""
+
+ module_paths = []
+ module_files = []
+ module_files_updated = False
+ loaded_modules = []
+ breakpoint = None
+
+ def __init__(self):
+ super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
+ gdb.COMPLETE_FILENAME)
+
+ def _update_module_files(self):
+ self.module_files = []
+ for path in self.module_paths:
+ gdb.write("scanning for modules in {0}\n".format(path))
+ for root, dirs, files in os.walk(path):
+ for name in files:
+ if name.endswith(".ko") or name.endswith(".ko.debug"):
+ self.module_files.append(root + "/" + name)
+ self.module_files_updated = True
+
+ def _get_module_file(self, module_name):
+ module_pattern = ".*/{0}\.ko(?:.debug)?$".format(
+ module_name.replace("_", r"[_\-]"))
+ for name in self.module_files:
+ if re.match(module_pattern, name) and os.path.exists(name):
+ return name
+ return None
+
+ def _section_arguments(self, module):
+ try:
+ sect_attrs = module['sect_attrs'].dereference()
+ except gdb.error:
+ return ""
+ attrs = sect_attrs['attrs']
+ section_name_to_address = {
+ attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
+ for n in range(int(sect_attrs['nsections']))}
+ args = []
+ for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
+ ".text", ".text.hot", ".text.unlikely"]:
+ address = section_name_to_address.get(section_name)
+ if address:
+ args.append(" -s {name} {addr}".format(
+ name=section_name, addr=str(address)))
+ return "".join(args)
+
+ def load_module_symbols(self, module):
+ module_name = module['name'].string()
+ module_addr = str(module['core_layout']['base']).split()[0]
+
+ module_file = self._get_module_file(module_name)
+ if not module_file and not self.module_files_updated:
+ self._update_module_files()
+ module_file = self._get_module_file(module_name)
+
+ if module_file:
+ if utils.is_target_arch('s390'):
+ # Module text is preceded by PLT stubs on s390.
+ module_arch = module['arch']
+ plt_offset = int(module_arch['plt_offset'])
+ plt_size = int(module_arch['plt_size'])
+ module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
+ gdb.write("loading @{addr}: {filename}\n".format(
+ addr=module_addr, filename=module_file))
+ cmdline = "add-symbol-file {filename} {addr}{sections}".format(
+ filename=module_file,
+ addr=module_addr,
+ sections=self._section_arguments(module))
+ gdb.execute(cmdline, to_string=True)
+ if module_name not in self.loaded_modules:
+ self.loaded_modules.append(module_name)
+ else:
+ gdb.write("no module object found for '{0}'\n".format(module_name))
+
+ def load_all_symbols(self):
+ gdb.write("loading vmlinux\n")
+
+ # Dropping symbols will disable all breakpoints. So save their states
+ # and restore them afterward.
+ saved_states = []
+ if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
+ for bp in gdb.breakpoints():
+ saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
+
+ # drop all current symbols and reload vmlinux
+ orig_vmlinux = 'vmlinux'
+ for obj in gdb.objfiles():
+ if (obj.filename.endswith('vmlinux') or
+ obj.filename.endswith('vmlinux.debug')):
+ orig_vmlinux = obj.filename
+ gdb.execute("symbol-file", to_string=True)
+ gdb.execute("symbol-file {0}".format(orig_vmlinux))
+
+ self.loaded_modules = []
+ module_list = modules.module_list()
+ if not module_list:
+ gdb.write("no modules found\n")
+ else:
+ [self.load_module_symbols(module) for module in module_list]
+
+ for saved_state in saved_states:
+ saved_state['breakpoint'].enabled = saved_state['enabled']
+
+ def invoke(self, arg, from_tty):
+ self.module_paths = [os.path.abspath(os.path.expanduser(p))
+ for p in arg.split()]
+ self.module_paths.append(os.getcwd())
+
+ # enforce update
+ self.module_files = []
+ self.module_files_updated = False
+
+ self.load_all_symbols()
+
+ if hasattr(gdb, 'Breakpoint'):
+ if self.breakpoint is not None:
+ self.breakpoint.delete()
+ self.breakpoint = None
+ self.breakpoint = LoadModuleBreakpoint(
+ "kernel/module/main.c:do_init_module", self)
+ else:
+ gdb.write("Note: symbol update on module loading not supported "
+ "with this gdb version\n")
+
+
+LxSymbols()
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
new file mode 100644
index 000000000..17ec19e9b
--- /dev/null
+++ b/scripts/gdb/linux/tasks.py
@@ -0,0 +1,140 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# task & thread tools
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import utils
+
+
+task_type = utils.CachedType("struct task_struct")
+
+
+def task_lists():
+ task_ptr_type = task_type.get_type().pointer()
+ init_task = gdb.parse_and_eval("init_task").address
+ t = g = init_task
+
+ while True:
+ while True:
+ yield t
+
+ t = utils.container_of(t['thread_group']['next'],
+ task_ptr_type, "thread_group")
+ if t == g:
+ break
+
+ t = g = utils.container_of(g['tasks']['next'],
+ task_ptr_type, "tasks")
+ if t == init_task:
+ return
+
+
+def get_task_by_pid(pid):
+ for task in task_lists():
+ if int(task['pid']) == pid:
+ return task
+ return None
+
+
+class LxTaskByPidFunc(gdb.Function):
+ """Find Linux task by PID and return the task_struct variable.
+
+$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and
+return that task_struct variable which PID matches."""
+
+ def __init__(self):
+ super(LxTaskByPidFunc, self).__init__("lx_task_by_pid")
+
+ def invoke(self, pid):
+ task = get_task_by_pid(pid)
+ if task:
+ return task.dereference()
+ else:
+ raise gdb.GdbError("No task of PID " + str(pid))
+
+
+LxTaskByPidFunc()
+
+
+class LxPs(gdb.Command):
+ """Dump Linux tasks."""
+
+ def __init__(self):
+ super(LxPs, self).__init__("lx-ps", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ gdb.write("{:>10} {:>12} {:>7}\n".format("TASK", "PID", "COMM"))
+ for task in task_lists():
+ gdb.write("{} {:^5} {}\n".format(
+ task.format_string().split()[0],
+ task["pid"].format_string(),
+ task["comm"].string()))
+
+
+LxPs()
+
+
+thread_info_type = utils.CachedType("struct thread_info")
+
+ia64_task_size = None
+
+
+def get_thread_info(task):
+ thread_info_ptr_type = thread_info_type.get_type().pointer()
+ if utils.is_target_arch("ia64"):
+ global ia64_task_size
+ if ia64_task_size is None:
+ ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)")
+ thread_info_addr = task.address + ia64_task_size
+ thread_info = thread_info_addr.cast(thread_info_ptr_type)
+ else:
+ if task.type.fields()[0].type == thread_info_type.get_type():
+ return task['thread_info']
+ thread_info = task['stack'].cast(thread_info_ptr_type)
+ return thread_info.dereference()
+
+
+class LxThreadInfoFunc (gdb.Function):
+ """Calculate Linux thread_info from task variable.
+
+$lx_thread_info(TASK): Given TASK, return the corresponding thread_info
+variable."""
+
+ def __init__(self):
+ super(LxThreadInfoFunc, self).__init__("lx_thread_info")
+
+ def invoke(self, task):
+ return get_thread_info(task)
+
+
+LxThreadInfoFunc()
+
+
+class LxThreadInfoByPidFunc (gdb.Function):
+ """Calculate Linux thread_info from task variable found by pid
+
+$lx_thread_info_by_pid(PID): Given PID, return the corresponding thread_info
+variable."""
+
+ def __init__(self):
+ super(LxThreadInfoByPidFunc, self).__init__("lx_thread_info_by_pid")
+
+ def invoke(self, pid):
+ task = get_task_by_pid(pid)
+ if task:
+ return get_thread_info(task.dereference())
+ else:
+ raise gdb.GdbError("No task of PID " + str(pid))
+
+
+LxThreadInfoByPidFunc()
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
new file mode 100644
index 000000000..51def847f
--- /dev/null
+++ b/scripts/gdb/linux/timerlist.py
@@ -0,0 +1,221 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2019 Google LLC.
+
+import binascii
+import gdb
+
+from linux import constants
+from linux import cpus
+from linux import rbtree
+from linux import utils
+
+timerqueue_node_type = utils.CachedType("struct timerqueue_node").get_type()
+hrtimer_type = utils.CachedType("struct hrtimer").get_type()
+
+
+def ktime_get():
+ """Returns the current time, but not very accurately
+
+ We can't read the hardware timer itself to add any nanoseconds
+ that need to be added since we last stored the time in the
+ timekeeper. But this is probably good enough for debug purposes."""
+ tk_core = gdb.parse_and_eval("&tk_core")
+
+ return tk_core['timekeeper']['tkr_mono']['base']
+
+
+def print_timer(rb_node, idx):
+ timerqueue = utils.container_of(rb_node, timerqueue_node_type.pointer(),
+ "node")
+ timer = utils.container_of(timerqueue, hrtimer_type.pointer(), "node")
+
+ function = str(timer['function']).split(" ")[1].strip("<>")
+ softexpires = timer['_softexpires']
+ expires = timer['node']['expires']
+ now = ktime_get()
+
+ text = " #{}: <{}>, {}, ".format(idx, timer, function)
+ text += "S:{:02x}\n".format(int(timer['state']))
+ text += " # expires at {}-{} nsecs [in {} to {} nsecs]\n".format(
+ softexpires, expires, softexpires - now, expires - now)
+ return text
+
+
+def print_active_timers(base):
+ curr = base['active']['next']['node']
+ curr = curr.address.cast(rbtree.rb_node_type.get_type().pointer())
+ idx = 0
+ while curr:
+ yield print_timer(curr, idx)
+ curr = rbtree.rb_next(curr)
+ idx += 1
+
+
+def print_base(base):
+ text = " .base: {}\n".format(base.address)
+ text += " .index: {}\n".format(base['index'])
+
+ text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution)
+
+ text += " .get_time: {}\n".format(base['get_time'])
+ if constants.LX_CONFIG_HIGH_RES_TIMERS:
+ text += " .offset: {} nsecs\n".format(base['offset'])
+ text += "active timers:\n"
+ text += "".join([x for x in print_active_timers(base)])
+ return text
+
+
+def print_cpu(hrtimer_bases, cpu, max_clock_bases):
+ cpu_base = cpus.per_cpu(hrtimer_bases, cpu)
+ jiffies = gdb.parse_and_eval("jiffies_64")
+ tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched")
+ ts = cpus.per_cpu(tick_sched_ptr, cpu)
+
+ text = "cpu: {}\n".format(cpu)
+ for i in range(max_clock_bases):
+ text += " clock {}:\n".format(i)
+ text += print_base(cpu_base['clock_base'][i])
+
+ if constants.LX_CONFIG_HIGH_RES_TIMERS:
+ fmts = [(" .{} : {} nsecs", 'expires_next'),
+ (" .{} : {}", 'hres_active'),
+ (" .{} : {}", 'nr_events'),
+ (" .{} : {}", 'nr_retries'),
+ (" .{} : {}", 'nr_hangs'),
+ (" .{} : {}", 'max_hang_time')]
+ text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts])
+ text += "\n"
+
+ if constants.LX_CONFIG_TICK_ONESHOT:
+ fmts = [(" .{} : {}", 'nohz_mode'),
+ (" .{} : {} nsecs", 'last_tick'),
+ (" .{} : {}", 'tick_stopped'),
+ (" .{} : {}", 'idle_jiffies'),
+ (" .{} : {}", 'idle_calls'),
+ (" .{} : {}", 'idle_sleeps'),
+ (" .{} : {} nsecs", 'idle_entrytime'),
+ (" .{} : {} nsecs", 'idle_waketime'),
+ (" .{} : {} nsecs", 'idle_exittime'),
+ (" .{} : {} nsecs", 'idle_sleeptime'),
+ (" .{}: {} nsecs", 'iowait_sleeptime'),
+ (" .{} : {}", 'last_jiffies'),
+ (" .{} : {}", 'next_timer'),
+ (" .{} : {} nsecs", 'idle_expires')]
+ text += "\n".join([s.format(f, ts[f]) for s, f in fmts])
+ text += "\njiffies: {}\n".format(jiffies)
+
+ text += "\n"
+
+ return text
+
+
+def print_tickdevice(td, cpu):
+ dev = td['evtdev']
+ text = "Tick Device: mode: {}\n".format(td['mode'])
+ if cpu < 0:
+ text += "Broadcast device\n"
+ else:
+ text += "Per CPU device: {}\n".format(cpu)
+
+ text += "Clock Event Device: "
+ if dev == 0:
+ text += "<NULL>\n"
+ return text
+
+ text += "{}\n".format(dev['name'])
+ text += " max_delta_ns: {}\n".format(dev['max_delta_ns'])
+ text += " min_delta_ns: {}\n".format(dev['min_delta_ns'])
+ text += " mult: {}\n".format(dev['mult'])
+ text += " shift: {}\n".format(dev['shift'])
+ text += " mode: {}\n".format(dev['state_use_accessors'])
+ text += " next_event: {} nsecs\n".format(dev['next_event'])
+
+ text += " set_next_event: {}\n".format(dev['set_next_event'])
+
+ members = [('set_state_shutdown', " shutdown: {}\n"),
+ ('set_state_periodic', " periodic: {}\n"),
+ ('set_state_oneshot', " oneshot: {}\n"),
+ ('set_state_oneshot_stopped', " oneshot stopped: {}\n"),
+ ('tick_resume', " resume: {}\n")]
+ for member, fmt in members:
+ if dev[member]:
+ text += fmt.format(dev[member])
+
+ text += " event_handler: {}\n".format(dev['event_handler'])
+ text += " retries: {}\n".format(dev['retries'])
+
+ return text
+
+
+def pr_cpumask(mask):
+ nr_cpu_ids = 1
+ if constants.LX_NR_CPUS > 1:
+ nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids")
+
+ inf = gdb.inferiors()[0]
+ bits = mask['bits']
+ num_bytes = (nr_cpu_ids + 7) / 8
+ buf = utils.read_memoryview(inf, bits, num_bytes).tobytes()
+ buf = binascii.b2a_hex(buf)
+ if type(buf) is not str:
+ buf=buf.decode()
+
+ chunks = []
+ i = num_bytes
+ while i > 0:
+ i -= 1
+ start = i * 2
+ end = start + 2
+ chunks.append(buf[start:end])
+ if i != 0 and i % 4 == 0:
+ chunks.append(',')
+
+ extra = nr_cpu_ids % 8
+ if 0 < extra <= 4:
+ chunks[0] = chunks[0][0] # Cut off the first 0
+
+ return "".join(chunks)
+
+
+class LxTimerList(gdb.Command):
+ """Print /proc/timer_list"""
+
+ def __init__(self):
+ super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases")
+ max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES")
+
+ text = "Timer List Version: gdb scripts\n"
+ text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases)
+ text += "now at {} nsecs\n".format(ktime_get())
+
+ for cpu in cpus.each_online_cpu():
+ text += print_cpu(hrtimer_bases, cpu, max_clock_bases)
+
+ if constants.LX_CONFIG_GENERIC_CLOCKEVENTS:
+ if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST:
+ bc_dev = gdb.parse_and_eval("&tick_broadcast_device")
+ text += print_tickdevice(bc_dev, -1)
+ text += "\n"
+ mask = gdb.parse_and_eval("tick_broadcast_mask")
+ mask = pr_cpumask(mask)
+ text += "tick_broadcast_mask: {}\n".format(mask)
+ if constants.LX_CONFIG_TICK_ONESHOT:
+ mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
+ mask = pr_cpumask(mask)
+ text += "tick_broadcast_oneshot_mask: {}\n".format(mask)
+ text += "\n"
+
+ tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
+ for cpu in cpus.each_online_cpu():
+ tick_dev = cpus.per_cpu(tick_cpu_devices, cpu)
+ text += print_tickdevice(tick_dev, cpu)
+ text += "\n"
+
+ gdb.write(text)
+
+
+LxTimerList()
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
new file mode 100644
index 000000000..7f36aee32
--- /dev/null
+++ b/scripts/gdb/linux/utils.py
@@ -0,0 +1,206 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# common utilities
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+
+class CachedType:
+ def __init__(self, name):
+ self._type = None
+ self._name = name
+
+ def _new_objfile_handler(self, event):
+ self._type = None
+ gdb.events.new_objfile.disconnect(self._new_objfile_handler)
+
+ def get_type(self):
+ if self._type is None:
+ self._type = gdb.lookup_type(self._name)
+ if self._type is None:
+ raise gdb.GdbError(
+ "cannot resolve type '{0}'".format(self._name))
+ if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'):
+ gdb.events.new_objfile.connect(self._new_objfile_handler)
+ return self._type
+
+
+long_type = CachedType("long")
+atomic_long_type = CachedType("atomic_long_t")
+
+def get_long_type():
+ global long_type
+ return long_type.get_type()
+
+def offset_of(typeobj, field):
+ element = gdb.Value(0).cast(typeobj)
+ return int(str(element[field].address).split()[0], 16)
+
+
+def container_of(ptr, typeobj, member):
+ return (ptr.cast(get_long_type()) -
+ offset_of(typeobj, member)).cast(typeobj)
+
+
+class ContainerOf(gdb.Function):
+ """Return pointer to containing data structure.
+
+$container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the
+data structure of the type TYPE in which PTR is the address of ELEMENT.
+Note that TYPE and ELEMENT have to be quoted as strings."""
+
+ def __init__(self):
+ super(ContainerOf, self).__init__("container_of")
+
+ def invoke(self, ptr, typename, elementname):
+ return container_of(ptr, gdb.lookup_type(typename.string()).pointer(),
+ elementname.string())
+
+
+ContainerOf()
+
+
+BIG_ENDIAN = 0
+LITTLE_ENDIAN = 1
+target_endianness = None
+
+
+def get_target_endianness():
+ global target_endianness
+ if target_endianness is None:
+ endian = gdb.execute("show endian", to_string=True)
+ if "little endian" in endian:
+ target_endianness = LITTLE_ENDIAN
+ elif "big endian" in endian:
+ target_endianness = BIG_ENDIAN
+ else:
+ raise gdb.GdbError("unknown endianness '{0}'".format(str(endian)))
+ return target_endianness
+
+
+def read_memoryview(inf, start, length):
+ m = inf.read_memory(start, length)
+ if type(m) is memoryview:
+ return m
+ return memoryview(m)
+
+
+def read_u16(buffer, offset):
+ buffer_val = buffer[offset:offset + 2]
+ value = [0, 0]
+
+ if type(buffer_val[0]) is str:
+ value[0] = ord(buffer_val[0])
+ value[1] = ord(buffer_val[1])
+ else:
+ value[0] = buffer_val[0]
+ value[1] = buffer_val[1]
+
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return value[0] + (value[1] << 8)
+ else:
+ return value[1] + (value[0] << 8)
+
+
+def read_u32(buffer, offset):
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return read_u16(buffer, offset) + (read_u16(buffer, offset + 2) << 16)
+ else:
+ return read_u16(buffer, offset + 2) + (read_u16(buffer, offset) << 16)
+
+
+def read_u64(buffer, offset):
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return read_u32(buffer, offset) + (read_u32(buffer, offset + 4) << 32)
+ else:
+ return read_u32(buffer, offset + 4) + (read_u32(buffer, offset) << 32)
+
+
+def read_ulong(buffer, offset):
+ if get_long_type().sizeof == 8:
+ return read_u64(buffer, offset)
+ else:
+ return read_u32(buffer, offset)
+
+atomic_long_counter_offset = atomic_long_type.get_type()['counter'].bitpos
+atomic_long_counter_sizeof = atomic_long_type.get_type()['counter'].type.sizeof
+
+def read_atomic_long(buffer, offset):
+ global atomic_long_counter_offset
+ global atomic_long_counter_sizeof
+
+ if atomic_long_counter_sizeof == 8:
+ return read_u64(buffer, offset + atomic_long_counter_offset)
+ else:
+ return read_u32(buffer, offset + atomic_long_counter_offset)
+
+target_arch = None
+
+
+def is_target_arch(arch):
+ if hasattr(gdb.Frame, 'architecture'):
+ return arch in gdb.newest_frame().architecture().name()
+ else:
+ global target_arch
+ if target_arch is None:
+ target_arch = gdb.execute("show architecture", to_string=True)
+ return arch in target_arch
+
+
+GDBSERVER_QEMU = 0
+GDBSERVER_KGDB = 1
+gdbserver_type = None
+
+
+def get_gdbserver_type():
+ def exit_handler(event):
+ global gdbserver_type
+ gdbserver_type = None
+ gdb.events.exited.disconnect(exit_handler)
+
+ def probe_qemu():
+ try:
+ return gdb.execute("monitor info version", to_string=True) != ""
+ except gdb.error:
+ return False
+
+ def probe_kgdb():
+ try:
+ thread_info = gdb.execute("info thread 2", to_string=True)
+ return "shadowCPU0" in thread_info
+ except gdb.error:
+ return False
+
+ global gdbserver_type
+ if gdbserver_type is None:
+ if probe_qemu():
+ gdbserver_type = GDBSERVER_QEMU
+ elif probe_kgdb():
+ gdbserver_type = GDBSERVER_KGDB
+ if gdbserver_type is not None and hasattr(gdb, 'events'):
+ gdb.events.exited.connect(exit_handler)
+ return gdbserver_type
+
+
+def gdb_eval_or_none(expresssion):
+ try:
+ return gdb.parse_and_eval(expresssion)
+ except gdb.error:
+ return None
+
+
+def dentry_name(d):
+ parent = d['d_parent']
+ if parent == d or parent == 0:
+ return ""
+ p = dentry_name(d['d_parent']) + "/"
+ return p + d['d_iname'].string()