linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands
@ 2016-01-20 11:15 Kieran Bingham
  2016-01-20 11:15 ` [PATCH 1/5] scripts/gdb: Provide linux constants Kieran Bingham
                   ` (4 more replies)
  0 siblings, 5 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

Hi Jan,

Following on from the initial commands provided earlier, I wanted to send an
early set of patches for review, feedback and iteration.

There is an /proc/interrupts command on the way too but that will be later,
and I wanted to get these started.

Particular commentry is either in the comments area of the patch, or in the
code itself.

A particular coding-style question is on the docstrings. Most of the existing
commands do not indent their multiline docstrings. Is this preferred?

And also, what is your take on line-length.
Should it be hard and fast always < 79, or OK > if it keeps things readable?:

pep8 proc.py
proc.py:321:80: E501 line too long (80 > 79 characters)
proc.py:363:80: E501 line too long (80 > 79 characters)
proc.py:365:80: E501 line too long (82 > 79 characters)
proc.py:378:80: E501 line too long (80 > 79 characters)

These 4 lines come from [PATCH 5/5] scripts/gdb: Add meminfo command
If I move the helper class functions out to file scope, that could trim
the 'self.' keyword, and make the lines a little shorter ...

Anyway, I look forward to hearing your comments, and any input from any other
interested parties too!

If anyone has any ideas for commands that they would like to see available,
or useful kernel parsers, now is a great time to get involved!


Regards
--
Kieran

Kieran Bingham (5):
  scripts/gdb: Provide linux constants
  scripts/gdb: Provide a kernel list item generator
  scripts/gdb: Add io resource readers
  scripts/gdb: Add mount point list command
  scripts/gdb: Add meminfo command

 scripts/gdb/linux/Makefile        |   9 +-
 scripts/gdb/linux/constants.py.in |  65 ++++++++
 scripts/gdb/linux/lists.py        |   9 +
 scripts/gdb/linux/proc.py         | 340 ++++++++++++++++++++++++++++++++++++++
 scripts/gdb/vmlinux-gdb.py        |   1 +
 5 files changed, 422 insertions(+), 2 deletions(-)
 create mode 100644 scripts/gdb/linux/constants.py.in

-- 
2.5.0

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/5] scripts/gdb: Provide linux constants
  2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
@ 2016-01-20 11:15 ` Kieran Bingham
  2016-01-23 15:05   ` Jan Kiszka
  2016-01-20 11:15 ` [PATCH 2/5] scripts/gdb: Provide a kernel list item generator Kieran Bingham
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

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

Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
---

I've added a 'constants.py' which is automatically generated. This allows
values not available to the debugger, through #defines to be provided to
our scripts.

The alternative method for this is to create a c-object file to obtain values
through symbols instead, and compile segments with -g3 to include macro
definitions in the debug-info.

I'd appreciate your thoughts on these options.

 scripts/gdb/linux/Makefile        |  9 +++++++--
 scripts/gdb/linux/constants.py.in | 22 ++++++++++++++++++++++
 scripts/gdb/vmlinux-gdb.py        |  1 +
 3 files changed, 30 insertions(+), 2 deletions(-)
 create mode 100644 scripts/gdb/linux/constants.py.in

diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile
index 6cf1ecf61057..50864f408ca8 100644
--- a/scripts/gdb/linux/Makefile
+++ b/scripts/gdb/linux/Makefile
@@ -2,10 +2,15 @@ always := gdb-scripts
 
 SRCTREE := $(shell cd $(srctree) && /bin/pwd)
 
-$(obj)/gdb-scripts:
+$(obj)/gdb-scripts: $(obj)/constants.py
 ifneq ($(KBUILD_SRC),)
 	$(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj)
 endif
 	@:
 
-clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py)
+$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in
+	@echo "  GDB PP  $@"
+	@$(CPP) -E -x c -P $(c_flags) $< > $@
+	@sed -i '1,/<!-- end-c-headers -->/d;' $@
+
+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..d84084ac945b
--- /dev/null
+++ b/scripts/gdb/linux/constants.py.in
@@ -0,0 +1,22 @@
+/*
+ * gdb helper commands and functions for Linux kernel debugging
+ *
+ *  Kernel constants derived from include files.
+ *
+ * Copyright (c) 2016 Linaro Ltd
+ *
+ * Authors:
+ *  Kieran Bingham <kieran.bingham@linaro.org>
+ *
+ * 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)
+
+/* 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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/5] scripts/gdb: Provide a kernel list item generator
  2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
  2016-01-20 11:15 ` [PATCH 1/5] scripts/gdb: Provide linux constants Kieran Bingham
@ 2016-01-20 11:15 ` Kieran Bingham
  2016-01-23 15:08   ` Jan Kiszka
  2016-01-20 11:15 ` [PATCH 3/5] scripts/gdb: Add io resource readers Kieran Bingham
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

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

Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
---

This is quite a useful wrapper to faciliate looping on lists.
It is sort of equivalent to the list_for_each_entry macro.

Let me know if it should be renamed, or live elsewhere.

 scripts/gdb/linux/lists.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
index 3a3775bc162b..d2c6ce165cb1 100644
--- a/scripts/gdb/linux/lists.py
+++ b/scripts/gdb/linux/lists.py
@@ -18,6 +18,15 @@ from linux import utils
 list_head = utils.CachedType("struct list_head")
 
 
+def items(list_type, list_location, item_list):
+    """items Generator return items from a kernel linked list"""
+    item_list_head = item_list
+    next_item = item_list_head['next'].dereference()
+    while next_item != item_list_head:
+        yield utils.container_of(next_item, list_type, list_location)
+        next_item = next_item['next'].dereference()
+
+
 def list_check(head):
     nb = 0
     if (head.type == list_head.get_type().pointer()):
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/5] scripts/gdb: Add io resource readers
  2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
  2016-01-20 11:15 ` [PATCH 1/5] scripts/gdb: Provide linux constants Kieran Bingham
  2016-01-20 11:15 ` [PATCH 2/5] scripts/gdb: Provide a kernel list item generator Kieran Bingham
@ 2016-01-20 11:15 ` Kieran Bingham
  2016-01-23 15:12   ` Jan Kiszka
  2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
  2016-01-20 11:15 ` [PATCH 5/5] scripts/gdb: Add meminfo command Kieran Bingham
  4 siblings, 1 reply; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

Provide iomem_resource and ioports_resource printers and command hooks

Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>

---

These two readers are a useful extract of kernel information.
This shows the power of having these commands in gdb/scripts as you can
halt a kernel as it's booting and read these as the structures grow.

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



 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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
                   ` (2 preceding siblings ...)
  2016-01-20 11:15 ` [PATCH 3/5] scripts/gdb: Add io resource readers Kieran Bingham
@ 2016-01-20 11:15 ` Kieran Bingham
  2016-01-20 11:42   ` Jan Kiszka
                     ` (2 more replies)
  2016-01-20 11:15 ` [PATCH 5/5] scripts/gdb: Add meminfo command Kieran Bingham
  4 siblings, 3 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

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

This is somewhat limited vs the /proc/mounts file, as that calls into
vfs hooks through the s_op functions to obtain extra information.

Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
---


In this patch, I'm interested in your opinions on coding styles.
Would you prefer to see the function helpers, (dentry_name, info_opts) where
they are, or inside the command as class members? Or perhaps defined in utils?

This also shows where I need to take constant information from the kernel.
In this case, they are simple numerical bitflags, and unlikely to change but
I didn't want to duplicate their values.


 scripts/gdb/linux/constants.py.in |  21 ++++++++
 scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 131 insertions(+)

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index d84084ac945b..739a15d2e984 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)
 
@@ -20,3 +24,20 @@
 <!-- end-c-headers -->
 
 import gdb
+
+/* linux/fs.h */
+LX_MS_RDONLY = MS_RDONLY
+LX_MS_SYNCHRONOUS = MS_SYNCHRONOUS
+LX_MS_MANDLOCK = MS_MANDLOCK
+LX_MS_DIRSYNC = MS_DIRSYNC
+LX_MS_NOATIME = MS_NOATIME
+LX_MS_NODIRATIME = MS_NODIRATIME
+
+/* linux/mount.h */
+LX_MNT_NOSUID = MNT_NOSUID
+LX_MNT_NODEV = MNT_NODEV
+LX_MNT_NOEXEC = MNT_NOEXEC
+LX_MNT_NOATIME = MNT_NOATIME
+LX_MNT_NODIRATIME = MNT_NODIRATIME
+LX_MNT_RELATIME = MNT_RELATIME
+
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index d855b2fd9a06..b79ce2a33a3d 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,109 @@ Equivalent to cat /proc/ioports on a running target"""
         return show_lx_resources("ioport_resource")
 
 LxIOPorts()
+
+
+# Mount namespace viewer
+#  /proc/mounts
+
+
+def dentry_name(d):
+    if d['d_parent'] == d:
+        return ""
+    p = dentry_name(d['d_parent']) + "/"
+    return p + d['d_iname'].string()
+
+
+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.items(mount_ptr_type, "mnt_list", namespace['list']):
+            # There appears to be a null entry at the end of the list...
+            if not vfs['mnt_parent']:
+                break
+
+            devname = vfs['mnt_devname'].string()
+            devname = devname if devname else "none"
+
+            pathname = ""
+            parent = vfs
+            while True:
+                mntpoint = parent['mnt_mountpoint']
+                pathname = 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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 5/5] scripts/gdb: Add meminfo command
  2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
                   ` (3 preceding siblings ...)
  2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
@ 2016-01-20 11:15 ` Kieran Bingham
  2016-01-23 15:21   ` Jan Kiszka
  4 siblings, 1 reply; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:15 UTC (permalink / raw)
  To: jan.kiszka
  Cc: Kieran Bingham, linux-kernel, maxime.coquelin, peter.griffin, lee.jones

Provide an equivalent of /proc/meminfo which should be available from
core dumps, or crashed kernels. This should allow a debugger to identify
if memory pressures were applicable in the instance of their issue

Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
---

This command has proved to be much more difficult that I first thought it
would be!

It also poses a couple of interesting issues, which is why I submit this
patch in a much more unfinished form.

The meminfo implementation at fs/proc/meminfo.c makes several function calls
to collate information, which makes duplicating here more difficult.

I suspect the best option here is to not present lines of which we can not
obtain accurate data for, (much better than presenting inaccurate information)

Would this go in agreement with you?

Finally, do you have any ideas on the best way to manage code which
is #ifdef'd on kernel config options? (#ifdef CONFIG_HIGHMEM for example).

In a similar vein to the constants.py, I considered that we could iterate all
of the kernel configuration options and store them in a dictionary some how.

That may be awkward, however, and I wondered what ideas anyone had!


 scripts/gdb/linux/constants.py.in |  22 +++++
 scripts/gdb/linux/proc.py         | 173 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 195 insertions(+)

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 739a15d2e984..306bd601ae4e 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -12,8 +12,15 @@
  *
  */
 
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/thread_info.h>
+
 #include <linux/fs.h>
+#include <linux/swap.h>
 #include <linux/mount.h>
+#include <linux/vmalloc.h>
+
 
 /* We need to stringify expanded macros so that they can be parsed */
 
@@ -41,3 +48,18 @@ LX_MNT_NOATIME = MNT_NOATIME
 LX_MNT_NODIRATIME = MNT_NODIRATIME
 LX_MNT_RELATIME = MNT_RELATIME
 
+/* asm/page.h */
+LX_PAGE_SHIFT = XSTRING(PAGE_SHIFT)
+lx_page_shift = gdb.parse_and_eval(LX_PAGE_SHIFT)
+
+/* asm/thread_info.h */
+LX_THREAD_SIZE = XSTRING(THREAD_SIZE)
+lx_thread_size = gdb.parse_and_eval(LX_THREAD_SIZE)
+
+/* linux/vmalloc.h */
+LX_VMALLOC_TOTAL = XSTRING(VMALLOC_TOTAL)
+lx_vmalloc_total = gdb.parse_and_eval(LX_VMALLOC_TOTAL)
+
+/* linux/swap.h */
+LX_MAX_SWAPFILES = XSTRING(MAX_SWAPFILES)
+lx_max_swapfiles = gdb.parse_and_eval(LX_MAX_SWAPFILES)
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index b79ce2a33a3d..ac9e1aac2403 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -206,3 +206,176 @@ values of that process namespace"""
                         info_opts(MNT_INFO, m_flags)))
 
 LxMounts()
+
+
+bdev_type = utils.CachedType("struct block_device")
+
+
+class LxMeminfo(gdb.Command):
+    """ Identify the memory usage, statistics, and availability
+
+Equivalent to cat /proc/meminfo on a running target """
+
+    def __init__(self):
+        super(LxMeminfo, self).__init__("lx-meminfo", gdb.COMMAND_DATA)
+
+    def K(self, val):
+        # Convert from PAGES to KB
+        return int(val << (constants.lx_page_shift - 10))
+
+    def page_K(self, remote_value):
+        # Obtain page value, and Convert from PAGES to KB
+        val = int(gdb.parse_and_eval(remote_value))
+        return self.K(val)
+
+    def gps(self, enum_zone_stat_item):
+        # Access the Global Page State structure
+        # I would prefer to read this structure in one go and then index
+        # from the enum. But we can't determine the enum values with out
+        # a call to GDB anyway so we may as well take the easy route and
+        # get the value.
+        remote_value = "vm_stat[" + enum_zone_stat_item + "].counter"
+        return int(gdb.parse_and_eval(remote_value))
+
+    def gps_K(self, enum_zone_stat_item):
+        return self.K(self.gps(enum_zone_stat_item))
+
+    def nr_blockdev_pages(self):
+        bdevs_head = gdb.parse_and_eval("all_bdevs")
+        pages = 0
+        for bdev in lists.items(bdev_type, "bd_list", bdevs_head):
+            pages += bdev['bd_inode']['i_mapping']['nrpages']
+        return pages
+
+    def total_swapcache_pages(self):
+        pages = 0
+        for i in range(0, constants.lx_max_swapfiles):
+            swap_space = "swapper_spaces[" + str(i) + "].nrpages"
+            pages += int(gdb.parse_and_eval(swap_space))
+        return pages
+
+    def vm_commit_limit(self, totalram_pages):
+        overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
+        overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
+        total_swap_pages = int(gdb.parse_and_eval("total_swap_pages"))
+        hugetlb_total_pages = 0  # hugetlb_total_pages()!!
+
+        if overcommit:
+            allowed = overcommit >> (constants.lx_page_shift - 10)
+        else:
+            allowed = ((totalram_pages - hugetlb_total_pages *
+                       overcommit_ratio / 100))
+
+        allowed += total_swap_pages
+        return allowed
+
+    # Main lx-meminfo command execution
+    def invoke(self, arg, from_tty):
+        totalram = int(gdb.parse_and_eval("totalram_pages"))
+        freeram = self.gps("NR_FREE_PAGES")
+        reclaimable = self.gps("NR_SLAB_RECLAIMABLE")
+        unreclaimable = self.gps("NR_SLAB_UNRECLAIMABLE")
+        slab = reclaimable + unreclaimable
+        # for_each_zone(zone)
+        #     wmark_low += zone->watermark[WMARK_LOW];
+        wmark_low = 0   # Zone parsing is unimplemented
+
+        available = freeram - wmark_low
+        available += reclaimable - min(reclaimable / 2, wmark_low)
+
+        bufferram = self.nr_blockdev_pages()
+        total_swapcache_pages = self.total_swapcache_pages()
+
+        file_pages = self.gps("NR_FILE_PAGES")
+        cached = file_pages - total_swapcache_pages - bufferram
+
+        # LRU Pages
+        active_pages_anon = self.gps("NR_ACTIVE_ANON")
+        inactive_pages_anon = self.gps("NR_INACTIVE_ANON")
+        active_pages_file = self.gps("NR_ACTIVE_FILE")
+        inactive_pages_file = self.gps("NR_INACTIVE_FILE")
+        unevictable_pages = self.gps("NR_UNEVICTABLE")
+        active_pages = active_pages_anon + active_pages_file
+        inactive_pages = inactive_pages_anon + inactive_pages_file
+
+        totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
+        # We can't run this on a core dump file ...
+        # if running target ()
+        freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
+        # else freehigh = 0
+
+        kernelstack = int(self.gps("NR_KERNEL_STACK") *
+                          constants.lx_thread_size / 1024)
+
+        commitlimit = self.vm_commit_limit(totalram)
+        committed_as = int(gdb.parse_and_eval("vm_committed_as.count"))
+
+        vmalloc_total = int(constants.lx_vmalloc_total >> 10)
+
+        gdb.write(
+            "MemTotal:       {:8d} kB\n".format(self.K(totalram)) +
+            "MemFree:        {:8d} kB\n".format(self.K(freeram)) +
+            "MemAvailable:   {:8d} kB\n".format(self.K(available)) +
+            "Buffers:        {:8d} kB\n".format(self.K(bufferram)) +
+            "Cached:         {:8d} kB\n".format(self.K(cached)) +
+            "SwapCached:     {:8d} kB\n".format(self.K(total_swapcache_pages)) +
+            "Active:         {:8d} kB\n".format(self.K(active_pages)) +
+            "Inactive:       {:8d} kB\n".format(self.K(inactive_pages)) +
+            "Active(anon):   {:8d} kB\n".format(self.K(active_pages_anon)) +
+            "Inactive(anon): {:8d} kB\n".format(self.K(inactive_pages_anon)) +
+            "Active(file):   {:8d} kB\n".format(self.K(active_pages_file)) +
+            "Inactive(file): {:8d} kB\n".format(self.K(inactive_pages_file)) +
+            "Unevictable:    {:8d} kB\n".format(self.K(unevictable_pages)) +
+            "Mlocked:        {:8d} kB\n".format(self.gps_K("NR_MLOCK"))
+            )
+        # ifdef CONFIG_HIGHMEM || core dump?
+        gdb.write(
+            "HighTotal:      {:8d} kB\n".format(self.K(totalhigh)) +
+            "HighFree:       {:8d} kB\n".format(self.K(freehigh)) +
+            "LowTotal:       {:8d} kB\n".format(self.K(totalram-totalhigh)) +
+            "LowFree:        {:8d} kB\n".format(self.K(freeram-freehigh))
+            )
+        # endif
+        # ifndef CONFIG_MMU
+        # gdb.write(
+        #    mmap_pages_allocated
+        #    )
+        # endif
+        gdb.write(
+            "SwapTotal:      {:8d} kB\n".format(self.K(0)) +
+            "SwapFree:       {:8d} kB\n".format(self.K(0)) +
+            "Dirty:          {:8d} kB\n".format(self.gps_K("NR_FILE_DIRTY")) +
+            "Writeback:      {:8d} kB\n".format(self.gps_K("NR_WRITEBACK")) +
+            "AnonPages:      {:8d} kB\n".format(self.gps_K("NR_ANON_PAGES")) +
+            "Mapped:         {:8d} kB\n".format(self.gps_K("NR_FILE_MAPPED")) +
+            "Shmem:          {:8d} kB\n".format(self.gps_K("NR_SHMEM")) +
+            "Slab:           {:8d} kB\n".format(self.K(slab)) +
+            "SReclaimable:   {:8d} kB\n".format(self.K(reclaimable)) +
+            "SUnreclaim:     {:8d} kB\n".format(self.K(unreclaimable)) +
+            "KernelStack:    {:8d} kB\n".format(kernelstack) +
+            "PageTables:     {:8d} kB\n".format(self.gps_K("NR_PAGETABLE"))
+            )
+
+        #  if CONFIG_QUICKLIST
+        #   "Quicklists:     {:8d} kB\n".format(self.K(quicklist)))
+
+        gdb.write(
+            "NFS_Unstable:   {:8d} kB\n".format(self.gps_K("NR_UNSTABLE_NFS")) +
+            "Bounce:         {:8d} kB\n".format(self.gps_K("NR_BOUNCE")) +
+            "WritebackTmp:   {:8d} kB\n".format(self.gps_K("NR_WRITEBACK_TEMP")) +
+            "CommitLimit:    {:8d} kB\n".format(self.K(commitlimit)) +
+            "Committed_AS:   {:8d} kB\n".format(self.K(committed_as)) +
+            "VmallocTotal:   {:8d} kB\n".format(vmalloc_total) +
+            "VmallocUsed:    {:8d} kB\n".format(0) +
+            "VmallocChunk:   {:8d} kB\n".format(0)
+            )
+        # if CONFIG_MEMORY_FAILURE
+        #   "HardwareCorrupted: %5lu kB\n"
+        # ifdef CONFIG_CMA
+        totalcma_pages = int(gdb.parse_and_eval("totalcma_pages"))
+        gdb.write(
+            "CmaTotal:       {:8d} kB\n".format(self.K(totalcma_pages)) +
+            "CmaFree:        {:8d} kB\n".format(self.gps_K("NR_FREE_CMA_PAGES"))
+            )
+
+LxMeminfo()
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
@ 2016-01-20 11:42   ` Jan Kiszka
  2016-01-20 11:51     ` Kieran Bingham
  2016-01-23 12:34   ` Jan Kiszka
  2016-01-23 15:27   ` Jan Kiszka
  2 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-20 11:42 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

Hi Kieran,

just a couple of quick comments:

On 2016-01-20 12:15, Kieran Bingham wrote:
> 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.

current_task? See LxCurrentFunc, could be factored out if usable. Or
what are you looking for?

> 
> Optionally, a user can specify a PID to list from that process'
> namespace
> 
> This is somewhat limited vs the /proc/mounts file, as that calls into
> vfs hooks through the s_op functions to obtain extra information.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> 
> In this patch, I'm interested in your opinions on coding styles.
> Would you prefer to see the function helpers, (dentry_name, info_opts) where
> they are, or inside the command as class members? Or perhaps defined in utils?

Need to look into this.

> 
> This also shows where I need to take constant information from the kernel.
> In this case, they are simple numerical bitflags, and unlikely to change but
> I didn't want to duplicate their values.

Maybe we can generate python files with the required constants from the
C headers during build? Similar to asm-offsets.c stuff.

> 
> 
>  scripts/gdb/linux/constants.py.in |  21 ++++++++
>  scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 131 insertions(+)
> 
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index d84084ac945b..739a15d2e984 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)
>  
> @@ -20,3 +24,20 @@
>  <!-- end-c-headers -->
>  
>  import gdb
> +
> +/* linux/fs.h */
> +LX_MS_RDONLY = MS_RDONLY
> +LX_MS_SYNCHRONOUS = MS_SYNCHRONOUS
> +LX_MS_MANDLOCK = MS_MANDLOCK
> +LX_MS_DIRSYNC = MS_DIRSYNC
> +LX_MS_NOATIME = MS_NOATIME
> +LX_MS_NODIRATIME = MS_NODIRATIME
> +
> +/* linux/mount.h */
> +LX_MNT_NOSUID = MNT_NOSUID
> +LX_MNT_NODEV = MNT_NODEV
> +LX_MNT_NOEXEC = MNT_NOEXEC
> +LX_MNT_NOATIME = MNT_NOATIME
> +LX_MNT_NODIRATIME = MNT_NODIRATIME
> +LX_MNT_RELATIME = MNT_RELATIME
> +
> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
> index d855b2fd9a06..b79ce2a33a3d 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,109 @@ Equivalent to cat /proc/ioports on a running target"""
>          return show_lx_resources("ioport_resource")
>  
>  LxIOPorts()
> +
> +
> +# Mount namespace viewer
> +#  /proc/mounts
> +
> +
> +def dentry_name(d):
> +    if d['d_parent'] == d:
> +        return ""
> +    p = dentry_name(d['d_parent']) + "/"
> +    return p + d['d_iname'].string()
> +
> +
> +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.items(mount_ptr_type, "mnt_list", namespace['list']):
> +            # There appears to be a null entry at the end of the list...
> +            if not vfs['mnt_parent']:
> +                break
> +
> +            devname = vfs['mnt_devname'].string()
> +            devname = devname if devname else "none"
> +
> +            pathname = ""
> +            parent = vfs
> +            while True:
> +                mntpoint = parent['mnt_mountpoint']
> +                pathname = 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()
> 

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:42   ` Jan Kiszka
@ 2016-01-20 11:51     ` Kieran Bingham
  2016-01-20 12:08       ` Jan Kiszka
  0 siblings, 1 reply; 20+ messages in thread
From: Kieran Bingham @ 2016-01-20 11:51 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

Hi Jan,

On 20/01/16 11:42, Jan Kiszka wrote:
> Hi Kieran,
> 
> just a couple of quick comments:
> 
> On 2016-01-20 12:15, Kieran Bingham wrote:
>> 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.
> 
> current_task? See LxCurrentFunc, could be factored out if usable. Or
> what are you looking for?

LxCurrentFunc relies on gdb.parse_and_eval("&current_task") which is not
available on ARM.

Although that is not what I was referring to in the comment. My meaning
was that once we have gdb-thread objects created, (my next phase of
work) then this command would operate on the thread currently selected
in gdb, based on the inferior_ptid.


>>
>> Optionally, a user can specify a PID to list from that process'
>> namespace
>>
>> This is somewhat limited vs the /proc/mounts file, as that calls into
>> vfs hooks through the s_op functions to obtain extra information.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>> ---
>>
>>
>> In this patch, I'm interested in your opinions on coding styles.
>> Would you prefer to see the function helpers, (dentry_name, info_opts) where
>> they are, or inside the command as class members? Or perhaps defined in utils?
> 
> Need to look into this.
> 
>>
>> This also shows where I need to take constant information from the kernel.
>> In this case, they are simple numerical bitflags, and unlikely to change but
>> I didn't want to duplicate their values.
> 
> Maybe we can generate python files with the required constants from the
> C headers during build? Similar to asm-offsets.c stuff.
> 


Yes, this is what I've implemented in [PATCH 1/5] ? Perhaps the mails
have reached you out-of-order.



>>
>>
>>  scripts/gdb/linux/constants.py.in |  21 ++++++++
>>  scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 131 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>> index d84084ac945b..739a15d2e984 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)
>>  
>> @@ -20,3 +24,20 @@
>>  <!-- end-c-headers -->
>>  
>>  import gdb
>> +
>> +/* linux/fs.h */
>> +LX_MS_RDONLY = MS_RDONLY
>> +LX_MS_SYNCHRONOUS = MS_SYNCHRONOUS
>> +LX_MS_MANDLOCK = MS_MANDLOCK
>> +LX_MS_DIRSYNC = MS_DIRSYNC
>> +LX_MS_NOATIME = MS_NOATIME
>> +LX_MS_NODIRATIME = MS_NODIRATIME
>> +
>> +/* linux/mount.h */
>> +LX_MNT_NOSUID = MNT_NOSUID
>> +LX_MNT_NODEV = MNT_NODEV
>> +LX_MNT_NOEXEC = MNT_NOEXEC
>> +LX_MNT_NOATIME = MNT_NOATIME
>> +LX_MNT_NODIRATIME = MNT_NODIRATIME
>> +LX_MNT_RELATIME = MNT_RELATIME
>> +
>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>> index d855b2fd9a06..b79ce2a33a3d 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,109 @@ Equivalent to cat /proc/ioports on a running target"""
>>          return show_lx_resources("ioport_resource")
>>  
>>  LxIOPorts()
>> +
>> +
>> +# Mount namespace viewer
>> +#  /proc/mounts
>> +
>> +
>> +def dentry_name(d):
>> +    if d['d_parent'] == d:
>> +        return ""
>> +    p = dentry_name(d['d_parent']) + "/"
>> +    return p + d['d_iname'].string()
>> +
>> +
>> +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.items(mount_ptr_type, "mnt_list", namespace['list']):
>> +            # There appears to be a null entry at the end of the list...
>> +            if not vfs['mnt_parent']:
>> +                break
>> +
>> +            devname = vfs['mnt_devname'].string()
>> +            devname = devname if devname else "none"
>> +
>> +            pathname = ""
>> +            parent = vfs
>> +            while True:
>> +                mntpoint = parent['mnt_mountpoint']
>> +                pathname = 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()
>>
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:51     ` Kieran Bingham
@ 2016-01-20 12:08       ` Jan Kiszka
  0 siblings, 0 replies; 20+ messages in thread
From: Jan Kiszka @ 2016-01-20 12:08 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:51, Kieran Bingham wrote:
> Hi Jan,
> 
> On 20/01/16 11:42, Jan Kiszka wrote:
>> Hi Kieran,
>>
>> just a couple of quick comments:
>>
>> On 2016-01-20 12:15, Kieran Bingham wrote:
>>> 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.
>>
>> current_task? See LxCurrentFunc, could be factored out if usable. Or
>> what are you looking for?
> 
> LxCurrentFunc relies on gdb.parse_and_eval("&current_task") which is not
> available on ARM.

Good point, not only affecting ARM. Needs to be fixed - lx-current() is
an important service.

> 
> Although that is not what I was referring to in the comment. My meaning
> was that once we have gdb-thread objects created, (my next phase of
> work) then this command would operate on the thread currently selected
> in gdb, based on the inferior_ptid.

I see. However, to have thread selection run with reasonable defaults,
we will need current() support as well.

> 
> 
>>>
>>> Optionally, a user can specify a PID to list from that process'
>>> namespace
>>>
>>> This is somewhat limited vs the /proc/mounts file, as that calls into
>>> vfs hooks through the s_op functions to obtain extra information.
>>>
>>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>>> ---
>>>
>>>
>>> In this patch, I'm interested in your opinions on coding styles.
>>> Would you prefer to see the function helpers, (dentry_name, info_opts) where
>>> they are, or inside the command as class members? Or perhaps defined in utils?
>>
>> Need to look into this.
>>
>>>
>>> This also shows where I need to take constant information from the kernel.
>>> In this case, they are simple numerical bitflags, and unlikely to change but
>>> I didn't want to duplicate their values.
>>
>> Maybe we can generate python files with the required constants from the
>> C headers during build? Similar to asm-offsets.c stuff.
>>
> 
> 
> Yes, this is what I've implemented in [PATCH 1/5] ? Perhaps the mails
> have reached you out-of-order.
> 

No, they were only processed in random order. Perfect!

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
  2016-01-20 11:42   ` Jan Kiszka
@ 2016-01-23 12:34   ` Jan Kiszka
  2016-01-23 15:27   ` Jan Kiszka
  2 siblings, 0 replies; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 12:34 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, Kieran Bingham wrote:
> 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
> 
> This is somewhat limited vs the /proc/mounts file, as that calls into
> vfs hooks through the s_op functions to obtain extra information.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> 
> In this patch, I'm interested in your opinions on coding styles.
> Would you prefer to see the function helpers, (dentry_name, info_opts) where
> they are, or inside the command as class members? Or perhaps defined in utils?
> 
> This also shows where I need to take constant information from the kernel.
> In this case, they are simple numerical bitflags, and unlikely to change but
> I didn't want to duplicate their values.
> 
> 
>  scripts/gdb/linux/constants.py.in |  21 ++++++++
>  scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 131 insertions(+)
> 
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index d84084ac945b..739a15d2e984 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)
>  

This hunk is malformed, indicating that 4 lines are added while there
are actually only 3 new ones. Could you check what went wrong? I'm
applying it manually for now.

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/5] scripts/gdb: Provide linux constants
  2016-01-20 11:15 ` [PATCH 1/5] scripts/gdb: Provide linux constants Kieran Bingham
@ 2016-01-23 15:05   ` Jan Kiszka
  2016-01-24  0:11     ` Kieran Bingham
  0 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 15:05 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, 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
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> I've added a 'constants.py' which is automatically generated. This allows
> values not available to the debugger, through #defines to be provided to
> our scripts.
> 
> The alternative method for this is to create a c-object file to obtain values
> through symbols instead, and compile segments with -g3 to include macro
> definitions in the debug-info.
> 
> I'd appreciate your thoughts on these options.

I cannot assess your second proposal. How invasive will it be? Is it
promising to reduce the maintenance? What will be the impact of -g3?

This approach seems pragmatic and sufficient. Would be fine with me
unless the other has significant advantages.

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] scripts/gdb: Provide a kernel list item generator
  2016-01-20 11:15 ` [PATCH 2/5] scripts/gdb: Provide a kernel list item generator Kieran Bingham
@ 2016-01-23 15:08   ` Jan Kiszka
  2016-01-24  0:15     ` Kieran Bingham
  0 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 15:08 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, Kieran Bingham wrote:
> Facilitate linked-list items by providing a generator to return
> the dereferenced, and type-cast objects from a kernel linked list
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> This is quite a useful wrapper to faciliate looping on lists.
> It is sort of equivalent to the list_for_each_entry macro.
> 
> Let me know if it should be renamed, or live elsewhere.

Location is fine. Maybe call it list_items?

> 
>  scripts/gdb/linux/lists.py | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
> index 3a3775bc162b..d2c6ce165cb1 100644
> --- a/scripts/gdb/linux/lists.py
> +++ b/scripts/gdb/linux/lists.py
> @@ -18,6 +18,15 @@ from linux import utils
>  list_head = utils.CachedType("struct list_head")
>  
>  
> +def items(list_type, list_location, item_list):
> +    """items Generator return items from a kernel linked list"""
> +    item_list_head = item_list
> +    next_item = item_list_head['next'].dereference()
> +    while next_item != item_list_head:
> +        yield utils.container_of(next_item, list_type, list_location)
> +        next_item = next_item['next'].dereference()
> +
> +
>  def list_check(head):
>      nb = 0
>      if (head.type == list_head.get_type().pointer()):
> 

Could you apply it on existing list iterations? module_list() seems like
a candidate, e.g.

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/5] scripts/gdb: Add io resource readers
  2016-01-20 11:15 ` [PATCH 3/5] scripts/gdb: Add io resource readers Kieran Bingham
@ 2016-01-23 15:12   ` Jan Kiszka
  2016-01-24  0:17     ` Kieran Bingham
  0 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 15:12 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, Kieran Bingham wrote:
> Provide iomem_resource and ioports_resource printers and command hooks
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> 
> ---
> 
> These two readers are a useful extract of kernel information.
> This shows the power of having these commands in gdb/scripts as you can
> halt a kernel as it's booting and read these as the structures grow.
> 
> It should be useful in the event that a kernel is not booting, you
> can identify what memory resources have been registered
> 

Ack. Maybe provide this reasoning in the commit log? I explains why we
want this which is too often lacking in the persistent logs...

> 
> 
>  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()
> 

Looks good to me.

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 5/5] scripts/gdb: Add meminfo command
  2016-01-20 11:15 ` [PATCH 5/5] scripts/gdb: Add meminfo command Kieran Bingham
@ 2016-01-23 15:21   ` Jan Kiszka
  2016-01-24  0:30     ` Kieran Bingham
  0 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 15:21 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, Kieran Bingham wrote:
> Provide an equivalent of /proc/meminfo which should be available from
> core dumps, or crashed kernels. This should allow a debugger to identify
> if memory pressures were applicable in the instance of their issue
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> This command has proved to be much more difficult that I first thought it
> would be!
> 
> It also poses a couple of interesting issues, which is why I submit this
> patch in a much more unfinished form.

Yeah, seems so - see below ;)

> 
> The meminfo implementation at fs/proc/meminfo.c makes several function calls
> to collate information, which makes duplicating here more difficult.
> 
> I suspect the best option here is to not present lines of which we can not
> obtain accurate data for, (much better than presenting inaccurate information)
> 
> Would this go in agreement with you?

I didn't grab the use cases yet, so just a general suggestion: if the
inaccurate information may still have some value, you can mark it as
inaccurate and still print it.

> 
> Finally, do you have any ideas on the best way to manage code which
> is #ifdef'd on kernel config options? (#ifdef CONFIG_HIGHMEM for example).
> 
> In a similar vein to the constants.py, I considered that we could iterate all
> of the kernel configuration options and store them in a dictionary some how.
> 
> That may be awkward, however, and I wondered what ideas anyone had!

Why not convert the (relevant) configs inside constants.py into a
python variable? CONFIG_IS_ENABLED should make this easy.

> 
> 
>  scripts/gdb/linux/constants.py.in |  22 +++++
>  scripts/gdb/linux/proc.py         | 173 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 195 insertions(+)
> 
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index 739a15d2e984..306bd601ae4e 100644
> --- a/scripts/gdb/linux/constants.py.in
> +++ b/scripts/gdb/linux/constants.py.in
> @@ -12,8 +12,15 @@
>   *
>   */
>  
> +#include <asm/page.h>
> +#include <asm/pgtable.h>
> +#include <asm/thread_info.h>
> +
>  #include <linux/fs.h>
> +#include <linux/swap.h>
>  #include <linux/mount.h>
> +#include <linux/vmalloc.h>
> +
>  
>  /* We need to stringify expanded macros so that they can be parsed */
>  
> @@ -41,3 +48,18 @@ LX_MNT_NOATIME = MNT_NOATIME
>  LX_MNT_NODIRATIME = MNT_NODIRATIME
>  LX_MNT_RELATIME = MNT_RELATIME
>  
> +/* asm/page.h */
> +LX_PAGE_SHIFT = XSTRING(PAGE_SHIFT)
> +lx_page_shift = gdb.parse_and_eval(LX_PAGE_SHIFT)
> +
> +/* asm/thread_info.h */
> +LX_THREAD_SIZE = XSTRING(THREAD_SIZE)
> +lx_thread_size = gdb.parse_and_eval(LX_THREAD_SIZE)
> +
> +/* linux/vmalloc.h */
> +LX_VMALLOC_TOTAL = XSTRING(VMALLOC_TOTAL)
> +lx_vmalloc_total = gdb.parse_and_eval(LX_VMALLOC_TOTAL)
> +
> +/* linux/swap.h */
> +LX_MAX_SWAPFILES = XSTRING(MAX_SWAPFILES)
> +lx_max_swapfiles = gdb.parse_and_eval(LX_MAX_SWAPFILES)
> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
> index b79ce2a33a3d..ac9e1aac2403 100644
> --- a/scripts/gdb/linux/proc.py
> +++ b/scripts/gdb/linux/proc.py
> @@ -206,3 +206,176 @@ values of that process namespace"""
>                          info_opts(MNT_INFO, m_flags)))
>  
>  LxMounts()
> +
> +
> +bdev_type = utils.CachedType("struct block_device")
> +
> +
> +class LxMeminfo(gdb.Command):
> +    """ Identify the memory usage, statistics, and availability
> +
> +Equivalent to cat /proc/meminfo on a running target """
> +
> +    def __init__(self):
> +        super(LxMeminfo, self).__init__("lx-meminfo", gdb.COMMAND_DATA)
> +
> +    def K(self, val):
> +        # Convert from PAGES to KB
> +        return int(val << (constants.lx_page_shift - 10))
> +
> +    def page_K(self, remote_value):
> +        # Obtain page value, and Convert from PAGES to KB
> +        val = int(gdb.parse_and_eval(remote_value))
> +        return self.K(val)
> +
> +    def gps(self, enum_zone_stat_item):
> +        # Access the Global Page State structure
> +        # I would prefer to read this structure in one go and then index
> +        # from the enum. But we can't determine the enum values with out
> +        # a call to GDB anyway so we may as well take the easy route and
> +        # get the value.
> +        remote_value = "vm_stat[" + enum_zone_stat_item + "].counter"
> +        return int(gdb.parse_and_eval(remote_value))
> +
> +    def gps_K(self, enum_zone_stat_item):
> +        return self.K(self.gps(enum_zone_stat_item))
> +
> +    def nr_blockdev_pages(self):
> +        bdevs_head = gdb.parse_and_eval("all_bdevs")
> +        pages = 0
> +        for bdev in lists.items(bdev_type, "bd_list", bdevs_head):
> +            pages += bdev['bd_inode']['i_mapping']['nrpages']
> +        return pages
> +
> +    def total_swapcache_pages(self):
> +        pages = 0
> +        for i in range(0, constants.lx_max_swapfiles):
> +            swap_space = "swapper_spaces[" + str(i) + "].nrpages"
> +            pages += int(gdb.parse_and_eval(swap_space))
> +        return pages
> +
> +    def vm_commit_limit(self, totalram_pages):
> +        overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
> +        overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
> +        total_swap_pages = int(gdb.parse_and_eval("total_swap_pages"))
> +        hugetlb_total_pages = 0  # hugetlb_total_pages()!!
> +
> +        if overcommit:
> +            allowed = overcommit >> (constants.lx_page_shift - 10)
> +        else:
> +            allowed = ((totalram_pages - hugetlb_total_pages *
> +                       overcommit_ratio / 100))
> +
> +        allowed += total_swap_pages
> +        return allowed
> +
> +    # Main lx-meminfo command execution
> +    def invoke(self, arg, from_tty):
> +        totalram = int(gdb.parse_and_eval("totalram_pages"))
> +        freeram = self.gps("NR_FREE_PAGES")
> +        reclaimable = self.gps("NR_SLAB_RECLAIMABLE")
> +        unreclaimable = self.gps("NR_SLAB_UNRECLAIMABLE")
> +        slab = reclaimable + unreclaimable
> +        # for_each_zone(zone)
> +        #     wmark_low += zone->watermark[WMARK_LOW];
> +        wmark_low = 0   # Zone parsing is unimplemented
> +
> +        available = freeram - wmark_low
> +        available += reclaimable - min(reclaimable / 2, wmark_low)
> +
> +        bufferram = self.nr_blockdev_pages()

Something goes wrong here:

(gdb) lx-meminfo 
Traceback (most recent call last):
  File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 286, in invoke
    bufferram = self.nr_blockdev_pages()
  File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 246, in nr_blockdev_pages
    for bdev in lists.items(bdev_type, "bd_list", bdevs_head):
  File "/data/linux/build-dbg/scripts/gdb/linux/lists.py", line 26, in items
    yield utils.container_of(next_item, list_type, list_location)
  File "/data/linux/build-dbg/scripts/gdb/linux/utils.py", line 52, in container_of
    offset_of(typeobj, member)).cast(typeobj)
  File "/data/linux/build-dbg/scripts/gdb/linux/utils.py", line 46, in offset_of
    element = gdb.Value(0).cast(typeobj)
RuntimeError: Argument must be a type.
Error occurred in Python command: Argument must be a type.

If you need my kernel config to reproduce, just let me know.

> +        total_swapcache_pages = self.total_swapcache_pages()
> +
> +        file_pages = self.gps("NR_FILE_PAGES")
> +        cached = file_pages - total_swapcache_pages - bufferram
> +
> +        # LRU Pages
> +        active_pages_anon = self.gps("NR_ACTIVE_ANON")
> +        inactive_pages_anon = self.gps("NR_INACTIVE_ANON")
> +        active_pages_file = self.gps("NR_ACTIVE_FILE")
> +        inactive_pages_file = self.gps("NR_INACTIVE_FILE")
> +        unevictable_pages = self.gps("NR_UNEVICTABLE")
> +        active_pages = active_pages_anon + active_pages_file
> +        inactive_pages = inactive_pages_anon + inactive_pages_file
> +
> +        totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
> +        # We can't run this on a core dump file ...
> +        # if running target ()
> +        freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
> +        # else freehigh = 0
> +
> +        kernelstack = int(self.gps("NR_KERNEL_STACK") *
> +                          constants.lx_thread_size / 1024)
> +
> +        commitlimit = self.vm_commit_limit(totalram)
> +        committed_as = int(gdb.parse_and_eval("vm_committed_as.count"))
> +
> +        vmalloc_total = int(constants.lx_vmalloc_total >> 10)
> +
> +        gdb.write(
> +            "MemTotal:       {:8d} kB\n".format(self.K(totalram)) +
> +            "MemFree:        {:8d} kB\n".format(self.K(freeram)) +
> +            "MemAvailable:   {:8d} kB\n".format(self.K(available)) +
> +            "Buffers:        {:8d} kB\n".format(self.K(bufferram)) +
> +            "Cached:         {:8d} kB\n".format(self.K(cached)) +
> +            "SwapCached:     {:8d} kB\n".format(self.K(total_swapcache_pages)) +
> +            "Active:         {:8d} kB\n".format(self.K(active_pages)) +
> +            "Inactive:       {:8d} kB\n".format(self.K(inactive_pages)) +
> +            "Active(anon):   {:8d} kB\n".format(self.K(active_pages_anon)) +
> +            "Inactive(anon): {:8d} kB\n".format(self.K(inactive_pages_anon)) +
> +            "Active(file):   {:8d} kB\n".format(self.K(active_pages_file)) +
> +            "Inactive(file): {:8d} kB\n".format(self.K(inactive_pages_file)) +
> +            "Unevictable:    {:8d} kB\n".format(self.K(unevictable_pages)) +
> +            "Mlocked:        {:8d} kB\n".format(self.gps_K("NR_MLOCK"))
> +            )
> +        # ifdef CONFIG_HIGHMEM || core dump?
> +        gdb.write(
> +            "HighTotal:      {:8d} kB\n".format(self.K(totalhigh)) +
> +            "HighFree:       {:8d} kB\n".format(self.K(freehigh)) +
> +            "LowTotal:       {:8d} kB\n".format(self.K(totalram-totalhigh)) +
> +            "LowFree:        {:8d} kB\n".format(self.K(freeram-freehigh))
> +            )
> +        # endif
> +        # ifndef CONFIG_MMU
> +        # gdb.write(
> +        #    mmap_pages_allocated
> +        #    )
> +        # endif
> +        gdb.write(
> +            "SwapTotal:      {:8d} kB\n".format(self.K(0)) +
> +            "SwapFree:       {:8d} kB\n".format(self.K(0)) +
> +            "Dirty:          {:8d} kB\n".format(self.gps_K("NR_FILE_DIRTY")) +
> +            "Writeback:      {:8d} kB\n".format(self.gps_K("NR_WRITEBACK")) +
> +            "AnonPages:      {:8d} kB\n".format(self.gps_K("NR_ANON_PAGES")) +
> +            "Mapped:         {:8d} kB\n".format(self.gps_K("NR_FILE_MAPPED")) +
> +            "Shmem:          {:8d} kB\n".format(self.gps_K("NR_SHMEM")) +
> +            "Slab:           {:8d} kB\n".format(self.K(slab)) +
> +            "SReclaimable:   {:8d} kB\n".format(self.K(reclaimable)) +
> +            "SUnreclaim:     {:8d} kB\n".format(self.K(unreclaimable)) +
> +            "KernelStack:    {:8d} kB\n".format(kernelstack) +
> +            "PageTables:     {:8d} kB\n".format(self.gps_K("NR_PAGETABLE"))
> +            )
> +
> +        #  if CONFIG_QUICKLIST
> +        #   "Quicklists:     {:8d} kB\n".format(self.K(quicklist)))
> +
> +        gdb.write(
> +            "NFS_Unstable:   {:8d} kB\n".format(self.gps_K("NR_UNSTABLE_NFS")) +
> +            "Bounce:         {:8d} kB\n".format(self.gps_K("NR_BOUNCE")) +
> +            "WritebackTmp:   {:8d} kB\n".format(self.gps_K("NR_WRITEBACK_TEMP")) +
> +            "CommitLimit:    {:8d} kB\n".format(self.K(commitlimit)) +
> +            "Committed_AS:   {:8d} kB\n".format(self.K(committed_as)) +
> +            "VmallocTotal:   {:8d} kB\n".format(vmalloc_total) +
> +            "VmallocUsed:    {:8d} kB\n".format(0) +
> +            "VmallocChunk:   {:8d} kB\n".format(0)
> +            )
> +        # if CONFIG_MEMORY_FAILURE
> +        #   "HardwareCorrupted: %5lu kB\n"
> +        # ifdef CONFIG_CMA
> +        totalcma_pages = int(gdb.parse_and_eval("totalcma_pages"))
> +        gdb.write(
> +            "CmaTotal:       {:8d} kB\n".format(self.K(totalcma_pages)) +
> +            "CmaFree:        {:8d} kB\n".format(self.gps_K("NR_FREE_CMA_PAGES"))
> +            )
> +
> +LxMeminfo()
> 

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
  2016-01-20 11:42   ` Jan Kiszka
  2016-01-23 12:34   ` Jan Kiszka
@ 2016-01-23 15:27   ` Jan Kiszka
  2016-01-24  0:24     ` Kieran Bingham
  2 siblings, 1 reply; 20+ messages in thread
From: Jan Kiszka @ 2016-01-23 15:27 UTC (permalink / raw)
  To: Kieran Bingham; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 2016-01-20 12:15, Kieran Bingham wrote:
> 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
> 
> This is somewhat limited vs the /proc/mounts file, as that calls into
> vfs hooks through the s_op functions to obtain extra information.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
> ---
> 
> 
> In this patch, I'm interested in your opinions on coding styles.
> Would you prefer to see the function helpers, (dentry_name, info_opts) where
> they are, or inside the command as class members? Or perhaps defined in utils?

Do you think they could be useful beyond this class? If not, stick them
inside. Refactoring can still be done once they are needed.

> 
> This also shows where I need to take constant information from the kernel.
> In this case, they are simple numerical bitflags, and unlikely to change but
> I didn't want to duplicate their values.
> 
> 
>  scripts/gdb/linux/constants.py.in |  21 ++++++++
>  scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 131 insertions(+)
> 
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index d84084ac945b..739a15d2e984 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)
>  
> @@ -20,3 +24,20 @@
>  <!-- end-c-headers -->
>  
>  import gdb
> +
> +/* linux/fs.h */
> +LX_MS_RDONLY = MS_RDONLY
> +LX_MS_SYNCHRONOUS = MS_SYNCHRONOUS
> +LX_MS_MANDLOCK = MS_MANDLOCK
> +LX_MS_DIRSYNC = MS_DIRSYNC
> +LX_MS_NOATIME = MS_NOATIME
> +LX_MS_NODIRATIME = MS_NODIRATIME
> +
> +/* linux/mount.h */
> +LX_MNT_NOSUID = MNT_NOSUID
> +LX_MNT_NODEV = MNT_NODEV
> +LX_MNT_NOEXEC = MNT_NOEXEC
> +LX_MNT_NOATIME = MNT_NOATIME
> +LX_MNT_NODIRATIME = MNT_NODIRATIME
> +LX_MNT_RELATIME = MNT_RELATIME
> +
> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
> index d855b2fd9a06..b79ce2a33a3d 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,109 @@ Equivalent to cat /proc/ioports on a running target"""
>          return show_lx_resources("ioport_resource")
>  
>  LxIOPorts()
> +
> +
> +# Mount namespace viewer
> +#  /proc/mounts
> +
> +
> +def dentry_name(d):
> +    if d['d_parent'] == d:
> +        return ""
> +    p = dentry_name(d['d_parent']) + "/"
> +    return p + d['d_iname'].string()
> +
> +
> +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.items(mount_ptr_type, "mnt_list", namespace['list']):
> +            # There appears to be a null entry at the end of the list...

"There appears to be" - hmm... Did you check this against the code?

> +            if not vfs['mnt_parent']:
> +                break
> +
> +            devname = vfs['mnt_devname'].string()
> +            devname = devname if devname else "none"
> +
> +            pathname = ""
> +            parent = vfs
> +            while True:
> +                mntpoint = parent['mnt_mountpoint']
> +                pathname = dentry_name(mntpoint) + pathname

I'm getting an error in this line:

(gdb) lx-mounts
devtmpfs /dev devtmpfs rw,relatime 0 0
tmpfs /dev/shm tmpfs rw,relatime 0 0
devpts /dev/pts devpts rw,relatime 0 0
/dev/sda2 / ext4 rw,relatime 0 0
proc /proc proc rw,nodiratime,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0
debugfs /sys/kernel/debug debugfs rw,relatime 0 0
securityfs /sys/kernel/security securityfs rw,relatime 0 0
fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
gvfs-fuse-daemon /home/jan/.gvfs fuse rw,relatime,nosuid,nodev 0 0
tracefs /sys/kernel/debug/tracing tracefs rw,relatime 0 0
Traceback (most recent call last):
  File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 185, in invoke
    pathname = dentry_name(mntpoint) + pathname
  File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 112, in dentry_name
    p = dentry_name(d['d_parent']) + "/"
  File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 110, in dentry_name
    if d['d_parent'] == d:
gdb.MemoryError: Cannot access memory at address 0x40
Error occurred in Python command: Cannot access memory at address 0x40

In this case, the dump should have stopped after the tracefs line.

> +                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()
> 

Jan

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/5] scripts/gdb: Provide linux constants
  2016-01-23 15:05   ` Jan Kiszka
@ 2016-01-24  0:11     ` Kieran Bingham
  0 siblings, 0 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-24  0:11 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones



On 23/01/16 15:05, Jan Kiszka wrote:
> On 2016-01-20 12:15, 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
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>> ---
>>
>> I've added a 'constants.py' which is automatically generated. This allows
>> values not available to the debugger, through #defines to be provided to
>> our scripts.
>>
>> The alternative method for this is to create a c-object file to obtain values
>> through symbols instead, and compile segments with -g3 to include macro
>> definitions in the debug-info.
>>
>> I'd appreciate your thoughts on these options.
> 
> I cannot assess your second proposal. How invasive will it be? Is it
> promising to reduce the maintenance? What will be the impact of -g3?
> 
> This approach seems pragmatic and sufficient. Would be fine with me
> unless the other has significant advantages.

At the moment, I believe the current method (generating a constants.py)
is my preferred method. It's less intrusive, and can be generated for a
kernel which is to be debugged, which perhaps didn't have GDB_SCRIPTS
enabled at the time.

A c-object file would be more limiting I believe.

Kieran

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/5] scripts/gdb: Provide a kernel list item generator
  2016-01-23 15:08   ` Jan Kiszka
@ 2016-01-24  0:15     ` Kieran Bingham
  0 siblings, 0 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-24  0:15 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones



On 23/01/16 15:08, Jan Kiszka wrote:
> On 2016-01-20 12:15, Kieran Bingham wrote:
>> Facilitate linked-list items by providing a generator to return
>> the dereferenced, and type-cast objects from a kernel linked list
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>> ---
>>
>> This is quite a useful wrapper to faciliate looping on lists.
>> It is sort of equivalent to the list_for_each_entry macro.
>>
>> Let me know if it should be renamed, or live elsewhere.
> 
> Location is fine. Maybe call it list_items?
> 
>>
>>  scripts/gdb/linux/lists.py | 9 +++++++++
>>  1 file changed, 9 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
>> index 3a3775bc162b..d2c6ce165cb1 100644
>> --- a/scripts/gdb/linux/lists.py
>> +++ b/scripts/gdb/linux/lists.py
>> @@ -18,6 +18,15 @@ from linux import utils
>>  list_head = utils.CachedType("struct list_head")
>>  
>>  
>> +def items(list_type, list_location, item_list):
>> +    """items Generator return items from a kernel linked list"""
>> +    item_list_head = item_list
>> +    next_item = item_list_head['next'].dereference()
>> +    while next_item != item_list_head:
>> +        yield utils.container_of(next_item, list_type, list_location)
>> +        next_item = next_item['next'].dereference()
>> +
>> +
>>  def list_check(head):
>>      nb = 0
>>      if (head.type == list_head.get_type().pointer()):
>>
> 
> Could you apply it on existing list iterations? module_list() seems like
> a candidate, e.g.

Yes, It probably is. I'll update, and add a patch to my series, and
check to see if there are any more.
--
Kieran

> 
> Jan
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/5] scripts/gdb: Add io resource readers
  2016-01-23 15:12   ` Jan Kiszka
@ 2016-01-24  0:17     ` Kieran Bingham
  0 siblings, 0 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-24  0:17 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones



On 23/01/16 15:12, Jan Kiszka wrote:
> On 2016-01-20 12:15, Kieran Bingham wrote:
>> Provide iomem_resource and ioports_resource printers and command hooks
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>>
>> ---
>>
>> These two readers are a useful extract of kernel information.
>> This shows the power of having these commands in gdb/scripts as you can
>> halt a kernel as it's booting and read these as the structures grow.
>>
>> It should be useful in the event that a kernel is not booting, you
>> can identify what memory resources have been registered
>>
> 
> Ack. Maybe provide this reasoning in the commit log? I explains why we
> want this which is too often lacking in the persistent logs...
> 

Perfect,
I'll update the commit for v2.

--
Kieran


>>
>>
>>  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()
>>
> 
> Looks good to me.
> 
> Jan
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/5] scripts/gdb: Add mount point list command
  2016-01-23 15:27   ` Jan Kiszka
@ 2016-01-24  0:24     ` Kieran Bingham
  0 siblings, 0 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-24  0:24 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones

On 23/01/16 15:27, Jan Kiszka wrote:
> On 2016-01-20 12:15, Kieran Bingham wrote:
>> 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
>>
>> This is somewhat limited vs the /proc/mounts file, as that calls into
>> vfs hooks through the s_op functions to obtain extra information.
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>> ---
>>
>>
>> In this patch, I'm interested in your opinions on coding styles.
>> Would you prefer to see the function helpers, (dentry_name, info_opts) where
>> they are, or inside the command as class members? Or perhaps defined in utils?
> 
> Do you think they could be useful beyond this class? If not, stick them
> inside. Refactoring can still be done once they are needed.
> 

Ok - that's good logic. Keep them close until needed elsewhere :)


>>
>> This also shows where I need to take constant information from the kernel.
>> In this case, they are simple numerical bitflags, and unlikely to change but
>> I didn't want to duplicate their values.
>>
>>
>>  scripts/gdb/linux/constants.py.in |  21 ++++++++
>>  scripts/gdb/linux/proc.py         | 110 ++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 131 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>> index d84084ac945b..739a15d2e984 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)
>>  
>> @@ -20,3 +24,20 @@
>>  <!-- end-c-headers -->
>>  
>>  import gdb
>> +
>> +/* linux/fs.h */
>> +LX_MS_RDONLY = MS_RDONLY
>> +LX_MS_SYNCHRONOUS = MS_SYNCHRONOUS
>> +LX_MS_MANDLOCK = MS_MANDLOCK
>> +LX_MS_DIRSYNC = MS_DIRSYNC
>> +LX_MS_NOATIME = MS_NOATIME
>> +LX_MS_NODIRATIME = MS_NODIRATIME
>> +
>> +/* linux/mount.h */
>> +LX_MNT_NOSUID = MNT_NOSUID
>> +LX_MNT_NODEV = MNT_NODEV
>> +LX_MNT_NOEXEC = MNT_NOEXEC
>> +LX_MNT_NOATIME = MNT_NOATIME
>> +LX_MNT_NODIRATIME = MNT_NODIRATIME
>> +LX_MNT_RELATIME = MNT_RELATIME
>> +
>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>> index d855b2fd9a06..b79ce2a33a3d 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,109 @@ Equivalent to cat /proc/ioports on a running target"""
>>          return show_lx_resources("ioport_resource")
>>  
>>  LxIOPorts()
>> +
>> +
>> +# Mount namespace viewer
>> +#  /proc/mounts
>> +
>> +
>> +def dentry_name(d):
>> +    if d['d_parent'] == d:
>> +        return ""
>> +    p = dentry_name(d['d_parent']) + "/"
>> +    return p + d['d_iname'].string()
>> +
>> +
>> +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.items(mount_ptr_type, "mnt_list", namespace['list']):
>> +            # There appears to be a null entry at the end of the list...
> 
> "There appears to be" - hmm... Did you check this against the code?


Not properly no... I'll check and see what happened.

I'll have a think as to the best defences to put in.
This one stopped a NULL dereference on mine, but as below clearly it
didn't stop them all!

> 
>> +            if not vfs['mnt_parent']:
>> +                break
>> +
>> +            devname = vfs['mnt_devname'].string()
>> +            devname = devname if devname else "none"
>> +
>> +            pathname = ""
>> +            parent = vfs
>> +            while True:
>> +                mntpoint = parent['mnt_mountpoint']
>> +                pathname = dentry_name(mntpoint) + pathname
> 
> I'm getting an error in this line:
> 
> (gdb) lx-mounts
> devtmpfs /dev devtmpfs rw,relatime 0 0
> tmpfs /dev/shm tmpfs rw,relatime 0 0
> devpts /dev/pts devpts rw,relatime 0 0
> /dev/sda2 / ext4 rw,relatime 0 0
> proc /proc proc rw,nodiratime,relatime 0 0
> sysfs /sys sysfs rw,relatime 0 0
> debugfs /sys/kernel/debug debugfs rw,relatime 0 0
> securityfs /sys/kernel/security securityfs rw,relatime 0 0
> fusectl /sys/fs/fuse/connections fusectl rw,relatime 0 0
> gvfs-fuse-daemon /home/jan/.gvfs fuse rw,relatime,nosuid,nodev 0 0
> tracefs /sys/kernel/debug/tracing tracefs rw,relatime 0 0
> Traceback (most recent call last):
>   File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 185, in invoke
>     pathname = dentry_name(mntpoint) + pathname
>   File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 112, in dentry_name
>     p = dentry_name(d['d_parent']) + "/"
>   File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 110, in dentry_name
>     if d['d_parent'] == d:
> gdb.MemoryError: Cannot access memory at address 0x40
> Error occurred in Python command: Cannot access memory at address 0x40
> 
> In this case, the dump should have stopped after the tracefs line.
> 

Interesting, I'll have to be more defensive.

I'm sure when I looked the proc show command seemed to have only the
list end and the dev name checks...

But we could by our nature run at any time - so it should be a bit more
defensive anyway

>> +                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()
>>
> 
> Jan
> 

Kieran

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 5/5] scripts/gdb: Add meminfo command
  2016-01-23 15:21   ` Jan Kiszka
@ 2016-01-24  0:30     ` Kieran Bingham
  0 siblings, 0 replies; 20+ messages in thread
From: Kieran Bingham @ 2016-01-24  0:30 UTC (permalink / raw)
  To: Jan Kiszka; +Cc: linux-kernel, maxime.coquelin, peter.griffin, lee.jones



On 23/01/16 15:21, Jan Kiszka wrote:
> On 2016-01-20 12:15, Kieran Bingham wrote:
>> Provide an equivalent of /proc/meminfo which should be available from
>> core dumps, or crashed kernels. This should allow a debugger to identify
>> if memory pressures were applicable in the instance of their issue
>>
>> Signed-off-by: Kieran Bingham <kieran.bingham@linaro.org>
>> ---
>>
>> This command has proved to be much more difficult that I first thought it
>> would be!
>>
>> It also poses a couple of interesting issues, which is why I submit this
>> patch in a much more unfinished form.
> 
> Yeah, seems so - see below ;)
> 
>>
>> The meminfo implementation at fs/proc/meminfo.c makes several function calls
>> to collate information, which makes duplicating here more difficult.
>>
>> I suspect the best option here is to not present lines of which we can not
>> obtain accurate data for, (much better than presenting inaccurate information)
>>
>> Would this go in agreement with you?
> 
> I didn't grab the use cases yet, so just a general suggestion: if the
> inaccurate information may still have some value, you can mark it as
> inaccurate and still print it.
> 
>>
>> Finally, do you have any ideas on the best way to manage code which
>> is #ifdef'd on kernel config options? (#ifdef CONFIG_HIGHMEM for example).
>>
>> In a similar vein to the constants.py, I considered that we could iterate all
>> of the kernel configuration options and store them in a dictionary some how.
>>
>> That may be awkward, however, and I wondered what ideas anyone had!
> 
> Why not convert the (relevant) configs inside constants.py into a
> python variable? CONFIG_IS_ENABLED should make this easy.


Aha, Excellent. I think I must not have seen the wood-for-the-trees on
this one!

Had a play, and I can't use IS_ENABLED(x) directly, as it generates (0
|| 1) from IS_BUILTIN and IS_MODULE - and which is not compatible with
python. However, in our use-case - we actually only want to switch on
BUILTIN's anyway, so I will use that.

If other use cases come up later, we can always extend to create our own
Python version of IS_ENABLED

>>
>>
>>  scripts/gdb/linux/constants.py.in |  22 +++++
>>  scripts/gdb/linux/proc.py         | 173 ++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 195 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>> index 739a15d2e984..306bd601ae4e 100644
>> --- a/scripts/gdb/linux/constants.py.in
>> +++ b/scripts/gdb/linux/constants.py.in
>> @@ -12,8 +12,15 @@
>>   *
>>   */
>>  
>> +#include <asm/page.h>
>> +#include <asm/pgtable.h>
>> +#include <asm/thread_info.h>
>> +
>>  #include <linux/fs.h>
>> +#include <linux/swap.h>
>>  #include <linux/mount.h>
>> +#include <linux/vmalloc.h>
>> +
>>  
>>  /* We need to stringify expanded macros so that they can be parsed */
>>  
>> @@ -41,3 +48,18 @@ LX_MNT_NOATIME = MNT_NOATIME
>>  LX_MNT_NODIRATIME = MNT_NODIRATIME
>>  LX_MNT_RELATIME = MNT_RELATIME
>>  
>> +/* asm/page.h */
>> +LX_PAGE_SHIFT = XSTRING(PAGE_SHIFT)
>> +lx_page_shift = gdb.parse_and_eval(LX_PAGE_SHIFT)
>> +
>> +/* asm/thread_info.h */
>> +LX_THREAD_SIZE = XSTRING(THREAD_SIZE)
>> +lx_thread_size = gdb.parse_and_eval(LX_THREAD_SIZE)
>> +
>> +/* linux/vmalloc.h */
>> +LX_VMALLOC_TOTAL = XSTRING(VMALLOC_TOTAL)
>> +lx_vmalloc_total = gdb.parse_and_eval(LX_VMALLOC_TOTAL)
>> +
>> +/* linux/swap.h */
>> +LX_MAX_SWAPFILES = XSTRING(MAX_SWAPFILES)
>> +lx_max_swapfiles = gdb.parse_and_eval(LX_MAX_SWAPFILES)
>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>> index b79ce2a33a3d..ac9e1aac2403 100644
>> --- a/scripts/gdb/linux/proc.py
>> +++ b/scripts/gdb/linux/proc.py
>> @@ -206,3 +206,176 @@ values of that process namespace"""
>>                          info_opts(MNT_INFO, m_flags)))
>>  
>>  LxMounts()
>> +
>> +
>> +bdev_type = utils.CachedType("struct block_device")
>> +
>> +
>> +class LxMeminfo(gdb.Command):
>> +    """ Identify the memory usage, statistics, and availability
>> +
>> +Equivalent to cat /proc/meminfo on a running target """
>> +
>> +    def __init__(self):
>> +        super(LxMeminfo, self).__init__("lx-meminfo", gdb.COMMAND_DATA)
>> +
>> +    def K(self, val):
>> +        # Convert from PAGES to KB
>> +        return int(val << (constants.lx_page_shift - 10))
>> +
>> +    def page_K(self, remote_value):
>> +        # Obtain page value, and Convert from PAGES to KB
>> +        val = int(gdb.parse_and_eval(remote_value))
>> +        return self.K(val)
>> +
>> +    def gps(self, enum_zone_stat_item):
>> +        # Access the Global Page State structure
>> +        # I would prefer to read this structure in one go and then index
>> +        # from the enum. But we can't determine the enum values with out
>> +        # a call to GDB anyway so we may as well take the easy route and
>> +        # get the value.
>> +        remote_value = "vm_stat[" + enum_zone_stat_item + "].counter"
>> +        return int(gdb.parse_and_eval(remote_value))
>> +
>> +    def gps_K(self, enum_zone_stat_item):
>> +        return self.K(self.gps(enum_zone_stat_item))
>> +
>> +    def nr_blockdev_pages(self):
>> +        bdevs_head = gdb.parse_and_eval("all_bdevs")
>> +        pages = 0
>> +        for bdev in lists.items(bdev_type, "bd_list", bdevs_head):
>> +            pages += bdev['bd_inode']['i_mapping']['nrpages']
>> +        return pages
>> +
>> +    def total_swapcache_pages(self):
>> +        pages = 0
>> +        for i in range(0, constants.lx_max_swapfiles):
>> +            swap_space = "swapper_spaces[" + str(i) + "].nrpages"
>> +            pages += int(gdb.parse_and_eval(swap_space))
>> +        return pages
>> +
>> +    def vm_commit_limit(self, totalram_pages):
>> +        overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
>> +        overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
>> +        total_swap_pages = int(gdb.parse_and_eval("total_swap_pages"))
>> +        hugetlb_total_pages = 0  # hugetlb_total_pages()!!
>> +
>> +        if overcommit:
>> +            allowed = overcommit >> (constants.lx_page_shift - 10)
>> +        else:
>> +            allowed = ((totalram_pages - hugetlb_total_pages *
>> +                       overcommit_ratio / 100))
>> +
>> +        allowed += total_swap_pages
>> +        return allowed
>> +
>> +    # Main lx-meminfo command execution
>> +    def invoke(self, arg, from_tty):
>> +        totalram = int(gdb.parse_and_eval("totalram_pages"))
>> +        freeram = self.gps("NR_FREE_PAGES")
>> +        reclaimable = self.gps("NR_SLAB_RECLAIMABLE")
>> +        unreclaimable = self.gps("NR_SLAB_UNRECLAIMABLE")
>> +        slab = reclaimable + unreclaimable
>> +        # for_each_zone(zone)
>> +        #     wmark_low += zone->watermark[WMARK_LOW];
>> +        wmark_low = 0   # Zone parsing is unimplemented
>> +
>> +        available = freeram - wmark_low
>> +        available += reclaimable - min(reclaimable / 2, wmark_low)
>> +
>> +        bufferram = self.nr_blockdev_pages()
> 
> Something goes wrong here:
> 
> (gdb) lx-meminfo 
> Traceback (most recent call last):
>   File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 286, in invoke
>     bufferram = self.nr_blockdev_pages()
>   File "/data/linux/build-dbg/scripts/gdb/linux/proc.py", line 246, in nr_blockdev_pages
>     for bdev in lists.items(bdev_type, "bd_list", bdevs_head):
>   File "/data/linux/build-dbg/scripts/gdb/linux/lists.py", line 26, in items
>     yield utils.container_of(next_item, list_type, list_location)
>   File "/data/linux/build-dbg/scripts/gdb/linux/utils.py", line 52, in container_of
>     offset_of(typeobj, member)).cast(typeobj)
>   File "/data/linux/build-dbg/scripts/gdb/linux/utils.py", line 46, in offset_of
>     element = gdb.Value(0).cast(typeobj)
> RuntimeError: Argument must be a type.
> Error occurred in Python command: Argument must be a type.
> 
> If you need my kernel config to reproduce, just let me know.


Yes please, send me a config, and I'll try to reproduce.


What is your test environment by the way?
I am running in QEmu for convenience mostly at the moment.

I hope to set up some automated scripts to test on different arch's too.

--
Kieran


> 
>> +        total_swapcache_pages = self.total_swapcache_pages()
>> +
>> +        file_pages = self.gps("NR_FILE_PAGES")
>> +        cached = file_pages - total_swapcache_pages - bufferram
>> +
>> +        # LRU Pages
>> +        active_pages_anon = self.gps("NR_ACTIVE_ANON")
>> +        inactive_pages_anon = self.gps("NR_INACTIVE_ANON")
>> +        active_pages_file = self.gps("NR_ACTIVE_FILE")
>> +        inactive_pages_file = self.gps("NR_INACTIVE_FILE")
>> +        unevictable_pages = self.gps("NR_UNEVICTABLE")
>> +        active_pages = active_pages_anon + active_pages_file
>> +        inactive_pages = inactive_pages_anon + inactive_pages_file
>> +
>> +        totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
>> +        # We can't run this on a core dump file ...
>> +        # if running target ()
>> +        freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
>> +        # else freehigh = 0
>> +
>> +        kernelstack = int(self.gps("NR_KERNEL_STACK") *
>> +                          constants.lx_thread_size / 1024)
>> +
>> +        commitlimit = self.vm_commit_limit(totalram)
>> +        committed_as = int(gdb.parse_and_eval("vm_committed_as.count"))
>> +
>> +        vmalloc_total = int(constants.lx_vmalloc_total >> 10)
>> +
>> +        gdb.write(
>> +            "MemTotal:       {:8d} kB\n".format(self.K(totalram)) +
>> +            "MemFree:        {:8d} kB\n".format(self.K(freeram)) +
>> +            "MemAvailable:   {:8d} kB\n".format(self.K(available)) +
>> +            "Buffers:        {:8d} kB\n".format(self.K(bufferram)) +
>> +            "Cached:         {:8d} kB\n".format(self.K(cached)) +
>> +            "SwapCached:     {:8d} kB\n".format(self.K(total_swapcache_pages)) +
>> +            "Active:         {:8d} kB\n".format(self.K(active_pages)) +
>> +            "Inactive:       {:8d} kB\n".format(self.K(inactive_pages)) +
>> +            "Active(anon):   {:8d} kB\n".format(self.K(active_pages_anon)) +
>> +            "Inactive(anon): {:8d} kB\n".format(self.K(inactive_pages_anon)) +
>> +            "Active(file):   {:8d} kB\n".format(self.K(active_pages_file)) +
>> +            "Inactive(file): {:8d} kB\n".format(self.K(inactive_pages_file)) +
>> +            "Unevictable:    {:8d} kB\n".format(self.K(unevictable_pages)) +
>> +            "Mlocked:        {:8d} kB\n".format(self.gps_K("NR_MLOCK"))
>> +            )
>> +        # ifdef CONFIG_HIGHMEM || core dump?
>> +        gdb.write(
>> +            "HighTotal:      {:8d} kB\n".format(self.K(totalhigh)) +
>> +            "HighFree:       {:8d} kB\n".format(self.K(freehigh)) +
>> +            "LowTotal:       {:8d} kB\n".format(self.K(totalram-totalhigh)) +
>> +            "LowFree:        {:8d} kB\n".format(self.K(freeram-freehigh))
>> +            )
>> +        # endif
>> +        # ifndef CONFIG_MMU
>> +        # gdb.write(
>> +        #    mmap_pages_allocated
>> +        #    )
>> +        # endif
>> +        gdb.write(
>> +            "SwapTotal:      {:8d} kB\n".format(self.K(0)) +
>> +            "SwapFree:       {:8d} kB\n".format(self.K(0)) +
>> +            "Dirty:          {:8d} kB\n".format(self.gps_K("NR_FILE_DIRTY")) +
>> +            "Writeback:      {:8d} kB\n".format(self.gps_K("NR_WRITEBACK")) +
>> +            "AnonPages:      {:8d} kB\n".format(self.gps_K("NR_ANON_PAGES")) +
>> +            "Mapped:         {:8d} kB\n".format(self.gps_K("NR_FILE_MAPPED")) +
>> +            "Shmem:          {:8d} kB\n".format(self.gps_K("NR_SHMEM")) +
>> +            "Slab:           {:8d} kB\n".format(self.K(slab)) +
>> +            "SReclaimable:   {:8d} kB\n".format(self.K(reclaimable)) +
>> +            "SUnreclaim:     {:8d} kB\n".format(self.K(unreclaimable)) +
>> +            "KernelStack:    {:8d} kB\n".format(kernelstack) +
>> +            "PageTables:     {:8d} kB\n".format(self.gps_K("NR_PAGETABLE"))
>> +            )
>> +
>> +        #  if CONFIG_QUICKLIST
>> +        #   "Quicklists:     {:8d} kB\n".format(self.K(quicklist)))
>> +
>> +        gdb.write(
>> +            "NFS_Unstable:   {:8d} kB\n".format(self.gps_K("NR_UNSTABLE_NFS")) +
>> +            "Bounce:         {:8d} kB\n".format(self.gps_K("NR_BOUNCE")) +
>> +            "WritebackTmp:   {:8d} kB\n".format(self.gps_K("NR_WRITEBACK_TEMP")) +
>> +            "CommitLimit:    {:8d} kB\n".format(self.K(commitlimit)) +
>> +            "Committed_AS:   {:8d} kB\n".format(self.K(committed_as)) +
>> +            "VmallocTotal:   {:8d} kB\n".format(vmalloc_total) +
>> +            "VmallocUsed:    {:8d} kB\n".format(0) +
>> +            "VmallocChunk:   {:8d} kB\n".format(0)
>> +            )
>> +        # if CONFIG_MEMORY_FAILURE
>> +        #   "HardwareCorrupted: %5lu kB\n"
>> +        # ifdef CONFIG_CMA
>> +        totalcma_pages = int(gdb.parse_and_eval("totalcma_pages"))
>> +        gdb.write(
>> +            "CmaTotal:       {:8d} kB\n".format(self.K(totalcma_pages)) +
>> +            "CmaFree:        {:8d} kB\n".format(self.gps_K("NR_FREE_CMA_PAGES"))
>> +            )
>> +
>> +LxMeminfo()
>>
> 
> Jan
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2016-01-24  0:30 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-20 11:15 [RFC PATCH 0/5] scripts/gdb: Linux awareness debug commands Kieran Bingham
2016-01-20 11:15 ` [PATCH 1/5] scripts/gdb: Provide linux constants Kieran Bingham
2016-01-23 15:05   ` Jan Kiszka
2016-01-24  0:11     ` Kieran Bingham
2016-01-20 11:15 ` [PATCH 2/5] scripts/gdb: Provide a kernel list item generator Kieran Bingham
2016-01-23 15:08   ` Jan Kiszka
2016-01-24  0:15     ` Kieran Bingham
2016-01-20 11:15 ` [PATCH 3/5] scripts/gdb: Add io resource readers Kieran Bingham
2016-01-23 15:12   ` Jan Kiszka
2016-01-24  0:17     ` Kieran Bingham
2016-01-20 11:15 ` [PATCH 4/5] scripts/gdb: Add mount point list command Kieran Bingham
2016-01-20 11:42   ` Jan Kiszka
2016-01-20 11:51     ` Kieran Bingham
2016-01-20 12:08       ` Jan Kiszka
2016-01-23 12:34   ` Jan Kiszka
2016-01-23 15:27   ` Jan Kiszka
2016-01-24  0:24     ` Kieran Bingham
2016-01-20 11:15 ` [PATCH 5/5] scripts/gdb: Add meminfo command Kieran Bingham
2016-01-23 15:21   ` Jan Kiszka
2016-01-24  0:30     ` Kieran Bingham

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).