2016-03-30 12:01:10

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 00/12] gdb/scripts: Linux awareness debug commands

Hopefully, version 4 of this patchset now starts to look like a useful set of
additions. Whilst this set drops the lx-interrupts command, I am actually quite
pleased with the effectiveness of the $lx_radix_tree_lookup function which
will be more generically useful across multiple subsystems.

I have also dropped lx-meminfo (for now) as it is just not in a viable state
for integration, whereas the other patches in this set (hopefully) are.

I believe that being able to identify the memory profiles of a crashed system
is a useful debug feature, however currently the command is presenting more
inaccurate information than I deem acceptable, and it will need to be worked
on separately. I will likely post this current state separately for reference
or if anyone is interested in it, but I don't want it's state to block these
patches.

I have run these against PEP8 for both version 1.4.6 (Ubuntu 14.04), and 1.6.2
(Ubuntu 15.10) which hopefully means we won't come across the issues of the
previous set, but I did not have easy access to v1.5.7.

I haven't yet discovered why lx-mounts prints a root / root line, where
/proc/mounts does not, but the ST implementation appears to also print this.

For convenience, this patch set submission can be found at
http://git.linaro.org/people/kieran.bingham/linux.git gdb-scripts-v4

v4:
- list generator improved after review comments
- Alternate PEP8 warnings fixed
- lx_radix_tree_lookup added as function with example in Documentation
- lx-cpus command added
- lx-interrupts dropped
- lx-meminfo dropped from this set (TBC separately)

v3:
- dentry_name moved to utils package
- PEP8 warnings fixed
- CPU iterators added
- Radix Tree parser added
- lx-interrupts added
- lx_thread_info_by_pid added

v2:
- constants.py integrated to KBuild
- Linked lists added from Jeff Mahoneys implementation
- Modules converted to use list generators
- !CONFIG_MODULES supported gracefully
- lx-meminfo command improved for !CONFIG_MMU targets

Kieran Bingham (12):
scripts/gdb: Provide linux constants
scripts/gdb: Provide kernel list item generators
scripts/gdb: Convert modules usage to lists functions
scripts/gdb: Provide exception catching parser
scripts/gdb: Support !CONFIG_MODULES gracefully
scripts/gdb: Provide a dentry_name VFS path helper
scripts/gdb: Add io resource readers
scripts/gdb: Add mount point list command
scripts/gdb: Add cpu iterators
scripts/gdb: Add a Radix Tree Parser
scripts/gdb: Add documentation example for radix tree
scripts/gdb: Add lx_thread_info_by_pid helper

Documentation/gdb-kernel-debugging.txt | 21 +++++
Kbuild | 10 +++
scripts/gdb/linux/Makefile | 12 ++-
scripts/gdb/linux/constants.py.in | 59 +++++++++++++
scripts/gdb/linux/cpus.py | 38 ++++++++
scripts/gdb/linux/lists.py | 21 +++++
scripts/gdb/linux/modules.py | 22 +++--
scripts/gdb/linux/proc.py | 156 +++++++++++++++++++++++++++++++++
scripts/gdb/linux/radixtree.py | 97 ++++++++++++++++++++
scripts/gdb/linux/tasks.py | 19 ++++
scripts/gdb/linux/utils.py | 15 ++++
scripts/gdb/vmlinux-gdb.py | 2 +
12 files changed, 459 insertions(+), 13 deletions(-)
create mode 100644 scripts/gdb/linux/constants.py.in
create mode 100644 scripts/gdb/linux/radixtree.py

--
2.5.0


2016-03-30 12:01:23

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 12/12] scripts/gdb: Add lx_thread_info_by_pid helper

The tasks module already provides helpers to find the task struct by
pid, and the thread_info by task struct; however this is cumbersome to
utilise on the gdb commandline.

Wrap these two functionalities together in an extra single helper to
allow exploring the thread info, from a PID value

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/tasks.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py
index 862a4ae24d49..1bf949c43b76 100644
--- a/scripts/gdb/linux/tasks.py
+++ b/scripts/gdb/linux/tasks.py
@@ -114,3 +114,22 @@ variable."""


LxThreadInfoFunc()
+
+
+class LxThreadInfoByPidFunc (gdb.Function):
+ """Calculate Linux thread_info from task variable found by pid
+
+$lx_thread_info_by_pid(PID): Given PID, return the corresponding thread_info
+variable."""
+
+ def __init__(self):
+ super(LxThreadInfoByPidFunc, self).__init__("lx_thread_info_by_pid")
+
+ def invoke(self, pid):
+ task = get_task_by_pid(pid)
+ if task:
+ return get_thread_info(task.dereference())
+ else:
+ raise gdb.GdbError("No task of PID " + str(pid))
+
+LxThreadInfoByPidFunc()
--
2.5.0

2016-03-30 12:01:21

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 11/12] scripts/gdb: Add documentation example for radix tree

Provide a worked example for utilising the lx_radix_tree_lookup function

Cc: Jonathan Corbet <[email protected]>
Cc: [email protected]

Signed-off-by: Kieran Bingham <[email protected]>
---
Documentation/gdb-kernel-debugging.txt | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt
index 7050ce8794b9..4ab7d43d0754 100644
--- a/Documentation/gdb-kernel-debugging.txt
+++ b/Documentation/gdb-kernel-debugging.txt
@@ -139,6 +139,27 @@ Examples of using the Linux-provided gdb helpers
start_comm = "swapper/2\000\000\000\000\000\000"
}

+ o Dig into a radix tree data structure, such as the IRQ descriptors:
+ (gdb) print (struct irq_desc)$lx_radix_tree_lookup(irq_desc_tree, 18)
+ $6 = {
+ irq_common_data = {
+ state_use_accessors = 67584,
+ handler_data = 0x0 <__vectors_start>,
+ msi_desc = 0x0 <__vectors_start>,
+ affinity = {{
+ bits = {65535}
+ }}
+ },
+ irq_data = {
+ mask = 0,
+ irq = 18,
+ hwirq = 27,
+ common = 0xee803d80,
+ chip = 0xc0eb0854 <gic_data>,
+ domain = 0xee808000,
+ parent_data = 0x0 <__vectors_start>,
+ chip_data = 0xc0eb0854 <gic_data>
+ } <... trimmed ...>

List of commands and functions
------------------------------
--
2.5.0

2016-03-30 12:01:19

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 03/12] scripts/gdb: Convert modules usage to lists functions

Simplify the module list functions with the new list_for_each_entry
abstractions

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/modules.py | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 0a35d6dbfb80..6d9e1199ce57 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -13,7 +13,7 @@

import gdb

-from linux import cpus, utils
+from linux import cpus, utils, lists


module_type = utils.CachedType("struct module")
@@ -23,12 +23,9 @@ 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

- while entry != end_of_list:
- yield utils.container_of(entry, module_ptr_type, "list")
- entry = entry['next']
+ for module in lists.list_for_each_entry(modules, module_ptr_type, "list"):
+ yield module


def find_module_by_name(name):
@@ -80,17 +77,15 @@ class LxLsmod(gdb.Command):
size=str(layout['size']),
ref=str(module['refcnt']['counter'])))

- 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")
+ sources = module['source_list']
+ for use in lists.list_for_each_entry(sources, t, "source_list"):
gdb.write("{separator}{name}".format(
separator=" " if first else ",",
name=use['source']['name'].string()))
first = False
- entry = entry['next']
+
gdb.write("\n")


--
2.5.0

2016-03-30 12:01:17

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 04/12] scripts/gdb: Provide exception catching parser

If we attempt to read a value that is not available to GDB, an exception
is raised. Most of the time, this is a good thing; however on occasion
we will want to be able to determine if a symbol is available.

By catching the exception to simply return None, we can determine if we
tried to read an invalid value, without the exception taking our execution
context away from us

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/utils.py | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 0893b326a28b..dbe2ad78048c 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -154,3 +154,10 @@ def get_gdbserver_type():
if gdbserver_type is not None and hasattr(gdb, 'events'):
gdb.events.exited.connect(exit_handler)
return gdbserver_type
+
+
+def gdb_eval_or_none(expresssion):
+ try:
+ return gdb.parse_and_eval(expresssion)
+ except:
+ return None
--
2.5.0

2016-03-30 12:02:09

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 09/12] scripts/gdb: Add cpu iterators

The linux kernel provides macro's for iterating against values from the
cpu_list masks. By providing some commonly used masks, we can mirror the
kernels helper macros with easy to use generators.

Signed-off-by: Kieran Bingham <[email protected]>

---
Changes from v3:
- rebased to adapt for the cpu_*_mask indirection layer removals
- cpu_active_mask added for completeness
- print_cpus() converted to lx-cpus command
---
scripts/gdb/linux/cpus.py | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index 4297b83fedef..fc316fb3a3d4 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -100,6 +100,44 @@ def cpu_list(mask_name):
yield cpu


+def each_online_cpu():
+ for cpu in cpu_list("__cpu_online_mask"):
+ yield cpu
+
+
+def each_present_cpu():
+ for cpu in cpu_list("__cpu_present_mask"):
+ yield cpu
+
+
+def each_possible_cpu():
+ for cpu in cpu_list("__cpu_possible_mask"):
+ yield cpu
+
+
+def each_active_cpu():
+ for cpu in cpu_list("__cpu_active_mask"):
+ yield cpu
+
+
+class LxCpus(gdb.Command):
+ """List CPU status arrays
+
+Displays the known state of each CPU based on the kernel masks
+and can help identify the state of hotplugged CPUs"""
+
+ def __init__(self):
+ super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu())))
+ gdb.write("Present CPUs : {}\n".format(list(each_present_cpu())))
+ gdb.write("Online CPUs : {}\n".format(list(each_online_cpu())))
+ gdb.write("Active CPUs : {}\n".format(list(each_active_cpu())))
+
+LxCpus()
+
+
class PerCpu(gdb.Function):
"""Return per-cpu variable.

--
2.5.0

2016-03-30 12:02:07

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 10/12] scripts/gdb: Add a Radix Tree Parser

Linux makes use of the Radix Tree data structure to store pointers indexed
by integer values. This structure is utilised across many structures in
the kernel including the IRQ descriptor tables, and several filesystems.

This module provides a method to lookup values from a structure given
its head node.

Usage:

The function lx_radix_tree_lookup, must be given a symbol of type
struct radix_tree_root, and an index into that tree.

The object returned is a generic integer value, and must be cast
correctly to the type based on the storage in the data structure.

For example, to print the irq descriptor in the sparse irq_desc_tree at
index 18, try the following:

(gdb) print (struct irq_desc)$lx_radix_tree_lookup(irq_desc_tree, 18)

Signed-off-by: Kieran Bingham <[email protected]>

---
Changes since v3:
- Added a lookup function helper
- Provide type checking on lookup()
- Remove outdated comment regarding RCU dereference
---
scripts/gdb/linux/constants.py.in | 6 +++
scripts/gdb/linux/radixtree.py | 97 +++++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
3 files changed, 104 insertions(+)
create mode 100644 scripts/gdb/linux/radixtree.py

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 57213ad8cf75..07e6c2befe36 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -14,6 +14,7 @@

#include <linux/fs.h>
#include <linux/mount.h>
+#include <linux/radix-tree.h>

/* We need to stringify expanded macros so that they can be parsed */

@@ -51,3 +52,8 @@ LX_VALUE(MNT_NOATIME)
LX_VALUE(MNT_NODIRATIME)
LX_VALUE(MNT_RELATIME)

+/* linux/radix-tree.h */
+LX_VALUE(RADIX_TREE_INDIRECT_PTR)
+LX_GDBPARSED(RADIX_TREE_HEIGHT_MASK)
+LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
+LX_GDBPARSED(RADIX_TREE_MAP_MASK)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
new file mode 100644
index 000000000000..0fdef4e2971a
--- /dev/null
+++ b/scripts/gdb/linux/radixtree.py
@@ -0,0 +1,97 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# Radix Tree Parser
+#
+# Copyright (c) 2016 Linaro Ltd
+#
+# Authors:
+# Kieran Bingham <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import utils
+from linux import constants
+
+radix_tree_root_type = utils.CachedType("struct radix_tree_root")
+radix_tree_node_type = utils.CachedType("struct radix_tree_node")
+
+
+def is_indirect_ptr(node):
+ long_type = utils.get_long_type()
+ return (node.cast(long_type) & constants.LX_RADIX_TREE_INDIRECT_PTR)
+
+
+def indirect_to_ptr(node):
+ long_type = utils.get_long_type()
+ node_type = node.type
+ indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INDIRECT_PTR
+ return indirect_ptr.cast(node_type)
+
+
+def maxindex(height):
+ height = height & constants.LX_RADIX_TREE_HEIGHT_MASK
+ return gdb.parse_and_eval("height_to_maxindex["+str(height)+"]")
+
+
+def lookup(root, index):
+ if root.type == radix_tree_root_type.get_type().pointer():
+ root = root.dereference()
+ elif root.type != radix_tree_root_type.get_type():
+ raise gdb.GdbError("Must be struct radix_tree_root not {}"
+ .format(root.type))
+
+ node = root['rnode']
+ if node is 0:
+ return None
+
+ if not (is_indirect_ptr(node)):
+ if (index > 0):
+ return None
+ return node
+
+ node = indirect_to_ptr(node)
+
+ height = node['path'] & constants.LX_RADIX_TREE_HEIGHT_MASK
+ if (index > maxindex(height)):
+ return None
+
+ shift = (height-1) * constants.LX_RADIX_TREE_MAP_SHIFT
+
+ while True:
+ new_index = (index >> shift) & constants.LX_RADIX_TREE_MAP_MASK
+ slot = node['slots'][new_index]
+
+ node = slot.cast(node.type.pointer()).dereference()
+ if node is 0:
+ return None
+
+ shift -= constants.LX_RADIX_TREE_MAP_SHIFT
+ height -= 1
+
+ if (height <= 0):
+ break
+
+ return node
+
+
+class LxRadixTree(gdb.Function):
+ """ Lookup and return a node from a RadixTree.
+
+$lx_radix_tree_lookup(root_node [, index]): Return the node at the given index.
+If index is omitted, the root node is dereferenced and returned."""
+
+ def __init__(self):
+ super(LxRadixTree, self).__init__("lx_radix_tree_lookup")
+
+ def invoke(self, root, index=0):
+ result = lookup(root, index)
+ if result is None:
+ raise gdb.GdbError("No entry in tree at index {}".format(index))
+
+ return result
+
+LxRadixTree()
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 6e0b0afd888a..3a80ad6eecad 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -31,3 +31,4 @@ else:
import linux.lists
import linux.proc
import linux.constants
+ import linux.radixtree
--
2.5.0

2016-03-30 12:01:14

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 05/12] scripts/gdb: Support !CONFIG_MODULES gracefully

If CONFIG_MODULES is not enabled, lx-lsmod tries to find
a non-existent symbol and generates an unfriendly traceback:

(gdb) lx-lsmod
Address Module Size Used by
Traceback (most recent call last):
File "scripts/gdb/linux/modules.py", line 75, in invoke
for module in module_list():
File "scripts/gdb/linux/modules.py", line 24, in module_list
module_ptr_type = module_type.get_type().pointer()
File "scripts/gdb/linux/utils.py", line 28, in get_type
self._type = gdb.lookup_type(self._name)
gdb.error: No struct type named module.
Error occurred in Python command: No struct type named module.

Catch the error and return an empty module_list() for a clean command
output as follows:

(gdb) lx-lsmod
Address Module Size Used by
(gdb)

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/modules.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 6d9e1199ce57..61cc8a533cd8 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -21,8 +21,11 @@ module_type = utils.CachedType("struct module")

def module_list():
global module_type
+ modules = utils.gdb_eval_or_none("modules")
+ if modules is None:
+ return
+
module_ptr_type = module_type.get_type().pointer()
- modules = gdb.parse_and_eval("modules")

for module in lists.list_for_each_entry(modules, module_ptr_type, "list"):
yield module
--
2.5.0

2016-03-30 12:02:48

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 07/12] scripts/gdb: Add io resource readers

Provide iomem_resource and ioports_resource printers and command hooks

It can be quite interesting to halt the kernel as it's booting and check
to see this list as it is being populated.

It should be useful in the event that a kernel is not booting, you
can identify what memory resources have been registered

Signed-off-by: Kieran Bingham <[email protected]>

---
Changes since v1:
Permanent commit message updated
---
scripts/gdb/linux/proc.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)

diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index 6e6709c1830c..d855b2fd9a06 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -39,3 +39,60 @@ class LxVersion(gdb.Command):
gdb.write(gdb.parse_and_eval("linux_banner").string())

LxVersion()
+
+
+# Resource Structure Printers
+# /proc/iomem
+# /proc/ioports
+
+def get_resources(resource, depth):
+ while resource:
+ yield resource, depth
+
+ child = resource['child']
+ if child:
+ for res, deep in get_resources(child, depth + 1):
+ yield res, deep
+
+ resource = resource['sibling']
+
+
+def show_lx_resources(resource_str):
+ resource = gdb.parse_and_eval(resource_str)
+ width = 4 if resource['end'] < 0x10000 else 8
+ # Iterate straight to the first child
+ for res, depth in get_resources(resource['child'], 0):
+ start = int(res['start'])
+ end = int(res['end'])
+ gdb.write(" " * depth * 2 +
+ "{0:0{1}x}-".format(start, width) +
+ "{0:0{1}x} : ".format(end, width) +
+ res['name'].string() + "\n")
+
+
+class LxIOMem(gdb.Command):
+ """Identify the IO memory resource locations defined by the kernel
+
+Equivalent to cat /proc/iomem on a running target"""
+
+ def __init__(self):
+ super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ return show_lx_resources("iomem_resource")
+
+LxIOMem()
+
+
+class LxIOPorts(gdb.Command):
+ """Identify the IO port resource locations defined by the kernel
+
+Equivalent to cat /proc/ioports on a running target"""
+
+ def __init__(self):
+ super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ return show_lx_resources("ioport_resource")
+
+LxIOPorts()
--
2.5.0

2016-03-30 12:02:47

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 08/12] scripts/gdb: Add mount point list command

lx-mounts will identify current mount points based on the 'init_task'
namespace by default, as we do not yet have a kernel thread list
implementation to select the current running thread.

Optionally, a user can specify a PID to list from that process'
namespace

Signed-off-by: Kieran Bingham <[email protected]>

---
Changes from v1:
- Updated to use LX_ constant macros
- Adjusted for new list_for_each_item() function
- Removed unnecessary Null check in vfs['mnt_parent']
- Tested and not needed. It probably occurred in early testing
with a bad iterator

Changes since v2:
- dentry path helper moved to utils module

Changes since v3
- Adjust vfs list_for_each_entry code alignments
---
scripts/gdb/linux/constants.py.in | 21 +++++++++
scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
2 files changed, 120 insertions(+)

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 79d9d0092452..57213ad8cf75 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -12,7 +12,11 @@
*
*/

+#include <linux/fs.h>
+#include <linux/mount.h>
+
/* We need to stringify expanded macros so that they can be parsed */
+
#define STRING(x) #x
#define XSTRING(x) STRING(x)

@@ -30,3 +34,20 @@
<!-- end-c-headers -->

import gdb
+
+/* linux/fs.h */
+LX_VALUE(MS_RDONLY)
+LX_VALUE(MS_SYNCHRONOUS)
+LX_VALUE(MS_MANDLOCK)
+LX_VALUE(MS_DIRSYNC)
+LX_VALUE(MS_NOATIME)
+LX_VALUE(MS_NODIRATIME)
+
+/* linux/mount.h */
+LX_VALUE(MNT_NOSUID)
+LX_VALUE(MNT_NODEV)
+LX_VALUE(MNT_NOEXEC)
+LX_VALUE(MNT_NOATIME)
+LX_VALUE(MNT_NODIRATIME)
+LX_VALUE(MNT_RELATIME)
+
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index d855b2fd9a06..38b1f09d1cd9 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -12,6 +12,10 @@
#

import gdb
+from linux import constants
+from linux import utils
+from linux import tasks
+from linux import lists


class LxCmdLine(gdb.Command):
@@ -96,3 +100,98 @@ Equivalent to cat /proc/ioports on a running target"""
return show_lx_resources("ioport_resource")

LxIOPorts()
+
+
+# Mount namespace viewer
+# /proc/mounts
+
+def info_opts(lst, opt):
+ opts = ""
+ for key, string in lst.items():
+ if opt & key:
+ opts += string
+ return opts
+
+
+FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
+ constants.LX_MS_MANDLOCK: ",mand",
+ constants.LX_MS_DIRSYNC: ",dirsync",
+ constants.LX_MS_NOATIME: ",noatime",
+ constants.LX_MS_NODIRATIME: ",nodiratime"}
+
+MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
+ constants.LX_MNT_NODEV: ",nodev",
+ constants.LX_MNT_NOEXEC: ",noexec",
+ constants.LX_MNT_NOATIME: ",noatime",
+ constants.LX_MNT_NODIRATIME: ",nodiratime",
+ constants.LX_MNT_RELATIME: ",relatime"}
+
+mount_type = utils.CachedType("struct mount")
+mount_ptr_type = mount_type.get_type().pointer()
+
+
+class LxMounts(gdb.Command):
+ """Report the VFS mounts of the current process namespace.
+
+Equivalent to cat /proc/mounts on a running target
+An integer value can be supplied to display the mount
+values of that process namespace"""
+
+ def __init__(self):
+ super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
+
+ # Equivalent to proc_namespace.c:show_vfsmnt
+ # However, that has the ability to call into s_op functions
+ # whereas we cannot and must make do with the information we can obtain.
+ def invoke(self, arg, from_tty):
+ argv = gdb.string_to_argv(arg)
+ if len(argv) >= 1:
+ try:
+ pid = int(argv[0])
+ except:
+ raise gdb.GdbError("Provide a PID as integer value")
+ else:
+ pid = 1
+
+ task = tasks.get_task_by_pid(pid)
+ if not task:
+ raise gdb.GdbError("Couldn't find a process with PID {}"
+ .format(pid))
+
+ namespace = task['nsproxy']['mnt_ns']
+ if not namespace:
+ raise gdb.GdbError("No namespace for current process")
+
+ for vfs in lists.list_for_each_entry(namespace['list'],
+ mount_ptr_type, "mnt_list"):
+ devname = vfs['mnt_devname'].string()
+ devname = devname if devname else "none"
+
+ pathname = ""
+ parent = vfs
+ while True:
+ mntpoint = parent['mnt_mountpoint']
+ pathname = utils.dentry_name(mntpoint) + pathname
+ if (parent == parent['mnt_parent']):
+ break
+ parent = parent['mnt_parent']
+
+ if (pathname == ""):
+ pathname = "/"
+
+ superblock = vfs['mnt']['mnt_sb']
+ fstype = superblock['s_type']['name'].string()
+ s_flags = int(superblock['s_flags'])
+ m_flags = int(vfs['mnt']['mnt_flags'])
+ rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
+
+ gdb.write(
+ "{} {} {} {}{}{} 0 0\n"
+ .format(devname,
+ pathname,
+ fstype,
+ rd,
+ info_opts(FS_INFO, s_flags),
+ info_opts(MNT_INFO, m_flags)))
+
+LxMounts()
--
2.5.0

2016-03-30 12:03:28

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 06/12] scripts/gdb: Provide a dentry_name VFS path helper

Walk the VFS entries, pre-pending the iname strings to generate a full
VFS path name from a dentry.

Signed-off-by: Kieran Bingham <[email protected]>

---
Changes since v2:
- dentry_name separated out from proc module for use elsewhere
---
scripts/gdb/linux/utils.py | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index dbe2ad78048c..de03a6b505bb 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -161,3 +161,11 @@ def gdb_eval_or_none(expresssion):
return gdb.parse_and_eval(expresssion)
except:
return None
+
+
+def dentry_name(d):
+ parent = d['d_parent']
+ if parent == d or parent == 0:
+ return ""
+ p = dentry_name(d['d_parent']) + "/"
+ return p + d['d_iname'].string()
--
2.5.0

2016-03-30 12:03:45

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 02/12] scripts/gdb: Provide kernel list item generators

Facilitate linked-list items by providing a generator to return
the dereferenced, and type-cast objects from a kernel linked list

CC: Jeff Mahoney <[email protected]>

Signed-off-by: Kieran Bingham <[email protected]>
---
Changes since v1:
- items function removed, and replaced with Jeff Mahoney's cleaner
implementations of list_for_each, and list_for_each_entry

Changes since v3:
- GdbError strings formatted on single line
- list_head type corrected to head.type
- .format() style preferred over %
---
scripts/gdb/linux/lists.py | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
index 3a3775bc162b..2f335fbd86fd 100644
--- a/scripts/gdb/linux/lists.py
+++ b/scripts/gdb/linux/lists.py
@@ -18,6 +18,27 @@ from linux import utils
list_head = utils.CachedType("struct list_head")


+def list_for_each(head):
+ if head.type == list_head.get_type().pointer():
+ head = head.dereference()
+ elif head.type != list_head.get_type():
+ raise gdb.GdbError("Must be struct list_head not {}"
+ .format(head.type))
+
+ node = head['next'].dereference()
+ while node.address != head.address:
+ yield node.address
+ node = node['next'].dereference()
+
+
+def list_for_each_entry(head, gdbtype, member):
+ for node in list_for_each(head):
+ if node.type != list_head.get_type().pointer():
+ raise TypeError("Type {} found. Expected struct list_head *."
+ .format(node.type))
+ yield utils.container_of(node, gdbtype, member)
+
+
def list_check(head):
nb = 0
if (head.type == list_head.get_type().pointer()):
--
2.5.0

2016-03-30 12:03:44

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv4 01/12] scripts/gdb: Provide linux constants

Some macro's and defines are needed when parsing memory, and without
compiling the kernel as -g3 they are not available in the debug-symbols.

We use the pre-processor here to extract constants to a dedicated module
for the linux debugger extensions

Top level Kbuild is used to call in and generate the constants file,
while maintaining dependencies on autogenerated files in
include/generated

CC: [email protected]
CC: [email protected]

Signed-off-by: Kieran Bingham <[email protected]>
---
Changes since v1:

Kbuild:
- Call to generate constants_py with dependancy on generated files

scripts/gdb/linux/Makefile:
- Use call if_changed, and correct quiet invocation
- Annouce "GEN $@" instead of GDB PP

scripts/gdb/linux/constants.py.in:
- Updated to use better macro implementations for one line defines
---
Kbuild | 10 ++++++++++
scripts/gdb/linux/Makefile | 12 +++++++++++-
scripts/gdb/linux/constants.py.in | 32 ++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
4 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 scripts/gdb/linux/constants.py.in

diff --git a/Kbuild b/Kbuild
index f55cefd9bf29..3d0ae152af7c 100644
--- a/Kbuild
+++ b/Kbuild
@@ -5,6 +5,7 @@
# 2) Generate timeconst.h
# 3) Generate asm-offsets.h (may need bounds.h and timeconst.h)
# 4) Check for missing system calls
+# 5) Generate constants.py (may need bounds.h)

# Default sed regexp - multiline due to syntax constraints
define sed-y
@@ -96,5 +97,14 @@ quiet_cmd_syscalls = CALL $<
missing-syscalls: scripts/checksyscalls.sh $(offsets-file) FORCE
$(call cmd,syscalls)

+#####
+# 5) Generate constants for Python GDB integration
+#
+
+extra-$(CONFIG_GDB_SCRIPTS) += build_constants_py
+
+build_constants_py: $(obj)/$(timeconst-file) $(obj)/$(bounds-file)
+ @$(MAKE) $(build)=scripts/gdb/linux $@
+
# Keep these three files during make clean
no-clean-files := $(bounds-file) $(offsets-file) $(timeconst-file)
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
index 6cf1ecf61057..cd129e65d1ff 100644
--- a/scripts/gdb/linux/Makefile
+++ b/scripts/gdb/linux/Makefile
@@ -8,4 +8,14 @@ ifneq ($(KBUILD_SRC),)
endif
@:

-clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py)
+quiet_cmd_gen_constants_py = GEN $@
+ cmd_gen_constants_py = \
+ $(CPP) -E -x c -P $(c_flags) $< > $@ ;\
+ sed -i '1,/<!-- end-c-headers -->/d;' $@
+
+$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in
+ $(call if_changed,gen_constants_py)
+
+build_constants_py: $(obj)/constants.py
+
+clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) $(obj)/constants.py
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
new file mode 100644
index 000000000000..79d9d0092452
--- /dev/null
+++ b/scripts/gdb/linux/constants.py.in
@@ -0,0 +1,32 @@
+/*
+ * gdb helper commands and functions for Linux kernel debugging
+ *
+ * Kernel constants derived from include files.
+ *
+ * Copyright (c) 2016 Linaro Ltd
+ *
+ * Authors:
+ * Kieran Bingham <[email protected]>
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ *
+ */
+
+/* We need to stringify expanded macros so that they can be parsed */
+#define STRING(x) #x
+#define XSTRING(x) STRING(x)
+
+#define LX_VALUE(x) LX_##x = x
+#define LX_GDBPARSED(x) LX_##x = gdb.parse_and_eval(XSTRING(x))
+
+/*
+ * IS_ENABLED generates (a || b) which is not compatible with python
+ * We can only switch on configuration items we know are available
+ * Therefore - IS_BUILTIN() is more appropriate
+ */
+#define LX_CONFIG(x) LX_##x = IS_BUILTIN(x)
+
+/* The build system will take care of deleting everything above this marker */
+<!-- end-c-headers -->
+
+import gdb
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index d5943eca19cd..6e0b0afd888a 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -30,3 +30,4 @@ else:
import linux.cpus
import linux.lists
import linux.proc
+ import linux.constants
--
2.5.0

2016-03-30 21:03:00

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv4 01/12] scripts/gdb: Provide linux constants

On 2016-03-30 14:00, Kieran Bingham wrote:
> Some macro's and defines are needed when parsing memory, and without
> compiling the kernel as -g3 they are not available in the debug-symbols.
>
> We use the pre-processor here to extract constants to a dedicated module
> for the linux debugger extensions
>
> Top level Kbuild is used to call in and generate the constants file,
> while maintaining dependencies on autogenerated files in
> include/generated
>
> CC: [email protected]
> CC: [email protected]
>
> Signed-off-by: Kieran Bingham <[email protected]>
> ---
> Changes since v1:
>
> Kbuild:
> - Call to generate constants_py with dependancy on generated files
>
> scripts/gdb/linux/Makefile:
> - Use call if_changed, and correct quiet invocation
> - Annouce "GEN $@" instead of GDB PP
>
> scripts/gdb/linux/constants.py.in:
> - Updated to use better macro implementations for one line defines
> ---

Please only use a single separator line (i.e. remove this one), or the
history blob above makes it into the commit log. But there is no need to
resend if that will remain the only remark.

Jan

--
Siemens AG, Corporate Technology, CT RDA ITP SES-DE
Corporate Competence Center Embedded Linux