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 gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format(
171 "mount", "super_block", "devname", "pathname", "fstype"))
173 for vfs in lists.list_for_each_entry(namespace['list'],
174 mount_ptr_type, "mnt_list"):
175 devname = vfs['mnt_devname'].string()
176 devname = devname if devname else "none"
181 mntpoint = parent['mnt_mountpoint']
182 pathname = utils.dentry_name(mntpoint) + pathname
183 if (parent == parent['mnt_parent']):
185 parent = parent['mnt_parent']
190 superblock = vfs['mnt']['mnt_sb']
191 fstype = superblock['s_type']['name'].string()
192 s_flags = int(superblock['s_flags'])
193 m_flags = int(vfs['mnt']['mnt_flags'])
194 rd = "ro" if (s_flags & constants.LX_SB_RDONLY) else "rw"
196 gdb.write("{} {} {} {} {} {}{}{} 0 0\n".format(
197 vfs.format_string(), superblock.format_string(), devname,
198 pathname, fstype, rd, info_opts(FS_INFO, s_flags),
199 info_opts(MNT_INFO, m_flags)))
205 class LxFdtDump(gdb.Command):
206 """Output Flattened Device Tree header and dump FDT blob to the filename
207 specified as the command argument. Equivalent to
208 'cat /proc/fdt > fdtdump.dtb' on a running target"""
211 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA,
212 gdb.COMPLETE_FILENAME)
214 def fdthdr_to_cpu(self, fdt_header):
216 fdt_header_be = ">IIIIIII"
217 fdt_header_le = "<IIIIIII"
219 if utils.get_target_endianness() == 1:
220 output_fmt = fdt_header_le
222 output_fmt = fdt_header_be
224 return unpack(output_fmt, pack(fdt_header_be,
226 fdt_header['totalsize'],
227 fdt_header['off_dt_struct'],
228 fdt_header['off_dt_strings'],
229 fdt_header['off_mem_rsvmap'],
230 fdt_header['version'],
231 fdt_header['last_comp_version']))
233 def invoke(self, arg, from_tty):
235 if not constants.LX_CONFIG_OF:
236 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n")
239 filename = "fdtdump.dtb"
243 py_fdt_header_ptr = gdb.parse_and_eval(
244 "(const struct fdt_header *) initial_boot_params")
245 py_fdt_header = py_fdt_header_ptr.dereference()
247 fdt_header = self.fdthdr_to_cpu(py_fdt_header)
249 if fdt_header[0] != constants.LX_OF_DT_HEADER:
250 raise gdb.GdbError("No flattened device tree magic found\n")
252 gdb.write("fdt_magic: 0x{:02X}\n".format(fdt_header[0]))
253 gdb.write("fdt_totalsize: 0x{:02X}\n".format(fdt_header[1]))
254 gdb.write("off_dt_struct: 0x{:02X}\n".format(fdt_header[2]))
255 gdb.write("off_dt_strings: 0x{:02X}\n".format(fdt_header[3]))
256 gdb.write("off_mem_rsvmap: 0x{:02X}\n".format(fdt_header[4]))
257 gdb.write("version: {}\n".format(fdt_header[5]))
258 gdb.write("last_comp_version: {}\n".format(fdt_header[6]))
260 inf = gdb.inferiors()[0]
261 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr,
262 fdt_header[1]).tobytes()
265 f = open(filename, 'wb')
267 raise gdb.GdbError("Could not open file to dump fdt")
272 gdb.write("Dumped fdt blob to " + filename + "\n")