Commit | Line | Data |
---|---|---|
2d061d99 KB |
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 | ||
14 | import gdb | |
c1a15399 KB |
15 | from linux import constants |
16 | from linux import utils | |
17 | from linux import tasks | |
18 | from linux import lists | |
2d061d99 KB |
19 | |
20 | ||
72bf92ec KB |
21 | class LxCmdLine(gdb.Command): |
22 | """ Report the Linux Commandline used in the current kernel. | |
23 | Equivalent to cat /proc/cmdline on a running target""" | |
24 | ||
25 | def __init__(self): | |
26 | super(LxCmdLine, self).__init__("lx-cmdline", gdb.COMMAND_DATA) | |
27 | ||
28 | def invoke(self, arg, from_tty): | |
29 | gdb.write(gdb.parse_and_eval("saved_command_line").string() + "\n") | |
30 | ||
31 | LxCmdLine() | |
32 | ||
33 | ||
2d061d99 KB |
34 | class LxVersion(gdb.Command): |
35 | """ Report the Linux Version of the current kernel. | |
36 | Equivalent to cat /proc/version on a running target""" | |
37 | ||
38 | def __init__(self): | |
39 | super(LxVersion, self).__init__("lx-version", gdb.COMMAND_DATA) | |
40 | ||
41 | def invoke(self, arg, from_tty): | |
42 | # linux_banner should contain a newline | |
43 | gdb.write(gdb.parse_and_eval("linux_banner").string()) | |
44 | ||
45 | LxVersion() | |
e7165a2d KB |
46 | |
47 | ||
48 | # Resource Structure Printers | |
49 | # /proc/iomem | |
50 | # /proc/ioports | |
51 | ||
52 | def get_resources(resource, depth): | |
53 | while resource: | |
54 | yield resource, depth | |
55 | ||
56 | child = resource['child'] | |
57 | if child: | |
58 | for res, deep in get_resources(child, depth + 1): | |
59 | yield res, deep | |
60 | ||
61 | resource = resource['sibling'] | |
62 | ||
63 | ||
64 | def show_lx_resources(resource_str): | |
65 | resource = gdb.parse_and_eval(resource_str) | |
66 | width = 4 if resource['end'] < 0x10000 else 8 | |
67 | # Iterate straight to the first child | |
68 | for res, depth in get_resources(resource['child'], 0): | |
69 | start = int(res['start']) | |
70 | end = int(res['end']) | |
71 | gdb.write(" " * depth * 2 + | |
72 | "{0:0{1}x}-".format(start, width) + | |
73 | "{0:0{1}x} : ".format(end, width) + | |
74 | res['name'].string() + "\n") | |
75 | ||
76 | ||
77 | class LxIOMem(gdb.Command): | |
78 | """Identify the IO memory resource locations defined by the kernel | |
79 | ||
80 | Equivalent to cat /proc/iomem on a running target""" | |
81 | ||
82 | def __init__(self): | |
83 | super(LxIOMem, self).__init__("lx-iomem", gdb.COMMAND_DATA) | |
84 | ||
85 | def invoke(self, arg, from_tty): | |
86 | return show_lx_resources("iomem_resource") | |
87 | ||
88 | LxIOMem() | |
89 | ||
90 | ||
91 | class LxIOPorts(gdb.Command): | |
92 | """Identify the IO port resource locations defined by the kernel | |
93 | ||
94 | Equivalent to cat /proc/ioports on a running target""" | |
95 | ||
96 | def __init__(self): | |
97 | super(LxIOPorts, self).__init__("lx-ioports", gdb.COMMAND_DATA) | |
98 | ||
99 | def invoke(self, arg, from_tty): | |
100 | return show_lx_resources("ioport_resource") | |
101 | ||
102 | LxIOPorts() | |
c1a15399 KB |
103 | |
104 | ||
105 | # Mount namespace viewer | |
106 | # /proc/mounts | |
107 | ||
108 | def info_opts(lst, opt): | |
109 | opts = "" | |
110 | for key, string in lst.items(): | |
111 | if opt & key: | |
112 | opts += string | |
113 | return opts | |
114 | ||
115 | ||
116 | FS_INFO = {constants.LX_MS_SYNCHRONOUS: ",sync", | |
117 | constants.LX_MS_MANDLOCK: ",mand", | |
118 | constants.LX_MS_DIRSYNC: ",dirsync", | |
119 | constants.LX_MS_NOATIME: ",noatime", | |
120 | constants.LX_MS_NODIRATIME: ",nodiratime"} | |
121 | ||
122 | MNT_INFO = {constants.LX_MNT_NOSUID: ",nosuid", | |
123 | constants.LX_MNT_NODEV: ",nodev", | |
124 | constants.LX_MNT_NOEXEC: ",noexec", | |
125 | constants.LX_MNT_NOATIME: ",noatime", | |
126 | constants.LX_MNT_NODIRATIME: ",nodiratime", | |
127 | constants.LX_MNT_RELATIME: ",relatime"} | |
128 | ||
129 | mount_type = utils.CachedType("struct mount") | |
130 | mount_ptr_type = mount_type.get_type().pointer() | |
131 | ||
132 | ||
133 | class LxMounts(gdb.Command): | |
134 | """Report the VFS mounts of the current process namespace. | |
135 | ||
136 | Equivalent to cat /proc/mounts on a running target | |
137 | An integer value can be supplied to display the mount | |
138 | values of that process namespace""" | |
139 | ||
140 | def __init__(self): | |
141 | super(LxMounts, self).__init__("lx-mounts", gdb.COMMAND_DATA) | |
142 | ||
143 | # Equivalent to proc_namespace.c:show_vfsmnt | |
144 | # However, that has the ability to call into s_op functions | |
145 | # whereas we cannot and must make do with the information we can obtain. | |
146 | def invoke(self, arg, from_tty): | |
147 | argv = gdb.string_to_argv(arg) | |
148 | if len(argv) >= 1: | |
149 | try: | |
150 | pid = int(argv[0]) | |
151 | except: | |
152 | raise gdb.GdbError("Provide a PID as integer value") | |
153 | else: | |
154 | pid = 1 | |
155 | ||
156 | task = tasks.get_task_by_pid(pid) | |
157 | if not task: | |
158 | raise gdb.GdbError("Couldn't find a process with PID {}" | |
159 | .format(pid)) | |
160 | ||
161 | namespace = task['nsproxy']['mnt_ns'] | |
162 | if not namespace: | |
163 | raise gdb.GdbError("No namespace for current process") | |
164 | ||
165 | for vfs in lists.list_for_each_entry(namespace['list'], | |
166 | mount_ptr_type, "mnt_list"): | |
167 | devname = vfs['mnt_devname'].string() | |
168 | devname = devname if devname else "none" | |
169 | ||
170 | pathname = "" | |
171 | parent = vfs | |
172 | while True: | |
173 | mntpoint = parent['mnt_mountpoint'] | |
174 | pathname = utils.dentry_name(mntpoint) + pathname | |
175 | if (parent == parent['mnt_parent']): | |
176 | break | |
177 | parent = parent['mnt_parent'] | |
178 | ||
179 | if (pathname == ""): | |
180 | pathname = "/" | |
181 | ||
182 | superblock = vfs['mnt']['mnt_sb'] | |
183 | fstype = superblock['s_type']['name'].string() | |
184 | s_flags = int(superblock['s_flags']) | |
185 | m_flags = int(vfs['mnt']['mnt_flags']) | |
186 | rd = "ro" if (s_flags & constants.LX_MS_RDONLY) else "rw" | |
187 | ||
188 | gdb.write( | |
189 | "{} {} {} {}{}{} 0 0\n" | |
190 | .format(devname, | |
191 | pathname, | |
192 | fstype, | |
193 | rd, | |
194 | info_opts(FS_INFO, s_flags), | |
195 | info_opts(MNT_INFO, m_flags))) | |
196 | ||
197 | LxMounts() |