xref: /openbmc/linux/scripts/gdb/linux/proc.py (revision e3b9f1e8)
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 <kieran.bingham@linaro.org>
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
19from struct import *
20
21
22class LxCmdLine(gdb.Command):
23    """ Report the Linux Commandline used in the current kernel.
24        Equivalent to cat /proc/cmdline on a running target"""
25
26    def __init__(self):
27        super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA)
28
29    def invoke(self, arg, from_tty):
30        gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
31
32LxCmdLine()
33
34
35class LxVersion(gdb.Command):
36    """ Report the Linux Version of the current kernel.
37        Equivalent to cat /proc/version on a running target"""
38
39    def __init__(self):
40        super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA)
41
42    def invoke(self, arg, from_tty):
43        # linux_banner should contain a newline
44        gdb.write(gdb.parse_and_eval("linux_banner").string())
45
46LxVersion()
47
48
49# Resource Structure Printers
50#  /proc/iomem
51#  /proc/ioports
52
53def get_resources(resource, depth):
54    while resource:
55        yield resource, depth
56
57        child = resource['child']
58        if child:
59            for res, deep in get_resources(child, depth + 1):
60                yield res, deep
61
62        resource = resource['sibling']
63
64
65def show_lx_resources(resource_str):
66        resource = gdb.parse_and_eval(resource_str)
67        width = 4 if resource['end'] < 0x10000 else 8
68        # Iterate straight to the first child
69        for res, depth in get_resources(resource['child'], 0):
70            start = int(res['start'])
71            end = int(res['end'])
72            gdb.write(" " * depth * 2 +
73                      "{0:0{1}x}-".format(start, width) +
74                      "{0:0{1}x} : ".format(end, width) +
75                      res['name'].string() + "\n")
76
77
78class LxIOMem(gdb.Command):
79    """Identify the IO memory resource locations defined by the kernel
80
81Equivalent to cat /proc/iomem on a running target"""
82
83    def __init__(self):
84        super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
85
86    def invoke(self, arg, from_tty):
87        return show_lx_resources("iomem_resource")
88
89LxIOMem()
90
91
92class LxIOPorts(gdb.Command):
93    """Identify the IO port resource locations defined by the kernel
94
95Equivalent to cat /proc/ioports on a running target"""
96
97    def __init__(self):
98        super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
99
100    def invoke(self, arg, from_tty):
101        return show_lx_resources("ioport_resource")
102
103LxIOPorts()
104
105
106# Mount namespace viewer
107#  /proc/mounts
108
109def info_opts(lst, opt):
110    opts = ""
111    for key, string in lst.items():
112        if opt & key:
113            opts += string
114    return opts
115
116
117FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync",
118           constants.LX_MS_MANDLOCK: ",mand",
119           constants.LX_MS_DIRSYNC: ",dirsync",
120           constants.LX_MS_NOATIME: ",noatime",
121           constants.LX_MS_NODIRATIME: ",nodiratime"}
122
123MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
124            constants.LX_MNT_NODEV: ",nodev",
125            constants.LX_MNT_NOEXEC: ",noexec",
126            constants.LX_MNT_NOATIME: ",noatime",
127            constants.LX_MNT_NODIRATIME: ",nodiratime",
128            constants.LX_MNT_RELATIME: ",relatime"}
129
130mount_type = utils.CachedType("struct mount")
131mount_ptr_type = mount_type.get_type().pointer()
132
133
134class LxMounts(gdb.Command):
135    """Report the VFS mounts of the current process namespace.
136
137Equivalent to cat /proc/mounts on a running target
138An integer value can be supplied to display the mount
139values of that process namespace"""
140
141    def __init__(self):
142        super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
143
144    # Equivalent to proc_namespace.c:show_vfsmnt
145    # However, that has the ability to call into s_op functions
146    # whereas we cannot and must make do with the information we can obtain.
147    def invoke(self, arg, from_tty):
148        argv = gdb.string_to_argv(arg)
149        if len(argv) >= 1:
150            try:
151                pid = int(argv[0])
152            except:
153                raise gdb.GdbError("Provide a PID as integer value")
154        else:
155            pid = 1
156
157        task = tasks.get_task_by_pid(pid)
158        if not task:
159            raise gdb.GdbError("Couldn't find a process with PID {}"
160                               .format(pid))
161
162        namespace = task['nsproxy']['mnt_ns']
163        if not namespace:
164            raise gdb.GdbError("No namespace for current process")
165
166        for vfs in lists.list_for_each_entry(namespace['list'],
167                                             mount_ptr_type, "mnt_list"):
168            devname = vfs['mnt_devname'].string()
169            devname = devname if devname else "none"
170
171            pathname = ""
172            parent = vfs
173            while True:
174                mntpoint = parent['mnt_mountpoint']
175                pathname = utils.dentry_name(mntpoint) + pathname
176                if (parent == parent['mnt_parent']):
177                    break
178                parent = parent['mnt_parent']
179
180            if (pathname == ""):
181                pathname = "/"
182
183            superblock = vfs['mnt']['mnt_sb']
184            fstype = superblock['s_type']['name'].string()
185            s_flags = int(superblock['s_flags'])
186            m_flags = int(vfs['mnt']['mnt_flags'])
187            rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw"
188
189            gdb.write(
190                "{} {} {} {}{}{} 0 0\n"
191                .format(devname,
192                        pathname,
193                        fstype,
194                        rd,
195                        info_opts(FS_INFO, s_flags),
196                        info_opts(MNT_INFO, m_flags)))
197
198LxMounts()
199
200
201class LxFdtDump(gdb.Command):
202    """Output Flattened Device Tree header and dump FDT blob to the filename
203       specified as the command argument. Equivalent to
204       'cat /proc/fdt > fdtdump.dtb' on a running target"""
205
206    def __init__(self):
207        super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA,
208                                        gdb.COMPLETE_FILENAME)
209
210    def fdthdr_to_cpu(self, fdt_header):
211
212        fdt_header_be = ">IIIIIII"
213        fdt_header_le = "<IIIIIII"
214
215        if utils.get_target_endianness() == 1:
216            output_fmt = fdt_header_le
217        else:
218            output_fmt = fdt_header_be
219
220        return unpack(output_fmt, pack(fdt_header_be,
221                                       fdt_header['magic'],
222                                       fdt_header['totalsize'],
223                                       fdt_header['off_dt_struct'],
224                                       fdt_header['off_dt_strings'],
225                                       fdt_header['off_mem_rsvmap'],
226                                       fdt_header['version'],
227                                       fdt_header['last_comp_version']))
228
229    def invoke(self, arg, from_tty):
230
231        if not constants.LX_CONFIG_OF:
232            raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n")
233
234        if len(arg) == 0:
235            filename = "fdtdump.dtb"
236        else:
237            filename = arg
238
239        py_fdt_header_ptr = gdb.parse_and_eval(
240            "(const struct fdt_header *) initial_boot_params")
241        py_fdt_header = py_fdt_header_ptr.dereference()
242
243        fdt_header = self.fdthdr_to_cpu(py_fdt_header)
244
245        if fdt_header[0] != constants.LX_OF_DT_HEADER:
246            raise gdb.GdbError("No flattened device tree magic found\n")
247
248        gdb.write("fdt_magic:         0x{:02X}\n".format(fdt_header[0]))
249        gdb.write("fdt_totalsize:     0x{:02X}\n".format(fdt_header[1]))
250        gdb.write("off_dt_struct:     0x{:02X}\n".format(fdt_header[2]))
251        gdb.write("off_dt_strings:    0x{:02X}\n".format(fdt_header[3]))
252        gdb.write("off_mem_rsvmap:    0x{:02X}\n".format(fdt_header[4]))
253        gdb.write("version:           {}\n".format(fdt_header[5]))
254        gdb.write("last_comp_version: {}\n".format(fdt_header[6]))
255
256        inf = gdb.inferiors()[0]
257        fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr,
258                                        fdt_header[1]).tobytes()
259
260        try:
261            f = open(filename, 'wb')
262        except:
263            raise gdb.GdbError("Could not open file to dump fdt")
264
265        f.write(fdt_buf)
266        f.close()
267
268        gdb.write("Dumped fdt blob to " + filename + "\n")
269
270LxFdtDump()
271