2014-06-06 17:20:00

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 00/22] Add gdb python scripts as kernel debugging helpers

Some time passed since the last posting. Basically, this round comes
with two changes:
- make the code Python 3 compatible [Pantelis Koukousoulas]
- rebase over recent Linux version, resolving a trivial conflict

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.

I'm still hoping someone will move this forward via his tree towards
upstream. Please let me know if any changes are required to enable this.

Jan

CC: "David S. Miller" <[email protected]>
CC: Fenghua Yu <[email protected]>
CC: Kay Sievers <[email protected]>
CC: [email protected]
CC: [email protected]
CC: [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]>

Jan Kiszka (21):
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

Pantelis Koukousoulas (1):
scripts/gdb: Port to python3 / gdb7.7

Documentation/gdb-kernel-debugging.txt | 160 +++++++++++++++++++++++++++++++
Makefile | 6 +-
kernel/module.c | 9 +-
lib/Kconfig.debug | 11 +++
scripts/Makefile | 3 +-
scripts/gdb/Makefile | 11 +++
scripts/gdb/linux/Makefile | 9 ++
scripts/gdb/linux/__init__.py | 0
scripts/gdb/linux/cpus.py | 142 ++++++++++++++++++++++++++++
scripts/gdb/linux/dmesg.py | 65 +++++++++++++
scripts/gdb/linux/modules.py | 114 ++++++++++++++++++++++
scripts/gdb/linux/symbols.py | 166 +++++++++++++++++++++++++++++++++
scripts/gdb/linux/tasks.py | 110 ++++++++++++++++++++++
scripts/gdb/linux/utils.py | 156 +++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 30 ++++++
15 files changed, 988 insertions(+), 4 deletions(-)
create mode 100644 Documentation/gdb-kernel-debugging.txt
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/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.1.1.298.ge7eed54


2014-06-06 17:19:41

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 08/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:19:46

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 03/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:19:45

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 06/22] module: Do not inline do_init_module

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 079c461..a5a5734 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3009,8 +3009,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.1.1.298.ge7eed54

2014-06-06 17:20:52

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 11/22] 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.

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.1.1.298.ge7eed54

2014-06-06 17:21:16

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 19/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:19:39

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 07/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:18

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 02/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:49

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 20/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:57

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 18/22] 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 <[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("&current_task")
+ return per_cpu(var_ptr, cpu).dereference()
+
+
+LxCurrentFunc()
--
1.8.1.1.298.ge7eed54

2014-06-06 17:25:03

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 22/22] scripts/gdb: Port to python3 / gdb7.7

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.1.1.298.ge7eed54

2014-06-06 17:25:00

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 13/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:59

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 14/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:55

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 12/22] scripts/gdb: Add task iteration class

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.1.1.298.ge7eed54

2014-06-06 17:24:54

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 16/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:52

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 04/22] scripts/gdb: Add module iteration class

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.1.1.298.ge7eed54

2014-06-06 17:24:50

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 10/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:48

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 05/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:47

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 01/22] 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 <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 | 11 +++++++++++
scripts/gdb/linux/Makefile | 9 +++++++++
scripts/gdb/linux/__init__.py | 0
scripts/gdb/vmlinux-gdb.py | 23 +++++++++++++++++++++++
7 files changed, 61 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 cdaa5b6..029e700 100644
--- a/Makefile
+++ b/Makefile
@@ -861,6 +861,9 @@ endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
+ifdef CONFIG_GDB_SCRIPTS
+ $(Q)ln -fsn $(srctree)/scripts/gdb/vmlinux-gdb.py
+endif
+$(call if_changed,link-vmlinux)

# The actual objects are generated when descending,
@@ -1109,7 +1112,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 819ac51..89be8f6 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -143,6 +143,17 @@ config DEBUG_INFO_REDUCED
DEBUG_INFO build and compile times are reduced too.
Only works with newer gcc versions.

+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 1d07860..9361e96 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -37,6 +37,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 selinux
+subdir- += basic kconfig package selinux gdb
diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile
new file mode 100644
index 0000000..c0e2fd3
--- /dev/null
+++ b/scripts/gdb/Makefile
@@ -0,0 +1,11 @@
+subdir-y := linux
+
+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)
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
new file mode 100644
index 0000000..34ccd06
--- /dev/null
+++ b/scripts/gdb/linux/Makefile
@@ -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)
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.1.1.298.ge7eed54

2014-06-06 17:24:45

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 09/22] 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 <[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.1.1.298.ge7eed54

2014-06-06 17:24:44

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 21/22] scripts/gdb: Add basic documentation

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.1.1.298.ge7eed54

2014-06-06 17:24:43

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 15/22] 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.

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.1.1.298.ge7eed54

2014-06-06 17:24:42

by Jan Kiszka

[permalink] [raw]
Subject: [PATCH v8 17/22] 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.

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.1.1.298.ge7eed54

2014-06-09 16:59:38

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH v8 00/22] Add gdb python scripts as kernel debugging helpers


I looked through the patches and from a quick look they all look good to
me. I especially like the support for per cpu variables. I had a lot of
trouble with those in the past with various debugging setups.

Acked-by: Andi Kleen <[email protected]>

-Andi
--
[email protected] -- Speaking for myself only.