diff options
Diffstat (limited to 'scripts/gdb/linux')
-rw-r--r-- | scripts/gdb/linux/.gitignore | 2 | ||||
-rw-r--r-- | scripts/gdb/linux/Makefile | 11 | ||||
-rw-r--r-- | scripts/gdb/linux/__init__.py | 1 | ||||
-rw-r--r-- | scripts/gdb/linux/cpus.py | 135 | ||||
-rw-r--r-- | scripts/gdb/linux/dmesg.py | 65 | ||||
-rw-r--r-- | scripts/gdb/linux/modules.py | 103 | ||||
-rw-r--r-- | scripts/gdb/linux/symbols.py | 177 | ||||
-rw-r--r-- | scripts/gdb/linux/tasks.py | 100 | ||||
-rw-r--r-- | scripts/gdb/linux/utils.py | 156 |
9 files changed, 750 insertions, 0 deletions
diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore new file mode 100644 index 000000000000..52e4e61140d1 --- /dev/null +++ b/scripts/gdb/linux/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | *.pyc | ||
2 | *.pyo | ||
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile new file mode 100644 index 000000000000..6cf1ecf61057 --- /dev/null +++ b/scripts/gdb/linux/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | always := gdb-scripts | ||
2 | |||
3 | SRCTREE := $(shell cd $(srctree) && /bin/pwd) | ||
4 | |||
5 | $(obj)/gdb-scripts: | ||
6 | ifneq ($(KBUILD_SRC),) | ||
7 | $(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj) | ||
8 | endif | ||
9 | @: | ||
10 | |||
11 | clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) | ||
diff --git a/scripts/gdb/linux/__init__.py b/scripts/gdb/linux/__init__.py new file mode 100644 index 000000000000..4680fb176337 --- /dev/null +++ b/scripts/gdb/linux/__init__.py | |||
@@ -0,0 +1 @@ | |||
# nothing to do for the initialization of this package | |||
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py new file mode 100644 index 000000000000..4297b83fedef --- /dev/null +++ b/scripts/gdb/linux/cpus.py | |||
@@ -0,0 +1,135 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # per-cpu tools | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2011-2013 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | |||
16 | from linux import tasks, utils | ||
17 | |||
18 | |||
19 | MAX_CPUS = 4096 | ||
20 | |||
21 | |||
22 | def get_current_cpu(): | ||
23 | if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU: | ||
24 | return gdb.selected_thread().num - 1 | ||
25 | elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB: | ||
26 | tid = gdb.selected_thread().ptid[2] | ||
27 | if tid > (0x100000000 - MAX_CPUS - 2): | ||
28 | return 0x100000000 - tid - 2 | ||
29 | else: | ||
30 | return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu'] | ||
31 | else: | ||
32 | raise gdb.GdbError("Sorry, obtaining the current CPU is not yet " | ||
33 | "supported with this gdb server.") | ||
34 | |||
35 | |||
36 | def per_cpu(var_ptr, cpu): | ||
37 | if cpu == -1: | ||
38 | cpu = get_current_cpu() | ||
39 | if utils.is_target_arch("sparc:v9"): | ||
40 | offset = gdb.parse_and_eval( | ||
41 | "trap_block[{0}].__per_cpu_base".format(str(cpu))) | ||
42 | else: | ||
43 | try: | ||
44 | offset = gdb.parse_and_eval( | ||
45 | "__per_cpu_offset[{0}]".format(str(cpu))) | ||
46 | except gdb.error: | ||
47 | # !CONFIG_SMP case | ||
48 | offset = 0 | ||
49 | pointer = var_ptr.cast(utils.get_long_type()) + offset | ||
50 | return pointer.cast(var_ptr.type).dereference() | ||
51 | |||
52 | |||
53 | cpu_mask = {} | ||
54 | |||
55 | |||
56 | def cpu_mask_invalidate(event): | ||
57 | global cpu_mask | ||
58 | cpu_mask = {} | ||
59 | gdb.events.stop.disconnect(cpu_mask_invalidate) | ||
60 | if hasattr(gdb.events, 'new_objfile'): | ||
61 | gdb.events.new_objfile.disconnect(cpu_mask_invalidate) | ||
62 | |||
63 | |||
64 | def cpu_list(mask_name): | ||
65 | global cpu_mask | ||
66 | mask = None | ||
67 | if mask_name in cpu_mask: | ||
68 | mask = cpu_mask[mask_name] | ||
69 | if mask is None: | ||
70 | mask = gdb.parse_and_eval(mask_name + ".bits") | ||
71 | if hasattr(gdb, 'events'): | ||
72 | cpu_mask[mask_name] = mask | ||
73 | gdb.events.stop.connect(cpu_mask_invalidate) | ||
74 | if hasattr(gdb.events, 'new_objfile'): | ||
75 | gdb.events.new_objfile.connect(cpu_mask_invalidate) | ||
76 | bits_per_entry = mask[0].type.sizeof * 8 | ||
77 | num_entries = mask.type.sizeof * 8 / bits_per_entry | ||
78 | entry = -1 | ||
79 | bits = 0 | ||
80 | |||
81 | while True: | ||
82 | while bits == 0: | ||
83 | entry += 1 | ||
84 | if entry == num_entries: | ||
85 | return | ||
86 | bits = mask[entry] | ||
87 | if bits != 0: | ||
88 | bit = 0 | ||
89 | break | ||
90 | |||
91 | while bits & 1 == 0: | ||
92 | bits >>= 1 | ||
93 | bit += 1 | ||
94 | |||
95 | cpu = entry * bits_per_entry + bit | ||
96 | |||
97 | bits >>= 1 | ||
98 | bit += 1 | ||
99 | |||
100 | yield cpu | ||
101 | |||
102 | |||
103 | class PerCpu(gdb.Function): | ||
104 | """Return per-cpu variable. | ||
105 | |||
106 | $lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the | ||
107 | given CPU number. If CPU is omitted, the CPU of the current context is used. | ||
108 | Note that VAR has to be quoted as string.""" | ||
109 | |||
110 | def __init__(self): | ||
111 | super(PerCpu, self).__init__("lx_per_cpu") | ||
112 | |||
113 | def invoke(self, var_name, cpu=-1): | ||
114 | var_ptr = gdb.parse_and_eval("&" + var_name.string()) | ||
115 | return per_cpu(var_ptr, cpu) | ||
116 | |||
117 | |||
118 | PerCpu() | ||
119 | |||
120 | |||
121 | class LxCurrentFunc(gdb.Function): | ||
122 | """Return current task. | ||
123 | |||
124 | $lx_current([CPU]): Return the per-cpu task variable for the given CPU | ||
125 | number. If CPU is omitted, the CPU of the current context is used.""" | ||
126 | |||
127 | def __init__(self): | ||
128 | super(LxCurrentFunc, self).__init__("lx_current") | ||
129 | |||
130 | def invoke(self, cpu=-1): | ||
131 | var_ptr = gdb.parse_and_eval("¤t_task") | ||
132 | return per_cpu(var_ptr, cpu).dereference() | ||
133 | |||
134 | |||
135 | LxCurrentFunc() | ||
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py new file mode 100644 index 000000000000..3c947f0c5dad --- /dev/null +++ b/scripts/gdb/linux/dmesg.py | |||
@@ -0,0 +1,65 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # kernel log buffer dump | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2011, 2012 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | import string | ||
16 | |||
17 | from linux import utils | ||
18 | |||
19 | |||
20 | class LxDmesg(gdb.Command): | ||
21 | """Print Linux kernel log buffer.""" | ||
22 | |||
23 | def __init__(self): | ||
24 | super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) | ||
25 | |||
26 | def invoke(self, arg, from_tty): | ||
27 | log_buf_addr = int(str(gdb.parse_and_eval("log_buf")).split()[0], 16) | ||
28 | log_first_idx = int(gdb.parse_and_eval("log_first_idx")) | ||
29 | log_next_idx = int(gdb.parse_and_eval("log_next_idx")) | ||
30 | log_buf_len = int(gdb.parse_and_eval("log_buf_len")) | ||
31 | |||
32 | inf = gdb.inferiors()[0] | ||
33 | start = log_buf_addr + log_first_idx | ||
34 | if log_first_idx < log_next_idx: | ||
35 | log_buf_2nd_half = -1 | ||
36 | length = log_next_idx - log_first_idx | ||
37 | log_buf = inf.read_memory(start, length) | ||
38 | else: | ||
39 | log_buf_2nd_half = log_buf_len - log_first_idx | ||
40 | log_buf = inf.read_memory(start, log_buf_2nd_half) + \ | ||
41 | inf.read_memory(log_buf_addr, log_next_idx) | ||
42 | |||
43 | pos = 0 | ||
44 | while pos < log_buf.__len__(): | ||
45 | length = utils.read_u16(log_buf[pos + 8:pos + 10]) | ||
46 | if length == 0: | ||
47 | if log_buf_2nd_half == -1: | ||
48 | gdb.write("Corrupted log buffer!\n") | ||
49 | break | ||
50 | pos = log_buf_2nd_half | ||
51 | continue | ||
52 | |||
53 | text_len = utils.read_u16(log_buf[pos + 10:pos + 12]) | ||
54 | text = log_buf[pos + 16:pos + 16 + text_len] | ||
55 | time_stamp = utils.read_u64(log_buf[pos:pos + 8]) | ||
56 | |||
57 | for line in memoryview(text).tobytes().splitlines(): | ||
58 | gdb.write("[{time:12.6f}] {line}\n".format( | ||
59 | time=time_stamp / 1000000000.0, | ||
60 | line=line)) | ||
61 | |||
62 | pos += length | ||
63 | |||
64 | |||
65 | LxDmesg() | ||
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py new file mode 100644 index 000000000000..a1504c4f1900 --- /dev/null +++ b/scripts/gdb/linux/modules.py | |||
@@ -0,0 +1,103 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # module tools | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2013 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | |||
16 | from linux import cpus, utils | ||
17 | |||
18 | |||
19 | module_type = utils.CachedType("struct module") | ||
20 | |||
21 | |||
22 | def module_list(): | ||
23 | global module_type | ||
24 | module_ptr_type = module_type.get_type().pointer() | ||
25 | modules = gdb.parse_and_eval("modules") | ||
26 | entry = modules['next'] | ||
27 | end_of_list = modules.address | ||
28 | |||
29 | while entry != end_of_list: | ||
30 | yield utils.container_of(entry, module_ptr_type, "list") | ||
31 | entry = entry['next'] | ||
32 | |||
33 | |||
34 | def find_module_by_name(name): | ||
35 | for module in module_list(): | ||
36 | if module['name'].string() == name: | ||
37 | return module | ||
38 | return None | ||
39 | |||
40 | |||
41 | class LxModule(gdb.Function): | ||
42 | """Find module by name and return the module variable. | ||
43 | |||
44 | $lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules | ||
45 | of the target and return that module variable which MODULE matches.""" | ||
46 | |||
47 | def __init__(self): | ||
48 | super(LxModule, self).__init__("lx_module") | ||
49 | |||
50 | def invoke(self, mod_name): | ||
51 | mod_name = mod_name.string() | ||
52 | module = find_module_by_name(mod_name) | ||
53 | if module: | ||
54 | return module.dereference() | ||
55 | else: | ||
56 | raise gdb.GdbError("Unable to find MODULE " + mod_name) | ||
57 | |||
58 | |||
59 | LxModule() | ||
60 | |||
61 | |||
62 | class LxLsmod(gdb.Command): | ||
63 | """List currently loaded modules.""" | ||
64 | |||
65 | _module_use_type = utils.CachedType("struct module_use") | ||
66 | |||
67 | def __init__(self): | ||
68 | super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA) | ||
69 | |||
70 | def invoke(self, arg, from_tty): | ||
71 | gdb.write( | ||
72 | "Address{0} Module Size Used by\n".format( | ||
73 | " " if utils.get_long_type().sizeof == 8 else "")) | ||
74 | |||
75 | for module in module_list(): | ||
76 | ref = 0 | ||
77 | module_refptr = module['refptr'] | ||
78 | for cpu in cpus.cpu_list("cpu_possible_mask"): | ||
79 | refptr = cpus.per_cpu(module_refptr, cpu) | ||
80 | ref += refptr['incs'] | ||
81 | ref -= refptr['decs'] | ||
82 | |||
83 | gdb.write("{address} {name:<19} {size:>8} {ref}".format( | ||
84 | address=str(module['module_core']).split()[0], | ||
85 | name=module['name'].string(), | ||
86 | size=str(module['core_size']), | ||
87 | ref=str(ref))) | ||
88 | |||
89 | source_list = module['source_list'] | ||
90 | t = self._module_use_type.get_type().pointer() | ||
91 | entry = source_list['next'] | ||
92 | first = True | ||
93 | while entry != source_list.address: | ||
94 | use = utils.container_of(entry, t, "source_list") | ||
95 | gdb.write("{separator}{name}".format( | ||
96 | separator=" " if first else ",", | ||
97 | name=use['source']['name'].string())) | ||
98 | first = False | ||
99 | entry = entry['next'] | ||
100 | gdb.write("\n") | ||
101 | |||
102 | |||
103 | LxLsmod() | ||
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py new file mode 100644 index 000000000000..cd5bea965d4e --- /dev/null +++ b/scripts/gdb/linux/symbols.py | |||
@@ -0,0 +1,177 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # load kernel and module symbols | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2011-2013 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | import os | ||
16 | import re | ||
17 | import string | ||
18 | |||
19 | from linux import modules, utils | ||
20 | |||
21 | |||
22 | if hasattr(gdb, 'Breakpoint'): | ||
23 | class LoadModuleBreakpoint(gdb.Breakpoint): | ||
24 | def __init__(self, spec, gdb_command): | ||
25 | super(LoadModuleBreakpoint, self).__init__(spec, internal=True) | ||
26 | self.silent = True | ||
27 | self.gdb_command = gdb_command | ||
28 | |||
29 | def stop(self): | ||
30 | module = gdb.parse_and_eval("mod") | ||
31 | module_name = module['name'].string() | ||
32 | cmd = self.gdb_command | ||
33 | |||
34 | # enforce update if object file is not found | ||
35 | cmd.module_files_updated = False | ||
36 | |||
37 | # Disable pagination while reporting symbol (re-)loading. | ||
38 | # The console input is blocked in this context so that we would | ||
39 | # get stuck waiting for the user to acknowledge paged output. | ||
40 | show_pagination = gdb.execute("show pagination", to_string=True) | ||
41 | pagination = show_pagination.endswith("on.\n") | ||
42 | gdb.execute("set pagination off") | ||
43 | |||
44 | if module_name in cmd.loaded_modules: | ||
45 | gdb.write("refreshing all symbols to reload module " | ||
46 | "'{0}'\n".format(module_name)) | ||
47 | cmd.load_all_symbols() | ||
48 | else: | ||
49 | cmd.load_module_symbols(module) | ||
50 | |||
51 | # restore pagination state | ||
52 | gdb.execute("set pagination %s" % ("on" if pagination else "off")) | ||
53 | |||
54 | return False | ||
55 | |||
56 | |||
57 | class LxSymbols(gdb.Command): | ||
58 | """(Re-)load symbols of Linux kernel and currently loaded modules. | ||
59 | |||
60 | The kernel (vmlinux) is taken from the current working directly. Modules (.ko) | ||
61 | are scanned recursively, starting in the same directory. Optionally, the module | ||
62 | search path can be extended by a space separated list of paths passed to the | ||
63 | lx-symbols command.""" | ||
64 | |||
65 | module_paths = [] | ||
66 | module_files = [] | ||
67 | module_files_updated = False | ||
68 | loaded_modules = [] | ||
69 | breakpoint = None | ||
70 | |||
71 | def __init__(self): | ||
72 | super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, | ||
73 | gdb.COMPLETE_FILENAME) | ||
74 | |||
75 | def _update_module_files(self): | ||
76 | self.module_files = [] | ||
77 | for path in self.module_paths: | ||
78 | gdb.write("scanning for modules in {0}\n".format(path)) | ||
79 | for root, dirs, files in os.walk(path): | ||
80 | for name in files: | ||
81 | if name.endswith(".ko"): | ||
82 | self.module_files.append(root + "/" + name) | ||
83 | self.module_files_updated = True | ||
84 | |||
85 | def _get_module_file(self, module_name): | ||
86 | module_pattern = ".*/{0}\.ko$".format( | ||
87 | module_name.replace("_", r"[_\-]")) | ||
88 | for name in self.module_files: | ||
89 | if re.match(module_pattern, name) and os.path.exists(name): | ||
90 | return name | ||
91 | return None | ||
92 | |||
93 | def _section_arguments(self, module): | ||
94 | try: | ||
95 | sect_attrs = module['sect_attrs'].dereference() | ||
96 | except gdb.error: | ||
97 | return "" | ||
98 | attrs = sect_attrs['attrs'] | ||
99 | section_name_to_address = { | ||
100 | attrs[n]['name'].string() : attrs[n]['address'] | ||
101 | for n in range(int(sect_attrs['nsections']))} | ||
102 | args = [] | ||
103 | for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]: | ||
104 | address = section_name_to_address.get(section_name) | ||
105 | if address: | ||
106 | args.append(" -s {name} {addr}".format( | ||
107 | name=section_name, addr=str(address))) | ||
108 | return "".join(args) | ||
109 | |||
110 | def load_module_symbols(self, module): | ||
111 | module_name = module['name'].string() | ||
112 | module_addr = str(module['module_core']).split()[0] | ||
113 | |||
114 | module_file = self._get_module_file(module_name) | ||
115 | if not module_file and not self.module_files_updated: | ||
116 | self._update_module_files() | ||
117 | module_file = self._get_module_file(module_name) | ||
118 | |||
119 | if module_file: | ||
120 | gdb.write("loading @{addr}: {filename}\n".format( | ||
121 | addr=module_addr, filename=module_file)) | ||
122 | cmdline = "add-symbol-file {filename} {addr}{sections}".format( | ||
123 | filename=module_file, | ||
124 | addr=module_addr, | ||
125 | sections=self._section_arguments(module)) | ||
126 | gdb.execute(cmdline, to_string=True) | ||
127 | if not module_name in self.loaded_modules: | ||
128 | self.loaded_modules.append(module_name) | ||
129 | else: | ||
130 | gdb.write("no module object found for '{0}'\n".format(module_name)) | ||
131 | |||
132 | def load_all_symbols(self): | ||
133 | gdb.write("loading vmlinux\n") | ||
134 | |||
135 | # Dropping symbols will disable all breakpoints. So save their states | ||
136 | # and restore them afterward. | ||
137 | saved_states = [] | ||
138 | if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: | ||
139 | for bp in gdb.breakpoints(): | ||
140 | saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) | ||
141 | |||
142 | # drop all current symbols and reload vmlinux | ||
143 | gdb.execute("symbol-file", to_string=True) | ||
144 | gdb.execute("symbol-file vmlinux") | ||
145 | |||
146 | self.loaded_modules = [] | ||
147 | module_list = modules.module_list() | ||
148 | if not module_list: | ||
149 | gdb.write("no modules found\n") | ||
150 | else: | ||
151 | [self.load_module_symbols(module) for module in module_list] | ||
152 | |||
153 | for saved_state in saved_states: | ||
154 | saved_state['breakpoint'].enabled = saved_state['enabled'] | ||
155 | |||
156 | def invoke(self, arg, from_tty): | ||
157 | self.module_paths = arg.split() | ||
158 | self.module_paths.append(os.getcwd()) | ||
159 | |||
160 | # enforce update | ||
161 | self.module_files = [] | ||
162 | self.module_files_updated = False | ||
163 | |||
164 | self.load_all_symbols() | ||
165 | |||
166 | if hasattr(gdb, 'Breakpoint'): | ||
167 | if not self.breakpoint is None: | ||
168 | self.breakpoint.delete() | ||
169 | self.breakpoint = None | ||
170 | self.breakpoint = LoadModuleBreakpoint( | ||
171 | "kernel/module.c:do_init_module", self) | ||
172 | else: | ||
173 | gdb.write("Note: symbol update on module loading not supported " | ||
174 | "with this gdb version\n") | ||
175 | |||
176 | |||
177 | LxSymbols() | ||
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py new file mode 100644 index 000000000000..e2037d9bb7eb --- /dev/null +++ b/scripts/gdb/linux/tasks.py | |||
@@ -0,0 +1,100 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # task & thread tools | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2011-2013 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | |||
16 | from linux import utils | ||
17 | |||
18 | |||
19 | task_type = utils.CachedType("struct task_struct") | ||
20 | |||
21 | def task_lists(): | ||
22 | global task_type | ||
23 | task_ptr_type = task_type.get_type().pointer() | ||
24 | init_task = gdb.parse_and_eval("init_task").address | ||
25 | t = g = init_task | ||
26 | |||
27 | while True: | ||
28 | while True: | ||
29 | yield t | ||
30 | |||
31 | t = utils.container_of(t['thread_group']['next'], | ||
32 | task_ptr_type, "thread_group") | ||
33 | if t == g: | ||
34 | break | ||
35 | |||
36 | t = g = utils.container_of(g['tasks']['next'], | ||
37 | task_ptr_type, "tasks") | ||
38 | if t == init_task: | ||
39 | return | ||
40 | |||
41 | def get_task_by_pid(pid): | ||
42 | for task in task_lists(): | ||
43 | if int(task['pid']) == pid: | ||
44 | return task | ||
45 | return None | ||
46 | |||
47 | |||
48 | class LxTaskByPidFunc(gdb.Function): | ||
49 | """Find Linux task by PID and return the task_struct variable. | ||
50 | |||
51 | $lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and | ||
52 | return that task_struct variable which PID matches.""" | ||
53 | |||
54 | def __init__(self): | ||
55 | super(LxTaskByPidFunc, self).__init__("lx_task_by_pid") | ||
56 | |||
57 | def invoke(self, pid): | ||
58 | task = get_task_by_pid(pid) | ||
59 | if task: | ||
60 | return task.dereference() | ||
61 | else: | ||
62 | raise gdb.GdbError("No task of PID " + str(pid)) | ||
63 | |||
64 | |||
65 | LxTaskByPidFunc() | ||
66 | |||
67 | |||
68 | thread_info_type = utils.CachedType("struct thread_info") | ||
69 | |||
70 | ia64_task_size = None | ||
71 | |||
72 | |||
73 | def get_thread_info(task): | ||
74 | global thread_info_type | ||
75 | thread_info_ptr_type = thread_info_type.get_type().pointer() | ||
76 | if utils.is_target_arch("ia64"): | ||
77 | global ia64_task_size | ||
78 | if ia64_task_size is None: | ||
79 | ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)") | ||
80 | thread_info_addr = task.address + ia64_task_size | ||
81 | thread_info = thread_info_addr.cast(thread_info_ptr_type) | ||
82 | else: | ||
83 | thread_info = task['stack'].cast(thread_info_ptr_type) | ||
84 | return thread_info.dereference() | ||
85 | |||
86 | |||
87 | class LxThreadInfoFunc (gdb.Function): | ||
88 | """Calculate Linux thread_info from task variable. | ||
89 | |||
90 | $lx_thread_info(TASK): Given TASK, return the corresponding thread_info | ||
91 | variable.""" | ||
92 | |||
93 | def __init__(self): | ||
94 | super(LxThreadInfoFunc, self).__init__("lx_thread_info") | ||
95 | |||
96 | def invoke(self, task): | ||
97 | return get_thread_info(task) | ||
98 | |||
99 | |||
100 | LxThreadInfoFunc() | ||
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py new file mode 100644 index 000000000000..128c306db3ee --- /dev/null +++ b/scripts/gdb/linux/utils.py | |||
@@ -0,0 +1,156 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # common utilities | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2011-2013 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Jan Kiszka <jan.kiszka@siemens.com> | ||
10 | # | ||
11 | # This work is licensed under the terms of the GNU GPL version 2. | ||
12 | # | ||
13 | |||
14 | import gdb | ||
15 | |||
16 | |||
17 | class CachedType: | ||
18 | def __init__(self, name): | ||
19 | self._type = None | ||
20 | self._name = name | ||
21 | |||
22 | def _new_objfile_handler(self, event): | ||
23 | self._type = None | ||
24 | gdb.events.new_objfile.disconnect(self._new_objfile_handler) | ||
25 | |||
26 | def get_type(self): | ||
27 | if self._type is None: | ||
28 | self._type = gdb.lookup_type(self._name) | ||
29 | if self._type is None: | ||
30 | raise gdb.GdbError( | ||
31 | "cannot resolve type '{0}'".format(self._name)) | ||
32 | if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'): | ||
33 | gdb.events.new_objfile.connect(self._new_objfile_handler) | ||
34 | return self._type | ||
35 | |||
36 | |||
37 | long_type = CachedType("long") | ||
38 | |||
39 | |||
40 | def get_long_type(): | ||
41 | global long_type | ||
42 | return long_type.get_type() | ||
43 | |||
44 | |||
45 | def offset_of(typeobj, field): | ||
46 | element = gdb.Value(0).cast(typeobj) | ||
47 | return int(str(element[field].address).split()[0], 16) | ||
48 | |||
49 | |||
50 | def container_of(ptr, typeobj, member): | ||
51 | return (ptr.cast(get_long_type()) - | ||
52 | offset_of(typeobj, member)).cast(typeobj) | ||
53 | |||
54 | |||
55 | class ContainerOf(gdb.Function): | ||
56 | """Return pointer to containing data structure. | ||
57 | |||
58 | $container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the | ||
59 | data structure of the type TYPE in which PTR is the address of ELEMENT. | ||
60 | Note that TYPE and ELEMENT have to be quoted as strings.""" | ||
61 | |||
62 | def __init__(self): | ||
63 | super(ContainerOf, self).__init__("container_of") | ||
64 | |||
65 | def invoke(self, ptr, typename, elementname): | ||
66 | return container_of(ptr, gdb.lookup_type(typename.string()).pointer(), | ||
67 | elementname.string()) | ||
68 | |||
69 | ContainerOf() | ||
70 | |||
71 | |||
72 | BIG_ENDIAN = 0 | ||
73 | LITTLE_ENDIAN = 1 | ||
74 | target_endianness = None | ||
75 | |||
76 | |||
77 | def get_target_endianness(): | ||
78 | global target_endianness | ||
79 | if target_endianness is None: | ||
80 | endian = gdb.execute("show endian", to_string=True) | ||
81 | if "little endian" in endian: | ||
82 | target_endianness = LITTLE_ENDIAN | ||
83 | elif "big endian" in endian: | ||
84 | target_endianness = BIG_ENDIAN | ||
85 | else: | ||
86 | raise gdb.GdgError("unknown endianness '{0}'".format(str(endian))) | ||
87 | return target_endianness | ||
88 | |||
89 | |||
90 | def read_u16(buffer): | ||
91 | if get_target_endianness() == LITTLE_ENDIAN: | ||
92 | return ord(buffer[0]) + (ord(buffer[1]) << 8) | ||
93 | else: | ||
94 | return ord(buffer[1]) + (ord(buffer[0]) << 8) | ||
95 | |||
96 | |||
97 | def read_u32(buffer): | ||
98 | if get_target_endianness() == LITTLE_ENDIAN: | ||
99 | return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16) | ||
100 | else: | ||
101 | return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16) | ||
102 | |||
103 | |||
104 | def read_u64(buffer): | ||
105 | if get_target_endianness() == LITTLE_ENDIAN: | ||
106 | return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32) | ||
107 | else: | ||
108 | return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32) | ||
109 | |||
110 | |||
111 | target_arch = None | ||
112 | |||
113 | |||
114 | def is_target_arch(arch): | ||
115 | if hasattr(gdb.Frame, 'architecture'): | ||
116 | return arch in gdb.newest_frame().architecture().name() | ||
117 | else: | ||
118 | global target_arch | ||
119 | if target_arch is None: | ||
120 | target_arch = gdb.execute("show architecture", to_string=True) | ||
121 | return arch in target_arch | ||
122 | |||
123 | |||
124 | GDBSERVER_QEMU = 0 | ||
125 | GDBSERVER_KGDB = 1 | ||
126 | gdbserver_type = None | ||
127 | |||
128 | |||
129 | def get_gdbserver_type(): | ||
130 | def exit_handler(event): | ||
131 | global gdbserver_type | ||
132 | gdbserver_type = None | ||
133 | gdb.events.exited.disconnect(exit_handler) | ||
134 | |||
135 | def probe_qemu(): | ||
136 | try: | ||
137 | return gdb.execute("monitor info version", to_string=True) != "" | ||
138 | except: | ||
139 | return False | ||
140 | |||
141 | def probe_kgdb(): | ||
142 | try: | ||
143 | thread_info = gdb.execute("info thread 2", to_string=True) | ||
144 | return "shadowCPU0" in thread_info | ||
145 | except: | ||
146 | return False | ||
147 | |||
148 | global gdbserver_type | ||
149 | if gdbserver_type is None: | ||
150 | if probe_qemu(): | ||
151 | gdbserver_type = GDBSERVER_QEMU | ||
152 | elif probe_kgdb(): | ||
153 | gdbserver_type = GDBSERVER_KGDB | ||
154 | if not gdbserver_type is None and hasattr(gdb, 'events'): | ||
155 | gdb.events.exited.connect(exit_handler) | ||
156 | return gdbserver_type | ||