proc.py (8747B)
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 32 33LxCmdLine() 34 35 36class LxVersion(gdb.Command): 37 """ Report the Linux Version of the current kernel. 38 Equivalent to cat /proc/version on a running target""" 39 40 def __init__(self): 41 super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) 42 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()) 46 47 48LxVersion() 49 50 51# Resource Structure Printers 52# /proc/iomem 53# /proc/ioports 54 55def get_resources(resource, depth): 56 while resource: 57 yield resource, depth 58 59 child = resource['child'] 60 if child: 61 for res, deep in get_resources(child, depth + 1): 62 yield res, deep 63 64 resource = resource['sibling'] 65 66 67def 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']) 73 end = int(res['end']) 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") 78 79 80class LxIOMem(gdb.Command): 81 """Identify the IO memory resource locations defined by the kernel 82 83Equivalent to cat /proc/iomem on a running target""" 84 85 def __init__(self): 86 super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) 87 88 def invoke(self, arg, from_tty): 89 return show_lx_resources("iomem_resource") 90 91 92LxIOMem() 93 94 95class LxIOPorts(gdb.Command): 96 """Identify the IO port resource locations defined by the kernel 97 98Equivalent to cat /proc/ioports on a running target""" 99 100 def __init__(self): 101 super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) 102 103 def invoke(self, arg, from_tty): 104 return show_lx_resources("ioport_resource") 105 106 107LxIOPorts() 108 109 110# Mount namespace viewer 111# /proc/mounts 112 113def info_opts(lst, opt): 114 opts = "" 115 for key, string in lst.items(): 116 if opt & key: 117 opts += string 118 return opts 119 120 121FS_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"} 126 127MNT_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"} 133 134mount_type = utils.CachedType("struct mount") 135mount_ptr_type = mount_type.get_type().pointer() 136 137 138class LxMounts(gdb.Command): 139 """Report the VFS mounts of the current process namespace. 140 141Equivalent to cat /proc/mounts on a running target 142An integer value can be supplied to display the mount 143values of that process namespace""" 144 145 def __init__(self): 146 super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) 147 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) 153 if len(argv) >= 1: 154 try: 155 pid = int(argv[0]) 156 except gdb.error: 157 raise gdb.GdbError("Provide a PID as integer value") 158 else: 159 pid = 1 160 161 task = tasks.get_task_by_pid(pid) 162 if not task: 163 raise gdb.GdbError("Couldn't find a process with PID {}" 164 .format(pid)) 165 166 namespace = task['nsproxy']['mnt_ns'] 167 if not namespace: 168 raise gdb.GdbError("No namespace for current process") 169 170 gdb.write("{:^18} {:^15} {:>9} {} {} options\n".format( 171 "mount", "super_block", "devname", "pathname", "fstype")) 172 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" 177 178 pathname = "" 179 parent = vfs 180 while True: 181 mntpoint = parent['mnt_mountpoint'] 182 pathname = utils.dentry_name(mntpoint) + pathname 183 if (parent == parent['mnt_parent']): 184 break 185 parent = parent['mnt_parent'] 186 187 if (pathname == ""): 188 pathname = "/" 189 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" 195 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))) 200 201 202LxMounts() 203 204 205class 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""" 209 210 def __init__(self): 211 super(LxFdtDump, self).__init__("lx-fdtdump", gdb.COMMAND_DATA, 212 gdb.COMPLETE_FILENAME) 213 214 def fdthdr_to_cpu(self, fdt_header): 215 216 fdt_header_be = ">IIIIIII" 217 fdt_header_le = "<IIIIIII" 218 219 if utils.get_target_endianness() == 1: 220 output_fmt = fdt_header_le 221 else: 222 output_fmt = fdt_header_be 223 224 return unpack(output_fmt, pack(fdt_header_be, 225 fdt_header['magic'], 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'])) 232 233 def invoke(self, arg, from_tty): 234 235 if not constants.LX_CONFIG_OF: 236 raise gdb.GdbError("Kernel not compiled with CONFIG_OF\n") 237 238 if len(arg) == 0: 239 filename = "fdtdump.dtb" 240 else: 241 filename = arg 242 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() 246 247 fdt_header = self.fdthdr_to_cpu(py_fdt_header) 248 249 if fdt_header[0] != constants.LX_OF_DT_HEADER: 250 raise gdb.GdbError("No flattened device tree magic found\n") 251 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])) 259 260 inf = gdb.inferiors()[0] 261 fdt_buf = utils.read_memoryview(inf, py_fdt_header_ptr, 262 fdt_header[1]).tobytes() 263 264 try: 265 f = open(filename, 'wb') 266 except gdb.error: 267 raise gdb.GdbError("Could not open file to dump fdt") 268 269 f.write(fdt_buf) 270 f.close() 271 272 gdb.write("Dumped fdt blob to " + filename + "\n") 273 274 275LxFdtDump()