Hi Thomas,
as discussed earlier today, I'm sending out a fresh rebase of the gdb
scripts so that you can organize the next steps towards merge. Thanks
for this in advance!
Changes since v9:
- rebase over recent Linux version
- use a generator instead of iterator for task list,
fixes thread-group iteration [Daniel Wagner]
- apply generator scheme on other iterations
- propose myself as maintainer
See http://lkml.indiana.edu/hypermail/linux/kernel/1210.0/01598.html for
the original description and
git://git.kiszka.org/linux.git queues/gdb-scripts
for the latest version.
There is still some issue with gdb >= 7.8 when parsing symbols of kernel
modules, because I didn't deliver those folks the requested core dump of
gdb so far - done now. So up to version 7.7 remains recommended for the
time being.
Jan
CC: Daniel Thompson <[email protected]>
CC: Daniel Wagner <[email protected]>
CC: "David S. Miller" <[email protected]>
CC: Fenghua Yu <[email protected]>
Cc: Jan Kiszka <[email protected]>
CC: Kay Sievers <[email protected]>
CC: [email protected]
CC: [email protected]
Cc: [email protected]
CC: [email protected]
Cc: Michal Marek <[email protected]>
CC: Michal Marek <[email protected]>
CC: Pantelis Koukousoulas <[email protected]>
CC: Rob Landley <[email protected]>
CC: Rusty Russell <[email protected]>
CC: [email protected]
CC: Tony Luck <[email protected]>
Daniel Thompson (1):
scripts/gdb: Ignore byte-compiled python files
Daniel Wagner (1):
scripts/gdb: Use a generator instead of iterator for task list
Jan Kiszka (24):
scripts/gdb: Add infrastructure
scripts/gdb: Add cache for type objects
scripts/gdb: Add container_of helper and convenience function
scripts/gdb: Add module iteration class
scripts/gdb: Add lx-symbols command
module: Do not inline do_init_module
scripts/gdb: Add automatic symbol reloading on module insertion
scripts/gdb: Add internal helper and convenience function to look up a
module
scripts/gdb: Add get_target_endianness helper
scripts/gdb: Add read_u16/32/64 helpers
scripts/gdb: Add lx-dmesg command
scripts/gdb: Add task iteration class
scripts/gdb: Add helper and convenience function to look up tasks
scripts/gdb: Add is_target_arch helper
scripts/gdb: Add internal helper and convenience function to retrieve
thread_info
scripts/gdb: Add get_gdbserver_type helper
scripts/gdb: Add internal helper and convenience function for per-cpu
lookup
scripts/gdb: Add lx_current convenience function
scripts/gdb: Add class to iterate over CPU masks
scripts/gdb: Add lx-lsmod command
scripts/gdb: Add basic documentation
scripts/gdb: Convert ModuleList to generator function
scripts/gdb: Convert CpuList to generator function
scripts/gdb: Define maintainer
Pantelis Koukousoulas (1):
scripts/gdb: Port to python3 / gdb7.7
.gitignore | 1 +
Documentation/gdb-kernel-debugging.txt | 160 +++++++++++++++++++++++++++++++
MAINTAINERS | 5 +
Makefile | 6 +-
kernel/module.c | 9 +-
lib/Kconfig.debug | 11 +++
scripts/Makefile | 3 +-
scripts/gdb/Makefile | 1 +
scripts/gdb/linux/.gitignore | 2 +
scripts/gdb/linux/Makefile | 11 +++
scripts/gdb/linux/__init__.py | 0
scripts/gdb/linux/cpus.py | 135 +++++++++++++++++++++++++++
scripts/gdb/linux/dmesg.py | 65 +++++++++++++
scripts/gdb/linux/modules.py | 103 ++++++++++++++++++++
scripts/gdb/linux/symbols.py | 166 +++++++++++++++++++++++++++++++++
scripts/gdb/linux/tasks.py | 100 ++++++++++++++++++++
scripts/gdb/linux/utils.py | 156 +++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 30 ++++++
18 files changed, 960 insertions(+), 4 deletions(-)
create mode 100644 Documentation/gdb-kernel-debugging.txt
create mode 100644 scripts/gdb/Makefile
create mode 100644 scripts/gdb/linux/.gitignore
create mode 100644 scripts/gdb/linux/Makefile
create mode 100644 scripts/gdb/linux/__init__.py
create mode 100644 scripts/gdb/linux/cpus.py
create mode 100644 scripts/gdb/linux/dmesg.py
create mode 100644 scripts/gdb/linux/modules.py
create mode 100644 scripts/gdb/linux/symbols.py
create mode 100644 scripts/gdb/linux/tasks.py
create mode 100644 scripts/gdb/linux/utils.py
create mode 100644 scripts/gdb/vmlinux-gdb.py
--
1.8.4.5
This adds a lsmod-like command to list all currently loaded modules of
the target.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/modules.py | 46 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 531f763..e7c99e9 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -13,7 +13,7 @@
import gdb
-from linux import utils
+from linux import cpus, utils
module_type = utils.CachedType("struct module")
@@ -65,3 +65,47 @@ of the target and return that module variable which MODULE matches."""
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 ModuleList():
+ ref = 0
+ module_refptr = module['refptr']
+ for cpu in cpus.CpuList("cpu_possible_mask"):
+ refptr = cpus.per_cpu(module_refptr, cpu)
+ ref += refptr['incs']
+ ref -= refptr['decs']
+
+ gdb.write("{address} {name:<19} {size:>8} {ref}".format(
+ address=str(module['module_core']).split()[0],
+ name=module['name'].string(),
+ size=module['core_size'],
+ ref=ref))
+
+ source_list = module['source_list']
+ t = self._module_use_type.get_type().pointer()
+ entry = source_list['next']
+ first = True
+ while entry != source_list.address:
+ use = utils.container_of(entry, t, "source_list")
+ gdb.write("{separator}{name}".format(
+ separator=" " if first else ",",
+ name=use['source']['name'].string()))
+ first = False
+ entry = entry['next']
+ gdb.write("\n")
+
+
+LxLsmod()
--
1.8.4.5
This function allows to obtain a per-cpu variable, either of the current
or an explicitly specified CPU.
Note: sparc64 version is untested.
CC: "David S. Miller" <[email protected]>
CC: [email protected]
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/cpus.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 69 insertions(+)
create mode 100644 scripts/gdb/linux/cpus.py
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
new file mode 100644
index 0000000..18337e0
--- /dev/null
+++ b/scripts/gdb/linux/cpus.py
@@ -0,0 +1,68 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# per-cpu tools
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# 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()
+
+
+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()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 4d7eb2c..4848928 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -27,3 +27,4 @@ else:
import linux.modules
import linux.dmesg
import linux.tasks
+ import linux.cpus
--
1.8.4.5
This provides the basic infrastructure to load kernel-specific python
helper scripts when debugging the kernel in gdb.
The loading mechanism is based on gdb loading for <objfile>-gdb.py when
opening <objfile>. Therefore, this places a corresponding link to the
main helper script into the output directory that contains vmlinux.
The main scripts will pull in submodules containing Linux specific gdb
commands and functions. To avoid polluting the source directory with
compiled python modules, we link to them from the object directory.
Due to gdb.parse_and_eval and string redirection for gdb.execute, we
depend on gdb >= 7.2.
This feature is enabled via CONFIG_GDB_SCRIPTS.
CC: Michal Marek <[email protected]>
CC: [email protected]
Signed-off-by: Jan Kiszka <[email protected]>
---
Makefile | 6 +++++-
lib/Kconfig.debug | 11 +++++++++++
scripts/Makefile | 3 ++-
scripts/gdb/Makefile | 1 +
scripts/gdb/linux/Makefile | 11 +++++++++++
scripts/gdb/linux/__init__.py | 0
scripts/gdb/vmlinux-gdb.py | 23 +++++++++++++++++++++++
7 files changed, 53 insertions(+), 2 deletions(-)
create mode 100644 scripts/gdb/Makefile
create mode 100644 scripts/gdb/linux/Makefile
create mode 100644 scripts/gdb/linux/__init__.py
create mode 100644 scripts/gdb/vmlinux-gdb.py
diff --git a/Makefile b/Makefile
index 00d618b..5917c1c 100644
--- a/Makefile
+++ b/Makefile
@@ -920,6 +920,9 @@ endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
+ifdef CONFIG_GDB_SCRIPTS
+ $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
+endif
+$(call if_changed,link-vmlinux)
# The actual objects are generated when descending,
@@ -1175,7 +1178,8 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \
Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
signing_key.priv signing_key.x509 x509.genkey \
extra_certificates signing_key.x509.keyid \
- signing_key.x509.signer include/linux/version.h
+ signing_key.x509.signer include/linux/version.h \
+ vmlinux-gdb.py
# clean - Delete most, but leave enough to build external modules
#
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 4e35a5d..33e1974 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -167,6 +167,17 @@ config DEBUG_INFO_DWARF4
But it significantly improves the success of resolving
variables in gdb on optimized code.
+config GDB_SCRIPTS
+ bool "Provide GDB scripts for kernel debugging"
+ select DEBUG_INFO
+ help
+ This creates the required links to GDB helper scripts in the
+ build directory. If you load vmlinux into gdb, the helper
+ scripts will be automatically imported by gdb as well, and
+ additional functions are available to analyze a Linux kernel
+ instance. See Documentation/gdb-kernel-debugging.txt for further
+ details.
+
config ENABLE_WARN_DEPRECATED
bool "Enable __deprecated logic"
default y
diff --git a/scripts/Makefile b/scripts/Makefile
index 72902b5..7413b1b 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -36,6 +36,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms
subdir-y += mod
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_DTC) += dtc
+subdir-$(CONFIG_GDB_SCRIPTS) += gdb
# Let clean descend into subdirs
-subdir- += basic kconfig package
+subdir- += basic kconfig package gdb
diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile
new file mode 100644
index 0000000..62f5f65
--- /dev/null
+++ b/scripts/gdb/Makefile
@@ -0,0 +1 @@
+subdir-y := linux
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
new file mode 100644
index 0000000..dcb63ab
--- /dev/null
+++ b/scripts/gdb/linux/Makefile
@@ -0,0 +1,11 @@
+always := gdb-scripts
+
+SRCTREE := $(shell cd $(srctree) && /bin/pwd)
+
+$(obj)/gdb-scripts:
+ifneq ($(KBUILD_SRC),)
+ $(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj)
+endif
+ @:
+
+clean-files := *.pyc $(if $(KBUILD_SRC),*.py)
diff --git a/scripts/gdb/linux/__init__.py b/scripts/gdb/linux/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
new file mode 100644
index 0000000..c1d90ce
--- /dev/null
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -0,0 +1,23 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# loader module
+#
+# Copyright (c) Siemens AG, 2012, 2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# 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")
--
1.8.4.5
Type lookups are very slow in gdb-python which is often noticeable when
iterating over a number of objects. Introduce the helper class
CachedType that keeps a reference to a gdb.Type object but also
refreshes it after an object file has been loaded.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 scripts/gdb/linux/utils.py
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
new file mode 100644
index 0000000..f883611
--- /dev/null
+++ b/scripts/gdb/linux/utils.py
@@ -0,0 +1,34 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# common utilities
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# 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
--
1.8.4.5
This provides a reliable breakpoint target, required for automatic
symbol loading via the gdb helper command 'lx-symbols'.
CC: Rusty Russell <[email protected]>
Signed-off-by: Jan Kiszka <[email protected]>
---
kernel/module.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/module.c b/kernel/module.c
index 88cec1d..da477eb 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3007,8 +3007,13 @@ static void do_mod_ctors(struct module *mod)
#endif
}
-/* This is where the real work happens */
-static int do_init_module(struct module *mod)
+/*
+ * This is where the real work happens.
+ *
+ * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
+ * helper command 'lx-symbols'.
+ */
+static noinline int do_init_module(struct module *mod)
{
int ret = 0;
--
1.8.4.5
This class allows to iterate over all tasks of the target.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/tasks.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 scripts/gdb/linux/tasks.py
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
new file mode 100644
index 0000000..cd25984
--- /dev/null
+++ b/scripts/gdb/linux/tasks.py
@@ -0,0 +1,46 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# task & thread tools
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# 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")
+
+
+class TaskList:
+ def __init__(self):
+ global task_type
+ self.task_ptr_type = task_type.get_type().pointer()
+ self.init_task = gdb.parse_and_eval("init_task")
+ self.curr_group = self.init_task.address
+ self.curr_task = None
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ t = self.curr_task
+ if not t or t == self.curr_group:
+ self.curr_group = \
+ utils.container_of(self.curr_group['tasks']['next'],
+ self.task_ptr_type, "tasks")
+ if self.curr_group == self.init_task.address:
+ raise StopIteration
+ t = self.curr_task = self.curr_group
+ else:
+ self.curr_task = \
+ utils.container_of(t['thread_group']['next'],
+ self.task_ptr_type, "thread_group")
+ return t
--
1.8.4.5
This helper caches to result of "show architecture" and matches the
provided arch (sub-)string against that output.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 808a265..71ee48c 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -106,3 +106,16 @@ def read_u64(buffer):
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
--
1.8.4.5
CC: Rob Landley <[email protected]>
CC: [email protected]
Signed-off-by: Jan Kiszka <[email protected]>
---
Documentation/gdb-kernel-debugging.txt | 160 +++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
create mode 100644 Documentation/gdb-kernel-debugging.txt
diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt
new file mode 100644
index 0000000..7050ce8
--- /dev/null
+++ b/Documentation/gdb-kernel-debugging.txt
@@ -0,0 +1,160 @@
+Debugging kernel and modules via gdb
+====================================
+
+The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware
+interfaces allow to debug the Linux kernel and its modules during runtime
+using gdb. Gdb comes with a powerful scripting interface for python. The
+kernel provides a collection of helper scripts that can simplify typical
+kernel debugging steps. This is a short tutorial about how to enable and use
+them. It focuses on QEMU/KVM virtual machines as target, but the examples can
+be transferred to the other gdb stubs as well.
+
+
+Requirements
+------------
+
+ o gdb 7.2+ (recommended: 7.4+) with python support enabled (typically true
+ for distributions)
+
+
+Setup
+-----
+
+ o Create a virtual Linux machine for QEMU/KVM (see http://www.linux-kvm.org and
+ http://www.qemu.org for more details). For cross-development,
+ http://landley.net/aboriginal/bin keeps a pool of machine images and
+ toolchains that can be helpful to start from.
+
+ o Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave
+ CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports
+ CONFIG_FRAME_POINTER, keep it enabled.
+
+ o Install that kernel on the guest.
+
+ Alternatively, QEMU allows to boot the kernel directly using -kernel,
+ -append, -initrd command line switches. This is generally only useful if
+ you do not depend on modules. See QEMU documentation for more details on
+ this mode.
+
+ o Enable the gdb stub of QEMU/KVM, either
+ - at VM startup time by appending "-s" to the QEMU command line
+ or
+ - during runtime by issuing "gdbserver" from the QEMU monitor
+ console
+
+ o cd /path/to/linux-build
+
+ o Start gdb: gdb vmlinux
+
+ Note: Some distros may restrict auto-loading of gdb scripts to known safe
+ directories. In case gdb reports to refuse loading vmlinux-gdb.py, add
+
+ add-auto-load-safe-path /path/to/linux-build
+
+ to ~/.gdbinit. See gdb help for more details.
+
+ o Attach to the booted guest:
+ (gdb) target remote :1234
+
+
+Examples of using the Linux-provided gdb helpers
+------------------------------------------------
+
+ o Load module (and main kernel) symbols:
+ (gdb) lx-symbols
+ loading vmlinux
+ scanning for modules in /home/user/linux/build
+ loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko
+ loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko
+ loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko
+ loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko
+ loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko
+ ...
+ loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko
+
+ o Set a breakpoint on some not yet loaded module function, e.g.:
+ (gdb) b btrfs_init_sysfs
+ Function "btrfs_init_sysfs" not defined.
+ Make breakpoint pending on future shared library load? (y or [n]) y
+ Breakpoint 1 (btrfs_init_sysfs) pending.
+
+ o Continue the target
+ (gdb) c
+
+ o Load the module on the target and watch the symbols being loaded as well as
+ the breakpoint hit:
+ loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko
+ loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko
+ loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko
+ loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko
+
+ Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36
+ 36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj);
+
+ o Dump the log buffer of the target kernel:
+ (gdb) lx-dmesg
+ [ 0.000000] Initializing cgroup subsys cpuset
+ [ 0.000000] Initializing cgroup subsys cpu
+ [ 0.000000] Linux version 3.8.0-rc4-dbg+ (...
+ [ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314
+ [ 0.000000] e820: BIOS-provided physical RAM map:
+ [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
+ [ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved
+ ....
+
+ o Examine fields of the current task struct:
+ (gdb) p $lx_current().pid
+ $1 = 4998
+ (gdb) p $lx_current().comm
+ $2 = "modprobe\000\000\000\000\000\000\000"
+
+ o Make use of the per-cpu function for the current or a specified CPU:
+ (gdb) p $lx_per_cpu("runqueues").nr_running
+ $3 = 1
+ (gdb) p $lx_per_cpu("runqueues", 2).nr_running
+ $4 = 0
+
+ o Dig into hrtimers using the container_of helper:
+ (gdb) set $next = $lx_per_cpu("hrtimer_bases").clock_base[0].active.next
+ (gdb) p *$container_of($next, "struct hrtimer", "node")
+ $5 = {
+ node = {
+ node = {
+ __rb_parent_color = 18446612133355256072,
+ rb_right = 0x0 <irq_stack_union>,
+ rb_left = 0x0 <irq_stack_union>
+ },
+ expires = {
+ tv64 = 1835268000000
+ }
+ },
+ _softexpires = {
+ tv64 = 1835268000000
+ },
+ function = 0xffffffff81078232 <tick_sched_timer>,
+ base = 0xffff88003fd0d6f0,
+ state = 1,
+ start_pid = 0,
+ start_site = 0xffffffff81055c1f <hrtimer_start_range_ns+20>,
+ start_comm = "swapper/2\000\000\000\000\000\000"
+ }
+
+
+List of commands and functions
+------------------------------
+
+The number of commands and convenience functions may evolve over the time,
+this is just a snapshot of the initial version:
+
+ (gdb) apropos lx
+ function lx_current -- Return current task
+ function lx_module -- Find module by name and return the module variable
+ function lx_per_cpu -- Return per-cpu variable
+ function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
+ function lx_thread_info -- Calculate Linux thread_info from task variable
+ lx-dmesg -- Print Linux kernel log buffer
+ lx-lsmod -- List currently loaded modules
+ lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
+
+Detailed help can be obtained via "help <command-name>" for commands and "help
+function <function-name>" for convenience functions.
--
1.8.4.5
This is a shorthand for *$lx_per_cpu("current_task"), i.e. a convenience
function to retrieve the currently running task of the active context.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/cpus.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index 18337e0..b683da9 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -66,3 +66,20 @@ Note that VAR has to be quoted as string."""
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("¤t_task")
+ return per_cpu(var_ptr, cpu).dereference()
+
+
+LxCurrentFunc()
--
1.8.4.5
This pokes into the log buffer of the debugged kernel, dumping it to the
gdb console. Helping in case the target should or can no longer execute
dmesg itself.
CC: Kay Sievers <[email protected]>
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/dmesg.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 65 insertions(+)
create mode 100644 scripts/gdb/linux/dmesg.py
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py
new file mode 100644
index 0000000..7650f24
--- /dev/null
+++ b/scripts/gdb/linux/dmesg.py
@@ -0,0 +1,64 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# kernel log buffer dump
+#
+# Copyright (c) Siemens AG, 2011, 2012
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+import string
+
+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("log_buf")).split()[0], 16)
+ log_first_idx = int(gdb.parse_and_eval("log_first_idx"))
+ log_next_idx = int(gdb.parse_and_eval("log_next_idx"))
+ log_buf_len = int(gdb.parse_and_eval("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 = inf.read_memory(start, length)
+ else:
+ log_buf_2nd_half = log_buf_len - log_first_idx
+ log_buf = inf.read_memory(start, log_buf_2nd_half) + \
+ inf.read_memory(log_buf_addr, log_next_idx)
+
+ 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])
+ time_stamp = utils.read_u64(log_buf[pos:pos + 8])
+
+ for line in log_buf[pos + 16:pos + 16 + text_len].splitlines():
+ gdb.write("[{time:12.6f}] {line}\n".format(
+ time=time_stamp / 1000000000.0,
+ line=line))
+
+ pos += length
+
+
+LxDmesg()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index cf2e716..fa66d23 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -25,3 +25,4 @@ else:
import linux.utils
import linux.symbols
import linux.modules
+ import linux.dmesg
--
1.8.4.5
Will be used first to count module references. It is optimized to read
the mask only once per stop.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/cpus.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index b683da9..c1441f2 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -50,6 +50,60 @@ def per_cpu(var_ptr, cpu):
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)
+
+
+class CpuList():
+ def __init__(self, mask_name):
+ global cpu_mask
+ self.mask = None
+ if mask_name in cpu_mask:
+ self.mask = cpu_mask[mask_name]
+ if self.mask is None:
+ self.mask = gdb.parse_and_eval(mask_name + ".bits")
+ if hasattr(gdb, 'events'):
+ cpu_mask[mask_name] = self.mask
+ gdb.events.stop.connect(cpu_mask_invalidate)
+ if hasattr(gdb.events, 'new_objfile'):
+ gdb.events.new_objfile.connect(cpu_mask_invalidate)
+ self.bits_per_entry = self.mask[0].type.sizeof * 8
+ self.num_entries = self.mask.type.sizeof * 8 / self.bits_per_entry
+ self.entry = -1
+ self.bits = 0
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ while self.bits == 0:
+ self.entry += 1
+ if self.entry == self.num_entries:
+ raise StopIteration
+ self.bits = self.mask[self.entry]
+ if self.bits != 0:
+ self.bit = 0
+ break
+
+ while self.bits & 1 == 0:
+ self.bits >>= 1
+ self.bit += 1
+
+ cpu = self.entry * self.bits_per_entry + self.bit
+
+ self.bits >>= 1
+ self.bit += 1
+
+ return cpu
+
+
class PerCpu(gdb.Function):
"""Return per-cpu variable.
--
1.8.4.5
Will soon be used for loading symbols, printing global variables or
listing modules.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/modules.py | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 scripts/gdb/linux/modules.py
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
new file mode 100644
index 0000000..8a65c3d
--- /dev/null
+++ b/scripts/gdb/linux/modules.py
@@ -0,0 +1,39 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# module tools
+#
+# Copyright (c) Siemens AG, 2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import utils
+
+
+module_type = utils.CachedType("struct module")
+
+
+class ModuleList:
+ def __init__(self):
+ global module_type
+ self.module_ptr_type = module_type.get_type().pointer()
+ modules = gdb.parse_and_eval("modules")
+ self.curr_entry = modules['next']
+ self.end_of_list = modules.address
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ entry = self.curr_entry
+ if entry != self.end_of_list:
+ self.curr_entry = entry['next']
+ return utils.container_of(entry, self.module_ptr_type, "list")
+ else:
+ raise StopIteration
--
1.8.4.5
Parse the target endianness from the output of "show endian" and cache
the result to return it via the new helper get_target_endiannes. We will
need it for reading integers from buffers that contain target memory.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index c9d705b..10a227b 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -67,3 +67,21 @@ Note that TYPE and ELEMENT have to be quoted as strings."""
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.GdgError("unknown endianness '{0}'".format(endian))
+ return target_endianness
--
1.8.4.5
From: Pantelis Koukousoulas <[email protected]>
I tried to use these scripts in an ubuntu 14.04 host (gdb 7.7 compiled
against python 3.3) but there were several errors.
I believe this patch fixes these issues so that the commands
now work (I tested lx-symbols, lx-dmesg, lx-lsmod).
Main issues that needed to be resolved:
* In python 2 iterators have a "next()" method. In python 3 it is
__next__() instead (so let's just add both).
* In older python versions there was an implicit conversion
in object.__format__() (used when an object is in string.format())
where it was converting the object to str first and then
calling str's __format__(). This has now been removed so
we must explicitly convert to str the objects for which
we need to keep this behavior.
* In dmesg.py: in python 3 log_buf is now a "memoryview" object
which needs to be converted to a string in order to use string
methods like "splitlines()". Luckily memoryview exists in
python 2.7.6 as well, so we can convert log_buf to memoryview
and use the same code in both python 2 and python 3.
This version of the patch has now been tested with gdb 7.7 and
both python 3.4 and python 2.7.6 (I think asking for at least
python 2.7.6 is a reasonable requirement instead of complicating
the code with version checks etc).
Signed-off-by: Pantelis Koukousoulas <[email protected]>
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/cpus.py | 5 ++++-
scripts/gdb/linux/dmesg.py | 3 ++-
scripts/gdb/linux/modules.py | 9 ++++++---
scripts/gdb/linux/symbols.py | 4 ++--
scripts/gdb/linux/tasks.py | 4 +++-
scripts/gdb/linux/utils.py | 2 +-
6 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index c1441f2..8045871 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -82,7 +82,7 @@ class CpuList():
def __iter__(self):
return self
- def next(self):
+ def __next__(self):
while self.bits == 0:
self.entry += 1
if self.entry == self.num_entries:
@@ -103,6 +103,9 @@ class CpuList():
return cpu
+ def next(self):
+ return self.__next__()
+
class PerCpu(gdb.Function):
"""Return per-cpu variable.
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py
index 7650f24..3c947f0 100644
--- a/scripts/gdb/linux/dmesg.py
+++ b/scripts/gdb/linux/dmesg.py
@@ -51,9 +51,10 @@ class LxDmesg(gdb.Command):
continue
text_len = utils.read_u16(log_buf[pos + 10:pos + 12])
+ text = log_buf[pos + 16:pos + 16 + text_len]
time_stamp = utils.read_u64(log_buf[pos:pos + 8])
- for line in log_buf[pos + 16:pos + 16 + text_len].splitlines():
+ for line in memoryview(text).tobytes().splitlines():
gdb.write("[{time:12.6f}] {line}\n".format(
time=time_stamp / 1000000000.0,
line=line))
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index e7c99e9..2dbf679 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -30,7 +30,7 @@ class ModuleList:
def __iter__(self):
return self
- def next(self):
+ def __next__(self):
entry = self.curr_entry
if entry != self.end_of_list:
self.curr_entry = entry['next']
@@ -38,6 +38,9 @@ class ModuleList:
else:
raise StopIteration
+ def next(self):
+ return self.__next__()
+
def find_module_by_name(name):
for module in ModuleList():
@@ -91,8 +94,8 @@ class LxLsmod(gdb.Command):
gdb.write("{address} {name:<19} {size:>8} {ref}".format(
address=str(module['module_core']).split()[0],
name=module['name'].string(),
- size=module['core_size'],
- ref=ref))
+ size=str(module['core_size']),
+ ref=str(ref)))
source_list = module['source_list']
t = self._module_use_type.get_type().pointer()
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index 139841f..ae757fd 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -73,7 +73,7 @@ lx-symbols command."""
def _get_module_file(self, module_name):
module_pattern = ".*/{0}\.ko$".format(
- string.replace(module_name, "_", r"[_\-]"))
+ module_name.replace("_", r"[_\-]"))
for name in self.module_files:
if re.match(module_pattern, name) and os.path.exists(name):
return name
@@ -87,7 +87,7 @@ lx-symbols command."""
attrs = sect_attrs['attrs']
section_name_to_address = {
attrs[n]['name'].string() : attrs[n]['address']
- for n in range(sect_attrs['nsections'])}
+ for n in range(int(sect_attrs['nsections']))}
args = []
for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
address = section_name_to_address.get(section_name)
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index 63cd6c5..0008e75 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -30,7 +30,7 @@ class TaskList:
def __iter__(self):
return self
- def next(self):
+ def __next__(self):
t = self.curr_task
if not t or t == self.curr_group:
self.curr_group = \
@@ -45,6 +45,8 @@ class TaskList:
self.task_ptr_type, "thread_group")
return t
+ def next(self):
+ return self.__next__()
def get_task_by_pid(pid):
for task in TaskList():
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index a4a1640..128c306 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -83,7 +83,7 @@ def get_target_endianness():
elif "big endian" in endian:
target_endianness = BIG_ENDIAN
else:
- raise gdb.GdgError("unknown endianness '{0}'".format(endian))
+ raise gdb.GdgError("unknown endianness '{0}'".format(str(endian)))
return target_endianness
--
1.8.4.5
This installs a silent breakpoint on the do_init_module function. The
breakpoint handler will try to load symbols from the module files found
during lx-symbols execution. This way, breakpoints can be set to module
initialization functions, and there is no need to explicitly call
lx-symbols after (re-)loading a module.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/symbols.py | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index bd21a96..139841f 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -19,6 +19,30 @@ import string
from linux import modules, utils
+if hasattr(gdb, 'Breakpoint'):
+ class LoadModuleBreakpoint(gdb.Breakpoint):
+ def __init__(self, spec, gdb_command):
+ super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
+ self.silent = True
+ self.gdb_command = gdb_command
+
+ def stop(self):
+ module = gdb.parse_and_eval("mod")
+ module_name = module['name'].string()
+ cmd = self.gdb_command
+
+ # enforce update if object file is not found
+ cmd.module_files_updated = False
+
+ 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)
+ return False
+
+
class LxSymbols(gdb.Command):
"""(Re-)load symbols of Linux kernel and currently loaded modules.
@@ -30,6 +54,8 @@ 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,
@@ -87,6 +113,8 @@ lx-symbols command."""
addr=module_addr,
sections=self._section_arguments(module))
gdb.execute(cmdline, to_string=True)
+ if not module_name in self.loaded_modules:
+ self.loaded_modules.append(module_name)
else:
gdb.write("no module object found for '{0}'\n".format(module_name))
@@ -104,6 +132,7 @@ lx-symbols command."""
gdb.execute("symbol-file", to_string=True)
gdb.execute("symbol-file vmlinux")
+ self.loaded_modules = []
module_list = modules.ModuleList()
if not module_list:
gdb.write("no modules found\n")
@@ -123,5 +152,15 @@ lx-symbols command."""
self.load_all_symbols()
+ if hasattr(gdb, 'Breakpoint'):
+ if not self.breakpoint is 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()
--
1.8.4.5
From: Daniel Wagner <[email protected]>
The iterator does not return any task_struct from the thread_group list
because the first condition in the 'if not t or ...' will only be the
first time None.
Instead of keeping track of the state ourself in the next() function,
we fall back using Python's generator.
Signed-off-by: Daniel Wagner <[email protected]>
Cc: Jan Kiszka <[email protected]>
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/tasks.py | 50 +++++++++++++++++++---------------------------
1 file changed, 20 insertions(+), 30 deletions(-)
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index 0008e75..e2037d9 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -18,38 +18,28 @@ from linux import utils
task_type = utils.CachedType("struct task_struct")
-
-class TaskList:
- def __init__(self):
- global task_type
- self.task_ptr_type = task_type.get_type().pointer()
- self.init_task = gdb.parse_and_eval("init_task")
- self.curr_group = self.init_task.address
- self.curr_task = None
-
- def __iter__(self):
- return self
-
- def __next__(self):
- t = self.curr_task
- if not t or t == self.curr_group:
- self.curr_group = \
- utils.container_of(self.curr_group['tasks']['next'],
- self.task_ptr_type, "tasks")
- if self.curr_group == self.init_task.address:
- raise StopIteration
- t = self.curr_task = self.curr_group
- else:
- self.curr_task = \
- utils.container_of(t['thread_group']['next'],
- self.task_ptr_type, "thread_group")
- return t
-
- def next(self):
- return self.__next__()
+def task_lists():
+ global task_type
+ 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 TaskList():
+ for task in task_lists():
if int(task['pid']) == pid:
return task
return None
--
1.8.4.5
Yet another code simplification.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/cpus.py | 71 ++++++++++++++++++++------------------------
scripts/gdb/linux/modules.py | 2 +-
2 files changed, 33 insertions(+), 40 deletions(-)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index 8045871..4297b83 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -61,50 +61,43 @@ def cpu_mask_invalidate(event):
gdb.events.new_objfile.disconnect(cpu_mask_invalidate)
-class CpuList():
- def __init__(self, mask_name):
- global cpu_mask
- self.mask = None
- if mask_name in cpu_mask:
- self.mask = cpu_mask[mask_name]
- if self.mask is None:
- self.mask = gdb.parse_and_eval(mask_name + ".bits")
- if hasattr(gdb, 'events'):
- cpu_mask[mask_name] = self.mask
- gdb.events.stop.connect(cpu_mask_invalidate)
- if hasattr(gdb.events, 'new_objfile'):
- gdb.events.new_objfile.connect(cpu_mask_invalidate)
- self.bits_per_entry = self.mask[0].type.sizeof * 8
- self.num_entries = self.mask.type.sizeof * 8 / self.bits_per_entry
- self.entry = -1
- self.bits = 0
-
- def __iter__(self):
- return self
-
- def __next__(self):
- while self.bits == 0:
- self.entry += 1
- if self.entry == self.num_entries:
- raise StopIteration
- self.bits = self.mask[self.entry]
- if self.bits != 0:
- self.bit = 0
+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 self.bits & 1 == 0:
- self.bits >>= 1
- self.bit += 1
-
- cpu = self.entry * self.bits_per_entry + self.bit
+ while bits & 1 == 0:
+ bits >>= 1
+ bit += 1
- self.bits >>= 1
- self.bit += 1
+ cpu = entry * bits_per_entry + bit
- return cpu
+ bits >>= 1
+ bit += 1
- def next(self):
- return self.__next__()
+ yield cpu
class PerCpu(gdb.Function):
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 6d49722..a1504c4 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -75,7 +75,7 @@ class LxLsmod(gdb.Command):
for module in module_list():
ref = 0
module_refptr = module['refptr']
- for cpu in cpus.CpuList("cpu_possible_mask"):
+ for cpu in cpus.cpu_list("cpu_possible_mask"):
refptr = cpus.per_cpu(module_refptr, cpu)
ref += refptr['incs']
ref -= refptr['decs']
--
1.8.4.5
Provide an internal helper with container_of semantics. As type lookups
are very slow in gdb-python and we need a type "long" for this, cache
the reference to this type object. Then export the helper also as a
convenience function form use at the gdb command line.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 35 +++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 2 ++
2 files changed, 37 insertions(+)
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index f883611..c9d705b 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -32,3 +32,38 @@ class CachedType:
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()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index c1d90ce..6495841 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -21,3 +21,5 @@ try:
except:
gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to "
"work.\n")
+else:
+ import linux.utils
--
1.8.4.5
This is probably the most useful helper when debugging kernel modules:
lx-symbols first reloads vmlinux. Then it searches recursively for *.ko
files in the specified paths and the current directory. Finally it walks
the kernel's module list, issuing the necessary add-symbol-file command
for each loaded module so that gdb knows which module symbol corresponds
to which address. It also looks up variable sections (bss, data, rodata)
and appends their address to the add-symbole-file command line. This
allows to access global module variables just like any other variable.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/symbols.py | 127 +++++++++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 128 insertions(+)
create mode 100644 scripts/gdb/linux/symbols.py
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
new file mode 100644
index 0000000..bd21a96
--- /dev/null
+++ b/scripts/gdb/linux/symbols.py
@@ -0,0 +1,127 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# load kernel and module symbols
+#
+# Copyright (c) Siemens AG, 2011-2013
+#
+# Authors:
+# Jan Kiszka <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+import os
+import re
+import string
+
+from linux import modules, utils
+
+
+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
+
+ 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(
+ string.replace(module_name, "_", 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]['name'].string() : attrs[n]['address']
+ for n in range(sect_attrs['nsections'])}
+ args = []
+ for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
+ 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['module_core']).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)
+ 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")
+
+ module_list = modules.ModuleList()
+ 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 = arg.split()
+ self.module_paths.append(os.getcwd())
+
+ # enforce update
+ self.module_files = []
+ self.module_files_updated = False
+
+ self.load_all_symbols()
+
+
+LxSymbols()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 6495841..0b0faa4 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -23,3 +23,4 @@ except:
"work.\n")
else:
import linux.utils
+ import linux.symbols
--
1.8.4.5
This helper probes the type of the gdb server. Supported are QEMU and
KGDB so far. Knowledge about the gdb server is required e.g. to retrieve
the current CPU or current task.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 71ee48c..a4a1640 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -119,3 +119,38 @@ def is_target_arch(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 not gdbserver_type is None and hasattr(gdb, 'events'):
+ gdb.events.exited.connect(exit_handler)
+ return gdbserver_type
--
1.8.4.5
I'm proposing myself for keeping an eye on these scripts and integrating
contributions.
Signed-off-by: Jan Kiszka <[email protected]>
---
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index c444907..062db33 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4066,6 +4066,11 @@ W: http://www.icp-vortex.com/
S: Supported
F: drivers/scsi/gdt*
+GDB KERNEL DEBUGGING HELPER SCRIPTS
+M: Jan Kiszka <[email protected]>
+S: Supported
+F: scripts/gdb/
+
GEMTEK FM RADIO RECEIVER DRIVER
M: Hans Verkuil <[email protected]>
L: [email protected]
--
1.8.4.5
Add the internal helper get_module_by_name to obtain the module
structure corresponding to the given name. Also export this service as a
convenience function.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/modules.py | 28 ++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 29 insertions(+)
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 8a65c3d..531f763 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -37,3 +37,31 @@ class ModuleList:
return utils.container_of(entry, self.module_ptr_type, "list")
else:
raise StopIteration
+
+
+def find_module_by_name(name):
+ for module in ModuleList():
+ 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()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 0b0faa4..cf2e716 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -24,3 +24,4 @@ except:
else:
import linux.utils
import linux.symbols
+ import linux.modules
--
1.8.4.5
Analogously to the task list, convert the module list to a generator
function. It noticeably simplifies the code.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/modules.py | 33 +++++++++++----------------------
scripts/gdb/linux/symbols.py | 2 +-
2 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 2dbf679..6d49722 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -19,31 +19,20 @@ from linux import cpus, utils
module_type = utils.CachedType("struct module")
-class ModuleList:
- def __init__(self):
- global module_type
- self.module_ptr_type = module_type.get_type().pointer()
- modules = gdb.parse_and_eval("modules")
- self.curr_entry = modules['next']
- self.end_of_list = modules.address
-
- def __iter__(self):
- return self
-
- def __next__(self):
- entry = self.curr_entry
- if entry != self.end_of_list:
- self.curr_entry = entry['next']
- return utils.container_of(entry, self.module_ptr_type, "list")
- else:
- raise StopIteration
+def module_list():
+ global module_type
+ module_ptr_type = module_type.get_type().pointer()
+ modules = gdb.parse_and_eval("modules")
+ entry = modules['next']
+ end_of_list = modules.address
- def next(self):
- return self.__next__()
+ while entry != end_of_list:
+ yield utils.container_of(entry, module_ptr_type, "list")
+ entry = entry['next']
def find_module_by_name(name):
- for module in ModuleList():
+ for module in module_list():
if module['name'].string() == name:
return module
return None
@@ -83,7 +72,7 @@ class LxLsmod(gdb.Command):
"Address{0} Module Size Used by\n".format(
" " if utils.get_long_type().sizeof == 8 else ""))
- for module in ModuleList():
+ for module in module_list():
ref = 0
module_refptr = module['refptr']
for cpu in cpus.CpuList("cpu_possible_mask"):
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index ae757fd..bf05e45 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -133,7 +133,7 @@ lx-symbols command."""
gdb.execute("symbol-file vmlinux")
self.loaded_modules = []
- module_list = modules.ModuleList()
+ module_list = modules.module_list()
if not module_list:
gdb.write("no modules found\n")
else:
--
1.8.4.5
From: Daniel Thompson <[email protected]>
Using the gdb scripts leaves byte-compiled python files in the scripts/
directory. These should be ignored by git and cleaned up by mrproper.
Signed-off-by: Daniel Thompson <[email protected]>
Cc: Michal Marek <[email protected]>
Cc: [email protected]
Signed-off-by: Jan Kiszka <[email protected]>
---
.gitignore | 1 +
Makefile | 2 +-
scripts/gdb/linux/.gitignore | 2 ++
3 files changed, 4 insertions(+), 1 deletion(-)
create mode 100644 scripts/gdb/linux/.gitignore
diff --git a/.gitignore b/.gitignore
index e213b27..17d4457 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,6 +43,7 @@ Module.symvers
/TAGS
/linux
/vmlinux
+/vmlinux-gdb.py
/vmlinuz
/System.map
/Module.markers
diff --git a/Makefile b/Makefile
index 5917c1c..e806f73 100644
--- a/Makefile
+++ b/Makefile
@@ -1179,7 +1179,7 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \
signing_key.priv signing_key.x509 x509.genkey \
extra_certificates signing_key.x509.keyid \
signing_key.x509.signer include/linux/version.h \
- vmlinux-gdb.py
+ vmlinux-gdb.py scripts/gdb/linux/*.py[co]
# clean - Delete most, but leave enough to build external modules
#
diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore
new file mode 100644
index 0000000..52e4e61
--- /dev/null
+++ b/scripts/gdb/linux/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+*.pyo
--
1.8.4.5
Add the helper task_by_pid that can look up a task by its PID. Also
export it as a convenience function.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/tasks.py | 27 +++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
2 files changed, 28 insertions(+)
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index cd25984..13bb97c 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -44,3 +44,30 @@ class TaskList:
utils.container_of(t['thread_group']['next'],
self.task_ptr_type, "thread_group")
return t
+
+
+def get_task_by_pid(pid):
+ for task in TaskList():
+ 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()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index fa66d23..4d7eb2c 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -26,3 +26,4 @@ else:
import linux.symbols
import linux.modules
import linux.dmesg
+ import linux.tasks
--
1.8.4.5
Add helpers for reading integers from target memory buffers. Required
when caching the memory access is more efficient than reading individual
values via gdb.
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/utils.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 10a227b..808a265 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -85,3 +85,24 @@ def get_target_endianness():
else:
raise gdb.GdgError("unknown endianness '{0}'".format(endian))
return target_endianness
+
+
+def read_u16(buffer):
+ if get_target_endianness() == LITTLE_ENDIAN:
+ return ord(buffer[0]) + (ord(buffer[1]) << 8)
+ else:
+ return ord(buffer[1]) + (ord(buffer[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)
--
1.8.4.5
Add the internal helper get_thread_info that calculates the thread_info
from a given task variable. Also export this service as a convenience
function.
Note: ia64 version is untested.
CC: Tony Luck <[email protected]>
CC: Fenghua Yu <[email protected]>
CC: [email protected]
Signed-off-by: Jan Kiszka <[email protected]>
---
scripts/gdb/linux/tasks.py | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index 13bb97c..63cd6c5 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -71,3 +71,38 @@ return that task_struct variable which PID matches."""
LxTaskByPidFunc()
+
+
+thread_info_type = utils.CachedType("struct thread_info")
+
+ia64_task_size = None
+
+
+def get_thread_info(task):
+ global thread_info_type
+ 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:
+ 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()
--
1.8.4.5