From 3ee7b3fa2cd0182628cca8d9bb5ce2d4722e8dc5 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:36 -0800 Subject: scripts/gdb: add infrastructure 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 -gdb.py when opening . 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. Signed-off-by: Jan Kiszka Acked-by: Michal Marek [kbuild stuff] Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 scripts/gdb/linux/Makefile (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile new file mode 100644 index 000000000000..6cf1ecf61057 --- /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 *.pyo $(if $(KBUILD_SRC),*.py) -- cgit v1.2.3 From 2b514827ef06fd69e1739e7f367712619dee7784 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:38 -0800 Subject: scripts/gdb: add cache for type objects 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/gdb/linux/utils.py (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py new file mode 100644 index 000000000000..f88361130e4c --- /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 +# +# 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 -- cgit v1.2.3 From b0fecd8c570310c5041035a94eda7a4610402ace Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:41 -0800 Subject: scripts/gdb: add container_of helper and convenience function 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 35 +++++++++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 2 ++ 2 files changed, 37 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index f88361130e4c..c9d705b62bfe 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 c1d90cea5288..649584105a72 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 -- cgit v1.2.3 From 850202e17df68c51593bab36a26c8d9279f8c029 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:44 -0800 Subject: scripts/gdb: add module iteration class Will soon be used for loading symbols, printing global variables or listing modules. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/modules.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 scripts/gdb/linux/modules.py (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py new file mode 100644 index 000000000000..8a65c3d5eecb --- /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 +# +# 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 -- cgit v1.2.3 From 66051720b84127cac32f34a30c91390c0ebf898f Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:47 -0800 Subject: scripts/gdb: add lx-symbols command 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- 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 (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py new file mode 100644 index 000000000000..bd21a9624428 --- /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 +# +# 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 649584105a72..0b0faa4cb5a0 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 -- cgit v1.2.3 From 82b41e3d6113291258c65281144bce946553dfed Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:52 -0800 Subject: scripts/gdb: add automatic symbol reloading on module insertion 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/symbols.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index bd21a9624428..139841fa7f70 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() -- cgit v1.2.3 From 7b599ef535a7faef53034fb7fb150b61057efe28 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:55 -0800 Subject: scripts/gdb: add internal helper and convenience function to look up a module 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/modules.py | 28 ++++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 29 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py index 8a65c3d5eecb..531f7632d03f 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 0b0faa4cb5a0..cf2e7161b280 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 -- cgit v1.2.3 From 7f994963745b9cea89a2816dae7cc3a1fc01adcc Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:46:58 -0800 Subject: scripts/gdb: add get_target_endianness helper 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index c9d705b62bfe..10a227bdb621 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 -- cgit v1.2.3 From 78e878172327b1b6aa6264b1d22f9a083f9ddaa6 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:01 -0800 Subject: scripts/gdb: add read_u16/32/64 helpers 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index 10a227bdb621..808a26596827 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) -- cgit v1.2.3 From ae7dbaad23f0346dc4e2476be63a9a5a376ee472 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:04 -0800 Subject: scripts/gdb: add lx-dmesg command 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. Signed-off-by: Jan Kiszka Cc: Kay Sievers Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- 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 (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py new file mode 100644 index 000000000000..7650f240ebcc --- /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 +# +# 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 cf2e7161b280..fa66d23ea563 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 -- cgit v1.2.3 From 7704d58a8509c65e3f7e4407ca2e5fa6360349c1 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:07 -0800 Subject: scripts/gdb: add task iteration class This class allows to iterate over all tasks of the target. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/tasks.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 scripts/gdb/linux/tasks.py (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py new file mode 100644 index 000000000000..cd259846ab4b --- /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 +# +# 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 -- cgit v1.2.3 From 4752871081ba4fbb3c539488a95e77d8011bfe49 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:10 -0800 Subject: scripts/gdb: add helper and convenience function to look up tasks 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/tasks.py | 27 +++++++++++++++++++++++++++ scripts/gdb/vmlinux-gdb.py | 1 + 2 files changed, 28 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py index cd259846ab4b..13bb97cd3312 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 fa66d23ea563..4d7eb2c409aa 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 -- cgit v1.2.3 From b24e2d21ac6efd23a67652870fac0cfb943d2264 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:12 -0800 Subject: scripts/gdb: add is_target_arch helper This helper caches to result of "show architecture" and matches the provided arch (sub-)string against that output. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index 808a26596827..71ee48ceb2b2 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 -- cgit v1.2.3 From cf7492e933c0df200f8fa46c3684e8bd20890ab2 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:15 -0800 Subject: scripts/gdb: add internal helper and convenience function to retrieve thread_info 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. Signed-off-by: Jan Kiszka Cc: Tony Luck Cc: Fenghua Yu Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/tasks.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py index 13bb97cd3312..63cd6c517e6d 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() -- cgit v1.2.3 From a4d86792c78d23257ab8ddd29ca16ce597361403 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:18 -0800 Subject: scripts/gdb: add get_gdbserver_type helper 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/utils.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py index 71ee48ceb2b2..a4a16403dc56 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 -- cgit v1.2.3 From fe7f9ed98dad611ceaf17403f1c5bfd016eadcaa Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:21 -0800 Subject: scripts/gdb: add internal helper and convenience function for per-cpu lookup This function allows to obtain a per-cpu variable, either of the current or an explicitly specified CPU. Note: sparc64 version is untested. Signed-off-by: Jan Kiszka Cc: "David S. Miller" Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- 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 (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py new file mode 100644 index 000000000000..18337e01ddef --- /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 +# +# 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 4d7eb2c409aa..48489285f119 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 -- cgit v1.2.3 From 116b47b4da037547585cebe4e3275ef68905d509 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:24 -0800 Subject: scripts/gdb: add lx_current convenience function 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 Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/cpus.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py index 18337e01ddef..b683da92f194 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() -- cgit v1.2.3 From 3d4cd9c94191f60cbb741cfbaa770d442c4680aa Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:27 -0800 Subject: scripts/gdb: add class to iterate over CPU masks Will be used first to count module references. It is optimized to read the mask only once per stop. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/cpus.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py index b683da92f194..c1441f23c0c2 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. -- cgit v1.2.3 From 5403727f985ba39967c899a56fff5bbd2c9a9f36 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:29 -0800 Subject: scripts/gdb: add lx-lsmod command This adds a lsmod-like command to list all currently loaded modules of the target. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/modules.py | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py index 531f7632d03f..e7c99e9c9620 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() -- cgit v1.2.3 From 276d97d90a2485f9a830a7a8242e4317b24c896f Mon Sep 17 00:00:00 2001 From: Pantelis Koukousoulas Date: Tue, 17 Feb 2015 13:47:35 -0800 Subject: scripts/gdb: port to python3 / gdb7.7 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 Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- 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(-) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py index c1441f23c0c2..8045871e2840 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 7650f240ebcc..3c947f0c5dad 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 e7c99e9c9620..2dbf6796ce4f 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 139841fa7f70..ae757fdf5ce6 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 63cd6c517e6d..0008e75f1c4f 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 a4a16403dc56..128c306db3ee 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 -- cgit v1.2.3 From 2478a8a15ccaddd68e84bb8791cd468f636673e9 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 17 Feb 2015 13:47:38 -0800 Subject: scripts/gdb: ignore byte-compiled python files Using the gdb scripts leaves byte-compiled python files in the scripts/ directory. These should be ignored by git. [jan.kiszka@siemens.com: drop redundant mrproper rule as suggested by Michal] Signed-off-by: Daniel Thompson Signed-off-by: Jan Kiszka Cc: Michal Marek Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .gitignore | 1 + scripts/gdb/linux/.gitignore | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 scripts/gdb/linux/.gitignore (limited to 'scripts/gdb/linux') diff --git a/.gitignore b/.gitignore index 9ac91060ea64..acb6afe6b7a3 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/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore new file mode 100644 index 000000000000..52e4e61140d1 --- /dev/null +++ b/scripts/gdb/linux/.gitignore @@ -0,0 +1,2 @@ +*.pyc +*.pyo -- cgit v1.2.3 From 54e2289a34e13d956acb841a00c3a6f06aced3f9 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Tue, 17 Feb 2015 13:47:41 -0800 Subject: scripts/gdb: use a generator instead of iterator for task list 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 Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/tasks.py | 50 +++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py index 0008e75f1c4f..e2037d9bb7eb 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 -- cgit v1.2.3 From fffb944c4e6d3882a7a15c494bd4cde36c68c39c Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:44 -0800 Subject: scripts/gdb: convert ModuleList to generator function Analogously to the task list, convert the module list to a generator function. It noticeably simplifies the code. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/modules.py | 33 +++++++++++---------------------- scripts/gdb/linux/symbols.py | 2 +- 2 files changed, 12 insertions(+), 23 deletions(-) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py index 2dbf6796ce4f..6d497229d026 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 ae757fdf5ce6..bf05e451c586 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: -- cgit v1.2.3 From a77e15e8b4ccaf43b3a527cbb882bf816c5a629d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:47 -0800 Subject: scripts/gdb: convert CpuList to generator function Yet another code simplification. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/cpus.py | 71 ++++++++++++++++++++------------------------ scripts/gdb/linux/modules.py | 2 +- 2 files changed, 33 insertions(+), 40 deletions(-) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py index 8045871e2840..4297b83fedef 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 6d497229d026..a1504c4f1900 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'] -- cgit v1.2.3 From a9c5bcfa43420365535ef42c1ff1e83aa056f25e Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 17 Feb 2015 13:47:52 -0800 Subject: scripts/gdb: disable pagination while printing from breakpoint handler While reporting the (refreshed) list of modules on automatic updates we may hit the page boundary of the output console and cause a stop if pagination is enabled. However, gdb does not accept user input while running over the breakpoint handler. So we get stuck, and the user is forced to interrupt gdb. Resolve this by disabling pagination during automatic symbol updates. We restore the user's configuration once done. Signed-off-by: Jan Kiszka Cc: Thomas Gleixner Cc: Jason Wessel Cc: Andi Kleen Cc: Ben Widawsky Cc: Borislav Petkov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/symbols.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index bf05e451c586..cd5bea965d4e 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -34,12 +34,23 @@ if hasattr(gdb, 'Breakpoint'): # 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 -- cgit v1.2.3 From 586a1a125eb157f0c2b9925ba878f24f4fe0c667 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 27 Feb 2015 15:51:53 -0800 Subject: scripts/gdb: add empty package initialization script This got lost during the initial merge process: Python requires an __init__.py script, even if empty, in order to accept a directory as package. Add it, this time as a non-empty file. Signed-off-by: Jan Kiszka Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/gdb/linux/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 scripts/gdb/linux/__init__.py (limited to 'scripts/gdb/linux') diff --git a/scripts/gdb/linux/__init__.py b/scripts/gdb/linux/__init__.py new file mode 100644 index 000000000000..4680fb176337 --- /dev/null +++ b/scripts/gdb/linux/__init__.py @@ -0,0 +1 @@ +# nothing to do for the initialization of this package -- cgit v1.2.3