2016-03-03 11:41:22

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

Hi Jan,

V3 of the patchset respun. Now finally adding the lx-interrupts command
after I resolved my issues with the Radix Tree parsing.

This command only provides the interrupts that are available generically,
and it seems that the /proc/interrupts function calls into arch specific
layers to add extra information about arch specific interrupts.

I'm not sure what to do about this yet - The values returned appear to be
accurarate - but it's just a subset of the information returned by proc.

lx_thread_info_by_pid has been useful to me while looking at thread
awareness, so I've included it into this patch set now. It makes finding
internal thread information much more convenient.

dentry_name has been moved to the utils module, as I am already using it
in another command, so it's just not appropriate to be in proc.py

The cpu_list mask iterators make calling for cpu in each_online_cpu() read
nicely, and I've left the print_cpus() function in for now as a hidden
helper. It can be used by calling:
python linux.cpus.print_cpus()
to check these generators, which I thought was quite nice - but I didn't
know if it warranted a full command class for this.

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

Patchset Changelog:
v3:
- Radix Tree parser introduced
- cpu_list mask iterators added
- lx-interrupts command implemented
- dentry_name function moved to utils
- lx-meminfo command PEP8 warnings fixed
- lx_thread_info_by_pid introduced

v2:
- Reworked iterators with improved versions from Jeff Mahoney
- Fixed !CONFIG_MODULES and !CONFIG_MMU support
- Improvements on lx-meminfo
- constants.py generated by Kbuild
- IS_BUILTIN facility used to provide LX_CONFIG values

v1:
- Introduced lx-iomem, lx-ioports, lx-mounts, lx-meminfo

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

Kbuild | 10 +
scripts/gdb/linux/Makefile | 12 +-
scripts/gdb/linux/constants.py.in | 93 ++++++++
scripts/gdb/linux/cpus.py | 21 ++
scripts/gdb/linux/lists.py | 20 ++
scripts/gdb/linux/modules.py | 22 +-
scripts/gdb/linux/proc.py | 449 ++++++++++++++++++++++++++++++++++++++
scripts/gdb/linux/radixtree.py | 74 +++++++
scripts/gdb/linux/tasks.py | 19 ++
scripts/gdb/linux/utils.py | 15 ++
scripts/gdb/vmlinux-gdb.py | 2 +
11 files changed, 724 insertions(+), 13 deletions(-)
create mode 100644 scripts/gdb/linux/constants.py.in
create mode 100644 scripts/gdb/linux/radixtree.py

--
2.5.0


2016-03-03 11:41:32

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 05/13] scripts/gdb: Support !CONFIG_MODULES gracefully

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

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

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

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

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

diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 571489b3b9c2..60224d8bbfb2 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -21,8 +21,11 @@ module_type = utils.CachedType("struct module")

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

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

2016-03-03 11:41:34

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 13/13] scripts/gdb: Add lx_thread_info_by_pid helper

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

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

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

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


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

2016-03-03 11:41:30

by Kieran Bingham

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

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

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

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

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

2016-03-03 11:43:39

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 12/13] scripts/gdb: Add interrupts command

Allow the debugger to present a list of all interrupts and their
associated counts. This will allow developers of drivers to identify
hardware interactions in the event of crashed kernels and core dumps.

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/constants.py.in | 1 +
scripts/gdb/linux/proc.py | 65 +++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index b38b9085c702..17b719ead54f 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -84,6 +84,7 @@ LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
LX_CONFIG(CONFIG_CMA)
LX_CONFIG(CONFIG_MMU)
LX_CONFIG(CONFIG_SWAP)
+LX_CONFIG(CONFIG_SPARSE_IRQ)

#ifndef CONFIG_NR_QUICK
#define CONFIG_NR_QUICK 0
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index e5a8dbe3aa3a..0676a14e8d48 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -16,6 +16,8 @@ from linux import constants
from linux import utils
from linux import tasks
from linux import lists
+from linux import cpus
+from linux import radixtree


class LxCmdLine(gdb.Command):
@@ -423,3 +425,66 @@ Equivalent to cat /proc/meminfo on a running target """
)

LxMeminfo()
+
+irq_desc = None
+irq_desc_type = utils.CachedType("struct irq_desc")
+
+
+class LxInterrupts(gdb.Command):
+ """ Report the interrupt counters of each CPU.
+ Equivalent to cat /proc/interrupts on a running target """
+
+ def __init__(self):
+ super(LxInterrupts, self).__init__("lx-interrupts", gdb.COMMAND_DATA)
+
+ def irq_to_desc(self, irq):
+ if constants.LX_CONFIG_SPARSE_IRQ:
+ irq_desc_tree = gdb.parse_and_eval("irq_desc_tree")
+ irq_desc_addr = radixtree.lookup(irq_desc_tree, irq)
+
+ if irq_desc_addr is None:
+ return None
+
+ if irq_desc_addr.address:
+ return irq_desc_addr.cast(irq_desc_type.get_type())
+ return None
+ else:
+ irq_desc_array = "irq_desc[{}]".format(irq)
+ return gdb.parse_and_eval(irq_desc_array)
+
+ def kstat_irqs_cpu(self, irq_desc, cpu):
+ return int(cpus.per_cpu(irq_desc['kstat_irqs'], cpu))
+
+ def invoke(self, arg, from_tty):
+ nr_irqs = int(gdb.parse_and_eval("nr_irqs"))
+
+ # Calculate the width of the first column
+ prec = 3
+ j = nr_irqs
+ while (j <= nr_irqs):
+ j *= 10
+ prec += 1
+
+ title = "{:{}}".format(" ", prec+8)
+ for cpu in cpus.each_online_cpu():
+ title += "CPU{:<8d}".format(cpu)
+ gdb.write(title + "\n")
+
+ for irq in range(0, nr_irqs):
+ desc = self.irq_to_desc(irq)
+ if desc is None:
+ continue
+
+ anycount = 0
+ for cpu in cpus.each_online_cpu():
+ anycount += self.kstat_irqs_cpu(desc, cpu)
+ if (anycount == 0):
+ continue
+
+ irq_line = "{:{}d}: ".format(irq, prec)
+ for cpu in cpus.each_online_cpu():
+ count = self.kstat_irqs_cpu(desc, cpu)
+ irq_line += "{:10d} ".format(count)
+ gdb.write(irq_line + "\n")
+
+LxInterrupts()
--
2.5.0

2016-03-03 11:43:58

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 11/13] scripts/gdb: Add a Radix Tree Parser

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

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

Signed-off-by: Kieran Bingham <[email protected]>
---
scripts/gdb/linux/constants.py.in | 7 +++-
scripts/gdb/linux/radixtree.py | 74 +++++++++++++++++++++++++++++++++++++++
scripts/gdb/vmlinux-gdb.py | 1 +
3 files changed, 81 insertions(+), 1 deletion(-)
create mode 100644 scripts/gdb/linux/radixtree.py

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 66562a8242bd..b38b9085c702 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -21,7 +21,7 @@
#include <linux/mount.h>
#include <linux/huge_mm.h>
#include <linux/vmalloc.h>
-
+#include <linux/radix-tree.h>

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

@@ -71,6 +71,11 @@ LX_GDBPARSED(VMALLOC_TOTAL)
/* linux/swap.h */
LX_GDBPARSED(MAX_SWAPFILES)

+/* linux/radix-tree.h */
+LX_VALUE(RADIX_TREE_INDIRECT_PTR)
+LX_GDBPARSED(RADIX_TREE_HEIGHT_MASK)
+LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
+LX_GDBPARSED(RADIX_TREE_MAP_MASK)

/* Kernel Configs */
LX_CONFIG(CONFIG_HIGHMEM)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
new file mode 100644
index 000000000000..299d171e3571
--- /dev/null
+++ b/scripts/gdb/linux/radixtree.py
@@ -0,0 +1,74 @@
+#
+# gdb helper commands and functions for Linux kernel debugging
+#
+# Radix Tree Parser
+#
+# Copyright (c) 2016 Linaro Ltd
+#
+# Authors:
+# Kieran Bingham <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL version 2.
+#
+
+import gdb
+
+from linux import utils
+from linux import constants
+
+radix_tree_root_type = utils.CachedType("struct radix_tree_root")
+radix_tree_node_type = utils.CachedType("struct radix_tree_node")
+
+
+def is_indirect_ptr(node):
+ long_type = utils.get_long_type()
+ return (node.cast(long_type) & constants.LX_RADIX_TREE_INDIRECT_PTR)
+
+
+def indirect_to_ptr(node):
+ long_type = utils.get_long_type()
+ node_type = node.type
+ indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INDIRECT_PTR
+ return indirect_ptr.cast(node_type)
+
+
+def maxindex(height):
+ height = height & constants.LX_RADIX_TREE_HEIGHT_MASK
+ return gdb.parse_and_eval("height_to_maxindex["+str(height)+"]")
+
+
+def lookup(root, index):
+ node = root['rnode']
+ if node is 0:
+ return None
+
+ if not (is_indirect_ptr(node)):
+ if (index > 0):
+ return None
+ return node
+
+ node = indirect_to_ptr(node)
+
+ height = node['path'] & constants.LX_RADIX_TREE_HEIGHT_MASK
+ if (index > maxindex(height)):
+ return None
+
+ shift = (height-1) * constants.LX_RADIX_TREE_MAP_SHIFT
+
+ while True:
+ new_index = (index >> shift) & constants.LX_RADIX_TREE_MAP_MASK
+ slot = node['slots'][new_index]
+
+ # Below needs a bit more verification ...
+ # node = rcu_dereference_raw(*slot);
+ node = slot.cast(node.type.pointer()).dereference()
+ if node is 0:
+ return None
+
+ shift -= constants.LX_RADIX_TREE_MAP_SHIFT
+ height -= 1
+
+ if (height <= 0):
+ break
+
+ return node
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
index 6e0b0afd888a..3a80ad6eecad 100644
--- a/scripts/gdb/vmlinux-gdb.py
+++ b/scripts/gdb/vmlinux-gdb.py
@@ -31,3 +31,4 @@ else:
import linux.lists
import linux.proc
import linux.constants
+ import linux.radixtree
--
2.5.0

2016-03-03 11:44:22

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 09/13] scripts/gdb: Add meminfo command

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 <[email protected]>

---

Changes from v1:
- Updated to use LX_ macros for constants
- Utilise the LX_CONFIG() options for conditional printing
- Fixed meminfo command on Jan's target .config
- Added missing segments to meminfo command (HUGEPAGE, QUICKLIST)
- Adjusted for new list_for_each_entry() function
- Fixed up for !CONFIG_SWAP and !CONFIG_MMU targets (Tested STM32)

Changes from v2:
- Reduce line size on output lines causing pep8 warnings
- Remove crept in 'pass' statement
---
scripts/gdb/linux/constants.py.in | 34 ++++++
scripts/gdb/linux/proc.py | 228 ++++++++++++++++++++++++++++++++++++++
2 files changed, 262 insertions(+)

diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index 57213ad8cf75..66562a8242bd 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -12,8 +12,16 @@
*
*/

+#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/huge_mm.h>
+#include <linux/vmalloc.h>
+

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

@@ -51,3 +59,29 @@ LX_VALUE(MNT_NOATIME)
LX_VALUE(MNT_NODIRATIME)
LX_VALUE(MNT_RELATIME)

+/* asm/page.h */
+LX_GDBPARSED(PAGE_SHIFT)
+
+/* asm/thread_info.h */
+LX_GDBPARSED(THREAD_SIZE)
+
+/* linux/vmalloc.h */
+LX_GDBPARSED(VMALLOC_TOTAL)
+
+/* linux/swap.h */
+LX_GDBPARSED(MAX_SWAPFILES)
+
+
+/* Kernel Configs */
+LX_CONFIG(CONFIG_HIGHMEM)
+LX_CONFIG(CONFIG_MEMORY_FAILURE)
+LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
+LX_CONFIG(CONFIG_CMA)
+LX_CONFIG(CONFIG_MMU)
+LX_CONFIG(CONFIG_SWAP)
+
+#ifndef CONFIG_NR_QUICK
+#define CONFIG_NR_QUICK 0
+#endif
+LX_VALUE(CONFIG_NR_QUICK)
+LX_CONFIG(CONFIG_QUICKLIST)
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index 115f20b07a54..e5a8dbe3aa3a 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -195,3 +195,231 @@ values of that process namespace"""
info_opts(MNT_INFO, m_flags)))

LxMounts()
+
+
+bdev_type = utils.CachedType("struct block_device")
+bdev_ptr_type = bdev_type.get_type().pointer()
+
+
+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.list_for_each_entry(bdevs_head,
+ bdev_ptr_type,
+ "bd_list"):
+ try:
+ pages += bdev['bd_inode']['i_mapping']['nrpages']
+ except:
+ # Any memory read failures are simply not counted
+ pass
+ return pages
+
+ def total_swapcache_pages(self):
+ pages = 0
+ if not constants.LX_CONFIG_SWAP:
+ return 0
+
+ for i in range(0, int(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):
+ total_swap_pages = 0
+ overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
+ overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
+
+ if constants.LX_CONFIG_SWAP:
+ 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
+
+ def quicklist_total_size(self):
+ count = 0
+ quicklist = utils.gdb_eval_or_none("quicklist")
+ if quicklist is None:
+ return 0
+
+ for cpu in cpus.each_online_cpu():
+ ql = cpus.per_cpu(quicklist, cpu)
+ for q in range(0, constants.LX_CONFIG_NR_QUICK):
+ # for (q = ql; q < ql + CONFIG_NR_QUICK; q++)
+ # count += q->nr_pages
+ count += ql[q]['nr_pages']
+
+ return count
+
+ # Main lx-meminfo command execution
+ # See fs/proc/meminfo.c:meminfo_proc_show()
+ 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()
+ swapcached = self.total_swapcache_pages()
+
+ file_pages = self.gps("NR_FILE_PAGES")
+ cached = file_pages - swapcached - 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
+
+ kernelstack = int(self.gps("NR_KERNEL_STACK") *
+ constants.LX_THREAD_SIZE / 1024)
+
+ commitlimit = int(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(swapcached)) +
+ "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"))
+ )
+
+ if constants.LX_CONFIG_HIGHMEM:
+ totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
+ freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
+ lowtotal = totalram - totalhigh
+ lowfree = freeram - freehigh
+ 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(lowtotal)) +
+ "LowFree: {:8d} kB\n".format(self.K(lowfree))
+ )
+
+ if not constants.LX_CONFIG_MMU:
+ mmap_pg_alloc = gdb.parse_and_eval("mmap_pages_allocated.counter")
+ gdb.write(
+ "MmapCopy: {:8d} kB\n".format(self.K(mmap_pg_alloc))
+ )
+
+ 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 constants.LX_CONFIG_QUICKLIST:
+ quicklist = self.quicklist_total_size()
+ gdb.write(
+ "Quicklists: {:8d} kB\n".format(self.K(quicklist))
+ )
+
+ nfsunstable = self.gps("NR_UNSTABLE_NFS")
+ nr_bounce = self.gps("NR_BOUNCE")
+ writebacktmp = self.gps("NR_WRITEBACK_TEMP")
+ gdb.write(
+ "NFS_Unstable: {:8d} kB\n".format(self.K(nfsunstable)) +
+ "Bounce: {:8d} kB\n".format(self.K(nr_bounce)) +
+ "WritebackTmp: {:8d} kB\n".format(self.K(writebacktmp))
+ )
+
+ gdb.write(
+ "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)
+ )
+
+ # These are always zero now
+ gdb.write(
+ "VmallocUsed: {:8d} kB\n".format(0) +
+ "VmallocChunk: {:8d} kB\n".format(0)
+ )
+
+ if constants.LX_CONFIG_MEMORY_FAILURE:
+ gdb.write(
+ "HardwareCorrupted: {:8d} kB\n"
+ )
+
+ if constants.LX_CONFIG_TRANSPARENT_HUGEPAGE:
+ huge = self.gps("NR_ANON_TRANSPARENT_HUGEPAGES")
+ # HPAGE_PMD_NR can not be determined in constants.py
+ gdb.write(
+ "AnonHugePages: {:8d} kB ( * HPAGE_PMD_NR )\n"
+ .format(self.K(huge))
+ )
+ if constants.LX_CONFIG_CMA:
+ totalcma_pages = int(gdb.parse_and_eval("totalcma_pages"))
+ cmafree = self.gps("NR_FREE_CMA_PAGES")
+ gdb.write(
+ "CmaTotal: {:8d} kB\n".format(self.K(totalcma_pages)) +
+ "CmaFree: {:8d} kB\n".format(self.K(cmafree))
+ )
+
+LxMeminfo()
--
2.5.0

2016-03-03 11:44:18

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 10/13] scripts/gdb: Add cpu iterators

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

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

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


+def each_online_cpu():
+ for cpu in cpu_list("cpu_online_mask"):
+ yield cpu
+
+
+def each_present_cpu():
+ for cpu in cpu_list("cpu_present_mask"):
+ yield cpu
+
+
+def each_possible_cpu():
+ for cpu in cpu_list("cpu_possible_mask"):
+ yield cpu
+
+
+def print_cpus():
+ gdb.write("Possible CPUS : {}\n".format(list(each_possible_cpu())))
+ gdb.write("Present CPUS : {}\n".format(list(each_present_cpu())))
+ gdb.write("Online CPUS : {}\n".format(list(each_online_cpu())))
+
+
class PerCpu(gdb.Function):
"""Return per-cpu variable.

--
2.5.0

2016-03-03 11:41:27

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 01/13] scripts/gdb: Provide linux constants

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

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

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

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

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

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

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

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

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

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

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

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

2016-03-03 11:46:49

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 08/13] scripts/gdb: Add mount point list command

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

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

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

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

Changes since v2:
- dentry path helper moved to utils module
---
scripts/gdb/linux/constants.py.in | 21 +++++++++
scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
2 files changed, 120 insertions(+)

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

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

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

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

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


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

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

2016-03-03 11:47:05

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 07/13] scripts/gdb: Add io resource readers

Provide iomem_resource and ioports_resource printers and command hooks

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

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

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

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

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

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

2016-03-03 11:47:29

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 04/13] scripts/gdb: Provide exception catching parser

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

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

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

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

2016-03-03 11:47:45

by Kieran Bingham

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

Simplify the module list functions with the new list_for_each_entry
abstractions

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

diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py
index 25db8cff44a2..571489b3b9c2 100644
--- a/scripts/gdb/linux/modules.py
+++ b/scripts/gdb/linux/modules.py
@@ -13,7 +13,7 @@

import gdb

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


module_type = utils.CachedType("struct module")
@@ -23,12 +23,9 @@ def module_list():
global module_type
module_ptr_type = module_type.get_type().pointer()
modules = gdb.parse_and_eval("modules")
- entry = modules['next']
- end_of_list = modules.address

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


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

- source_list = module['source_list']
t = self._module_use_type.get_type().pointer()
- entry = source_list['next']
first = True
- while entry != source_list.address:
- use = utils.container_of(entry, t, "source_list")
+ sources = module['source_list']
+ for use in lists.list_for_each_entry(sources, t, "source_list"):
gdb.write("{separator}{name}".format(
separator=" " if first else ",",
name=use['source']['name'].string()))
first = False
- entry = entry['next']
+
gdb.write("\n")


--
2.5.0

2016-03-03 11:47:58

by Kieran Bingham

[permalink] [raw]
Subject: [PATCHv3 02/13] scripts/gdb: Provide kernel list item generators

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

CC: Jeff Mahoney <[email protected]>

Signed-off-by: Kieran Bingham <[email protected]>
---
Changes since v1:
* items function removed, and replaced with Jeff Mahoney's cleaner
implementations of list_for_each, and list_for_each_entry
---
scripts/gdb/linux/lists.py | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

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


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

2016-03-08 03:47:15

by Jeff Mahoney

[permalink] [raw]
Subject: Re: [PATCHv3 02/13] scripts/gdb: Provide kernel list item generators

On 3/3/16 6:40 AM, Kieran Bingham wrote:
> Facilitate linked-list items by providing a generator to return
> the dereferenced, and type-cast objects from a kernel linked list
>
> CC: Jeff Mahoney <[email protected]>
>
> Signed-off-by: Kieran Bingham <[email protected]>
> ---
> Changes since v1:
> * items function removed, and replaced with Jeff Mahoney's cleaner
> implementations of list_for_each, and list_for_each_entry
> ---
> scripts/gdb/linux/lists.py | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)
>
> diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
> index 3a3775bc162b..9f4503738e26 100644
> --- a/scripts/gdb/linux/lists.py
> +++ b/scripts/gdb/linux/lists.py
> @@ -18,6 +18,26 @@ from linux import utils
> list_head = utils.CachedType("struct list_head")
>
>
> +def list_for_each(head):
> + if head.type == list_head.get_type().pointer():
> + head = head.dereference()
> + elif head.type != list_head.get_type():
> + raise gdb.GdbError("Must be struct list_head not %s" % list_head.type)

Shouldn't this be % head.type?

> +
> + node = head['next'].dereference()
> + while node.address != head.address:
> + yield node.address
> + node = node['next'].dereference()
> +
> +
> +def list_for_each_entry(head, gdbtype, member):
> + for node in list_for_each(head):
> + if node.type != list_head.get_type().pointer():
> + raise TypeError("Type %s found. "
> + "Expected struct list_head *." % node.type)

Nit, but FWIW, I've adopted the kernel style of always keeping strings
on one line so they're easily greppable.

-Jeff

--
Jeff Mahoney
SUSE Labs


Attachments:
signature.asc (881.00 B)
OpenPGP digital signature

2016-03-08 07:55:36

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 02/13] scripts/gdb: Provide kernel list item generators

On 08/03/16 10:47, Jeff Mahoney wrote:
> On 3/3/16 6:40 AM, Kieran Bingham wrote:
>> Facilitate linked-list items by providing a generator to return
>> the dereferenced, and type-cast objects from a kernel linked list
>>
>> CC: Jeff Mahoney <[email protected]>
>>
>> Signed-off-by: Kieran Bingham <[email protected]>
>> ---
>> Changes since v1:
>> * items function removed, and replaced with Jeff Mahoney's cleaner
>> implementations of list_for_each, and list_for_each_entry
>> ---
>> scripts/gdb/linux/lists.py | 20 ++++++++++++++++++++
>> 1 file changed, 20 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py
>> index 3a3775bc162b..9f4503738e26 100644
>> --- a/scripts/gdb/linux/lists.py
>> +++ b/scripts/gdb/linux/lists.py
>> @@ -18,6 +18,26 @@ from linux import utils
>> list_head = utils.CachedType("struct list_head")
>>
>>
>> +def list_for_each(head):
>> + if head.type == list_head.get_type().pointer():
>> + head = head.dereference()
>> + elif head.type != list_head.get_type():
>> + raise gdb.GdbError("Must be struct list_head not %s" % list_head.type)
>
> Shouldn't this be % head.type?


Ahh yes, good spot thanks!

>
>> +
>> + node = head['next'].dereference()
>> + while node.address != head.address:
>> + yield node.address
>> + node = node['next'].dereference()
>> +
>> +
>> +def list_for_each_entry(head, gdbtype, member):
>> + for node in list_for_each(head):
>> + if node.type != list_head.get_type().pointer():
>> + raise TypeError("Type %s found. "
>> + "Expected struct list_head *." % node.type)
>
> Nit, but FWIW, I've adopted the kernel style of always keeping strings
> on one line so they're easily greppable.

Absolutely! good point. Looks like I was trying to make things fit for
the PEP8 tool. Not sure why I didn't pull just the arg to the next line.

Fixed up locally for the next spin.

Also I think as the rest of the kernel python code is using .format() it
probably makes sense to swap that too to match.

>
> -Jeff
>


Thanks for the eyes.

Regards
--
Kieran

2016-03-13 16:33:49

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 10/13] scripts/gdb: Add cpu iterators

On 2016-03-03 12:41, Kieran Bingham wrote:
> The linux kernel provides macro's for iterating against values from the
> cpu_list masks. By providing some commonly used masks, we can mirror the
> kernels helper macros with easy to use generators.
>
> Signed-off-by: Kieran Bingham <[email protected]>
> ---
> scripts/gdb/linux/cpus.py | 21 +++++++++++++++++++++
> 1 file changed, 21 insertions(+)
>
> diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
> index 4297b83fedef..59e24472c507 100644
> --- a/scripts/gdb/linux/cpus.py
> +++ b/scripts/gdb/linux/cpus.py
> @@ -100,6 +100,27 @@ def cpu_list(mask_name):
> yield cpu
>
>
> +def each_online_cpu():
> + for cpu in cpu_list("cpu_online_mask"):
> + yield cpu
> +
> +
> +def each_present_cpu():
> + for cpu in cpu_list("cpu_present_mask"):
> + yield cpu
> +
> +
> +def each_possible_cpu():
> + for cpu in cpu_list("cpu_possible_mask"):
> + yield cpu
> +

This patch requires rebasing over master (these masks became macros).

In general, please base on master or even next before posting.

> +
> +def print_cpus():
> + gdb.write("Possible CPUS : {}\n".format(list(each_possible_cpu())))
> + gdb.write("Present CPUS : {}\n".format(list(each_present_cpu())))
> + gdb.write("Online CPUS : {}\n".format(list(each_online_cpu())))

I think this can be useful. Let's make it a real, discoverable command.
lx-cpus?

> +
> +
> class PerCpu(gdb.Function):
> """Return per-cpu variable.
>
>

Jan

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

2016-03-13 16:34:25

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 08/13] scripts/gdb: Add mount point list command

On 2016-03-03 12:41, 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
>
> Signed-off-by: Kieran Bingham <[email protected]>
>
> ---
> Changes from v1:
> - Updated to use LX_ constant macros
> - Adjusted for new list_for_each_item() function
> - Removed unnessary Null check in vfs['mnt_parent']
> - Tested and not needed. It probably occured in early testing
> with a bad iterator
>
> Changes since v2:
> - dentry path helper moved to utils module
> ---
> scripts/gdb/linux/constants.py.in | 21 +++++++++
> scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 120 insertions(+)
>
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index 79d9d0092452..57213ad8cf75 100644
> --- a/scripts/gdb/linux/constants.py.in
> +++ b/scripts/gdb/linux/constants.py.in
> @@ -12,7 +12,11 @@
> *
> */
>
> +#include <linux/fs.h>
> +#include <linux/mount.h>
> +
> /* We need to stringify expanded macros so that they can be parsed */
> +
> #define STRING(x) #x
> #define XSTRING(x) STRING(x)
>
> @@ -30,3 +34,20 @@
> <!-- end-c-headers -->
>
> import gdb
> +
> +/* linux/fs.h */
> +LX_VALUE(MS_RDONLY)
> +LX_VALUE(MS_SYNCHRONOUS)
> +LX_VALUE(MS_MANDLOCK)
> +LX_VALUE(MS_DIRSYNC)
> +LX_VALUE(MS_NOATIME)
> +LX_VALUE(MS_NODIRATIME)
> +
> +/* linux/mount.h */
> +LX_VALUE(MNT_NOSUID)
> +LX_VALUE(MNT_NODEV)
> +LX_VALUE(MNT_NOEXEC)
> +LX_VALUE(MNT_NOATIME)
> +LX_VALUE(MNT_NODIRATIME)
> +LX_VALUE(MNT_RELATIME)
> +
> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
> index d855b2fd9a06..115f20b07a54 100644
> --- a/scripts/gdb/linux/proc.py
> +++ b/scripts/gdb/linux/proc.py
> @@ -12,6 +12,10 @@
> #
>
> import gdb
> +from linux import constants
> +from linux import utils
> +from linux import tasks
> +from linux import lists
>
>
> class LxCmdLine(gdb.Command):
> @@ -96,3 +100,98 @@ Equivalent to cat /proc/ioports on a running target"""
> return show_lx_resources("ioport_resource")
>
> LxIOPorts()
> +
> +
> +# Mount namespace viewer
> +# /proc/mounts
> +
> +def info_opts(lst, opt):
> + opts = ""
> + for key, string in lst.items():
> + if opt & key:
> + opts += string
> + return opts
> +
> +
> +FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
> + constants.LX_MS_MANDLOCK: ",mand",
> + constants.LX_MS_DIRSYNC: ",dirsync",
> + constants.LX_MS_NOATIME: ",noatime",
> + constants.LX_MS_NODIRATIME: ",nodiratime"}
> +
> +MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
> + constants.LX_MNT_NODEV: ",nodev",
> + constants.LX_MNT_NOEXEC: ",noexec",
> + constants.LX_MNT_NOATIME: ",noatime",
> + constants.LX_MNT_NODIRATIME: ",nodiratime",
> + constants.LX_MNT_RELATIME: ",relatime"}
> +
> +mount_type = utils.CachedType("struct mount")
> +mount_ptr_type = mount_type.get_type().pointer()
> +
> +
> +class LxMounts(gdb.Command):
> + """Report the VFS mounts of the current process namespace.
> +
> +Equivalent to cat /proc/mounts on a running target
> +An integer value can be supplied to display the mount
> +values of that process namespace"""
> +
> + def __init__(self):
> + super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
> +
> + # Equivalent to proc_namespace.c:show_vfsmnt
> + # However, that has the ability to call into s_op functions
> + # whereas we cannot and must make do with the information we can obtain.
> + def invoke(self, arg, from_tty):
> + argv = gdb.string_to_argv(arg)
> + if len(argv) >= 1:
> + try:
> + pid = int(argv[0])
> + except:
> + raise gdb.GdbError("Provide a PID as integer value")
> + else:
> + pid = 1
> +
> + task = tasks.get_task_by_pid(pid)
> + if not task:
> + raise gdb.GdbError("Couldn't find a process with PID {}"
> + .format(pid))
> +
> + namespace = task['nsproxy']['mnt_ns']
> + if not namespace:
> + raise gdb.GdbError("No namespace for current process")
> +
> + for vfs in lists.list_for_each_entry(
> + namespace['list'], mount_ptr_type, "mnt_list"):

pep8 and /me prefer

for vfs in lists.list_for_each_entry(namespace['list'],
mount_ptr_type, "mnt_list"):

> + devname = vfs['mnt_devname'].string()
> + devname = devname if devname else "none"
> +
> + pathname = ""
> + parent = vfs
> + while True:
> + mntpoint = parent['mnt_mountpoint']
> + pathname = utils.dentry_name(mntpoint) + pathname
> + if (parent == parent['mnt_parent']):
> + break
> + parent = parent['mnt_parent']
> +
> + if (pathname == ""):
> + pathname = "/"
> +
> + superblock = vfs['mnt']['mnt_sb']
> + fstype = superblock['s_type']['name'].string()
> + s_flags = int(superblock['s_flags'])
> + m_flags = int(vfs['mnt']['mnt_flags'])
> + rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
> +
> + gdb.write(
> + "{} {} {} {}{}{} 0 0\n"
> + .format(devname,
> + pathname,
> + fstype,
> + rd,
> + info_opts(FS_INFO, s_flags),
> + info_opts(MNT_INFO, m_flags)))
> +
> +LxMounts()
>

This doesn't list all parameters of a mount. Can this be fixed easily?

I also noticed that this script lists rootfs, /proc/mounts
interestingly not. Do you know why?

Jan

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

2016-03-13 16:34:48

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 09/13] scripts/gdb: Add meminfo command

On 2016-03-03 12:41, 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
>

Sound useful.

> Signed-off-by: Kieran Bingham <[email protected]>
>
> ---
>
> Changes from v1:
> - Updated to use LX_ macros for constants
> - Utilise the LX_CONFIG() options for conditional printing
> - Fixed meminfo command on Jan's target .config
> - Added missing segments to meminfo command (HUGEPAGE, QUICKLIST)
> - Adjusted for new list_for_each_entry() function
> - Fixed up for !CONFIG_SWAP and !CONFIG_MMU targets (Tested STM32)
>
> Changes from v2:
> - Reduce line size on output lines causing pep8 warnings
> - Remove crept in 'pass' statement
> ---
> scripts/gdb/linux/constants.py.in | 34 ++++++
> scripts/gdb/linux/proc.py | 228 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 262 insertions(+)
>
> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
> index 57213ad8cf75..66562a8242bd 100644
> --- a/scripts/gdb/linux/constants.py.in
> +++ b/scripts/gdb/linux/constants.py.in
> @@ -12,8 +12,16 @@
> *
> */
>
> +#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/huge_mm.h>
> +#include <linux/vmalloc.h>
> +
>
> /* We need to stringify expanded macros so that they can be parsed */
>
> @@ -51,3 +59,29 @@ LX_VALUE(MNT_NOATIME)
> LX_VALUE(MNT_NODIRATIME)
> LX_VALUE(MNT_RELATIME)
>
> +/* asm/page.h */
> +LX_GDBPARSED(PAGE_SHIFT)
> +
> +/* asm/thread_info.h */
> +LX_GDBPARSED(THREAD_SIZE)
> +
> +/* linux/vmalloc.h */
> +LX_GDBPARSED(VMALLOC_TOTAL)
> +
> +/* linux/swap.h */
> +LX_GDBPARSED(MAX_SWAPFILES)
> +
> +
> +/* Kernel Configs */
> +LX_CONFIG(CONFIG_HIGHMEM)
> +LX_CONFIG(CONFIG_MEMORY_FAILURE)
> +LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
> +LX_CONFIG(CONFIG_CMA)
> +LX_CONFIG(CONFIG_MMU)
> +LX_CONFIG(CONFIG_SWAP)
> +
> +#ifndef CONFIG_NR_QUICK
> +#define CONFIG_NR_QUICK 0
> +#endif
> +LX_VALUE(CONFIG_NR_QUICK)
> +LX_CONFIG(CONFIG_QUICKLIST)
> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
> index 115f20b07a54..e5a8dbe3aa3a 100644
> --- a/scripts/gdb/linux/proc.py
> +++ b/scripts/gdb/linux/proc.py
> @@ -195,3 +195,231 @@ values of that process namespace"""
> info_opts(MNT_INFO, m_flags)))
>
> LxMounts()
> +
> +
> +bdev_type = utils.CachedType("struct block_device")
> +bdev_ptr_type = bdev_type.get_type().pointer()
> +
> +
> +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.list_for_each_entry(bdevs_head,
> + bdev_ptr_type,
> + "bd_list"):
> + try:
> + pages += bdev['bd_inode']['i_mapping']['nrpages']
> + except:
> + # Any memory read failures are simply not counted
> + pass
> + return pages
> +
> + def total_swapcache_pages(self):
> + pages = 0
> + if not constants.LX_CONFIG_SWAP:
> + return 0
> +
> + for i in range(0, int(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):
> + total_swap_pages = 0
> + overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
> + overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
> +
> + if constants.LX_CONFIG_SWAP:
> + 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
> +
> + def quicklist_total_size(self):
> + count = 0
> + quicklist = utils.gdb_eval_or_none("quicklist")
> + if quicklist is None:
> + return 0
> +
> + for cpu in cpus.each_online_cpu():
> + ql = cpus.per_cpu(quicklist, cpu)
> + for q in range(0, constants.LX_CONFIG_NR_QUICK):
> + # for (q = ql; q < ql + CONFIG_NR_QUICK; q++)
> + # count += q->nr_pages
> + count += ql[q]['nr_pages']
> +
> + return count
> +
> + # Main lx-meminfo command execution
> + # See fs/proc/meminfo.c:meminfo_proc_show()
> + 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()
> + swapcached = self.total_swapcache_pages()
> +
> + file_pages = self.gps("NR_FILE_PAGES")
> + cached = file_pages - swapcached - 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
> +
> + kernelstack = int(self.gps("NR_KERNEL_STACK") *
> + constants.LX_THREAD_SIZE / 1024)
> +
> + commitlimit = int(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(swapcached)) +
> + "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"))
> + )
> +
> + if constants.LX_CONFIG_HIGHMEM:
> + totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
> + freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
> + lowtotal = totalram - totalhigh
> + lowfree = freeram - freehigh
> + 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(lowtotal)) +
> + "LowFree: {:8d} kB\n".format(self.K(lowfree))
> + )
> +
> + if not constants.LX_CONFIG_MMU:
> + mmap_pg_alloc = gdb.parse_and_eval("mmap_pages_allocated.counter")
> + gdb.write(
> + "MmapCopy: {:8d} kB\n".format(self.K(mmap_pg_alloc))
> + )
> +
> + 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 constants.LX_CONFIG_QUICKLIST:
> + quicklist = self.quicklist_total_size()
> + gdb.write(
> + "Quicklists: {:8d} kB\n".format(self.K(quicklist))

scripts/gdb/linux/proc.py:381:16: E121 continuation line under-indented
for hanging indent

Please make sure to run pep8 on the series before posting.

Jan

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

2016-03-13 16:35:55

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

On 2016-03-03 12:40, Kieran Bingham wrote:
> Hi Jan,
>
> V3 of the patchset respun. Now finally adding the lx-interrupts command
> after I resolved my issues with the Radix Tree parsing.
>
> This command only provides the interrupts that are available generically,
> and it seems that the /proc/interrupts function calls into arch specific
> layers to add extra information about arch specific interrupts.
>
> I'm not sure what to do about this yet - The values returned appear to be
> accurarate - but it's just a subset of the information returned by proc.
>

I didn't test this (due to the breakage in patch 10): Can you give
examples of what is missing, e.g. on ARM or x86?

> lx_thread_info_by_pid has been useful to me while looking at thread
> awareness, so I've included it into this patch set now. It makes finding
> internal thread information much more convenient.
>
> dentry_name has been moved to the utils module, as I am already using it
> in another command, so it's just not appropriate to be in proc.py
>
> The cpu_list mask iterators make calling for cpu in each_online_cpu() read
> nicely, and I've left the print_cpus() function in for now as a hidden
> helper. It can be used by calling:
> python linux.cpus.print_cpus()
> to check these generators, which I thought was quite nice - but I didn't
> know if it warranted a full command class for this.
>
> For convenience, this patch set submission can be found at
> http://git.linaro.org/people/kieran.bingham/linux.git gdb-scripts-2016-03-03-lkml-submission
>
> Patchset Changelog:
> v3:
> - Radix Tree parser introduced
> - cpu_list mask iterators added
> - lx-interrupts command implemented
> - dentry_name function moved to utils
> - lx-meminfo command PEP8 warnings fixed
> - lx_thread_info_by_pid introduced
>
> v2:
> - Reworked iterators with improved versions from Jeff Mahoney
> - Fixed !CONFIG_MODULES and !CONFIG_MMU support
> - Improvements on lx-meminfo
> - constants.py generated by Kbuild
> - IS_BUILTIN facility used to provide LX_CONFIG values
>
> v1:
> - Introduced lx-iomem, lx-ioports, lx-mounts, lx-meminfo
>
> Kieran Bingham (13):
> scripts/gdb: Provide linux constants
> scripts/gdb: Provide kernel list item generators
> scripts/gdb: Convert modules usage to lists functions
> scripts/gdb: Provide exception catching parser
> scripts/gdb: Support !CONFIG_MODULES gracefully
> scripts/gdb: Provide a dentry_name VFS path helper
> scripts/gdb: Add io resource readers
> scripts/gdb: Add mount point list command
> scripts/gdb: Add meminfo command
> scripts/gdb: Add cpu iterators
> scripts/gdb: Add a Radix Tree Parser
> scripts/gdb: Add interrupts command
> scripts/gdb: Add lx_thread_info_by_pid helper
>
> Kbuild | 10 +
> scripts/gdb/linux/Makefile | 12 +-
> scripts/gdb/linux/constants.py.in | 93 ++++++++
> scripts/gdb/linux/cpus.py | 21 ++
> scripts/gdb/linux/lists.py | 20 ++
> scripts/gdb/linux/modules.py | 22 +-
> scripts/gdb/linux/proc.py | 449 ++++++++++++++++++++++++++++++++++++++
> scripts/gdb/linux/radixtree.py | 74 +++++++
> scripts/gdb/linux/tasks.py | 19 ++
> scripts/gdb/linux/utils.py | 15 ++
> scripts/gdb/vmlinux-gdb.py | 2 +
> 11 files changed, 724 insertions(+), 13 deletions(-)
> create mode 100644 scripts/gdb/linux/constants.py.in
> create mode 100644 scripts/gdb/linux/radixtree.py
>

Besides the required rebase and, thus, the missing adjustment of the cpu
masks, I only have minor remarks. Maybe I will have more once I can play
with lx-interrupts ;).

However, I have a growing concern - I think we already discussed this
offline: Without automated tests, all these helpers may quickly fall
apart as the kernel changes. This should not block these patches, but
maybe you can think about some testing approaches before we have dozens
of helpers which can only be validated manually.

Thanks,
Jan

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

2016-03-13 18:16:51

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 09/13] scripts/gdb: Add meminfo command

On 13/03/16 16:34, Jan Kiszka wrote:
> On 2016-03-03 12:41, 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
>>
>
> Sound useful.
>
>> Signed-off-by: Kieran Bingham <[email protected]>
>>
>> ---
>>
>> Changes from v1:
>> - Updated to use LX_ macros for constants
>> - Utilise the LX_CONFIG() options for conditional printing
>> - Fixed meminfo command on Jan's target .config
>> - Added missing segments to meminfo command (HUGEPAGE, QUICKLIST)
>> - Adjusted for new list_for_each_entry() function
>> - Fixed up for !CONFIG_SWAP and !CONFIG_MMU targets (Tested STM32)
>>
>> Changes from v2:
>> - Reduce line size on output lines causing pep8 warnings
>> - Remove crept in 'pass' statement
>> ---
>> scripts/gdb/linux/constants.py.in | 34 ++++++
>> scripts/gdb/linux/proc.py | 228 ++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 262 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>> index 57213ad8cf75..66562a8242bd 100644
>> --- a/scripts/gdb/linux/constants.py.in
>> +++ b/scripts/gdb/linux/constants.py.in
>> @@ -12,8 +12,16 @@
>> *
>> */
>>
>> +#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/huge_mm.h>
>> +#include <linux/vmalloc.h>
>> +
>>
>> /* We need to stringify expanded macros so that they can be parsed */
>>
>> @@ -51,3 +59,29 @@ LX_VALUE(MNT_NOATIME)
>> LX_VALUE(MNT_NODIRATIME)
>> LX_VALUE(MNT_RELATIME)
>>
>> +/* asm/page.h */
>> +LX_GDBPARSED(PAGE_SHIFT)
>> +
>> +/* asm/thread_info.h */
>> +LX_GDBPARSED(THREAD_SIZE)
>> +
>> +/* linux/vmalloc.h */
>> +LX_GDBPARSED(VMALLOC_TOTAL)
>> +
>> +/* linux/swap.h */
>> +LX_GDBPARSED(MAX_SWAPFILES)
>> +
>> +
>> +/* Kernel Configs */
>> +LX_CONFIG(CONFIG_HIGHMEM)
>> +LX_CONFIG(CONFIG_MEMORY_FAILURE)
>> +LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
>> +LX_CONFIG(CONFIG_CMA)
>> +LX_CONFIG(CONFIG_MMU)
>> +LX_CONFIG(CONFIG_SWAP)
>> +
>> +#ifndef CONFIG_NR_QUICK
>> +#define CONFIG_NR_QUICK 0
>> +#endif
>> +LX_VALUE(CONFIG_NR_QUICK)
>> +LX_CONFIG(CONFIG_QUICKLIST)
>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>> index 115f20b07a54..e5a8dbe3aa3a 100644
>> --- a/scripts/gdb/linux/proc.py
>> +++ b/scripts/gdb/linux/proc.py
>> @@ -195,3 +195,231 @@ values of that process namespace"""
>> info_opts(MNT_INFO, m_flags)))
>>
>> LxMounts()
>> +
>> +
>> +bdev_type = utils.CachedType("struct block_device")
>> +bdev_ptr_type = bdev_type.get_type().pointer()
>> +
>> +
>> +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.list_for_each_entry(bdevs_head,
>> + bdev_ptr_type,
>> + "bd_list"):
>> + try:
>> + pages += bdev['bd_inode']['i_mapping']['nrpages']
>> + except:
>> + # Any memory read failures are simply not counted
>> + pass
>> + return pages
>> +
>> + def total_swapcache_pages(self):
>> + pages = 0
>> + if not constants.LX_CONFIG_SWAP:
>> + return 0
>> +
>> + for i in range(0, int(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):
>> + total_swap_pages = 0
>> + overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
>> + overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
>> +
>> + if constants.LX_CONFIG_SWAP:
>> + 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
>> +
>> + def quicklist_total_size(self):
>> + count = 0
>> + quicklist = utils.gdb_eval_or_none("quicklist")
>> + if quicklist is None:
>> + return 0
>> +
>> + for cpu in cpus.each_online_cpu():
>> + ql = cpus.per_cpu(quicklist, cpu)
>> + for q in range(0, constants.LX_CONFIG_NR_QUICK):
>> + # for (q = ql; q < ql + CONFIG_NR_QUICK; q++)
>> + # count += q->nr_pages
>> + count += ql[q]['nr_pages']
>> +
>> + return count
>> +
>> + # Main lx-meminfo command execution
>> + # See fs/proc/meminfo.c:meminfo_proc_show()
>> + 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()
>> + swapcached = self.total_swapcache_pages()
>> +
>> + file_pages = self.gps("NR_FILE_PAGES")
>> + cached = file_pages - swapcached - 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
>> +
>> + kernelstack = int(self.gps("NR_KERNEL_STACK") *
>> + constants.LX_THREAD_SIZE / 1024)
>> +
>> + commitlimit = int(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(swapcached)) +
>> + "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"))
>> + )
>> +
>> + if constants.LX_CONFIG_HIGHMEM:
>> + totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
>> + freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
>> + lowtotal = totalram - totalhigh
>> + lowfree = freeram - freehigh
>> + 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(lowtotal)) +
>> + "LowFree: {:8d} kB\n".format(self.K(lowfree))
>> + )
>> +
>> + if not constants.LX_CONFIG_MMU:
>> + mmap_pg_alloc = gdb.parse_and_eval("mmap_pages_allocated.counter")
>> + gdb.write(
>> + "MmapCopy: {:8d} kB\n".format(self.K(mmap_pg_alloc))
>> + )
>> +
>> + 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 constants.LX_CONFIG_QUICKLIST:
>> + quicklist = self.quicklist_total_size()
>> + gdb.write(
>> + "Quicklists: {:8d} kB\n".format(self.K(quicklist))
>
> scripts/gdb/linux/proc.py:381:16: E121 continuation line under-indented
> for hanging indent
>
> Please make sure to run pep8 on the series before posting.

My apologies for the error, but on my laptop pep8 runs clean?

What version are you running?
I have the following:

$ pep8 --version
1.6.2

Perhaps I need to update something here.

Kieran

2016-03-13 18:39:34

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 10/13] scripts/gdb: Add cpu iterators

On 13/03/16 16:33, Jan Kiszka wrote:
> On 2016-03-03 12:41, Kieran Bingham wrote:
>> The linux kernel provides macro's for iterating against values from the
>> cpu_list masks. By providing some commonly used masks, we can mirror the
>> kernels helper macros with easy to use generators.
>>
>> Signed-off-by: Kieran Bingham <[email protected]>
>> ---
>> scripts/gdb/linux/cpus.py | 21 +++++++++++++++++++++
>> 1 file changed, 21 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
>> index 4297b83fedef..59e24472c507 100644
>> --- a/scripts/gdb/linux/cpus.py
>> +++ b/scripts/gdb/linux/cpus.py
>> @@ -100,6 +100,27 @@ def cpu_list(mask_name):
>> yield cpu
>>
>>
>> +def each_online_cpu():
>> + for cpu in cpu_list("cpu_online_mask"):
>> + yield cpu
>> +
>> +
>> +def each_present_cpu():
>> + for cpu in cpu_list("cpu_present_mask"):
>> + yield cpu
>> +
>> +
>> +def each_possible_cpu():
>> + for cpu in cpu_list("cpu_possible_mask"):
>> + yield cpu
>> +
>
> This patch requires rebasing over master (these masks became macros).

Ah, yes Ok - I see.

Changed between 4b804c85dc37d...5aec01b834fd6

I'll fixup, and rebase on next.

> In general, please base on master or even next before posting.

Ack. Of course,

And of course this highlights your concerns about the need for continual
testing as well!

>
>> +
>> +def print_cpus():
>> + gdb.write("Possible CPUS : {}\n".format(list(each_possible_cpu())))
>> + gdb.write("Present CPUS : {}\n".format(list(each_present_cpu())))
>> + gdb.write("Online CPUS : {}\n".format(list(each_online_cpu())))
>
> I think this can be useful. Let's make it a real, discoverable command.
> lx-cpus?

Ack.

Looking through the above breakage, I see there is also a
cpu_active_mask, so I'll add that for completeness.

--
Kieran

>
>> +
>> +
>> class PerCpu(gdb.Function):
>> """Return per-cpu variable.
>>
>>
>
> Jan
>

2016-03-13 19:08:39

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 09/13] scripts/gdb: Add meminfo command

On 2016-03-13 19:16, Kieran Bingham wrote:
> On 13/03/16 16:34, Jan Kiszka wrote:
>> On 2016-03-03 12:41, 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
>>>
>>
>> Sound useful.
>>
>>> Signed-off-by: Kieran Bingham <[email protected]>
>>>
>>> ---
>>>
>>> Changes from v1:
>>> - Updated to use LX_ macros for constants
>>> - Utilise the LX_CONFIG() options for conditional printing
>>> - Fixed meminfo command on Jan's target .config
>>> - Added missing segments to meminfo command (HUGEPAGE, QUICKLIST)
>>> - Adjusted for new list_for_each_entry() function
>>> - Fixed up for !CONFIG_SWAP and !CONFIG_MMU targets (Tested STM32)
>>>
>>> Changes from v2:
>>> - Reduce line size on output lines causing pep8 warnings
>>> - Remove crept in 'pass' statement
>>> ---
>>> scripts/gdb/linux/constants.py.in | 34 ++++++
>>> scripts/gdb/linux/proc.py | 228 ++++++++++++++++++++++++++++++++++++++
>>> 2 files changed, 262 insertions(+)
>>>
>>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>>> index 57213ad8cf75..66562a8242bd 100644
>>> --- a/scripts/gdb/linux/constants.py.in
>>> +++ b/scripts/gdb/linux/constants.py.in
>>> @@ -12,8 +12,16 @@
>>> *
>>> */
>>>
>>> +#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/huge_mm.h>
>>> +#include <linux/vmalloc.h>
>>> +
>>>
>>> /* We need to stringify expanded macros so that they can be parsed */
>>>
>>> @@ -51,3 +59,29 @@ LX_VALUE(MNT_NOATIME)
>>> LX_VALUE(MNT_NODIRATIME)
>>> LX_VALUE(MNT_RELATIME)
>>>
>>> +/* asm/page.h */
>>> +LX_GDBPARSED(PAGE_SHIFT)
>>> +
>>> +/* asm/thread_info.h */
>>> +LX_GDBPARSED(THREAD_SIZE)
>>> +
>>> +/* linux/vmalloc.h */
>>> +LX_GDBPARSED(VMALLOC_TOTAL)
>>> +
>>> +/* linux/swap.h */
>>> +LX_GDBPARSED(MAX_SWAPFILES)
>>> +
>>> +
>>> +/* Kernel Configs */
>>> +LX_CONFIG(CONFIG_HIGHMEM)
>>> +LX_CONFIG(CONFIG_MEMORY_FAILURE)
>>> +LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
>>> +LX_CONFIG(CONFIG_CMA)
>>> +LX_CONFIG(CONFIG_MMU)
>>> +LX_CONFIG(CONFIG_SWAP)
>>> +
>>> +#ifndef CONFIG_NR_QUICK
>>> +#define CONFIG_NR_QUICK 0
>>> +#endif
>>> +LX_VALUE(CONFIG_NR_QUICK)
>>> +LX_CONFIG(CONFIG_QUICKLIST)
>>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>>> index 115f20b07a54..e5a8dbe3aa3a 100644
>>> --- a/scripts/gdb/linux/proc.py
>>> +++ b/scripts/gdb/linux/proc.py
>>> @@ -195,3 +195,231 @@ values of that process namespace"""
>>> info_opts(MNT_INFO, m_flags)))
>>>
>>> LxMounts()
>>> +
>>> +
>>> +bdev_type = utils.CachedType("struct block_device")
>>> +bdev_ptr_type = bdev_type.get_type().pointer()
>>> +
>>> +
>>> +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.list_for_each_entry(bdevs_head,
>>> + bdev_ptr_type,
>>> + "bd_list"):
>>> + try:
>>> + pages += bdev['bd_inode']['i_mapping']['nrpages']
>>> + except:
>>> + # Any memory read failures are simply not counted
>>> + pass
>>> + return pages
>>> +
>>> + def total_swapcache_pages(self):
>>> + pages = 0
>>> + if not constants.LX_CONFIG_SWAP:
>>> + return 0
>>> +
>>> + for i in range(0, int(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):
>>> + total_swap_pages = 0
>>> + overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
>>> + overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
>>> +
>>> + if constants.LX_CONFIG_SWAP:
>>> + 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
>>> +
>>> + def quicklist_total_size(self):
>>> + count = 0
>>> + quicklist = utils.gdb_eval_or_none("quicklist")
>>> + if quicklist is None:
>>> + return 0
>>> +
>>> + for cpu in cpus.each_online_cpu():
>>> + ql = cpus.per_cpu(quicklist, cpu)
>>> + for q in range(0, constants.LX_CONFIG_NR_QUICK):
>>> + # for (q = ql; q < ql + CONFIG_NR_QUICK; q++)
>>> + # count += q->nr_pages
>>> + count += ql[q]['nr_pages']
>>> +
>>> + return count
>>> +
>>> + # Main lx-meminfo command execution
>>> + # See fs/proc/meminfo.c:meminfo_proc_show()
>>> + 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()
>>> + swapcached = self.total_swapcache_pages()
>>> +
>>> + file_pages = self.gps("NR_FILE_PAGES")
>>> + cached = file_pages - swapcached - 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
>>> +
>>> + kernelstack = int(self.gps("NR_KERNEL_STACK") *
>>> + constants.LX_THREAD_SIZE / 1024)
>>> +
>>> + commitlimit = int(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(swapcached)) +
>>> + "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"))
>>> + )
>>> +
>>> + if constants.LX_CONFIG_HIGHMEM:
>>> + totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
>>> + freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
>>> + lowtotal = totalram - totalhigh
>>> + lowfree = freeram - freehigh
>>> + 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(lowtotal)) +
>>> + "LowFree: {:8d} kB\n".format(self.K(lowfree))
>>> + )
>>> +
>>> + if not constants.LX_CONFIG_MMU:
>>> + mmap_pg_alloc = gdb.parse_and_eval("mmap_pages_allocated.counter")
>>> + gdb.write(
>>> + "MmapCopy: {:8d} kB\n".format(self.K(mmap_pg_alloc))
>>> + )
>>> +
>>> + 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 constants.LX_CONFIG_QUICKLIST:
>>> + quicklist = self.quicklist_total_size()
>>> + gdb.write(
>>> + "Quicklists: {:8d} kB\n".format(self.K(quicklist))
>>
>> scripts/gdb/linux/proc.py:381:16: E121 continuation line under-indented
>> for hanging indent
>>
>> Please make sure to run pep8 on the series before posting.
>
> My apologies for the error, but on my laptop pep8 runs clean?
>
> What version are you running?
> I have the following:
>
> $ pep8 --version
> 1.6.2

Hmm, 1.5.7 here - pep8 regression?

The problem above is simply a missing space (4 required, instead of 3).

Jan

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

2016-03-14 12:13:27

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 09/13] scripts/gdb: Add meminfo command



On 13/03/16 19:08, Jan Kiszka wrote:
> On 2016-03-13 19:16, Kieran Bingham wrote:
>> On 13/03/16 16:34, Jan Kiszka wrote:
>>> On 2016-03-03 12:41, 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
>>>>
>>>
>>> Sound useful.
>>>
>>>> Signed-off-by: Kieran Bingham <[email protected]>
>>>>
>>>> ---
>>>>
>>>> Changes from v1:
>>>> - Updated to use LX_ macros for constants
>>>> - Utilise the LX_CONFIG() options for conditional printing
>>>> - Fixed meminfo command on Jan's target .config
>>>> - Added missing segments to meminfo command (HUGEPAGE, QUICKLIST)
>>>> - Adjusted for new list_for_each_entry() function
>>>> - Fixed up for !CONFIG_SWAP and !CONFIG_MMU targets (Tested STM32)
>>>>
>>>> Changes from v2:
>>>> - Reduce line size on output lines causing pep8 warnings
>>>> - Remove crept in 'pass' statement
>>>> ---
>>>> scripts/gdb/linux/constants.py.in | 34 ++++++
>>>> scripts/gdb/linux/proc.py | 228 ++++++++++++++++++++++++++++++++++++++
>>>> 2 files changed, 262 insertions(+)
>>>>
>>>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>>>> index 57213ad8cf75..66562a8242bd 100644
>>>> --- a/scripts/gdb/linux/constants.py.in
>>>> +++ b/scripts/gdb/linux/constants.py.in
>>>> @@ -12,8 +12,16 @@
>>>> *
>>>> */
>>>>
>>>> +#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/huge_mm.h>
>>>> +#include <linux/vmalloc.h>
>>>> +
>>>>
>>>> /* We need to stringify expanded macros so that they can be parsed */
>>>>
>>>> @@ -51,3 +59,29 @@ LX_VALUE(MNT_NOATIME)
>>>> LX_VALUE(MNT_NODIRATIME)
>>>> LX_VALUE(MNT_RELATIME)
>>>>
>>>> +/* asm/page.h */
>>>> +LX_GDBPARSED(PAGE_SHIFT)
>>>> +
>>>> +/* asm/thread_info.h */
>>>> +LX_GDBPARSED(THREAD_SIZE)
>>>> +
>>>> +/* linux/vmalloc.h */
>>>> +LX_GDBPARSED(VMALLOC_TOTAL)
>>>> +
>>>> +/* linux/swap.h */
>>>> +LX_GDBPARSED(MAX_SWAPFILES)
>>>> +
>>>> +
>>>> +/* Kernel Configs */
>>>> +LX_CONFIG(CONFIG_HIGHMEM)
>>>> +LX_CONFIG(CONFIG_MEMORY_FAILURE)
>>>> +LX_CONFIG(CONFIG_TRANSPARENT_HUGEPAGE)
>>>> +LX_CONFIG(CONFIG_CMA)
>>>> +LX_CONFIG(CONFIG_MMU)
>>>> +LX_CONFIG(CONFIG_SWAP)
>>>> +
>>>> +#ifndef CONFIG_NR_QUICK
>>>> +#define CONFIG_NR_QUICK 0
>>>> +#endif
>>>> +LX_VALUE(CONFIG_NR_QUICK)
>>>> +LX_CONFIG(CONFIG_QUICKLIST)
>>>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>>>> index 115f20b07a54..e5a8dbe3aa3a 100644
>>>> --- a/scripts/gdb/linux/proc.py
>>>> +++ b/scripts/gdb/linux/proc.py
>>>> @@ -195,3 +195,231 @@ values of that process namespace"""
>>>> info_opts(MNT_INFO, m_flags)))
>>>>
>>>> LxMounts()
>>>> +
>>>> +
>>>> +bdev_type = utils.CachedType("struct block_device")
>>>> +bdev_ptr_type = bdev_type.get_type().pointer()
>>>> +
>>>> +
>>>> +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.list_for_each_entry(bdevs_head,
>>>> + bdev_ptr_type,
>>>> + "bd_list"):
>>>> + try:
>>>> + pages += bdev['bd_inode']['i_mapping']['nrpages']
>>>> + except:
>>>> + # Any memory read failures are simply not counted
>>>> + pass
>>>> + return pages
>>>> +
>>>> + def total_swapcache_pages(self):
>>>> + pages = 0
>>>> + if not constants.LX_CONFIG_SWAP:
>>>> + return 0
>>>> +
>>>> + for i in range(0, int(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):
>>>> + total_swap_pages = 0
>>>> + overcommit = int(gdb.parse_and_eval("sysctl_overcommit_kbytes"))
>>>> + overcommit_ratio = int(gdb.parse_and_eval("sysctl_overcommit_ratio"))
>>>> +
>>>> + if constants.LX_CONFIG_SWAP:
>>>> + 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
>>>> +
>>>> + def quicklist_total_size(self):
>>>> + count = 0
>>>> + quicklist = utils.gdb_eval_or_none("quicklist")
>>>> + if quicklist is None:
>>>> + return 0
>>>> +
>>>> + for cpu in cpus.each_online_cpu():
>>>> + ql = cpus.per_cpu(quicklist, cpu)
>>>> + for q in range(0, constants.LX_CONFIG_NR_QUICK):
>>>> + # for (q = ql; q < ql + CONFIG_NR_QUICK; q++)
>>>> + # count += q->nr_pages
>>>> + count += ql[q]['nr_pages']
>>>> +
>>>> + return count
>>>> +
>>>> + # Main lx-meminfo command execution
>>>> + # See fs/proc/meminfo.c:meminfo_proc_show()
>>>> + 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()
>>>> + swapcached = self.total_swapcache_pages()
>>>> +
>>>> + file_pages = self.gps("NR_FILE_PAGES")
>>>> + cached = file_pages - swapcached - 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
>>>> +
>>>> + kernelstack = int(self.gps("NR_KERNEL_STACK") *
>>>> + constants.LX_THREAD_SIZE / 1024)
>>>> +
>>>> + commitlimit = int(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(swapcached)) +
>>>> + "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"))
>>>> + )
>>>> +
>>>> + if constants.LX_CONFIG_HIGHMEM:
>>>> + totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
>>>> + freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
>>>> + lowtotal = totalram - totalhigh
>>>> + lowfree = freeram - freehigh
>>>> + 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(lowtotal)) +
>>>> + "LowFree: {:8d} kB\n".format(self.K(lowfree))
>>>> + )
>>>> +
>>>> + if not constants.LX_CONFIG_MMU:
>>>> + mmap_pg_alloc = gdb.parse_and_eval("mmap_pages_allocated.counter")
>>>> + gdb.write(
>>>> + "MmapCopy: {:8d} kB\n".format(self.K(mmap_pg_alloc))
>>>> + )
>>>> +
>>>> + 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 constants.LX_CONFIG_QUICKLIST:
>>>> + quicklist = self.quicklist_total_size()
>>>> + gdb.write(
>>>> + "Quicklists: {:8d} kB\n".format(self.K(quicklist))
>>>
>>> scripts/gdb/linux/proc.py:381:16: E121 continuation line under-indented
>>> for hanging indent
>>>
>>> Please make sure to run pep8 on the series before posting.
>>
>> My apologies for the error, but on my laptop pep8 runs clean?
>>
>> What version are you running?
>> I have the following:
>>
>> $ pep8 --version
>> 1.6.2
>
> Hmm, 1.5.7 here - pep8 regression?
>
> The problem above is simply a missing space (4 required, instead of 3).

Ok - I see it.
Fixed up locally ready for a v4

I will try to downgrade to 1.5.7, and retest before v4. Then try to
figure out where to report a regression on the tool!

>
> Jan
>

2016-03-14 14:39:26

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 08/13] scripts/gdb: Add mount point list command

On 13/03/16 16:34, Jan Kiszka wrote:
> On 2016-03-03 12:41, 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
>>
>> Signed-off-by: Kieran Bingham <[email protected]>
>>
>> ---
>> Changes from v1:
>> - Updated to use LX_ constant macros
>> - Adjusted for new list_for_each_item() function
>> - Removed unnecessary Null check in vfs['mnt_parent']
>> - Tested and not needed. It probably occurred in early testing
>> with a bad iterator
>>
>> Changes since v2:
>> - dentry path helper moved to utils module
>> ---
>> scripts/gdb/linux/constants.py.in | 21 +++++++++
>> scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 120 insertions(+)
>>
>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>> index 79d9d0092452..57213ad8cf75 100644
>> --- a/scripts/gdb/linux/constants.py.in
>> +++ b/scripts/gdb/linux/constants.py.in
>> @@ -12,7 +12,11 @@
>> *
>> */
>>
>> +#include <linux/fs.h>
>> +#include <linux/mount.h>
>> +
>> /* We need to stringify expanded macros so that they can be parsed */
>> +
>> #define STRING(x) #x
>> #define XSTRING(x) STRING(x)
>>
>> @@ -30,3 +34,20 @@
>> <!-- end-c-headers -->
>>
>> import gdb
>> +
>> +/* linux/fs.h */
>> +LX_VALUE(MS_RDONLY)
>> +LX_VALUE(MS_SYNCHRONOUS)
>> +LX_VALUE(MS_MANDLOCK)
>> +LX_VALUE(MS_DIRSYNC)
>> +LX_VALUE(MS_NOATIME)
>> +LX_VALUE(MS_NODIRATIME)
>> +
>> +/* linux/mount.h */
>> +LX_VALUE(MNT_NOSUID)
>> +LX_VALUE(MNT_NODEV)
>> +LX_VALUE(MNT_NOEXEC)
>> +LX_VALUE(MNT_NOATIME)
>> +LX_VALUE(MNT_NODIRATIME)
>> +LX_VALUE(MNT_RELATIME)
>> +
>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>> index d855b2fd9a06..115f20b07a54 100644
>> --- a/scripts/gdb/linux/proc.py
>> +++ b/scripts/gdb/linux/proc.py
>> @@ -12,6 +12,10 @@
>> #
>>
>> import gdb
>> +from linux import constants
>> +from linux import utils
>> +from linux import tasks
>> +from linux import lists
>>
>>
>> class LxCmdLine(gdb.Command):
>> @@ -96,3 +100,98 @@ Equivalent to cat /proc/ioports on a running target"""
>> return show_lx_resources("ioport_resource")
>>
>> LxIOPorts()
>> +
>> +
>> +# Mount namespace viewer
>> +# /proc/mounts
>> +
>> +def info_opts(lst, opt):
>> + opts = ""
>> + for key, string in lst.items():
>> + if opt & key:
>> + opts += string
>> + return opts
>> +
>> +
>> +FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
>> + constants.LX_MS_MANDLOCK: ",mand",
>> + constants.LX_MS_DIRSYNC: ",dirsync",
>> + constants.LX_MS_NOATIME: ",noatime",
>> + constants.LX_MS_NODIRATIME: ",nodiratime"}
>> +
>> +MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
>> + constants.LX_MNT_NODEV: ",nodev",
>> + constants.LX_MNT_NOEXEC: ",noexec",
>> + constants.LX_MNT_NOATIME: ",noatime",
>> + constants.LX_MNT_NODIRATIME: ",nodiratime",
>> + constants.LX_MNT_RELATIME: ",relatime"}
>> +
>> +mount_type = utils.CachedType("struct mount")
>> +mount_ptr_type = mount_type.get_type().pointer()
>> +
>> +
>> +class LxMounts(gdb.Command):
>> + """Report the VFS mounts of the current process namespace.
>> +
>> +Equivalent to cat /proc/mounts on a running target
>> +An integer value can be supplied to display the mount
>> +values of that process namespace"""
>> +
>> + def __init__(self):
>> + super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
>> +
>> + # Equivalent to proc_namespace.c:show_vfsmnt
>> + # However, that has the ability to call into s_op functions
>> + # whereas we cannot and must make do with the information we can obtain.
>> + def invoke(self, arg, from_tty):
>> + argv = gdb.string_to_argv(arg)
>> + if len(argv) >= 1:
>> + try:
>> + pid = int(argv[0])
>> + except:
>> + raise gdb.GdbError("Provide a PID as integer value")
>> + else:
>> + pid = 1
>> +
>> + task = tasks.get_task_by_pid(pid)
>> + if not task:
>> + raise gdb.GdbError("Couldn't find a process with PID {}"
>> + .format(pid))
>> +
>> + namespace = task['nsproxy']['mnt_ns']
>> + if not namespace:
>> + raise gdb.GdbError("No namespace for current process")
>> +
>> + for vfs in lists.list_for_each_entry(
>> + namespace['list'], mount_ptr_type, "mnt_list"):
>
> pep8 and /me prefer
>
> for vfs in lists.list_for_each_entry(namespace['list'],
> mount_ptr_type, "mnt_list"):

Ack. No problem.

Did pep8 tool generate a warning here? (Just wondering if this is
another instance of my tool behaving differently)

>
>> + devname = vfs['mnt_devname'].string()
>> + devname = devname if devname else "none"
>> +
>> + pathname = ""
>> + parent = vfs
>> + while True:
>> + mntpoint = parent['mnt_mountpoint']
>> + pathname = utils.dentry_name(mntpoint) + pathname
>> + if (parent == parent['mnt_parent']):
>> + break
>> + parent = parent['mnt_parent']
>> +
>> + if (pathname == ""):
>> + pathname = "/"
>> +
>> + superblock = vfs['mnt']['mnt_sb']
>> + fstype = superblock['s_type']['name'].string()
>> + s_flags = int(superblock['s_flags'])
>> + m_flags = int(vfs['mnt']['mnt_flags'])
>> + rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
>> +
>> + gdb.write(
>> + "{} {} {} {}{}{} 0 0\n"
>> + .format(devname,
>> + pathname,
>> + fstype,
>> + rd,
>> + info_opts(FS_INFO, s_flags),
>> + info_opts(MNT_INFO, m_flags)))
>> +
>> +LxMounts()
>>
>
> This doesn't list all parameters of a mount. Can this be fixed easily?

Not easily I'm afraid: The comment at the top was hoping to highlight this:

>> + # 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.

Each VFS can call into an extra hook to provide extra option
information. We would need to duplicate each of those implementations :(

>
> I also noticed that this script lists rootfs, /proc/mounts
> interestingly not. Do you know why?

That is somewhat intriguing! - No, not yet - I did some digging and
haven't come up with a clear answer yet. I'll let you know when I find out!

Kieran

>
> Jan
>

2016-03-14 14:40:12

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

On 13/03/16 16:35, Jan Kiszka wrote:
> On 2016-03-03 12:40, Kieran Bingham wrote:
>> Hi Jan,
>>
>> V3 of the patchset respun. Now finally adding the lx-interrupts command
>> after I resolved my issues with the Radix Tree parsing.
>>
>> This command only provides the interrupts that are available generically,
>> and it seems that the /proc/interrupts function calls into arch specific
>> layers to add extra information about arch specific interrupts.
>>
>> I'm not sure what to do about this yet - The values returned appear to be
>> accurate - but it's just a subset of the information returned by proc.
>>
>
> I didn't test this (due to the breakage in patch 10): Can you give
> examples of what is missing, e.g. on ARM or x86?
On ARM this is :

(gdb) lx-interrupts
CPU0 CPU1
18: 587828 150187
36: 66574 0
41: 8 0
42: 106 0
43: 100 0
(gdb) c


vs

root@ArmACookieMonster:~# cat /proc/interrupts
CPU0 CPU1
18: 588057 150274 GIC-0 27 Edge arch_timer
20: 0 0 GIC-0 34 Level timer
36: 66599 0 GIC-0 47 Level eth0
39: 0 0 GIC-0 41 Level mmci-pl18x (cmd)
40: 0 0 GIC-0 42 Level mmci-pl18x (pio)
41: 8 0 GIC-0 44 Level kmi-pl050
42: 106 0 GIC-0 45 Level kmi-pl050
43: 112 0 GIC-0 37 Level uart-pl011
49: 0 0 GIC-0 36 Level rtc-pl031
IPI0: 0 1 CPU wakeup interrupts
IPI1: 0 0 Timer broadcast interrupts
IPI2: 15867 281362 Rescheduling interrupts
IPI3: 0 6 Function call interrupts
IPI4: 0 0 CPU stop interrupts
IPI5: 0 0 IRQ work interrupts
IPI6: 0 0 completion interrupts
Err: 0

So quite a substantial subset :(

>
>> lx_thread_info_by_pid has been useful to me while looking at thread
>> awareness, so I've included it into this patch set now. It makes finding
>> internal thread information much more convenient.
>>
>> dentry_name has been moved to the utils module, as I am already using it
>> in another command, so it's just not appropriate to be in proc.py
>>
>> The cpu_list mask iterators make calling for cpu in each_online_cpu() read
>> nicely, and I've left the print_cpus() function in for now as a hidden
>> helper. It can be used by calling:
>> python linux.cpus.print_cpus()
>> to check these generators, which I thought was quite nice - but I didn't
>> know if it warranted a full command class for this.
>>
>> For convenience, this patch set submission can be found at
>> http://git.linaro.org/people/kieran.bingham/linux.git gdb-scripts-2016-03-03-lkml-submission
>>
>> Patchset Changelog:
>> v3:
>> - Radix Tree parser introduced
>> - cpu_list mask iterators added
>> - lx-interrupts command implemented
>> - dentry_name function moved to utils
>> - lx-meminfo command PEP8 warnings fixed
>> - lx_thread_info_by_pid introduced
>>
>> v2:
>> - Reworked iterators with improved versions from Jeff Mahoney
>> - Fixed !CONFIG_MODULES and !CONFIG_MMU support
>> - Improvements on lx-meminfo
>> - constants.py generated by Kbuild
>> - IS_BUILTIN facility used to provide LX_CONFIG values
>>
>> v1:
>> - Introduced lx-iomem, lx-ioports, lx-mounts, lx-meminfo
>>
>> Kieran Bingham (13):
>> scripts/gdb: Provide linux constants
>> scripts/gdb: Provide kernel list item generators
>> scripts/gdb: Convert modules usage to lists functions
>> scripts/gdb: Provide exception catching parser
>> scripts/gdb: Support !CONFIG_MODULES gracefully
>> scripts/gdb: Provide a dentry_name VFS path helper
>> scripts/gdb: Add io resource readers
>> scripts/gdb: Add mount point list command
>> scripts/gdb: Add meminfo command
>> scripts/gdb: Add cpu iterators
>> scripts/gdb: Add a Radix Tree Parser
>> scripts/gdb: Add interrupts command
>> scripts/gdb: Add lx_thread_info_by_pid helper
>>
>> Kbuild | 10 +
>> scripts/gdb/linux/Makefile | 12 +-
>> scripts/gdb/linux/constants.py.in | 93 ++++++++
>> scripts/gdb/linux/cpus.py | 21 ++
>> scripts/gdb/linux/lists.py | 20 ++
>> scripts/gdb/linux/modules.py | 22 +-
>> scripts/gdb/linux/proc.py | 449 ++++++++++++++++++++++++++++++++++++++
>> scripts/gdb/linux/radixtree.py | 74 +++++++
>> scripts/gdb/linux/tasks.py | 19 ++
>> scripts/gdb/linux/utils.py | 15 ++
>> scripts/gdb/vmlinux-gdb.py | 2 +
>> 11 files changed, 724 insertions(+), 13 deletions(-)
>> create mode 100644 scripts/gdb/linux/constants.py.in
>> create mode 100644 scripts/gdb/linux/radixtree.py
>>
>
> Besides the required rebase and, thus, the missing adjustment of the cpu
> masks, I only have minor remarks. Maybe I will have more once I can play
> with lx-interrupts ;).

Ok - well it's fixed up locally, it's just a matter of prefixing the
strings with two underscores. I should be able to get the updated
patches out soon I hope.

> However, I have a growing concern - I think we already discussed this
> offline: Without automated tests, all these helpers may quickly fall
> apart as the kernel changes. This should not block these patches, but
> maybe you can think about some testing approaches before we have dozens
> of helpers which can only be validated manually.

Yes, indeed - and the breakage already seen between v4.4 and v4.5 only
solidifies the need!

I was able to chat to a few of the Linaro LAVA guys on this topic while
I was at Connect, and they already have virtual kernels being boot
tested. This should provide a good infrastructure to run some automated
tests on.

So we have the infrastructure - we just need to get the tests written,
and included.

--
Kieran

> Thanks,
> Jan
>

2016-03-14 15:05:32

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 08/13] scripts/gdb: Add mount point list command

On 2016-03-14 15:39, Kieran Bingham wrote:
> On 13/03/16 16:34, Jan Kiszka wrote:
>> On 2016-03-03 12:41, 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
>>>
>>> Signed-off-by: Kieran Bingham <[email protected]>
>>>
>>> ---
>>> Changes from v1:
>>> - Updated to use LX_ constant macros
>>> - Adjusted for new list_for_each_item() function
>>> - Removed unnecessary Null check in vfs['mnt_parent']
>>> - Tested and not needed. It probably occurred in early testing
>>> with a bad iterator
>>>
>>> Changes since v2:
>>> - dentry path helper moved to utils module
>>> ---
>>> scripts/gdb/linux/constants.py.in | 21 +++++++++
>>> scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
>>> 2 files changed, 120 insertions(+)
>>>
>>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>>> index 79d9d0092452..57213ad8cf75 100644
>>> --- a/scripts/gdb/linux/constants.py.in
>>> +++ b/scripts/gdb/linux/constants.py.in
>>> @@ -12,7 +12,11 @@
>>> *
>>> */
>>>
>>> +#include <linux/fs.h>
>>> +#include <linux/mount.h>
>>> +
>>> /* We need to stringify expanded macros so that they can be parsed */
>>> +
>>> #define STRING(x) #x
>>> #define XSTRING(x) STRING(x)
>>>
>>> @@ -30,3 +34,20 @@
>>> <!-- end-c-headers -->
>>>
>>> import gdb
>>> +
>>> +/* linux/fs.h */
>>> +LX_VALUE(MS_RDONLY)
>>> +LX_VALUE(MS_SYNCHRONOUS)
>>> +LX_VALUE(MS_MANDLOCK)
>>> +LX_VALUE(MS_DIRSYNC)
>>> +LX_VALUE(MS_NOATIME)
>>> +LX_VALUE(MS_NODIRATIME)
>>> +
>>> +/* linux/mount.h */
>>> +LX_VALUE(MNT_NOSUID)
>>> +LX_VALUE(MNT_NODEV)
>>> +LX_VALUE(MNT_NOEXEC)
>>> +LX_VALUE(MNT_NOATIME)
>>> +LX_VALUE(MNT_NODIRATIME)
>>> +LX_VALUE(MNT_RELATIME)
>>> +
>>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>>> index d855b2fd9a06..115f20b07a54 100644
>>> --- a/scripts/gdb/linux/proc.py
>>> +++ b/scripts/gdb/linux/proc.py
>>> @@ -12,6 +12,10 @@
>>> #
>>>
>>> import gdb
>>> +from linux import constants
>>> +from linux import utils
>>> +from linux import tasks
>>> +from linux import lists
>>>
>>>
>>> class LxCmdLine(gdb.Command):
>>> @@ -96,3 +100,98 @@ Equivalent to cat /proc/ioports on a running target"""
>>> return show_lx_resources("ioport_resource")
>>>
>>> LxIOPorts()
>>> +
>>> +
>>> +# Mount namespace viewer
>>> +# /proc/mounts
>>> +
>>> +def info_opts(lst, opt):
>>> + opts = ""
>>> + for key, string in lst.items():
>>> + if opt & key:
>>> + opts += string
>>> + return opts
>>> +
>>> +
>>> +FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
>>> + constants.LX_MS_MANDLOCK: ",mand",
>>> + constants.LX_MS_DIRSYNC: ",dirsync",
>>> + constants.LX_MS_NOATIME: ",noatime",
>>> + constants.LX_MS_NODIRATIME: ",nodiratime"}
>>> +
>>> +MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
>>> + constants.LX_MNT_NODEV: ",nodev",
>>> + constants.LX_MNT_NOEXEC: ",noexec",
>>> + constants.LX_MNT_NOATIME: ",noatime",
>>> + constants.LX_MNT_NODIRATIME: ",nodiratime",
>>> + constants.LX_MNT_RELATIME: ",relatime"}
>>> +
>>> +mount_type = utils.CachedType("struct mount")
>>> +mount_ptr_type = mount_type.get_type().pointer()
>>> +
>>> +
>>> +class LxMounts(gdb.Command):
>>> + """Report the VFS mounts of the current process namespace.
>>> +
>>> +Equivalent to cat /proc/mounts on a running target
>>> +An integer value can be supplied to display the mount
>>> +values of that process namespace"""
>>> +
>>> + def __init__(self):
>>> + super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
>>> +
>>> + # Equivalent to proc_namespace.c:show_vfsmnt
>>> + # However, that has the ability to call into s_op functions
>>> + # whereas we cannot and must make do with the information we can obtain.
>>> + def invoke(self, arg, from_tty):
>>> + argv = gdb.string_to_argv(arg)
>>> + if len(argv) >= 1:
>>> + try:
>>> + pid = int(argv[0])
>>> + except:
>>> + raise gdb.GdbError("Provide a PID as integer value")
>>> + else:
>>> + pid = 1
>>> +
>>> + task = tasks.get_task_by_pid(pid)
>>> + if not task:
>>> + raise gdb.GdbError("Couldn't find a process with PID {}"
>>> + .format(pid))
>>> +
>>> + namespace = task['nsproxy']['mnt_ns']
>>> + if not namespace:
>>> + raise gdb.GdbError("No namespace for current process")
>>> +
>>> + for vfs in lists.list_for_each_entry(
>>> + namespace['list'], mount_ptr_type, "mnt_list"):
>>
>> pep8 and /me prefer
>>
>> for vfs in lists.list_for_each_entry(namespace['list'],
>> mount_ptr_type, "mnt_list"):
>
> Ack. No problem.
>
> Did pep8 tool generate a warning here? (Just wondering if this is
> another instance of my tool behaving differently)

Yep.

>
>>
>>> + devname = vfs['mnt_devname'].string()
>>> + devname = devname if devname else "none"
>>> +
>>> + pathname = ""
>>> + parent = vfs
>>> + while True:
>>> + mntpoint = parent['mnt_mountpoint']
>>> + pathname = utils.dentry_name(mntpoint) + pathname
>>> + if (parent == parent['mnt_parent']):
>>> + break
>>> + parent = parent['mnt_parent']
>>> +
>>> + if (pathname == ""):
>>> + pathname = "/"
>>> +
>>> + superblock = vfs['mnt']['mnt_sb']
>>> + fstype = superblock['s_type']['name'].string()
>>> + s_flags = int(superblock['s_flags'])
>>> + m_flags = int(vfs['mnt']['mnt_flags'])
>>> + rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
>>> +
>>> + gdb.write(
>>> + "{} {} {} {}{}{} 0 0\n"
>>> + .format(devname,
>>> + pathname,
>>> + fstype,
>>> + rd,
>>> + info_opts(FS_INFO, s_flags),
>>> + info_opts(MNT_INFO, m_flags)))
>>> +
>>> +LxMounts()
>>>
>>
>> This doesn't list all parameters of a mount. Can this be fixed easily?
>
> Not easily I'm afraid: The comment at the top was hoping to highlight this:
>
>>> + # 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.

Sorry, missed this.

>
> Each VFS can call into an extra hook to provide extra option
> information. We would need to duplicate each of those implementations :(

So these options are not available as string somewhere?

Jan

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

2016-03-14 15:09:17

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

On 2016-03-14 15:40, Kieran Bingham wrote:
> On 13/03/16 16:35, Jan Kiszka wrote:
>> On 2016-03-03 12:40, Kieran Bingham wrote:
>>> Hi Jan,
>>>
>>> V3 of the patchset respun. Now finally adding the lx-interrupts command
>>> after I resolved my issues with the Radix Tree parsing.
>>>
>>> This command only provides the interrupts that are available generically,
>>> and it seems that the /proc/interrupts function calls into arch specific
>>> layers to add extra information about arch specific interrupts.
>>>
>>> I'm not sure what to do about this yet - The values returned appear to be
>>> accurate - but it's just a subset of the information returned by proc.
>>>
>>
>> I didn't test this (due to the breakage in patch 10): Can you give
>> examples of what is missing, e.g. on ARM or x86?
> On ARM this is :
>
> (gdb) lx-interrupts
> CPU0 CPU1
> 18: 587828 150187
> 36: 66574 0
> 41: 8 0
> 42: 106 0
> 43: 100 0
> (gdb) c
>
>
> vs
>
> root@ArmACookieMonster:~# cat /proc/interrupts
> CPU0 CPU1
> 18: 588057 150274 GIC-0 27 Edge arch_timer
> 20: 0 0 GIC-0 34 Level timer
> 36: 66599 0 GIC-0 47 Level eth0
> 39: 0 0 GIC-0 41 Level mmci-pl18x (cmd)
> 40: 0 0 GIC-0 42 Level mmci-pl18x (pio)
> 41: 8 0 GIC-0 44 Level kmi-pl050
> 42: 106 0 GIC-0 45 Level kmi-pl050
> 43: 112 0 GIC-0 37 Level uart-pl011
> 49: 0 0 GIC-0 36 Level rtc-pl031
> IPI0: 0 1 CPU wakeup interrupts
> IPI1: 0 0 Timer broadcast interrupts
> IPI2: 15867 281362 Rescheduling interrupts
> IPI3: 0 6 Function call interrupts
> IPI4: 0 0 CPU stop interrupts
> IPI5: 0 0 IRQ work interrupts
> IPI6: 0 0 completion interrupts
> Err: 0
>
> So quite a substantial subset :(

Indeed. Given this delta, I'm reluctant to include that command at this
point.

>
>>
>>> lx_thread_info_by_pid has been useful to me while looking at thread
>>> awareness, so I've included it into this patch set now. It makes finding
>>> internal thread information much more convenient.
>>>
>>> dentry_name has been moved to the utils module, as I am already using it
>>> in another command, so it's just not appropriate to be in proc.py
>>>
>>> The cpu_list mask iterators make calling for cpu in each_online_cpu() read
>>> nicely, and I've left the print_cpus() function in for now as a hidden
>>> helper. It can be used by calling:
>>> python linux.cpus.print_cpus()
>>> to check these generators, which I thought was quite nice - but I didn't
>>> know if it warranted a full command class for this.
>>>
>>> For convenience, this patch set submission can be found at
>>> http://git.linaro.org/people/kieran.bingham/linux.git gdb-scripts-2016-03-03-lkml-submission
>>>
>>> Patchset Changelog:
>>> v3:
>>> - Radix Tree parser introduced
>>> - cpu_list mask iterators added
>>> - lx-interrupts command implemented
>>> - dentry_name function moved to utils
>>> - lx-meminfo command PEP8 warnings fixed
>>> - lx_thread_info_by_pid introduced
>>>
>>> v2:
>>> - Reworked iterators with improved versions from Jeff Mahoney
>>> - Fixed !CONFIG_MODULES and !CONFIG_MMU support
>>> - Improvements on lx-meminfo
>>> - constants.py generated by Kbuild
>>> - IS_BUILTIN facility used to provide LX_CONFIG values
>>>
>>> v1:
>>> - Introduced lx-iomem, lx-ioports, lx-mounts, lx-meminfo
>>>
>>> Kieran Bingham (13):
>>> scripts/gdb: Provide linux constants
>>> scripts/gdb: Provide kernel list item generators
>>> scripts/gdb: Convert modules usage to lists functions
>>> scripts/gdb: Provide exception catching parser
>>> scripts/gdb: Support !CONFIG_MODULES gracefully
>>> scripts/gdb: Provide a dentry_name VFS path helper
>>> scripts/gdb: Add io resource readers
>>> scripts/gdb: Add mount point list command
>>> scripts/gdb: Add meminfo command
>>> scripts/gdb: Add cpu iterators
>>> scripts/gdb: Add a Radix Tree Parser
>>> scripts/gdb: Add interrupts command
>>> scripts/gdb: Add lx_thread_info_by_pid helper
>>>
>>> Kbuild | 10 +
>>> scripts/gdb/linux/Makefile | 12 +-
>>> scripts/gdb/linux/constants.py.in | 93 ++++++++
>>> scripts/gdb/linux/cpus.py | 21 ++
>>> scripts/gdb/linux/lists.py | 20 ++
>>> scripts/gdb/linux/modules.py | 22 +-
>>> scripts/gdb/linux/proc.py | 449 ++++++++++++++++++++++++++++++++++++++
>>> scripts/gdb/linux/radixtree.py | 74 +++++++
>>> scripts/gdb/linux/tasks.py | 19 ++
>>> scripts/gdb/linux/utils.py | 15 ++
>>> scripts/gdb/vmlinux-gdb.py | 2 +
>>> 11 files changed, 724 insertions(+), 13 deletions(-)
>>> create mode 100644 scripts/gdb/linux/constants.py.in
>>> create mode 100644 scripts/gdb/linux/radixtree.py
>>>
>>
>> Besides the required rebase and, thus, the missing adjustment of the cpu
>> masks, I only have minor remarks. Maybe I will have more once I can play
>> with lx-interrupts ;).
>
> Ok - well it's fixed up locally, it's just a matter of prefixing the
> strings with two underscores. I should be able to get the updated
> patches out soon I hope.
>
>> However, I have a growing concern - I think we already discussed this
>> offline: Without automated tests, all these helpers may quickly fall
>> apart as the kernel changes. This should not block these patches, but
>> maybe you can think about some testing approaches before we have dozens
>> of helpers which can only be validated manually.
>
> Yes, indeed - and the breakage already seen between v4.4 and v4.5 only
> solidifies the need!
>
> I was able to chat to a few of the Linaro LAVA guys on this topic while
> I was at Connect, and they already have virtual kernels being boot
> tested. This should provide a good infrastructure to run some automated
> tests on.

Sounds good!

>
> So we have the infrastructure - we just need to get the tests written,
> and included.

Yeah, "minor" detail.

Jan

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

2016-03-14 17:18:42

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

On 14/03/16 15:09, Jan Kiszka wrote:
> On 2016-03-14 15:40, Kieran Bingham wrote:
>> On 13/03/16 16:35, Jan Kiszka wrote:
>>> On 2016-03-03 12:40, Kieran Bingham wrote:
>>>> Hi Jan,
>>>>
>>>> V3 of the patchset respun. Now finally adding the lx-interrupts command
>>>> after I resolved my issues with the Radix Tree parsing.
>>>>
>>>> This command only provides the interrupts that are available generically,
>>>> and it seems that the /proc/interrupts function calls into arch specific
>>>> layers to add extra information about arch specific interrupts.
>>>>
>>>> I'm not sure what to do about this yet - The values returned appear to be
>>>> accurate - but it's just a subset of the information returned by proc.
>>>>
>>>
>>> I didn't test this (due to the breakage in patch 10): Can you give
>>> examples of what is missing, e.g. on ARM or x86?
>> On ARM this is :
>>
>> (gdb) lx-interrupts
>> CPU0 CPU1
>> 18: 587828 150187
>> 36: 66574 0
>> 41: 8 0
>> 42: 106 0
>> 43: 100 0
>> (gdb) c
>>
>>
>> vs
>>
>> root@ArmACookieMonster:~# cat /proc/interrupts
>> CPU0 CPU1
>> 18: 588057 150274 GIC-0 27 Edge arch_timer
>> 20: 0 0 GIC-0 34 Level timer
>> 36: 66599 0 GIC-0 47 Level eth0
>> 39: 0 0 GIC-0 41 Level mmci-pl18x (cmd)
>> 40: 0 0 GIC-0 42 Level mmci-pl18x (pio)
>> 41: 8 0 GIC-0 44 Level kmi-pl050
>> 42: 106 0 GIC-0 45 Level kmi-pl050
>> 43: 112 0 GIC-0 37 Level uart-pl011
>> 49: 0 0 GIC-0 36 Level rtc-pl031
>> IPI0: 0 1 CPU wakeup interrupts
>> IPI1: 0 0 Timer broadcast interrupts
>> IPI2: 15867 281362 Rescheduling interrupts
>> IPI3: 0 6 Function call interrupts
>> IPI4: 0 0 CPU stop interrupts
>> IPI5: 0 0 IRQ work interrupts
>> IPI6: 0 0 completion interrupts
>> Err: 0
>>
>> So quite a substantial subset :(
>
> Indeed. Given this delta, I'm reluctant to include that command at this
> point.

Ok, understandable...

I think the radix-tree lookup could be useful for people though.
This is used across filesystems, and other places.

Perhaps I should wrap this up into a gdb.Function rather than drop it?


>>>> lx_thread_info_by_pid has been useful to me while looking at thread
>>>> awareness, so I've included it into this patch set now. It makes finding
>>>> internal thread information much more convenient.
>>>>
>>>> dentry_name has been moved to the utils module, as I am already using it
>>>> in another command, so it's just not appropriate to be in proc.py
>>>>
>>>> The cpu_list mask iterators make calling for cpu in each_online_cpu() read
>>>> nicely, and I've left the print_cpus() function in for now as a hidden
>>>> helper. It can be used by calling:
>>>> python linux.cpus.print_cpus()
>>>> to check these generators, which I thought was quite nice - but I didn't
>>>> know if it warranted a full command class for this.
>>>>
>>>> For convenience, this patch set submission can be found at
>>>> http://git.linaro.org/people/kieran.bingham/linux.git gdb-scripts-2016-03-03-lkml-submission
>>>>
>>>> Patchset Changelog:
>>>> v3:
>>>> - Radix Tree parser introduced
>>>> - cpu_list mask iterators added
>>>> - lx-interrupts command implemented
>>>> - dentry_name function moved to utils
>>>> - lx-meminfo command PEP8 warnings fixed
>>>> - lx_thread_info_by_pid introduced
>>>>
>>>> v2:
>>>> - Reworked iterators with improved versions from Jeff Mahoney
>>>> - Fixed !CONFIG_MODULES and !CONFIG_MMU support
>>>> - Improvements on lx-meminfo
>>>> - constants.py generated by Kbuild
>>>> - IS_BUILTIN facility used to provide LX_CONFIG values
>>>>
>>>> v1:
>>>> - Introduced lx-iomem, lx-ioports, lx-mounts, lx-meminfo
>>>>
>>>> Kieran Bingham (13):
>>>> scripts/gdb: Provide linux constants
>>>> scripts/gdb: Provide kernel list item generators
>>>> scripts/gdb: Convert modules usage to lists functions
>>>> scripts/gdb: Provide exception catching parser
>>>> scripts/gdb: Support !CONFIG_MODULES gracefully
>>>> scripts/gdb: Provide a dentry_name VFS path helper
>>>> scripts/gdb: Add io resource readers
>>>> scripts/gdb: Add mount point list command
>>>> scripts/gdb: Add meminfo command
>>>> scripts/gdb: Add cpu iterators
>>>> scripts/gdb: Add a Radix Tree Parser
>>>> scripts/gdb: Add interrupts command
>>>> scripts/gdb: Add lx_thread_info_by_pid helper
>>>>
>>>> Kbuild | 10 +
>>>> scripts/gdb/linux/Makefile | 12 +-
>>>> scripts/gdb/linux/constants.py.in | 93 ++++++++
>>>> scripts/gdb/linux/cpus.py | 21 ++
>>>> scripts/gdb/linux/lists.py | 20 ++
>>>> scripts/gdb/linux/modules.py | 22 +-
>>>> scripts/gdb/linux/proc.py | 449 ++++++++++++++++++++++++++++++++++++++
>>>> scripts/gdb/linux/radixtree.py | 74 +++++++
>>>> scripts/gdb/linux/tasks.py | 19 ++
>>>> scripts/gdb/linux/utils.py | 15 ++
>>>> scripts/gdb/vmlinux-gdb.py | 2 +
>>>> 11 files changed, 724 insertions(+), 13 deletions(-)
>>>> create mode 100644 scripts/gdb/linux/constants.py.in
>>>> create mode 100644 scripts/gdb/linux/radixtree.py
>>>>
>>>
>>> Besides the required rebase and, thus, the missing adjustment of the cpu
>>> masks, I only have minor remarks. Maybe I will have more once I can play
>>> with lx-interrupts ;).
>>
>> Ok - well it's fixed up locally, it's just a matter of prefixing the
>> strings with two underscores. I should be able to get the updated
>> patches out soon I hope.
>>
>>> However, I have a growing concern - I think we already discussed this
>>> offline: Without automated tests, all these helpers may quickly fall
>>> apart as the kernel changes. This should not block these patches, but
>>> maybe you can think about some testing approaches before we have dozens
>>> of helpers which can only be validated manually.
>>
>> Yes, indeed - and the breakage already seen between v4.4 and v4.5 only
>> solidifies the need!
>>
>> I was able to chat to a few of the Linaro LAVA guys on this topic while
>> I was at Connect, and they already have virtual kernels being boot
>> tested. This should provide a good infrastructure to run some automated
>> tests on.
>
> Sounds good!
>
>>
>> So we have the infrastructure - we just need to get the tests written,
>> and included.
>
> Yeah, "minor" detail.

Isn't it ever :D

>
> Jan
>

2016-03-14 17:32:52

by Jan Kiszka

[permalink] [raw]
Subject: Re: [PATCHv3 00/13] scripts/gdb: Linux awareness debug commands

On 2016-03-14 18:18, Kieran Bingham wrote:
> On 14/03/16 15:09, Jan Kiszka wrote:
>> On 2016-03-14 15:40, Kieran Bingham wrote:
>>> On 13/03/16 16:35, Jan Kiszka wrote:
>>>> On 2016-03-03 12:40, Kieran Bingham wrote:
>>>>> Hi Jan,
>>>>>
>>>>> V3 of the patchset respun. Now finally adding the lx-interrupts command
>>>>> after I resolved my issues with the Radix Tree parsing.
>>>>>
>>>>> This command only provides the interrupts that are available generically,
>>>>> and it seems that the /proc/interrupts function calls into arch specific
>>>>> layers to add extra information about arch specific interrupts.
>>>>>
>>>>> I'm not sure what to do about this yet - The values returned appear to be
>>>>> accurate - but it's just a subset of the information returned by proc.
>>>>>
>>>>
>>>> I didn't test this (due to the breakage in patch 10): Can you give
>>>> examples of what is missing, e.g. on ARM or x86?
>>> On ARM this is :
>>>
>>> (gdb) lx-interrupts
>>> CPU0 CPU1
>>> 18: 587828 150187
>>> 36: 66574 0
>>> 41: 8 0
>>> 42: 106 0
>>> 43: 100 0
>>> (gdb) c
>>>
>>>
>>> vs
>>>
>>> root@ArmACookieMonster:~# cat /proc/interrupts
>>> CPU0 CPU1
>>> 18: 588057 150274 GIC-0 27 Edge arch_timer
>>> 20: 0 0 GIC-0 34 Level timer
>>> 36: 66599 0 GIC-0 47 Level eth0
>>> 39: 0 0 GIC-0 41 Level mmci-pl18x (cmd)
>>> 40: 0 0 GIC-0 42 Level mmci-pl18x (pio)
>>> 41: 8 0 GIC-0 44 Level kmi-pl050
>>> 42: 106 0 GIC-0 45 Level kmi-pl050
>>> 43: 112 0 GIC-0 37 Level uart-pl011
>>> 49: 0 0 GIC-0 36 Level rtc-pl031
>>> IPI0: 0 1 CPU wakeup interrupts
>>> IPI1: 0 0 Timer broadcast interrupts
>>> IPI2: 15867 281362 Rescheduling interrupts
>>> IPI3: 0 6 Function call interrupts
>>> IPI4: 0 0 CPU stop interrupts
>>> IPI5: 0 0 IRQ work interrupts
>>> IPI6: 0 0 completion interrupts
>>> Err: 0
>>>
>>> So quite a substantial subset :(
>>
>> Indeed. Given this delta, I'm reluctant to include that command at this
>> point.
>
> Ok, understandable...
>
> I think the radix-tree lookup could be useful for people though.
> This is used across filesystems, and other places.
>
> Perhaps I should wrap this up into a gdb.Function rather than drop it?
>

Sounds good. Maybe also augment Documentation/gdb-kernel-debugging.txt
with a nice example for this (and for other non-obvious features).

Jan

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

2016-03-15 10:46:44

by Kieran Bingham

[permalink] [raw]
Subject: Re: [PATCHv3 08/13] scripts/gdb: Add mount point list command

On 14/03/16 15:05, Jan Kiszka wrote:
> On 2016-03-14 15:39, Kieran Bingham wrote:
>> On 13/03/16 16:34, Jan Kiszka wrote:
>>> On 2016-03-03 12:41, 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
>>>>
>>>> Signed-off-by: Kieran Bingham <[email protected]>
>>>>
>>>> ---
>>>> Changes from v1:
>>>> - Updated to use LX_ constant macros
>>>> - Adjusted for new list_for_each_item() function
>>>> - Removed unnecessary Null check in vfs['mnt_parent']
>>>> - Tested and not needed. It probably occurred in early testing
>>>> with a bad iterator
>>>>
>>>> Changes since v2:
>>>> - dentry path helper moved to utils module
>>>> ---
>>>> scripts/gdb/linux/constants.py.in | 21 +++++++++
>>>> scripts/gdb/linux/proc.py | 99 +++++++++++++++++++++++++++++++++++++++
>>>> 2 files changed, 120 insertions(+)
>>>>
>>>> diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
>>>> index 79d9d0092452..57213ad8cf75 100644
>>>> --- a/scripts/gdb/linux/constants.py.in
>>>> +++ b/scripts/gdb/linux/constants.py.in
>>>> @@ -12,7 +12,11 @@
>>>> *
>>>> */
>>>>
>>>> +#include <linux/fs.h>
>>>> +#include <linux/mount.h>
>>>> +
>>>> /* We need to stringify expanded macros so that they can be parsed */
>>>> +
>>>> #define STRING(x) #x
>>>> #define XSTRING(x) STRING(x)
>>>>
>>>> @@ -30,3 +34,20 @@
>>>> <!-- end-c-headers -->
>>>>
>>>> import gdb
>>>> +
>>>> +/* linux/fs.h */
>>>> +LX_VALUE(MS_RDONLY)
>>>> +LX_VALUE(MS_SYNCHRONOUS)
>>>> +LX_VALUE(MS_MANDLOCK)
>>>> +LX_VALUE(MS_DIRSYNC)
>>>> +LX_VALUE(MS_NOATIME)
>>>> +LX_VALUE(MS_NODIRATIME)
>>>> +
>>>> +/* linux/mount.h */
>>>> +LX_VALUE(MNT_NOSUID)
>>>> +LX_VALUE(MNT_NODEV)
>>>> +LX_VALUE(MNT_NOEXEC)
>>>> +LX_VALUE(MNT_NOATIME)
>>>> +LX_VALUE(MNT_NODIRATIME)
>>>> +LX_VALUE(MNT_RELATIME)
>>>> +
>>>> diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
>>>> index d855b2fd9a06..115f20b07a54 100644
>>>> --- a/scripts/gdb/linux/proc.py
>>>> +++ b/scripts/gdb/linux/proc.py
>>>> @@ -12,6 +12,10 @@
>>>> #
>>>>
>>>> import gdb
>>>> +from linux import constants
>>>> +from linux import utils
>>>> +from linux import tasks
>>>> +from linux import lists
>>>>
>>>>
>>>> class LxCmdLine(gdb.Command):
>>>> @@ -96,3 +100,98 @@ Equivalent to cat /proc/ioports on a running target"""
>>>> return show_lx_resources("ioport_resource")
>>>>
>>>> LxIOPorts()
>>>> +
>>>> +
>>>> +# Mount namespace viewer
>>>> +# /proc/mounts
>>>> +
>>>> +def info_opts(lst, opt):
>>>> + opts = ""
>>>> + for key, string in lst.items():
>>>> + if opt & key:
>>>> + opts += string
>>>> + return opts
>>>> +
>>>> +
>>>> +FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
>>>> + constants.LX_MS_MANDLOCK: ",mand",
>>>> + constants.LX_MS_DIRSYNC: ",dirsync",
>>>> + constants.LX_MS_NOATIME: ",noatime",
>>>> + constants.LX_MS_NODIRATIME: ",nodiratime"}
>>>> +
>>>> +MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
>>>> + constants.LX_MNT_NODEV: ",nodev",
>>>> + constants.LX_MNT_NOEXEC: ",noexec",
>>>> + constants.LX_MNT_NOATIME: ",noatime",
>>>> + constants.LX_MNT_NODIRATIME: ",nodiratime",
>>>> + constants.LX_MNT_RELATIME: ",relatime"}
>>>> +
>>>> +mount_type = utils.CachedType("struct mount")
>>>> +mount_ptr_type = mount_type.get_type().pointer()
>>>> +
>>>> +
>>>> +class LxMounts(gdb.Command):
>>>> + """Report the VFS mounts of the current process namespace.
>>>> +
>>>> +Equivalent to cat /proc/mounts on a running target
>>>> +An integer value can be supplied to display the mount
>>>> +values of that process namespace"""
>>>> +
>>>> + def __init__(self):
>>>> + super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
>>>> +
>>>> + # Equivalent to proc_namespace.c:show_vfsmnt
>>>> + # However, that has the ability to call into s_op functions
>>>> + # whereas we cannot and must make do with the information we can obtain.
>>>> + def invoke(self, arg, from_tty):
>>>> + argv = gdb.string_to_argv(arg)
>>>> + if len(argv) >= 1:
>>>> + try:
>>>> + pid = int(argv[0])
>>>> + except:
>>>> + raise gdb.GdbError("Provide a PID as integer value")
>>>> + else:
>>>> + pid = 1
>>>> +
>>>> + task = tasks.get_task_by_pid(pid)
>>>> + if not task:
>>>> + raise gdb.GdbError("Couldn't find a process with PID {}"
>>>> + .format(pid))
>>>> +
>>>> + namespace = task['nsproxy']['mnt_ns']
>>>> + if not namespace:
>>>> + raise gdb.GdbError("No namespace for current process")
>>>> +
>>>> + for vfs in lists.list_for_each_entry(
>>>> + namespace['list'], mount_ptr_type, "mnt_list"):
>>>
>>> pep8 and /me prefer
>>>
>>> for vfs in lists.list_for_each_entry(namespace['list'],
>>> mount_ptr_type, "mnt_list"):
>>
>> Ack. No problem.
>>
>> Did pep8 tool generate a warning here? (Just wondering if this is
>> another instance of my tool behaving differently)
>
> Yep.

Ok, well I have updated this locally anyway now.

>
>>
>>>
>>>> + devname = vfs['mnt_devname'].string()
>>>> + devname = devname if devname else "none"
>>>> +
>>>> + pathname = ""
>>>> + parent = vfs
>>>> + while True:
>>>> + mntpoint = parent['mnt_mountpoint']
>>>> + pathname = utils.dentry_name(mntpoint) + pathname
>>>> + if (parent == parent['mnt_parent']):
>>>> + break
>>>> + parent = parent['mnt_parent']
>>>> +
>>>> + if (pathname == ""):
>>>> + pathname = "/"
>>>> +
>>>> + superblock = vfs['mnt']['mnt_sb']
>>>> + fstype = superblock['s_type']['name'].string()
>>>> + s_flags = int(superblock['s_flags'])
>>>> + m_flags = int(vfs['mnt']['mnt_flags'])
>>>> + rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
>>>> +
>>>> + gdb.write(
>>>> + "{} {} {} {}{}{} 0 0\n"
>>>> + .format(devname,
>>>> + pathname,
>>>> + fstype,
>>>> + rd,
>>>> + info_opts(FS_INFO, s_flags),
>>>> + info_opts(MNT_INFO, m_flags)))
>>>> +
>>>> +LxMounts()
>>>>
>>>
>>> This doesn't list all parameters of a mount. Can this be fixed easily?
>>
>> Not easily I'm afraid: The comment at the top was hoping to highlight this:
>>
>>>> + # 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.
>
> Sorry, missed this.

np.

>
>>
>> Each VFS can call into an extra hook to provide extra option
>> information. We would need to duplicate each of those implementations :(
>
> So these options are not available as string somewhere?

Short answer: No. Not generically.

Long answer: (after having dug around last night)

The extra operations are determined through several layers of
indirection, and function pointers which end up then parsing the options
however they like, and printing them through the seq_file interface.

First through the .show_options of the super_operations kernfs_sops
struct, which indirects through kernfs_sop_show_options() to call any
kernfs_syscall_ops->show_options function registered.

Some super_operations structures register generic_show_options() which
will map the dentry_root->d_sb->s_options as a string.

It looks like around 14 filesystems use this generic show options,
However, none of the filesystems I have mounted use this...

Looking at a few of the others, they are free to store their options
however they like. For example reiserfs stores as flags which are then
expanded by their specific _show_options...

Regards

Kieran

> Jan
>