[PATCH 8/8] scripts/gdb: Add meminfo command

From: Kieran Bingham
Date: Mon Feb 22 2016 - 10:24:46 EST


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

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

---

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)
---
scripts/gdb/linux/constants.py.in | 34 ++++++
scripts/gdb/linux/proc.py | 219 ++++++++++++++++++++++++++++++++++++++
2 files changed, 253 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 44804e10493e..95933f66ea3e 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -204,3 +204,222 @@ 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()
+ total_swapcache_pages = self.total_swapcache_pages()
+
+ file_pages = self.gps("NR_FILE_PAGES")
+ cached = file_pages - total_swapcache_pages - bufferram
+
+ # LRU Pages
+ active_pages_anon = self.gps("NR_ACTIVE_ANON")
+ inactive_pages_anon = self.gps("NR_INACTIVE_ANON")
+ active_pages_file = self.gps("NR_ACTIVE_FILE")
+ inactive_pages_file = self.gps("NR_INACTIVE_FILE")
+ unevictable_pages = self.gps("NR_UNEVICTABLE")
+ active_pages = active_pages_anon + active_pages_file
+ inactive_pages = inactive_pages_anon + inactive_pages_file
+
+ 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(total_swapcache_pages)) +
+ "Active: {:8d} kB\n".format(self.K(active_pages)) +
+ "Inactive: {:8d} kB\n".format(self.K(inactive_pages)) +
+ "Active(anon): {:8d} kB\n".format(self.K(active_pages_anon)) +
+ "Inactive(anon): {:8d} kB\n".format(self.K(inactive_pages_anon)) +
+ "Active(file): {:8d} kB\n".format(self.K(active_pages_file)) +
+ "Inactive(file): {:8d} kB\n".format(self.K(inactive_pages_file)) +
+ "Unevictable: {:8d} kB\n".format(self.K(unevictable_pages)) +
+ "Mlocked: {:8d} kB\n".format(self.gps_K("NR_MLOCK"))
+ )
+ if constants.LX_CONFIG_HIGHMEM:
+ totalhigh = int(gdb.parse_and_eval("totalhigh_pages"))
+ freehigh = int(gdb.parse_and_eval("nr_free_highpages()"))
+ gdb.write(
+ "HighTotal: {:8d} kB\n".format(self.K(totalhigh)) +
+ "HighFree: {:8d} kB\n".format(self.K(freehigh)) +
+ "LowTotal: {:8d} kB\n".format(self.K(totalram-totalhigh)) +
+ "LowFree: {:8d} kB\n".format(self.K(freeram-freehigh))
+ )
+ if not constants.LX_CONFIG_MMU:
+ mmap_pages_allocated = gdb.parse_and_eval("mmap_pages_allocated.counter")
+ gdb.write(
+ "MmapCopy: {:8d} kB\n".format(self.K(mmap_pages_allocated))
+
+ )
+ pass
+
+ 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))
+ )
+
+ gdb.write(
+ "NFS_Unstable: {:8d} kB\n".format(self.gps_K("NR_UNSTABLE_NFS")) +
+ "Bounce: {:8d} kB\n".format(self.gps_K("NR_BOUNCE")) +
+ "WritebackTmp: {:8d} kB\n".format(self.gps_K("NR_WRITEBACK_TEMP"))
+ )
+
+ 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"))
+ gdb.write(
+ "CmaTotal: {:8d} kB\n".format(self.K(totalcma_pages)) +
+ "CmaFree: {:8d} kB\n".format(self.gps_K("NR_FREE_CMA_PAGES"))
+ )
+
+LxMeminfo()
--
2.5.0