2 # gdb helper commands and functions for Linux kernel debugging
4 # Kernel proc information reader
6 # Copyright (c) 2016 Linaro Ltd
11 # This work is licensed under the terms of the GNU GPL version 2.
15 from linux import constants
16 from linux import utils
17 from linux import tasks
18 from linux import lists
22 class LxCmdLine(gdb.Command):
23 """ Report the Linux Commandline used in the current kernel.
24 Equivalent to cat /proc/cmdline on a running target"""
27 super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA)
29 def invoke(self, arg, from_tty):
30 gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n")
36 class LxVersion(gdb.Command):
37 """ Report the Linux Version of the current kernel.
38 Equivalent to cat /proc/version on a running target"""
41 super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA)
43 def invoke(self, arg, from_tty):
44 # linux_banner should contain a newline
45 gdb.write(gdb.parse_and_eval("(char *)linux_banner").string())
51 # Resource Structure Printers
55 def get_resources(resource, depth):
59 child = resource['child']
61 for res, deep in get_resources(child, depth + 1):
64 resource = resource['sibling']
67 def show_lx_resources(resource_str):
68 resource = gdb.parse_and_eval(resource_str)
69 width = 4 if resource['end'] < 0x10000 else 8
70 # Iterate straight to the first child
71 for res, depth in get_resources(resource['child'], 0):
72 start = int(res['start'])
74 gdb.write(" " * depth * 2 +
75 "{0:0{1}x}-".format(start, width) +
76 "{0:0{1}x} : ".format(end, width) +
77 res['name'].string() + "\n")
80 class LxIOMem(gdb.Command):
81 """Identify the IO memory resource locations defined by the kernel
83 Equivalent to cat /proc/iomem on a running target"""
86 super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA)
88 def invoke(self, arg, from_tty):
89 return show_lx_resources("iomem_resource")
95 class LxIOPorts(gdb.Command):
96 """Identify the IO port resource locations defined by the kernel
98 Equivalent to cat /proc/ioports on a running target"""
101 super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA)
103 def invoke(self, arg, from_tty):
104 return show_lx_resources("ioport_resource")
110 # Mount namespace viewer
113 def info_opts(lst, opt):
115 for key, string in lst.items():
121 FS_INFO = {constants.LX_SB_SYNCHRONOUS: ",sync",
122 constants.LX_SB_MANDLOCK: ",mand",
123 constants.LX_SB_DIRSYNC: ",dirsync",
124 constants.LX_SB_NOATIME: ",noatime",
125 constants.LX_SB_NODIRATIME: ",nodiratime"}
127 MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid",
128 constants.LX_MNT_NODEV: ",nodev",
129 constants.LX_MNT_NOEXEC: ",noexec",
130 constants.LX_MNT_NOATIME: ",noatime",
131 constants.LX_MNT_NODIRATIME: ",nodiratime",
132 constants.LX_MNT_RELATIME: ",relatime"}
134 mount_type = utils.CachedType("struct mount")
135 mount_ptr_type = mount_type.get_type().pointer()
138 class LxMounts(gdb.Command):
139 """Report the VFS mounts of the current process namespace.
141 Equivalent to cat /proc/mounts on a running target
142 An integer value can be supplied to display the mount
143 values of that process namespace"""
146 super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA)
148 # Equivalent to proc_namespace.c:show_vfsmnt
149 # However, that has the ability to call into s_op functions
150 # whereas we cannot and must make do with the information we can obtain.
151 def invoke(self, arg, from_tty):
152 argv = gdb.string_to_argv(arg)
157 raise gdb.GdbError("Provide a PID as integer value")
161 task = tasks.get_task_by_pid(pid)
163 raise gdb.GdbError("Couldn't find a process with PID {}"
166 namespace = task['nsproxy']['mnt_ns']
168 raise gdb.GdbError("No namespace for current process")
170 for vfs in lists.list_for_each_entry(namespace['list'],
171 mount_ptr_type, "mnt_list"):
172 devname = vfs['mnt_devname'].string()
173 devname = devname if devname else "none"
178 mntpoint = parent['mnt_mountpoint']
179 pathname = utils.dentry_name(mntpoint) + pathname
180 if (parent == parent['mnt_parent']):
182 parent = parent['mnt_parent']
187 superblock = vfs['mnt']['mnt_sb']
188 fstype = superblock['s_type']['name'].string()
189 s_flags = int(superblock['s_flags'])
190 m_flags = int(vfs['mnt']['mnt_flags'])
191 rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw"
194 "{} {} {} {}{}{} 0 0\n"
199 info_opts(FS_INFO, s_flags),
200 info_opts(MNT_INFO, m_flags)))
206 class LxFdtDump(gdb.Command):
207 """Output Flattened Device Tree header and dump FDT blob to the filename
208 specified as the command argument. Equivalent to
209 'cat /proc/fdt > fdtdump.dtb' on a running target"""
212 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA,
213 gdb.COMPLETE_FILENAME)
215 def fdthdr_to_cpu(self, fdt_header):
217 fdt_header_be = ">IIIIIII"
218 fdt_header_le = "<IIIIIII"
220 if utils.get_target_endianness() == 1:
221 output_fmt = fdt_header_le
223 output_fmt = fdt_header_be
225 return unpack(output_fmt, pack(fdt_header_be,
227 fdt_header['totalsize'],
228 fdt_header['off_dt_struct'],
229 fdt_header['off_dt_strings'],
230 fdt_header['off_mem_rsvmap'],
231 fdt_header['version'],
232 fdt_header['last_comp_version']))
234 def invoke(self, arg, from_tty):
236 if not constants.LX_CONFIG_OF:
237 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n")
240 filename = "fdtdump.dtb"
244 py_fdt_header_ptr = gdb.parse_and_eval(
245 "(const struct fdt_header *) initial_boot_params")
246 py_fdt_header = py_fdt_header_ptr.dereference()
248 fdt_header = self.fdthdr_to_cpu(py_fdt_header)
250 if fdt_header[0] != constants.LX_OF_DT_HEADER:
251 raise gdb.GdbError("No flattened device tree magic found\n")
253 gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0]))
254 gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1]))
255 gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2]))
256 gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3]))
257 gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4]))
258 gdb.write("version: {}\n".format(fdt_header[5]))
259 gdb.write("last_comp_version: {}\n".format(fdt_header[6]))
261 inf = gdb.inferiors()[0]
262 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr,
263 fdt_header[1]).tobytes()
266 f = open(filename, 'wb')
268 raise gdb.GdbError("Could not open file to dump fdt")
273 gdb.write("Dumped fdt blob to " + filename + "\n")