proc.py 8.6 KB

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