summaryrefslogtreecommitdiff
path: root/scripts/gdb/linux
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/gdb/linux')
-rw-r--r--scripts/gdb/linux/Makefile2
-rw-r--r--scripts/gdb/linux/clk.py76
-rw-r--r--scripts/gdb/linux/config.py44
-rw-r--r--scripts/gdb/linux/constants.py.in18
-rw-r--r--scripts/gdb/linux/cpus.py1
-rw-r--r--scripts/gdb/linux/device.py182
-rw-r--r--scripts/gdb/linux/dmesg.py16
-rw-r--r--scripts/gdb/linux/genpd.py83
-rw-r--r--scripts/gdb/linux/lists.py26
-rw-r--r--scripts/gdb/linux/proc.py10
-rw-r--r--scripts/gdb/linux/rbtree.py177
-rw-r--r--scripts/gdb/linux/symbols.py21
-rw-r--r--scripts/gdb/linux/tasks.py2
-rw-r--r--scripts/gdb/linux/timerlist.py219
-rw-r--r--scripts/gdb/linux/utils.py32
15 files changed, 878 insertions, 31 deletions
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
index 3df395a9c2ce..124755087510 100644
--- a/scripts/gdb/linux/Makefile
+++ b/scripts/gdb/linux/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
-ifneq ($(KBUILD_SRC),)
+ifdef building_out_of_srctree
symlinks := $(patsubst $(srctree)/$(src)/%,%,$(wildcard $(srctree)/$(src)/*.py))
diff --git a/scripts/gdb/linux/clk.py b/scripts/gdb/linux/clk.py
new file mode 100644
index 000000000000..061aecfa294e
--- /dev/null
+++ b/scripts/gdb/linux/clk.py
@@ -0,0 +1,76 @@
+# 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):
+ 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 000000000000..90e1565b1967
--- /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 + 8")
+ py_config_size = gdb.parse_and_eval(
+ "sizeof(kernel_config_data) - 1 - 8 * 2")
+ 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
index d3319a80788a..2efbec6b6b8d 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -12,9 +12,12 @@
*
*/
+#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 */
@@ -36,6 +39,10 @@
import gdb
+/* 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)
@@ -44,6 +51,9 @@ 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)
@@ -52,8 +62,16 @@ 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
index ca11e8df31b6..008e62f3190d 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -135,6 +135,7 @@ and can help identify the state of hotplugged CPUs"""
gdb.write("Online CPUs : {}\n".format(list(each_online_cpu())))
gdb.write("Active CPUs : {}\n".format(list(each_active_cpu())))
+
LxCpus()
diff --git a/scripts/gdb/linux/device.py b/scripts/gdb/linux/device.py
new file mode 100644
index 000000000000..16376c5cfec6
--- /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
index 6d2e09a2ad2f..2fa7bb83885f 100644
--- a/scripts/gdb/linux/dmesg.py
+++ b/scripts/gdb/linux/dmesg.py
@@ -16,6 +16,8 @@ import sys
from linux import utils
+printk_log_type = utils.CachedType("struct printk_log")
+
class LxDmesg(gdb.Command):
"""Print Linux kernel log buffer."""
@@ -42,9 +44,14 @@ class LxDmesg(gdb.Command):
b = utils.read_memoryview(inf, log_buf_addr, log_next_idx)
log_buf = a.tobytes() + b.tobytes()
+ length_offset = printk_log_type.get_type()['len'].bitpos // 8
+ text_len_offset = printk_log_type.get_type()['text_len'].bitpos // 8
+ time_stamp_offset = printk_log_type.get_type()['ts_nsec'].bitpos // 8
+ text_offset = printk_log_type.get_type().sizeof
+
pos = 0
while pos < log_buf.__len__():
- length = utils.read_u16(log_buf[pos + 8:pos + 10])
+ length = utils.read_u16(log_buf, pos + length_offset)
if length == 0:
if log_buf_2nd_half == -1:
gdb.write("Corrupted log buffer!\n")
@@ -52,10 +59,11 @@ class LxDmesg(gdb.Command):
pos = log_buf_2nd_half
continue
- text_len = utils.read_u16(log_buf[pos + 10:pos + 12])
- text = log_buf[pos + 16:pos + 16 + text_len].decode(
+ text_len = utils.read_u16(log_buf, pos + text_len_offset)
+ text_start = pos + text_offset
+ text = log_buf[text_start:text_start + text_len].decode(
encoding='utf8', errors='replace')
- time_stamp = utils.read_u64(log_buf[pos:pos + 8])
+ time_stamp = utils.read_u64(log_buf, pos + time_stamp_offset)
for line in text.splitlines():
msg = u"[{time:12.6f}] {line}\n".format(
diff --git a/scripts/gdb/linux/genpd.py b/scripts/gdb/linux/genpd.py
new file mode 100644
index 000000000000..6ca93bd2949e
--- /dev/null
+++ b/scripts/gdb/linux/genpd.py
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) NXP 2019
+
+import gdb
+import sys
+
+from linux.utils import CachedType
+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'])
+
+ slave_names = []
+ for link in list_for_each_entry(
+ genpd['master_links'],
+ device_link_type.get_type().pointer(),
+ 'master_node'):
+ slave_names.apend(link['slave']['name'])
+
+ gdb.write('%-30s %-15s %s\n' % (
+ genpd['name'].string(),
+ status_string,
+ ', '.join(slave_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):
+ gdb.write('domain status slaves\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
index 2f335fbd86fd..c487ddf09d38 100644
--- a/scripts/gdb/linux/lists.py
+++ b/scripts/gdb/linux/lists.py
@@ -16,13 +16,15 @@ 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 gdb.GdbError("Must be struct list_head not {}"
+ raise TypeError("Must be struct list_head not {}"
.format(head.type))
node = head['next'].dereference()
@@ -33,9 +35,24 @@ def list_for_each(head):
def list_for_each_entry(head, gdbtype, member):
for node in list_for_each(head):
- if node.type != list_head.get_type().pointer():
- raise TypeError("Type {} found. Expected struct list_head *."
- .format(node.type))
+ 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)
@@ -110,4 +127,5 @@ class LxListChk(gdb.Command):
raise gdb.GdbError("lx-list-check takes one argument")
list_check(gdb.parse_and_eval(argv[0]))
+
LxListChk()
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index 2f01a958eb22..6a56bba233a9 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -29,6 +29,7 @@ class LxCmdLine(gdb.Command):
def invoke(self, arg, from_tty):
gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
+
LxCmdLine()
@@ -43,6 +44,7 @@ class LxVersion(gdb.Command):
# linux_banner should contain a newline
gdb.write(gdb.parse_and_eval("(char *)linux_banner").string())
+
LxVersion()
@@ -86,6 +88,7 @@ Equivalent to cat /proc/iomem on a running target"""
def invoke(self, arg, from_tty):
return show_lx_resources("iomem_resource")
+
LxIOMem()
@@ -100,6 +103,7 @@ Equivalent to cat /proc/ioports on a running target"""
def invoke(self, arg, from_tty):
return show_lx_resources("ioport_resource")
+
LxIOPorts()
@@ -149,7 +153,7 @@ values of that process namespace"""
if len(argv) >= 1:
try:
pid = int(argv[0])
- except:
+ except gdb.error:
raise gdb.GdbError("Provide a PID as integer value")
else:
pid = 1
@@ -195,6 +199,7 @@ values of that process namespace"""
info_opts(FS_INFO, s_flags),
info_opts(MNT_INFO, m_flags)))
+
LxMounts()
@@ -259,7 +264,7 @@ class LxFdtDump(gdb.Command):
try:
f = open(filename, 'wb')
- except:
+ except gdb.error:
raise gdb.GdbError("Could not open file to dump fdt")
f.write(fdt_buf)
@@ -267,4 +272,5 @@ class LxFdtDump(gdb.Command):
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 000000000000..39db889b874c
--- /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 = node.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 is 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 = node.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 is 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
index 004b0ac7fa72..be984aa29b75 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -15,7 +15,7 @@ import gdb
import os
import re
-from linux import modules
+from linux import modules, utils
if hasattr(gdb, 'Breakpoint'):
@@ -77,12 +77,12 @@ lx-symbols command."""
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"):
+ 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$".format(
+ 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):
@@ -99,7 +99,8 @@ lx-symbols command."""
attrs[n]['name'].string(): attrs[n]['address']
for n in range(int(sect_attrs['nsections']))}
args = []
- for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
+ 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(
@@ -116,6 +117,12 @@ lx-symbols command."""
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(
@@ -139,8 +146,12 @@ lx-symbols command."""
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'):
+ orig_vmlinux = obj.filename
gdb.execute("symbol-file", to_string=True)
- gdb.execute("symbol-file vmlinux")
+ gdb.execute("symbol-file {0}".format(orig_vmlinux))
self.loaded_modules = []
module_list = modules.module_list()
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index f6ab3ccf698f..0301dc1e0138 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -79,6 +79,7 @@ class LxPs(gdb.Command):
pid=task["pid"],
comm=task["comm"].string()))
+
LxPs()
@@ -134,4 +135,5 @@ variable."""
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 000000000000..071d0dd5a634
--- /dev/null
+++ b/scripts/gdb/linux/timerlist.py
@@ -0,0 +1,219 @@
+# 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 xrange(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)
+
+ 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
index 50805874cfc3..ea94221dbd39 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -66,6 +66,7 @@ Note that TYPE and ELEMENT have to be quoted as strings."""
return container_of(ptr, gdb.lookup_type(typename.string()).pointer(),
elementname.string())
+
ContainerOf()
@@ -91,15 +92,16 @@ def read_memoryview(inf, start, length):
return memoryview(inf.read_memory(start, length))
-def read_u16(buffer):
+def read_u16(buffer, offset):
+ buffer_val = buffer[offset:offset + 2]
value = [0, 0]
- if type(buffer[0]) is str:
- value[0] = ord(buffer[0])
- value[1] = ord(buffer[1])
+ if type(buffer_val[0]) is str:
+ value[0] = ord(buffer_val[0])
+ value[1] = ord(buffer_val[1])
else:
- value[0] = buffer[0]
- value[1] = buffer[1]
+ value[0] = buffer_val[0]
+ value[1] = buffer_val[1]
if get_target_endianness() == LITTLE_ENDIAN:
return value[0] + (value[1] << 8)
@@ -107,18 +109,18 @@ def read_u16(buffer):
return value[1] + (value[0] << 8)
-def read_u32(buffer):
+def read_u32(buffer, offset):
if get_target_endianness() == LITTLE_ENDIAN:
- return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16)
+ return read_u16(buffer, offset) + (read_u16(buffer, offset + 2) << 16)
else:
- return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16)
+ return read_u16(buffer, offset + 2) + (read_u16(buffer, offset) << 16)
-def read_u64(buffer):
+def read_u64(buffer, offset):
if get_target_endianness() == LITTLE_ENDIAN:
- return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32)
+ return read_u32(buffer, offset) + (read_u32(buffer, offset + 4) << 32)
else:
- return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32)
+ return read_u32(buffer, offset + 4) + (read_u32(buffer, offset) << 32)
target_arch = None
@@ -148,14 +150,14 @@ def get_gdbserver_type():
def probe_qemu():
try:
return gdb.execute("monitor info version", to_string=True) != ""
- except:
+ 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:
+ except gdb.error:
return False
global gdbserver_type
@@ -172,7 +174,7 @@ def get_gdbserver_type():
def gdb_eval_or_none(expresssion):
try:
return gdb.parse_and_eval(expresssion)
- except:
+ except gdb.error:
return None