Add gdb python scripts as kernel debugging helpers
Subsystems (2)
cover Add gdb python scripts as kernel debugging helpers
I'm sitting too long on this stuff, time to finally role it out:
This adds the infrastructure and first tools that make kernel debugging through gdb more comfortable. Since 7.0, gdb supports python scripting. And this opens the doors to automate steps like the tedious loading of module symbols at the right address, resolving per-cpu variables or even retrieving the current kernel log without resuming an stopped target.
Many of the helpers naturally depend on the layout of structures or internal mechanics of the kernel. So the best place to maintain such things, keeping them consistent with the corresponding kernel is, well, the kernel itself.
While these scripts have been originally developed for debugging via QEMU/KVM, I've now also added the required bits for KGDB. Works fine, but as QEMU/KVM tends to outperform KGDB it remains the recommendation
- when available.
There are two architecture dependencies so far, one regarding per-cpu, the other regarding thread_info calculation. None of them I was able to test on a target, so I'm counting on review/testing by the corresponding communities.
This series should be considered the foundation of much more kernel state exploration helper, e.g. around tasks, timers, locks, sockets - I guess people will have even more ideas.
Hope it's useful!
PS: Also available via git://git.kiszka.org/linux.git queues/gdb-scripts
Jan Kiszka (13):
scripts/gdb: Add infrastructure
scripts/gdb: Add container_of helper and convenience function scripts/gdb: Add lx-symbols command
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 helper
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
Makefile | 6 ++- scripts/Makefile | 3 +- scripts/gdb/Makefile | 9 +++ scripts/gdb/dmesg.py | 63 +++++++++++++++++++++ scripts/gdb/percpu.py | 76 +++++++++++++++++++++++++ scripts/gdb/symbols.py | 88 +++++++++++++++++++++++++++++ scripts/gdb/task.py | 108 +++++++++++++++++++++++++++++++++++ scripts/gdb/utils.py | 134 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 26 +++++++++ 9 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 scripts/gdb/Makefile create mode 100644 scripts/gdb/dmesg.py create mode 100644 scripts/gdb/percpu.py create mode 100644 scripts/gdb/symbols.py create mode 100644 scripts/gdb/task.py create mode 100644 scripts/gdb/utils.py create mode 100644 scripts/gdb/vmlinux-gdb.py
Signature · 1 line
1.7.3.4
PATCH 1/13 scripts/gdb: Add infrastructure
From: Jan Kiszka <[email protected]>
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, we depend on gdb >= 7.1.
This feature depends on CONFIG_DEBUG_INFO.
--- Makefile | 6 +++++- scripts/Makefile | 3 ++- scripts/gdb/Makefile | 9 +++++++++ scripts/gdb/vmlinux-gdb.py | 20 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 scripts/gdb/Makefile create mode 100644 scripts/gdb/vmlinux-gdb.py
Makefile +5 -1
@@ -758,6 +758,9 @@ endif ifdef CONFIG_BUILD_DOCSRC $(Q)$(MAKE) $(build)=Documentation endif +ifdef CONFIG_DEBUG_INFO + $(Q)ln -fsn $(srctree)/scripts/gdb/vmlinux-gdb.py +endif +$(call if_changed,link-vmlinux) # The actual objects are generated when descending,
@@ -995,7 +998,8 @@ MRPROPER_DIRS += include/config usr/include include/generated \ arch/*/include/generated MRPROPER_FILES += .config .config.old .version .old_version \ include/linux/version.h \ - Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS + Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ + vmlinux-gdb.py # clean - Delete most, but leave enough to build external modules #
scripts/Makefile +2 -1
@@ -35,6 +35,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms subdir-y += mod subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_DTC) += dtc +subdir-$(CONFIG_DEBUG_INFO) += gdb # Let clean descend into subdirs -subdir- += basic kconfig package selinux +subdir- += basic kconfig package selinux gdb
scripts/gdb/Makefile new file +9 -0
@@ -0,0 +1,9 @@ +always := gdb-scripts + +$(obj)/gdb-scripts: +ifneq ($(KBUILD_SRC),) + $(Q)ln -fsn $(srctree)/$(obj)/*.py $(objtree)/$(obj) +endif + @: + +clean-files := *.pyc $(if $(KBUILD_SRC),*.py)
scripts/gdb/vmlinux-gdb.py new file +20 -0
@@ -0,0 +1,20 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# loader module +# +# Copyright (c) 2012 Siemens AG +# +# Authors: +# Jan Kiszka <[email protected]> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import os + +sys.path = [ os.path.dirname(__file__) + "/scripts/gdb" ] + sys.path + +if not gdb.VERSION >= "7.1": + print "NOTE: gdb 7.1 or later required for Linux helper scripts " \ + "to work."
Signature · 1 line
1.7.3.4
PATCH 2/13 scripts/gdb: Add container_of helper and convenience function
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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.
--- scripts/gdb/utils.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 2 + 2 files changed, 57 insertions(+), 0 deletions(-) create mode 100644 scripts/gdb/utils.py
scripts/gdb/utils.py new file +55 -0
@@ -0,0 +1,55 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# common utilities +# +# Copyright (c) 2011, 2012 Siemens AG +# +# Authors: +# Jan Kiszka <[email protected]> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +long_type = None + +def get_type(type_name): + t = gdb.lookup_type(type_name) + if t == None: + raise gdb.GdbError("cannot resolve type '%s'" % type_name) + return t + +def get_long_type(): + global long_type + if long_type == None: + long_type = get_type("long") + return long_type + + +def offset_of(typeobj, field): + element = gdb.Value(0).cast(typeobj) + return int(str(element[field].address), 16) + +def container_of(ptr, typeobj, member): + return (ptr.cast(get_long_type()) - + offset_of(typeobj, member)).cast(typeobj) + + +class ContainerOf(gdb.Function): + __doc__ = "Return pointer to containing data structure.\n" \ + "\n" \ + "$container_of(PTR, \"TYPE\", \"ELEMENT\"): Given PTR, return a pointer to the\n" \ + "data structure of the type TYPE in which PTR is the address of ELEMENT.\n" \ + "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()
scripts/gdb/vmlinux-gdb.py +2 -0
@@ -18,3 +18,5 @@ sys.path = [ os.path.dirname(__file__) + "/scripts/gdb" ] + sys.path if not gdb.VERSION >= "7.1": print "NOTE: gdb 7.1 or later required for Linux helper scripts " \ "to work." +else: + import utils
Signature · 1 line
1.7.3.4
PATCH 3/13 scripts/gdb: Add lx-symbols command
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
This is probably the most useful helper when debugging kernel modules:
lx-symbols will first reload 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 know which module symbol correspond to which address.
--- scripts/gdb/symbols.py | 88 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 89 insertions(+), 0 deletions(-) create mode 100644 scripts/gdb/symbols.py
scripts/gdb/symbols.py new file +88 -0
@@ -0,0 +1,88 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# load kernel and module symbols +# +# Copyright (c) 2011, 2012 Siemens AG +# +# 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 utils import * + +class LinuxSymbols(gdb.Command): + __doc__ = "(Re-)load symbols of Linux kernel and currently loaded modules.\n" \ + "\n" \ + "The kernel (vmlinux) is taken from the current working directly. Modules (.ko)\n" \ + "are scanned recursively, starting in the same directory. Optionally, the module\n" \ + "search path can be extended by a space separated list of paths passed to the\n" \ + "lx-symbols command." + + def __init__(self): + super(LinuxSymbols, self).__init__("lx-symbols", + gdb.COMMAND_FILES, + gdb.COMPLETE_FILENAME) + + def invoke(self, arg, from_tty): + print "loading vmlinux" + gdb.execute("symbol-file vmlinux") + + modules = gdb.parse_and_eval("modules") + entry = modules['next'] + if entry == modules.address: + print "no modules found" + return + + module_files = [] + paths = arg.split() + paths.append(os.getcwd()) + for path in paths: + print "scanning for modules in " + path + for root, dirs, files in os.walk(path): + for name in files: + if re.match(r".*\.ko$", name): + module_files.append(root + \ + "/" + name) + + module_type = gdb.lookup_type("struct module").pointer() + + while entry != modules.address: + module = container_of(entry, module_type, "list") + module_name = module['name'].string() + module_addr = str(module['module_core']) + module_pattern = ".*/" + \ + string.replace(module_name, "_", r"[_\-]") + \ + r"\.ko$" + module_path = "" + for name in module_files: + if re.match(module_pattern, name): + module_path = name + break + + if module_path: + print "loading @" + module_addr + ": " + \ + module_path + if gdb.VERSION >= "7.2": + gdb.execute("add-symbol-file " + \ + module_path + " " + \ + module_addr, + to_string = True) + else: + gdb.execute("add-symbol-file " + \ + module_path + " " + \ + module_addr) + else: + print "no module object found for '" + \ + module_name + "'" + + entry = entry['next'] + +LinuxSymbols()
scripts/gdb/vmlinux-gdb.py +1 -0
@@ -20,3 +20,4 @@ if not gdb.VERSION >= "7.1": "to work." else: import utils + import symbols
Signature · 1 line
1.7.3.4
PATCH 4/13 scripts/gdb: Add get_target_endianness helper
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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.
--- scripts/gdb/utils.py | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
scripts/gdb/utils.py +17 -0
@@ -53,3 +53,20 @@ class ContainerOf(gdb.Function): elementname.string()) ContainerOf() + + +BIG_ENDIAN = 0 +LITTLE_ENDIAN = 1 +target_endianness = None + +def get_target_endianness(): + global target_endianness + if target_endianness == None: + endian = gdb.execute("show endian", False, True) + if endian.find("little endian") >= 0: + target_endianness = LITTLE_ENDIAN + elif endian.find("big endian") >= 0: + target_endianness = BIG_ENDIAN + else: + raise gdb.GdgError("unknown endianness '%s'" % endian) + return target_endianness
Signature · 1 line
1.7.3.4
PATCH 5/13 scripts/gdb: Add read_u16/32/64 helpers
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
Add helpers for reading integers from target memory buffers. Required when caching the memory access is more efficient than reading individual values via gdb.
--- scripts/gdb/utils.py | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-)
scripts/gdb/utils.py +18 -0
@@ -70,3 +70,21 @@ def get_target_endianness(): else: raise gdb.GdgError("unknown endianness '%s'" % 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)
Signature · 1 line
1.7.3.4
PATCH 6/13 scripts/gdb: Add lx-dmesg command
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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.
--- scripts/gdb/dmesg.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 64 insertions(+), 0 deletions(-) create mode 100644 scripts/gdb/dmesg.py
scripts/gdb/dmesg.py new file +63 -0
@@ -0,0 +1,63 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# kernel log buffer dump +# +# Copyright (c) 2011, 2012 Siemens AG +# +# Authors: +# Jan Kiszka <[email protected]> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +import string + +from utils import * + +class LinuxDmesg(gdb.Command): + __doc__ = "Print Linux kernel log buffer." + + def __init__(self): + super(LinuxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + log_buf_addr = int(str(gdb.parse_and_eval("(void *)log_buf")), + 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 = read_u16(log_buf[pos + 8 : pos + 10]) + if length == 0: + if log_buf_2nd_half == -1: + print "Corrupted log buffer!" + break + pos = log_buf_2nd_half + continue + + text_len = read_u16(log_buf[pos + 10 : pos + 12]) + time_stamp = read_u64(log_buf[pos : pos + 8]) + + for line in log_buf[pos + 16 : + pos + 16 + text_len].splitlines(): + print "[%13.6f] " % \ + (time_stamp / 1000000000.0) + line + + pos += length + +LinuxDmesg()
scripts/gdb/vmlinux-gdb.py +1 -0
@@ -21,3 +21,4 @@ if not gdb.VERSION >= "7.1": else: import utils import symbols + import dmesg
Signature · 1 line
1.7.3.4
Re: [PATCH 06/13] scripts/gdb: Add lx-dmesg command
Quoted 5 lines · Jan Kiszka
From: Jan Kiszka <[email protected]>
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.
Thanks. That's very useful. I recently ran into this problem that the new log buffer is a pain to dump from qemu gdb.
fwiw there's already some kdump macros in Documentation/kdump Since you obsoleted them you could remove those (and for log buffer they do not work anymore)
I don't like the lx-* prefixes.
-Andi
Signature · 1 line
[email protected] -- Speaking for myself only
Re: [PATCH 06/13] scripts/gdb: Add lx-dmesg command
Quoted 8 lines · Andi Kleen
Quoted 5 lines · Jan Kiszka
From: Jan Kiszka <[email protected]>
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.
Quoted 6 lines
Thanks. That's very useful. I recently ran into this problem that the new log buffer is a pain to dump from qemu gdb.
fwiw there's already some kdump macros in
Documentation/kdumpSince you obsoleted them you could remove those (and for log buffer they do not work anymore)
Oh, indeed - and pretty dusty now. I would suggest to remove it when all its feature are either broken or replaced by python scripts. Need to look at the details, but stack backtraces is definitely still on the todo list here.
Quoted 2 lines
I don't like the lx-* prefixes.
In this case I think we could add the feature also under "dmesg". But I would generally like to avoid potential namespace clashes with gdb or other helpers, therefore the common prefix. Also, if you type "lx-<tab>", you now get a list of all Linux-related commands.
Jan
PATCH 7/13 scripts/gdb: Add task iteration helper
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
The internal helper for_each_task iterates over all tasks of the target, calling the provided function on each. For performance reasons, we cache a reference to the gdb type object of a task.
--- scripts/gdb/task.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 40 insertions(+), 0 deletions(-) create mode 100644 scripts/gdb/task.py
scripts/gdb/task.py new file +40 -0
@@ -0,0 +1,40 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# task & thread tools +# +# Copyright (c) 2011, 2012 Siemens AG +# +# Authors: +# Jan Kiszka <[email protected]> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from utils import * + +task_ptr_type = None + +def get_task_ptr_type(): + global task_ptr_type + if task_ptr_type == None: + task_ptr_type = get_type("struct task_struct").pointer() + return task_ptr_type + +def for_each_task(func, arg = None): + init_task = gdb.parse_and_eval("init_task") + g = init_task.address + while True: + g = container_of(g['tasks']['next'], get_task_ptr_type(), + "tasks") + if g == init_task.address: + break; + t = g + while True: + func(t, arg) + t = container_of(t['thread_group']['next'], + get_task_ptr_type(), "thread_group") + if t == g: + break
Signature · 1 line
1.7.3.4
PATCH 8/13 scripts/gdb: Add helper and convenience function to look up tasks
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
Add the helper task_by_pid that can look up a task by its PID. Also export it as a convenience function.
--- scripts/gdb/task.py | 29 +++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 30 insertions(+), 0 deletions(-)
scripts/gdb/task.py +29 -0
@@ -38,3 +38,32 @@ def for_each_task(func, arg = None): get_task_ptr_type(), "thread_group") if t == g: break + +def get_task_by_pid(pid): + def match_pid(t, arg): + if int(t['pid']) == arg['pid']: + arg['task'] = t + + arg = { 'pid': pid, 'task': None } + for_each_task(match_pid, arg) + + return arg['task'] + + +class LxTaskByPidFunc(gdb.Function): + __doc__ = "Find Linux task by PID and return the task_struct variable.\n" \ + "\n" \ + "$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and\n" \ + "return that task_struct variable which PID matches.\n" + + 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()
scripts/gdb/vmlinux-gdb.py +1 -0
@@ -22,3 +22,4 @@ else: import utils import symbols import dmesg + import task
Signature · 1 line
1.7.3.4
PATCH 9/13 scripts/gdb: Add is_target_arch helper
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
This helper caches to result of "show architecture" and matches the provided arch (sub-)string against that output.
--- scripts/gdb/utils.py | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-)
scripts/gdb/utils.py +9 -0
@@ -88,3 +88,12 @@ 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): + global target_arch + if target_arch == None: + target_arch = gdb.execute("show architecture", False, True) + return target_arch.find(arch) >= 0
Signature · 1 line
1.7.3.4
PATCH 10/13 scripts/gdb: Add internal helper and convenience function to retrieve thread_info
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
Add the internal helper get_thread_info that calculated the thread_info from a given task variable. Also export this service as a convenience function.
Note: ia64 version is untested.
--- scripts/gdb/task.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 39 insertions(+), 0 deletions(-)
scripts/gdb/task.py +39 -0
@@ -67,3 +67,42 @@ class LxTaskByPidFunc(gdb.Function): raise gdb.GdbError("No task of PID " + str(pid)) LxTaskByPidFunc() + + +thread_info_ptr_type = None + +def get_thread_info_ptr_type(): + global thread_info_ptr_type + if thread_info_ptr_type = None: + thread_info_ptr_type = get_type('struct thread_info').pointer() + return thread_info_ptr_type + +ia64_task_size = None + +def get_thread_info(task): + if is_target_arch("ia64"): + global ia64_task_size + if ia64_task_size = 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(get_thread_info_ptr_type()) + else: + thread_info = task['stack'].cast(get_thread_info_ptr_type()) + return thread_info.dereference() + + +class LxThreadInfoFunc (gdb.Function): + # Calculate Linux thread_info from task variable. + __doc__ = "Calculate Linux thread_info from task variable.\n" \ + "\n" \ + "$lx_thread_info(TASK): Given TASK, return the corresponding thread_info\n" \ + "variable.\n" + + def __init__(self): + super(LxThreadInfoFunc, self).__init__("lx_thread_info") + + def invoke(self, task): + return get_thread_info(task) + +LxThreadInfoFunc()
Signature · 1 line
1.7.3.4
PATCH 11/13 scripts/gdb: Add get_gdbserver_type helper
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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.
--- scripts/gdb/utils.py | 35 +++++++++++++++++++++++++++++++++++ 1 files changed, 35 insertions(+), 0 deletions(-)
scripts/gdb/utils.py +35 -0
@@ -97,3 +97,38 @@ def is_target_arch(arch): if target_arch == None: target_arch = gdb.execute("show architecture", False, True) return target_arch.find(arch) >= 0 + + +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", False, + True) != "" + except: + return False + + def probe_kgdb(): + try: + thread_info = gdb.execute("info thread 2", False, True) + return thread_info.find("shadowCPU0") >= 0 + except: + return False + + global gdbserver_type + if gdbserver_type == None: + if probe_qemu(): + gdbserver_type = GDBSERVER_QEMU + elif probe_kgdb(): + gdbserver_type = GDBSERVER_KGDB + if gdbserver_type != None: + gdb.events.exited.connect(exit_handler) + return gdbserver_type
Signature · 1 line
1.7.3.4
PATCH 12/13 scripts/gdb: Add internal helper and convenience function for per-cpu lookup
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
This function allows to obtain a per-cpu variable, either of the current or an explicitly specified CPU.
Note: sparc64 version is untested.
--- scripts/gdb/percpu.py | 61 ++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 62 insertions(+), 0 deletions(-) create mode 100644 scripts/gdb/percpu.py
scripts/gdb/percpu.py new file +61 -0
@@ -0,0 +1,61 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# per-cpu tools +# +# Copyright (c) 2011, 2012 Siemens AG +# +# Authors: +# Jan Kiszka <[email protected]> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from utils import * +from task import * + +MAX_CPUS = 4096 + +def get_current_cpu(): + if get_gdbserver_type() = GDBSERVER_QEMU: + return gdb.selected_thread().num - 1 + elif get_gdbserver_type() = GDBSERVER_KGDB: + tid = gdb.selected_thread().ptid[2] + if tid > (0x100000000 - MAX_CPUS - 2): + return 0x100000000 - tid - 2 + else: + return get_thread_info(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_name, cpu): + var = gdb.parse_and_eval("&" + var_name) + if cpu = -1: + cpu = get_current_cpu() + if is_target_arch("sparc:v9"): + offset = gdb.parse_and_eval("trap_block[" + str(cpu) + + "].__per_cpu_base") + else: + offset = gdb.parse_and_eval("__per_cpu_offset[" + str(cpu) + + "]") + pointer = var.cast(get_long_type()) + offset + return pointer.cast(var.type).dereference() + + +class PerCpu(gdb.Function): + __doc__ = "Return per-cpu variable.\n" \ + "\n" \ + "$lx_per_cpu(\"VAR\"[, CPU]): Return the per-cpu variable called VAR for the\n" \ + "given CPU number. If CPU is omitted, the CPU of the current context is used.\n" \ + "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): + return per_cpu(var_name.string(), cpu) + +PerCpu()
scripts/gdb/vmlinux-gdb.py +1 -0
@@ -23,3 +23,4 @@ else: import symbols import dmesg import task + import percpu
Signature · 1 line
1.7.3.4
PATCH 13/13 scripts/gdb: Add lx_current convenience function
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
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.
--- scripts/gdb/percpu.py | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
scripts/gdb/percpu.py +15 -0
@@ -59,3 +59,18 @@ class PerCpu(gdb.Function): return per_cpu(var_name.string(), cpu) PerCpu() + + +class LxCurrentFunc(gdb.Function): + __doc__ = "Return current task.\n" \ + "\n" \ + "$lx_current([CPU]): Return the per-cpu task variable for the given CPU\n" \ + "number. If CPU is omitted, the CPU of the current context is used." + + def __init__(self): + super(LxCurrentFunc, self).__init__("lx_current") + + def invoke(self, cpu = -1): + return per_cpu("current_task", cpu).dereference() + +LxCurrentFunc()
Signature · 1 line
1.7.3.4