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