xref: /linux-6.15/scripts/gdb/linux/proc.py (revision bb970707)
1#
2# gdb helper commands and functions for Linux kernel debugging
3#
4#  Kernel proc information reader
5#
6# Copyright (c) 2016 Linaro Ltd
7#
8# Authors:
9#  Kieran Bingham <[email protected]>
10#
11# This work is licensed under the terms of the GNU GPL version 2.
12#
13
14import gdb
15from linux import constants
16from linux import utils
17from linux import tasks
18from linux import lists
19
20
21class LxCmdLine(gdb.Command):
22    """ Report the Linux Commandline used in the current kernel.
23        Equivalent to cat /proc/cmdline on a running target"""
24
25    def __init__(self):
26        super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA)
27
28    def invoke(self, arg, from_tty):
29        gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
30
31LxCmdLine()
32
33
34class LxVersion(gdb.Command):
35    """ Report the Linux Version of the current kernel.
36        Equivalent to cat /proc/version on a running target"""
37
38    def __init__(self):
39        super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA)
40
41    def invoke(self, arg, from_tty):
42        # linux_banner should contain a newline
43        gdb.write(gdb.parse_and_eval("linux_banner").string())
44
45LxVersion()
46
47
48# Resource Structure Printers
49#  /proc/iomem
50#  /proc/ioports
51
52def get_resources(resource, depth):
53    while resource:
54        yield resource, depth
55
56        child = resource['child']
57        if child:
58            for res, deep in get_resources(child, depth + 1):
59                yield res, deep
60
61        resource = resource['sibling']
62
63
64def show_lx_resources(resource_str):
65        resource = gdb.parse_and_eval(resource_str)
66        width = 4 if resource['end'] < 0x10000 else 8
67        # Iterate straight to the first child
68        for res, depth in get_resources(resource['child'], 0):
69            start = int(res['start'])
70            end = int(res['end'])
71            gdb.write(" " * depth * 2 +
72                      "{0:0{1}x}-".format(start, width) +
73                      "{0:0{1}x} : ".format(end, width) +
74                      res['name'].string() + "\n")
75
76
77class LxIOMem(gdb.Command):
78    """Identify the IO memory resource locations defined by the kernel
79
80Equivalent to cat /proc/iomem on a running target"""
81
82    def __init__(self):
83        super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
84
85    def invoke(self, arg, from_tty):
86        return show_lx_resources("iomem_resource")
87
88LxIOMem()
89
90
91class LxIOPorts(gdb.Command):
92    """Identify the IO port resource locations defined by the kernel
93
94Equivalent to cat /proc/ioports on a running target"""
95
96    def __init__(self):
97        super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
98
99    def invoke(self, arg, from_tty):
100        return show_lx_resources("ioport_resource")
101
102LxIOPorts()
103
104
105# Mount namespace viewer
106#  /proc/mounts
107
108def info_opts(lst, opt):
109    opts = ""
110    for key, string in lst.items():
111        if opt & key:
112            opts += string
113    return opts
114
115
116FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
117           constants.LX_MS_MANDLOCK: ",mand",
118           constants.LX_MS_DIRSYNC: ",dirsync",
119           constants.LX_MS_NOATIME: ",noatime",
120           constants.LX_MS_NODIRATIME: ",nodiratime"}
121
122MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
123            constants.LX_MNT_NODEV: ",nodev",
124            constants.LX_MNT_NOEXEC: ",noexec",
125            constants.LX_MNT_NOATIME: ",noatime",
126            constants.LX_MNT_NODIRATIME: ",nodiratime",
127            constants.LX_MNT_RELATIME: ",relatime"}
128
129mount_type = utils.CachedType("struct mount")
130mount_ptr_type = mount_type.get_type().pointer()
131
132
133class LxMounts(gdb.Command):
134    """Report the VFS mounts of the current process namespace.
135
136Equivalent to cat /proc/mounts on a running target
137An integer value can be supplied to display the mount
138values of that process namespace"""
139
140    def __init__(self):
141        super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
142
143    # Equivalent to proc_namespace.c:show_vfsmnt
144    # However, that has the ability to call into s_op functions
145    # whereas we cannot and must make do with the information we can obtain.
146    def invoke(self, arg, from_tty):
147        argv = gdb.string_to_argv(arg)
148        if len(argv) >= 1:
149            try:
150                pid = int(argv[0])
151            except:
152                raise gdb.GdbError("Provide a PID as integer value")
153        else:
154            pid = 1
155
156        task = tasks.get_task_by_pid(pid)
157        if not task:
158            raise gdb.GdbError("Couldn't find a process with PID {}"
159                               .format(pid))
160
161        namespace = task['nsproxy']['mnt_ns']
162        if not namespace:
163            raise gdb.GdbError("No namespace for current process")
164
165        for vfs in lists.list_for_each_entry(namespace['list'],
166                                             mount_ptr_type, "mnt_list"):
167            devname = vfs['mnt_devname'].string()
168            devname = devname if devname else "none"
169
170            pathname = ""
171            parent = vfs
172            while True:
173                mntpoint = parent['mnt_mountpoint']
174                pathname = utils.dentry_name(mntpoint) + pathname
175                if (parent == parent['mnt_parent']):
176                    break
177                parent = parent['mnt_parent']
178
179            if (pathname == ""):
180                pathname = "/"
181
182            superblock = vfs['mnt']['mnt_sb']
183            fstype = superblock['s_type']['name'].string()
184            s_flags = int(superblock['s_flags'])
185            m_flags = int(vfs['mnt']['mnt_flags'])
186            rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
187
188            gdb.write(
189                "{} {} {} {}{}{} 0 0\n"
190                .format(devname,
191                        pathname,
192                        fstype,
193                        rd,
194                        info_opts(FS_INFO, s_flags),
195                        info_opts(MNT_INFO, m_flags)))
196
197LxMounts()
198