All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ye Liu <ye.liu@linux.dev>
To: linux-kernel@vger.kernel.org, akpm@linux-foundation.org
Cc: linux-toolchains@vger.kernel.org, linux-mm@kvack.org,
	Ye Liu <liuye@kylinos.cn>
Subject: [PATCH] tools/drgn: Add script to display page state for a given PID and VADDR
Date: Tue, 15 Apr 2025 15:50:24 +0800	[thread overview]
Message-ID: <20250415075024.248232-1-ye.liu@linux.dev> (raw)

From: Ye Liu <liuye@kylinos.cn>

Introduces a new drgn script, `show_page_info.py`, which allows users
to analyze the state of a page given a process ID (PID) and a virtual
address (VADDR). This can help kernel developers or debuggers easily
inspect page-related information in a live kernel or vmcore.

The script extracts information such as the page flags, mapping, and
other metadata relevant to diagnosing memory issues.

Currently, there is no specific maintainer entry for `tools/drgn/` in the
MAINTAINERS file. Therefore, this patch is sent to the general kernel and
tools mailing lists for review.

Output example:
sudo ./show_page_info.py 1 0x7f2c7de4c000
PID : 1 Comm : systemd mm : 0xffff888116b6d440
User Virtual Address : 0x7f2c7de4c000
Page Address    : 0xffffea000b3a4000
raw: 0017ffffc000416c ffffea00045d1b08 ffffea000456d408 ffff888104521970
raw: 0000000000000000 ffff8881083fdb60 0000006900000018 ffff888107a41000
Page Flags      : PG_referenced|PG_uptodate|PG_lru|PG_head|PG_active|
                  PG_private|PG_reported
Page Size       : 16384
Page PFN        : 0x2ce900
Page Physical   : 0x2ce900000
Page Virtual    : 0xffff8882ce900000
Page Refcount   : 105
Page Mapcount   : 24
Page Index      : 0x0
Page Memcg Data : 0xffff888107a41000
Memcg Name      : init.scope
Memcg Path      : /sys/fs/cgroup/memory/init.scope
Page Mapping    : 0xffff888104521970
Page Anon/File  : File
Page VMA        : 0xffff888109e135e8
VMA Start       : 0x7f2c7de4c000
VMA End         : 0x7f2c7de58000
This page is part of a compound page.
This page is the head page of a compound page.
Head Page       : 0xffffea000b3a4000
Compound Order  : 2
Number of Pages : 4

Signed-off-by: Ye Liu <liuye@kylinos.cn>
---
 tools/drgn/show_page_info.py | 120 +++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 tools/drgn/show_page_info.py

diff --git a/tools/drgn/show_page_info.py b/tools/drgn/show_page_info.py
new file mode 100644
index 000000000000..70d0cd97c7f0
--- /dev/null
+++ b/tools/drgn/show_page_info.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env drgn
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2025 Ye Liu <liuye@kylinos.cn>
+
+import argparse
+from drgn import Object
+from drgn.helpers.linux import find_task, follow_page, page_size
+from drgn.helpers.linux.mm import (
+    decode_page_flags, page_to_pfn, page_to_phys, page_to_virt, vma_find,
+    PageSlab, PageCompound, PageHead, PageTail, compound_head, compound_order, compound_nr
+)
+from drgn.helpers.linux.cgroup import cgroup_name, cgroup_path
+
+DESC = """
+This is a drgn script to show the page state.
+For more info on drgn, visit https://github.com/osandov/drgn.
+"""
+
+MEMCG_DATA_OBJEXTS = 1 << 0
+MEMCG_DATA_KMEM = 1 << 1
+__NR_MEMCG_DATA_FLAGS = 1 << 2
+
+def format_page_data(data):
+    """Format raw page data into a readable hex dump."""
+    chunks = [data[i:i+8] for i in range(0, len(data), 8)]
+    hex_chunks = ["".join(f"{b:02x}" for b in chunk[::-1]) for chunk in chunks]
+    lines = [" ".join(hex_chunks[i:i+4]) for i in range(0, len(hex_chunks), 4)]
+    return "\n".join(f"raw: {line}" for line in lines)
+
+def get_memcg_info(page):
+    """Retrieve memory cgroup information for a page."""
+    memcg_data = page.memcg_data.value_()
+    if memcg_data & MEMCG_DATA_OBJEXTS:
+        memcg_value = 0
+    elif memcg_data & MEMCG_DATA_KMEM:
+        objcg = Object(prog, "struct obj_cgroup *", address=memcg_data & ~__NR_MEMCG_DATA_FLAGS)
+        memcg_value = objcg.memcg.value_()
+    else:
+        memcg_value = memcg_data & ~__NR_MEMCG_DATA_FLAGS
+
+    memcg = Object(prog, "struct mem_cgroup *", address=memcg_value)
+    cgrp = memcg.css.cgroup
+    return cgroup_name(cgrp).decode(), f"/sys/fs/cgroup/memory{cgroup_path(cgrp).decode()}"
+
+def show_page_state(page, addr, mm, pid, task):
+    """Display detailed information about a page."""
+    print(f'==============================================================')
+    print(f'PID : {pid} Comm : {task.comm.string_().decode()} mm : {hex(mm)}')
+    print(f'User Virtual Address : {hex(addr)}')
+    print(f'Page Address    : {hex(page.value_())}')
+    print(f'--------------------------------------------------------------')
+
+    print(format_page_data(prog.read(page.value_(), 64)))
+    print(f'--------------------------------------------------------------')
+
+    print(f'Page Flags      : {decode_page_flags(page)}')
+    print(f'Page Size       : {page_size(page).value_()}')
+    print(f'Page PFN        : {hex(page_to_pfn(page).value_())}')
+    print(f'Page Physical   : {hex(page_to_phys(page).value_())}')
+    print(f'Page Virtual    : {hex(page_to_virt(page).value_())}')
+    print(f'Page Refcount   : {page._refcount.counter.value_()}')
+    print(f'Page Mapcount   : {page._mapcount.counter.value_()}')
+    print(f'Page Index      : {hex(page.index.value_())}')
+    print(f'Page Memcg Data : {hex(page.memcg_data.value_())}')
+
+    memcg_name, memcg_path = get_memcg_info(page)
+    print(f'Memcg Name      : {memcg_name}')
+    print(f'Memcg Path      : {memcg_path}')
+    print(f'--------------------------------------------------------------')
+
+    print(f'Page Mapping    : {hex(page.mapping.value_())}')
+    print(f'Page Anon/File  : {"Anon" if page.mapping.value_() & 0x1 else "File"}')
+
+    vma = vma_find(mm, addr)
+    print(f'Page VMA        : {hex(vma.value_())}')
+    print(f'VMA Start       : {hex(vma.vm_start.value_())}')
+    print(f'VMA End         : {hex(vma.vm_end.value_())}')
+    print(f'--------------------------------------------------------------')
+
+    if PageSlab(page):
+        print("This page belongs to the slab allocator.")
+
+    if PageCompound(page):
+        print("This page is part of a compound page.")
+        if PageHead(page):
+            print("This page is the head page of a compound page.")
+        if PageTail(page):
+            print("This page is the tail page of a compound page.")
+        print(f'Head Page       : {hex(compound_head(page).value_())}')
+        print(f'Compound Order  : {compound_order(page).value_()}')
+        print(f'Number of Pages : {compound_nr(page).value_()}')
+    else:
+        print("This page is not part of a compound page.")
+    print(f'==============================================================')
+
+def main():
+    """Main function to parse arguments and display page state."""
+    parser = argparse.ArgumentParser(description=DESC, formatter_class=argparse.RawTextHelpFormatter)
+    parser.add_argument('pid', metavar='PID', type=int, help='Target process ID (PID)')
+    parser.add_argument('vaddr', metavar='VADDR', type=str, help='Target virtual address in hexadecimal format (e.g., 0x7fff1234abcd)')
+    args = parser.parse_args()
+
+    try:
+        vaddr = int(args.vaddr, 16)
+    except ValueError:
+        print(f"Error: Invalid virtual address format: {args.vaddr}")
+        return
+
+    task = find_task(args.pid)
+    mm = task.mm
+    page = follow_page(mm, vaddr)
+
+    if page:
+        show_page_state(page, vaddr, mm, args.pid, task)
+    else:
+        print(f"Address {hex(vaddr)} is not mapped.")
+
+if __name__ == "__main__":
+    main()
+
-- 
2.25.1


             reply	other threads:[~2025-04-15  7:51 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-15  7:50 Ye Liu [this message]
2025-04-16  2:14 ` [PATCH] tools/drgn: Add script to display page state for a given PID and VADDR Andrew Morton
2025-04-16  2:46   ` Ye Liu
2025-04-16  3:28     ` Sweet Tea Dorminy
2025-04-16  4:02       ` Paul E. McKenney
2025-04-17  1:29         ` Ye Liu
2025-04-17  6:06           ` Omar Sandoval
2025-04-17  8:18             ` Ye Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250415075024.248232-1-ye.liu@linux.dev \
    --to=ye.liu@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-toolchains@vger.kernel.org \
    --cc=liuye@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.