This is a handful of changes to the kernel's gdb scripts to do some more
debugging with kgdb. The first patch allows the vmlinux to be reloaded
from where it was specified on the command line so that this set of
scripts can be used from anywhere. The second patch adds a script to
dump the config.gz to a file on the host debugging machine. The third
patch adds some rb tree utilities and the last patch uses those rb tree
walking utilities to dump out the contents of /proc/timer_list from a
system under debug.
I'm guessing that Andrew will pick these patches up. I don't know who
maintains these gdb scripts but it looks like Andrew has been doing the
lifting recently.
Cc: Douglas Anderson <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Kieran Bingham <[email protected]>
Cc: Jan Kiszka <[email protected]>
Cc: Jackie Liu <[email protected]>
Stephen Boyd (4):
scripts/gdb: Find vmlinux where it was before
scripts/gdb: Add kernel config dumping command
scripts/gdb: Add rb tree iterating utilities
scripts/gdb: Add a timer list command
scripts/gdb/linux/config.py | 48 ++++++++
scripts/gdb/linux/constants.py.in | 8 ++
scripts/gdb/linux/rbtree.py | 169 ++++++++++++++++++++++++++
scripts/gdb/linux/symbols.py | 6 +-
scripts/gdb/linux/timerlist.py | 194 ++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 3 +
6 files changed, 427 insertions(+), 1 deletion(-)
create mode 100644 scripts/gdb/linux/config.py
create mode 100644 scripts/gdb/linux/rbtree.py
create mode 100644 scripts/gdb/linux/timerlist.py
base-commit: 9e98c678c2d6ae3a17cb2de55d17f69dddaa231b
--
Sent by a computer through tubes
Implement a command to print the timer list, much like how
/proc/timer_list is implemented. This can be used to look at the pending
timers on a crashed system.
Cc: Douglas Anderson <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Kieran Bingham <[email protected]>
Cc: Jan Kiszka <[email protected]>
Cc: Jackie Liu <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
scripts/gdb/linux/constants.py.in | 8 ++
scripts/gdb/linux/timerlist.py | 194 ++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
3 files changed, 203 insertions(+)
create mode 100644 scripts/gdb/linux/timerlist.py
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index d3319a80788a..2db8183d909c 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -15,6 +15,7 @@
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/of_fdt.h>
+#include <linux/hrtimer.h>
/* We need to stringify expanded macros so that they can be parsed */
@@ -44,6 +45,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)
@@ -56,4 +60,8 @@ LX_VALUE(MNT_RELATIME)
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_OF)
+LX_CONFIG(CONFIG_TICK_ONESHOT)
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py
new file mode 100644
index 000000000000..9b0111aa11a0
--- /dev/null
+++ b/scripts/gdb/linux/timerlist.py
@@ -0,0 +1,194 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright 2019 Google LLC.
+
+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")
+hrtimer_type = utils.CachedType("struct hrtimer")
+
+
+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.get_type().pointer(), "node")
+ timer = utils.container_of(timerqueue, hrtimer_type.get_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")
+ 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:
+ text += """ .expires_next : {} nsecs
+ .hres_active : {}
+ .nr_events : {}
+ .nr_retries : {}
+ .nr_hangs : {}
+ .max_hang_time : {}
+""".format(cpu_base['expires_next'],
+ cpu_base['hres_active'],
+ cpu_base['nr_events'],
+ cpu_base['nr_retries'],
+ cpu_base['nr_hangs'],
+ cpu_base['max_hang_time'])
+ if constants.LX_CONFIG_TICK_ONESHOT:
+ text += """ .nohz_mode : {}
+ .last_tick : {} nsecs
+ .tick_stopped : {}
+ .idle_jiffies : {}
+ .idle_calls : {}
+ .idle_sleeps : {}
+ .idle_entrytime : {} nsecs
+ .idle_waketime : {} nsecs
+ .idle_exittime : {} nsecs
+ .idle_sleeptime : {} nsecs
+ .iowait_sleeptime: {} nsecs
+ .last_jiffies : {}
+ .next_timer : {}
+ .idle_expires : {} nsecs
+jiffies: {}
+""".format(ts['nohz_mode'],
+ ts['last_tick'],
+ ts['tick_stopped'],
+ ts['idle_jiffies'],
+ ts['idle_calls'],
+ ts['idle_sleeps'],
+ ts['idle_entrytime'],
+ ts['idle_waketime'],
+ ts['idle_exittime'],
+ ts['idle_sleeptime'],
+ ts['iowait_sleeptime'],
+ ts['last_jiffies'],
+ ts['next_timer'],
+ ts['idle_expires'],
+ 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
+
+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")
+ text += "tick_broadcast_mask: {}\n".format(mask) # TODO: format properly
+ if constants.LX_CONFIG_TICK_ONESHOT:
+ mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask")
+ text += "tick_broadcast_oneshot_mask: {}\n".format(mask) # TODO: format properly
+ text += "\n"
+
+ tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device")
+ text += "\n".join([print_tickdevice(cpus.per_cpu(tick_cpu_devices, cpu), cpu) for cpu in cpus.each_online_cpu()])
+
+ gdb.write(text)
+
+LxTimerList()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 89e4aa4f8966..033578cc4cd7 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -33,3 +33,4 @@ else:
import linux.rbtree
import linux.proc
import linux.constants
+ import linux.timerlist
--
Sent by a computer through tubes
Implement gdb functions for rb_first(), rb_last(), rb_next(), and
rb_prev(). These can be useful to iterate through the kernel's red-black
trees.
Cc: Douglas Anderson <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Kieran Bingham <[email protected]>
Cc: Jan Kiszka <[email protected]>
Cc: Jackie Liu <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
scripts/gdb/linux/rbtree.py | 169 ++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 170 insertions(+)
create mode 100644 scripts/gdb/linux/rbtree.py
diff --git a/scripts/gdb/linux/rbtree.py b/scripts/gdb/linux/rbtree.py
new file mode 100644
index 000000000000..fd7b0b961ca1
--- /dev/null
+++ b/scripts/gdb/linux/rbtree.py
@@ -0,0 +1,169 @@
+# 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/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index be0efb5dda5b..89e4aa4f8966 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -30,5 +30,6 @@ else:
import linux.config
import linux.cpus
import linux.lists
+ import linux.rbtree
import linux.proc
import linux.constants
--
Sent by a computer through tubes
lx-configdump <file> dumps the contents of the gzipped .config to a text
file when the config is included in the kernel with CONFIG_IKCONFIG. By
default, the file written is called config.txt, but it can be any user
supplied filename as well. If the kernel config is in a module
(configs.ko), then it can be loaded along with symbols for the module
loaded with 'lx-symbols' and then this command will still work.
Obviously if you have the whole vmlinux then this can also be achieved
with scripts/extract-ikconfig, but this gdb script can be useful to
confirm that the memory contents of the config in memory and the vmlinux
contents on disk match what is expected.
Cc: Douglas Anderson <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Kieran Bingham <[email protected]>
Cc: Jan Kiszka <[email protected]>
Cc: Jackie Liu <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
scripts/gdb/linux/config.py | 48 +++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 49 insertions(+)
create mode 100644 scripts/gdb/linux/config.py
diff --git a/scripts/gdb/linux/config.py b/scripts/gdb/linux/config.py
new file mode 100644
index 000000000000..400f09bb2665
--- /dev/null
+++ b/scripts/gdb/linux/config.py
@@ -0,0 +1,48 @@
+# 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) - 2 - 8 * 2")
+ except:
+ 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)
+ try:
+ f = open(filename, 'wb')
+ except:
+ raise gdb.GdbError("Could not open file to dump config")
+
+ f.write(config_buf)
+ f.close()
+
+ gdb.write("Dumped config to " + filename + "\n")
+
+LxConfigDump()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 6e0b0afd888a..be0efb5dda5b 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -27,6 +27,7 @@ else:
import linux.modules
import linux.dmesg
import linux.tasks
+ import linux.config
import linux.cpus
import linux.lists
import linux.proc
--
Sent by a computer through tubes
If I run 'gdb <path/to/vmlinux>' and there's the vmlinux-gdb.py file
there I can properly see symbols and use the lx commands provided by the
GDB scripts. But once I run 'lx-symbols' at the command prompt, gdb
reloads the vmlinux symbols assuming that this script was run from the
directory that has vmlinux at the root. That isn't always true, but we
could just look and see what symbols were already loaded and use that
instead. Let's do that so this can work by being invoked anywhere.
Cc: Douglas Anderson <[email protected]>
Cc: Nikolay Borisov <[email protected]>
Cc: Kieran Bingham <[email protected]>
Cc: Jan Kiszka <[email protected]>
Cc: Jackie Liu <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
scripts/gdb/linux/symbols.py | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index 004b0ac7fa72..2f5b95f09fa0 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -139,8 +139,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()
--
Sent by a computer through tubes
Hi Stephen,
Thank you for these patches,
Could you check them through with PEP8 please?
While we are not entirely "pep8 clean", Your series adds the following
warnings:
linux/config.py:10:1: E302 expected 2 blank lines, found 1
linux/config.py:17:41: E128 continuation line under-indented for visual
indent
linux/config.py:30:9: E722 do not use bare 'except'
linux/config.py:35:41: E128 continuation line under-indented for visual
indent
linux/config.py:40:9: E722 do not use bare 'except'
linux/config.py:48:1: E305 expected 2 blank lines after class or
function definition, found 1
linux/rbtree.py:18:17: E128 continuation line under-indented for visual
indent
linux/rbtree.py:29:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:34:17: E128 continuation line under-indented for visual
indent
linux/rbtree.py:45:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:49:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:52:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:57:17: E128 continuation line under-indented for visual
indent
linux/rbtree.py:75:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:80:17: E128 continuation line under-indented for visual
indent
linux/rbtree.py:115:1: E305 expected 2 blank lines after class or
function definition, found 1
linux/rbtree.py:117:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:133:1: E305 expected 2 blank lines after class or
function definition, found 1
linux/rbtree.py:135:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:151:1: E305 expected 2 blank lines after class or
function definition, found 1
linux/rbtree.py:153:1: E302 expected 2 blank lines, found 1
linux/rbtree.py:169:1: E305 expected 2 blank lines after class or
function definition, found 1
linux/timerlist.py:26:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:27:80: E501 line too long (95 > 79 characters)
linux/timerlist.py:28:80: E501 line too long (85 > 79 characters)
linux/timerlist.py:37:80: E501 line too long (81 > 79 characters)
linux/timerlist.py:38:13: E128 continuation line under-indented for
visual indent
linux/timerlist.py:41:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:50:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:63:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:81:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:82:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:83:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:84:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:85:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:103:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:104:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:105:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:106:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:107:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:108:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:109:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:110:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:111:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:112:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:113:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:114:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:115:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:116:12: E122 continuation line missing indentation or
outdented
linux/timerlist.py:122:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:150:15: E124 closing bracket does not match visual
indentation
linux/timerlist.py:160:1: E302 expected 2 blank lines, found 1
linux/timerlist.py:183:65: E261 at least two spaces before inline comment
linux/timerlist.py:183:80: E501 line too long (88 > 79 characters)
linux/timerlist.py:186:77: E261 at least two spaces before inline comment
linux/timerlist.py:186:80: E501 line too long (100 > 79 characters)
linux/timerlist.py:190:80: E501 line too long (125 > 79 characters)
linux/timerlist.py:194:1: E305 expected 2 blank lines after class or
function definition, found 1
--
Regards
Kieran
On 25/03/2019 18:45, Stephen Boyd wrote:
> This is a handful of changes to the kernel's gdb scripts to do some more
> debugging with kgdb. The first patch allows the vmlinux to be reloaded
> from where it was specified on the command line so that this set of
> scripts can be used from anywhere. The second patch adds a script to
> dump the config.gz to a file on the host debugging machine. The third
> patch adds some rb tree utilities and the last patch uses those rb tree
> walking utilities to dump out the contents of /proc/timer_list from a
> system under debug.
>
> I'm guessing that Andrew will pick these patches up. I don't know who
> maintains these gdb scripts but it looks like Andrew has been doing the
> lifting recently.
>
> Cc: Douglas Anderson <[email protected]>
> Cc: Nikolay Borisov <[email protected]>
> Cc: Kieran Bingham <[email protected]>
> Cc: Jan Kiszka <[email protected]>
> Cc: Jackie Liu <[email protected]>
>
> Stephen Boyd (4):
> scripts/gdb: Find vmlinux where it was before
> scripts/gdb: Add kernel config dumping command
> scripts/gdb: Add rb tree iterating utilities
> scripts/gdb: Add a timer list command
>
> scripts/gdb/linux/config.py | 48 ++++++++
> scripts/gdb/linux/constants.py.in | 8 ++
> scripts/gdb/linux/rbtree.py | 169 ++++++++++++++++++++++++++
> scripts/gdb/linux/symbols.py | 6 +-
> scripts/gdb/linux/timerlist.py | 194 ++++++++++++++++++++++++++++++
> scripts/gdb/vmlinux-gdb.py | 3 +
> 6 files changed, 427 insertions(+), 1 deletion(-)
> create mode 100644 scripts/gdb/linux/config.py
> create mode 100644 scripts/gdb/linux/rbtree.py
> create mode 100644 scripts/gdb/linux/timerlist.py
>
>
> base-commit: 9e98c678c2d6ae3a17cb2de55d17f69dddaa231b
>
--
--
Kieran
Hi Stephen,
On 25/03/2019 18:45, Stephen Boyd wrote:
> Implement gdb functions for rb_first(), rb_last(), rb_next(), and
> rb_prev(). These can be useful to iterate through the kernel's red-black
> trees.
I definitely approve of getting data-structure helpers into scripts/gdb,
as it will greatly assist debug options but my last attempt to do this
was with the radix-tree which I had to give up on as the internals were
changing rapidly and caused continuous breakage to the helpers.
Do you foresee any similar issue here? Or is the corresponding RB code
in the kernel fairly 'stable'?
Please could we make sure whomever maintains the RBTree code is aware of
the python implementation?
That said, MAINTAINERS doesn't actually seem to list any ownership over
the rb-tree code, and get_maintainers.pl [0] seems to be pointing at
Andrew as the probable route in for that code so perhaps that's already
in place :D
[0]
> ./scripts/get_maintainer.pl -f ./include/linux/rbtree*
> Andrew Morton <[email protected]> (commit_signer:3/2=100%,commit_signer:2/1=100%)
> Sebastian Andrzej Siewior <[email protected]> (commit_signer:1/2=50%,authored:1/2=50%,added_lines:1/3=33%,commit_signer:1/1=100%,authored:1/1=100%,added_lines:1/1=100%)
> Wei Yang <[email protected]> (commit_signer:1/2=50%,authored:1/2=50%,added_lines:2/3=67%,removed_lines:2/2=100%)
> [email protected] (open list)
--
Regards
Kieran
>
> Cc: Douglas Anderson <[email protected]>
> Cc: Nikolay Borisov <[email protected]>
> Cc: Kieran Bingham <[email protected]>
> Cc: Jan Kiszka <[email protected]>
> Cc: Jackie Liu <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> scripts/gdb/linux/rbtree.py | 169 ++++++++++++++++++++++++++++++++++++
> scripts/gdb/vmlinux-gdb.py | 1 +
> 2 files changed, 170 insertions(+)
> create mode 100644 scripts/gdb/linux/rbtree.py
>
> diff --git a/scripts/gdb/linux/rbtree.py b/scripts/gdb/linux/rbtree.py
> new file mode 100644
> index 000000000000..fd7b0b961ca1
> --- /dev/null
> +++ b/scripts/gdb/linux/rbtree.py
> @@ -0,0 +1,169 @@
> +# 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/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
> index be0efb5dda5b..89e4aa4f8966 100644
> --- a/scripts/gdb/vmlinux-gdb.py
> +++ b/scripts/gdb/vmlinux-gdb.py
> @@ -30,5 +30,6 @@ else:
> import linux.config
> import linux.cpus
> import linux.lists
> + import linux.rbtree
> import linux.proc
> import linux.constants
>
--
--
Kieran
Quoting Kieran Bingham (2019-03-26 01:52:10)
> Hi Stephen,
>
> On 25/03/2019 18:45, Stephen Boyd wrote:
> > Implement gdb functions for rb_first(), rb_last(), rb_next(), and
> > rb_prev(). These can be useful to iterate through the kernel's red-black
> > trees.
>
> I definitely approve of getting data-structure helpers into scripts/gdb,
> as it will greatly assist debug options but my last attempt to do this
> was with the radix-tree which I had to give up on as the internals were
> changing rapidly and caused continuous breakage to the helpers.
Thanks for the background on radix-tree. I haven't looked at that yet,
but I suppose I'll want to have that too at some point.
>
> Do you foresee any similar issue here? Or is the corresponding RB code
> in the kernel fairly 'stable'?
>
>
> Please could we make sure whomever maintains the RBTree code is aware of
> the python implementation?
>
> That said, MAINTAINERS doesn't actually seem to list any ownership over
> the rb-tree code, and get_maintainers.pl [0] seems to be pointing at
> Andrew as the probable route in for that code so perhaps that's already
> in place :D
I don't think that the rb tree implementation is going to change. It
feels similar to the list API. I suppose this problem of keeping things
in sync is a more general problem than just data-structures changing.
The only solution I can offer is to have more testing and usage of these
scripts. Unless gdb can "simulate" or run arbitrary code for us then I
think we're stuck reimplementing kernel internal code in gdb scripts so
that we can get debug info out.
On 26.03.19 18:05, Stephen Boyd wrote:
> Quoting Kieran Bingham (2019-03-26 01:52:10)
>> Hi Stephen,
>>
>> On 25/03/2019 18:45, Stephen Boyd wrote:
>>> Implement gdb functions for rb_first(), rb_last(), rb_next(), and
>>> rb_prev(). These can be useful to iterate through the kernel's red-black
>>> trees.
>>
>> I definitely approve of getting data-structure helpers into scripts/gdb,
>> as it will greatly assist debug options but my last attempt to do this
>> was with the radix-tree which I had to give up on as the internals were
>> changing rapidly and caused continuous breakage to the helpers.
>
> Thanks for the background on radix-tree. I haven't looked at that yet,
> but I suppose I'll want to have that too at some point.
>
>>
>> Do you foresee any similar issue here? Or is the corresponding RB code
>> in the kernel fairly 'stable'?
>>
>>
>> Please could we make sure whomever maintains the RBTree code is aware of
>> the python implementation?
>>
>> That said, MAINTAINERS doesn't actually seem to list any ownership over
>> the rb-tree code, and get_maintainers.pl [0] seems to be pointing at
>> Andrew as the probable route in for that code so perhaps that's already
>> in place :D
>
> I don't think that the rb tree implementation is going to change. It
> feels similar to the list API. I suppose this problem of keeping things
> in sync is a more general problem than just data-structures changing.
> The only solution I can offer is to have more testing and usage of these
> scripts. Unless gdb can "simulate" or run arbitrary code for us then I
> think we're stuck reimplementing kernel internal code in gdb scripts so
> that we can get debug info out.
>
Could we possibly leave some link in form of comment in the related headers or
implementations? Won't magically solve the problem but at least increase changes
that author actually read them when they start changing the C implementations.
Jan
--
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux
Quoting Kieran Bingham (2019-03-26 01:39:43)
> Hi Stephen,
>
> Thank you for these patches,
>
> Could you check them through with PEP8 please?
>
> While we are not entirely "pep8 clean", Your series adds the following
> warnings:
Sure. I didn't know what was desired here because python in the kernel
is not checked by anything like checkpatch. I've fixed it all up and
things got a little shorter so it must be better! Now to test it out.
Quoting Jan Kiszka (2019-03-26 10:21:21)
> On 26.03.19 18:05, Stephen Boyd wrote:
> > Quoting Kieran Bingham (2019-03-26 01:52:10)
> >>
> >> Do you foresee any similar issue here? Or is the corresponding RB code
> >> in the kernel fairly 'stable'?
> >>
> >>
> >> Please could we make sure whomever maintains the RBTree code is aware of
> >> the python implementation?
> >>
> >> That said, MAINTAINERS doesn't actually seem to list any ownership over
> >> the rb-tree code, and get_maintainers.pl [0] seems to be pointing at
> >> Andrew as the probable route in for that code so perhaps that's already
> >> in place :D
> >
> > I don't think that the rb tree implementation is going to change. It
> > feels similar to the list API. I suppose this problem of keeping things
> > in sync is a more general problem than just data-structures changing.
> > The only solution I can offer is to have more testing and usage of these
> > scripts. Unless gdb can "simulate" or run arbitrary code for us then I
> > think we're stuck reimplementing kernel internal code in gdb scripts so
> > that we can get debug info out.
> >
>
> Could we possibly leave some link in form of comment in the related headers or
> implementations? Won't magically solve the problem but at least increase changes
> that author actually read them when they start changing the C implementations.
>
Sure. Can you propose some sort of patch against the 'list'
implementation for this? I'd like to just use whatever policy is decided
here.
Hi Stephen,
On 26/03/2019 17:05, Stephen Boyd wrote:
> Quoting Kieran Bingham (2019-03-26 01:52:10)
>> Hi Stephen,
>>
>> On 25/03/2019 18:45, Stephen Boyd wrote:
>>> Implement gdb functions for rb_first(), rb_last(), rb_next(), and
>>> rb_prev(). These can be useful to iterate through the kernel's red-black
>>> trees.
>>
>> I definitely approve of getting data-structure helpers into scripts/gdb,
>> as it will greatly assist debug options but my last attempt to do this
>> was with the radix-tree which I had to give up on as the internals were
>> changing rapidly and caused continuous breakage to the helpers.
>
> Thanks for the background on radix-tree. I haven't looked at that yet,
> but I suppose I'll want to have that too at some point.
Sure, it will be useful to get going again, if you get round to it -
feel free to either dig out my old patches from the list, or
git-history. (I believe they actually made it into the kernel but I had
to revert them because of the breakage, and no time to continue that
development).
Or of course - start from scratch might also be a good option :D
>> Do you foresee any similar issue here? Or is the corresponding RB code
>> in the kernel fairly 'stable'?
>>
>>
>> Please could we make sure whomever maintains the RBTree code is aware of
>> the python implementation?
>>
>> That said, MAINTAINERS doesn't actually seem to list any ownership over
>> the rb-tree code, and get_maintainers.pl [0] seems to be pointing at
>> Andrew as the probable route in for that code so perhaps that's already
>> in place :D
>
> I don't think that the rb tree implementation is going to change. It
> feels similar to the list API. I suppose this problem of keeping things
> in sync is a more general problem than just data-structures changing.
> The only solution I can offer is to have more testing and usage of these
> scripts. Unless gdb can "simulate" or run arbitrary code for us then I
> think we're stuck reimplementing kernel internal code in gdb scripts so
> that we can get debug info out.
I agree - RB seems a lot more stable than the radix-tree was back when I
tried to mirror that implementation.
I would hope at some point we could get some automated tests going for
scripts/gdb as we see more and more functionality.
Everything should be automatable using GDB hooked up to QEmu.
GDB can 'run' arbitrary functions - but it's not a good idea as we won't
know the state of the target, and of course in the case of crash-dump
examination - the target won't even exist.
Anyway, I'm glad this is all useful to you - let us know if there's
anything we can do to help. Myself and Jan are trying to take care of
scripts/gdb - but there's not a lot of active development of new
features currently - so I'm very pleased to see your contributions !
--
Kieran