summaryrefslogtreecommitdiffstats
path: root/scripts/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/gdb')
-rw-r--r--scripts/gdb/Makefile1
-rw-r--r--scripts/gdb/linux/.gitignore3
-rw-r--r--scripts/gdb/linux/Makefile24
-rw-r--r--scripts/gdb/linux/__init__.py1
-rw-r--r--scripts/gdb/linux/constants.py.in59
-rw-r--r--scripts/gdb/linux/cpus.py173
-rw-r--r--scripts/gdb/linux/dmesg.py73
-rw-r--r--scripts/gdb/linux/lists.py113
-rw-r--r--scripts/gdb/linux/modules.py95
-rw-r--r--scripts/gdb/linux/proc.py270
-rw-r--r--scripts/gdb/linux/symbols.py177
-rw-r--r--scripts/gdb/linux/tasks.py137
-rw-r--r--scripts/gdb/linux/utils.py184
-rw-r--r--scripts/gdb/vmlinux-gdb.py33
14 files changed, 1343 insertions, 0 deletions
diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile
new file mode 100644
index 000000000..62f5f65be
--- /dev/null
+++ b/scripts/gdb/Makefile
@@ -0,0 +1 @@
+subdir-y := linux
diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore
new file mode 100644
index 000000000..257354384
--- /dev/null
+++ b/scripts/gdb/linux/.gitignore
@@ -0,0 +1,3 @@
+*.pyc
+*.pyo
+constants.py
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
new file mode 100644
index 000000000..aba23be98
--- /dev/null
+++ b/scripts/gdb/linux/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+always := gdb-scripts
+
+SRCTREE := $(abspath $(srctree))
+
+$(obj)/gdb-scripts:
+ifneq ($(KBUILD_SRC),)
+ $(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj)
+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;' $@
+
+targets += constants.py
+$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in FORCE
+ $(call if_changed_dep,gen_constants_py)
+
+build_constants_py: $(obj)/constants.py
+ @:
+
+clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) $(obj)/constants.py
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/constants.py.in b/scripts/gdb/linux/constants.py.in
new file mode 100644
index 000000000..7aad82406
--- /dev/null
+++ b/scripts/gdb/linux/constants.py.in
@@ -0,0 +1,59 @@
+/*
+ * 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/fs.h>
+#include <linux/mount.h>
+#include <linux/of_fdt.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
+
+/* linux/fs.h */
+LX_VALUE(MS_RDONLY)
+LX_VALUE(MS_SYNCHRONOUS)
+LX_VALUE(MS_MANDLOCK)
+LX_VALUE(MS_DIRSYNC)
+LX_VALUE(MS_NOATIME)
+LX_VALUE(MS_NODIRATIME)
+
+/* 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/of_fdt.h> */
+LX_VALUE(OF_DT_HEADER)
+
+/* Kernel Configs */
+LX_CONFIG(CONFIG_OF)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
new file mode 100644
index 000000000..ca11e8df3
--- /dev/null
+++ b/scripts/gdb/linux/cpus.py
@@ -0,0 +1,173 @@
+#
+# 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
+
+
+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()
+
+
+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):
+ var_ptr = gdb.parse_and_eval("&current_task")
+ return per_cpu(var_ptr, cpu).dereference()
+
+
+LxCurrentFunc()
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py
new file mode 100644
index 000000000..6d2e09a2a
--- /dev/null
+++ b/scripts/gdb/linux/dmesg.py
@@ -0,0 +1,73 @@
+#
+# 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
+
+
+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):
+ log_buf_addr = int(str(gdb.parse_and_eval(
+ "(void *)'printk.c'::log_buf")).split()[0], 16)
+ log_first_idx = int(gdb.parse_and_eval("'printk.c'::log_first_idx"))
+ log_next_idx = int(gdb.parse_and_eval("'printk.c'::log_next_idx"))
+ log_buf_len = int(gdb.parse_and_eval("'printk.c'::log_buf_len"))
+
+ inf = gdb.inferiors()[0]
+ start = log_buf_addr + log_first_idx
+ if log_first_idx < log_next_idx:
+ log_buf_2nd_half = -1
+ length = log_next_idx - log_first_idx
+ log_buf = utils.read_memoryview(inf, start, length).tobytes()
+ else:
+ log_buf_2nd_half = log_buf_len - log_first_idx
+ a = utils.read_memoryview(inf, start, log_buf_2nd_half)
+ b = utils.read_memoryview(inf, log_buf_addr, log_next_idx)
+ log_buf = a.tobytes() + b.tobytes()
+
+ pos = 0
+ while pos < log_buf.__len__():
+ length = utils.read_u16(log_buf[pos + 8:pos + 10])
+ if length == 0:
+ if log_buf_2nd_half == -1:
+ gdb.write("Corrupted log buffer!\n")
+ break
+ 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(
+ encoding='utf8', errors='replace')
+ time_stamp = utils.read_u64(log_buf[pos:pos + 8])
+
+ 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)
+
+ pos += length
+
+
+LxDmesg()
diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
new file mode 100644
index 000000000..2f335fbd8
--- /dev/null
+++ b/scripts/gdb/linux/lists.py
@@ -0,0 +1,113 @@
+#
+# 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")
+
+
+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 {}"
+ .format(head.type))
+
+ 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):
+ 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 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..0aebd7565
--- /dev/null
+++ b/scripts/gdb/linux/proc.py
@@ -0,0 +1,270 @@
+#
+# 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_MS_SYNCHRONOUS: ",sync",
+ constants.LX_MS_MANDLOCK: ",mand",
+ constants.LX_MS_DIRSYNC: ",dirsync",
+ constants.LX_MS_NOATIME: ",noatime",
+ constants.LX_MS_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:
+ 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")
+
+ 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_MS_RDONLY) else "rw"
+
+ gdb.write(
+ "{} {} {} {}{}{} 0 0\n"
+ .format(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:
+ 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/symbols.py b/scripts/gdb/linux/symbols.py
new file mode 100644
index 000000000..aa4f87e3d
--- /dev/null
+++ b/scripts/gdb/linux/symbols.py
@@ -0,0 +1,177 @@
+#
+# 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
+
+
+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"):
+ self.module_files.append(root + "/" + name)
+ self.module_files_updated = True
+
+ def _get_module_file(self, module_name):
+ module_pattern = ".*/{0}\.ko$".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:
+ 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
+ gdb.execute("symbol-file", to_string=True)
+ gdb.execute("symbol-file 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.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.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..f6ab3ccf6
--- /dev/null
+++ b/scripts/gdb/linux/tasks.py
@@ -0,0 +1,137 @@
+#
+# 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):
+ for task in task_lists():
+ gdb.write("{address} {pid} {comm}\n".format(
+ address=task,
+ pid=task["pid"],
+ comm=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/utils.py b/scripts/gdb/linux/utils.py
new file mode 100644
index 000000000..50805874c
--- /dev/null
+++ b/scripts/gdb/linux/utils.py
@@ -0,0 +1,184 @@
+#
+# 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")
+
+
+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):
+ return memoryview(inf.read_memory(start, length))
+
+
+def read_u16(buffer):
+ value = [0, 0]
+
+ if type(buffer[0]) is str:
+ value[0] = ord(buffer[0])
+ value[1] = ord(buffer[1])
+ else:
+ value[0] = buffer[0]
+ value[1] = buffer[1]
+
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return value[0] + (value[1] << 8)
+ else:
+ return value[1] + (value[0] << 8)
+
+
+def read_u32(buffer):
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16)
+ else:
+ return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16)
+
+
+def read_u64(buffer):
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32)
+ else:
+ return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32)
+
+
+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:
+ return False
+
+ def probe_kgdb():
+ try:
+ thread_info = gdb.execute("info thread 2", to_string=True)
+ return "shadowCPU0" in thread_info
+ except:
+ 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:
+ 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()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
new file mode 100644
index 000000000..6e0b0afd8
--- /dev/null
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -0,0 +1,33 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# loader module
+#
+# Copyright (c) Siemens AG, 2012, 2013
+#
+# Authors:
+# Jan Kiszka <jan.kiszka@siemens.com>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import os
+
+sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb")
+
+try:
+ gdb.parse_and_eval("0")
+ gdb.execute("", to_string=True)
+except:
+ gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to "
+ "work.\n")
+else:
+ import linux.utils
+ import linux.symbols
+ import linux.modules
+ import linux.dmesg
+ import linux.tasks
+ import linux.cpus
+ import linux.lists
+ import linux.proc
+ import linux.constants