diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /scripts/gdb | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'scripts/gdb')
-rw-r--r-- | scripts/gdb/Makefile | 2 | ||||
-rw-r--r-- | scripts/gdb/linux/.gitignore | 4 | ||||
-rw-r--r-- | scripts/gdb/linux/Makefile | 25 | ||||
-rw-r--r-- | scripts/gdb/linux/__init__.py | 1 | ||||
-rw-r--r-- | scripts/gdb/linux/clk.py | 76 | ||||
-rw-r--r-- | scripts/gdb/linux/config.py | 44 | ||||
-rw-r--r-- | scripts/gdb/linux/constants.py.in | 77 | ||||
-rw-r--r-- | scripts/gdb/linux/cpus.py | 174 | ||||
-rw-r--r-- | scripts/gdb/linux/device.py | 182 | ||||
-rw-r--r-- | scripts/gdb/linux/dmesg.py | 154 | ||||
-rw-r--r-- | scripts/gdb/linux/genpd.py | 83 | ||||
-rw-r--r-- | scripts/gdb/linux/lists.py | 131 | ||||
-rw-r--r-- | scripts/gdb/linux/modules.py | 95 | ||||
-rw-r--r-- | scripts/gdb/linux/proc.py | 275 | ||||
-rw-r--r-- | scripts/gdb/linux/rbtree.py | 177 | ||||
-rw-r--r-- | scripts/gdb/linux/symbols.py | 187 | ||||
-rw-r--r-- | scripts/gdb/linux/tasks.py | 140 | ||||
-rw-r--r-- | scripts/gdb/linux/timerlist.py | 219 | ||||
-rw-r--r-- | scripts/gdb/linux/utils.py | 193 | ||||
-rw-r--r-- | scripts/gdb/vmlinux-gdb.py | 39 |
20 files changed, 2278 insertions, 0 deletions
diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile new file mode 100644 index 000000000..3fca1937d --- /dev/null +++ b/scripts/gdb/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | subdir-y := linux | ||
diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore new file mode 100644 index 000000000..43234cbcb --- /dev/null +++ b/scripts/gdb/linux/.gitignore | |||
@@ -0,0 +1,4 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | *.pyc | ||
3 | *.pyo | ||
4 | constants.py | ||
diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile new file mode 100644 index 000000000..124755087 --- /dev/null +++ b/scripts/gdb/linux/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | ifdef building_out_of_srctree | ||
4 | |||
5 | symlinks := $(patsubst $(srctree)/$(src)/%,%,$(wildcard $(srctree)/$(src)/*.py)) | ||
6 | |||
7 | quiet_cmd_symlink = SYMLINK $@ | ||
8 | cmd_symlink = ln -fsn $(patsubst $(obj)/%,$(abspath $(srctree))/$(src)/%,$@) $@ | ||
9 | |||
10 | extra-y += $(symlinks) | ||
11 | $(addprefix $(obj)/, $(symlinks)): FORCE | ||
12 | $(call if_changed,symlink) | ||
13 | |||
14 | endif | ||
15 | |||
16 | quiet_cmd_gen_constants_py = GEN $@ | ||
17 | cmd_gen_constants_py = \ | ||
18 | $(CPP) -E -x c -P $(c_flags) $< > $@ ;\ | ||
19 | sed -i '1,/<!-- end-c-headers -->/d;' $@ | ||
20 | |||
21 | extra-y += constants.py | ||
22 | $(obj)/constants.py: $(src)/constants.py.in FORCE | ||
23 | $(call if_changed_dep,gen_constants_py) | ||
24 | |||
25 | clean-files := *.pyc *.pyo | ||
diff --git a/scripts/gdb/linux/__init__.py b/scripts/gdb/linux/__init__.py new file mode 100644 index 000000000..4680fb176 --- /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/clk.py b/scripts/gdb/linux/clk.py new file mode 100644 index 000000000..061aecfa2 --- /dev/null +++ b/scripts/gdb/linux/clk.py | |||
@@ -0,0 +1,76 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright (c) NXP 2019 | ||
4 | |||
5 | import gdb | ||
6 | import sys | ||
7 | |||
8 | from linux import utils, lists, constants | ||
9 | |||
10 | clk_core_type = utils.CachedType("struct clk_core") | ||
11 | |||
12 | |||
13 | def clk_core_for_each_child(hlist_head): | ||
14 | return lists.hlist_for_each_entry(hlist_head, | ||
15 | clk_core_type.get_type().pointer(), "child_node") | ||
16 | |||
17 | |||
18 | class LxClkSummary(gdb.Command): | ||
19 | """Print clk tree summary | ||
20 | |||
21 | Output is a subset of /sys/kernel/debug/clk/clk_summary | ||
22 | |||
23 | No calls are made during printing, instead a (c) if printed after values which | ||
24 | are cached and potentially out of date""" | ||
25 | |||
26 | def __init__(self): | ||
27 | super(LxClkSummary, self).__init__("lx-clk-summary", gdb.COMMAND_DATA) | ||
28 | |||
29 | def show_subtree(self, clk, level): | ||
30 | gdb.write("%*s%-*s %7d %8d %8d %11lu%s\n" % ( | ||
31 | level * 3 + 1, "", | ||
32 | 30 - level * 3, | ||
33 | clk['name'].string(), | ||
34 | clk['enable_count'], | ||
35 | clk['prepare_count'], | ||
36 | clk['protect_count'], | ||
37 | clk['rate'], | ||
38 | '(c)' if clk['flags'] & constants.LX_CLK_GET_RATE_NOCACHE else ' ')) | ||
39 | |||
40 | for child in clk_core_for_each_child(clk['children']): | ||
41 | self.show_subtree(child, level + 1) | ||
42 | |||
43 | def invoke(self, arg, from_tty): | ||
44 | gdb.write(" enable prepare protect \n") | ||
45 | gdb.write(" clock count count count rate \n") | ||
46 | gdb.write("------------------------------------------------------------------------\n") | ||
47 | for clk in clk_core_for_each_child(gdb.parse_and_eval("clk_root_list")): | ||
48 | self.show_subtree(clk, 0) | ||
49 | for clk in clk_core_for_each_child(gdb.parse_and_eval("clk_orphan_list")): | ||
50 | self.show_subtree(clk, 0) | ||
51 | |||
52 | |||
53 | LxClkSummary() | ||
54 | |||
55 | |||
56 | class LxClkCoreLookup(gdb.Function): | ||
57 | """Find struct clk_core by name""" | ||
58 | |||
59 | def __init__(self): | ||
60 | super(LxClkCoreLookup, self).__init__("lx_clk_core_lookup") | ||
61 | |||
62 | def lookup_hlist(self, hlist_head, name): | ||
63 | for child in clk_core_for_each_child(hlist_head): | ||
64 | if child['name'].string() == name: | ||
65 | return child | ||
66 | result = self.lookup_hlist(child['children'], name) | ||
67 | if result: | ||
68 | return result | ||
69 | |||
70 | def invoke(self, name): | ||
71 | name = name.string() | ||
72 | return (self.lookup_hlist(gdb.parse_and_eval("clk_root_list"), name) or | ||
73 | self.lookup_hlist(gdb.parse_and_eval("clk_orphan_list"), name)) | ||
74 | |||
75 | |||
76 | LxClkCoreLookup() | ||
diff --git a/scripts/gdb/linux/config.py b/scripts/gdb/linux/config.py new file mode 100644 index 000000000..8843ab3cb --- /dev/null +++ b/scripts/gdb/linux/config.py | |||
@@ -0,0 +1,44 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright 2019 Google LLC. | ||
4 | |||
5 | import gdb | ||
6 | import zlib | ||
7 | |||
8 | from linux import utils | ||
9 | |||
10 | |||
11 | class LxConfigDump(gdb.Command): | ||
12 | """Output kernel config to the filename specified as the command | ||
13 | argument. Equivalent to 'zcat /proc/config.gz > config.txt' on | ||
14 | a running target""" | ||
15 | |||
16 | def __init__(self): | ||
17 | super(LxConfigDump, self).__init__("lx-configdump", gdb.COMMAND_DATA, | ||
18 | gdb.COMPLETE_FILENAME) | ||
19 | |||
20 | def invoke(self, arg, from_tty): | ||
21 | if len(arg) == 0: | ||
22 | filename = "config.txt" | ||
23 | else: | ||
24 | filename = arg | ||
25 | |||
26 | try: | ||
27 | py_config_ptr = gdb.parse_and_eval("&kernel_config_data") | ||
28 | py_config_ptr_end = gdb.parse_and_eval("&kernel_config_data_end") | ||
29 | py_config_size = py_config_ptr_end - py_config_ptr | ||
30 | except gdb.error as e: | ||
31 | raise gdb.GdbError("Can't find config, enable CONFIG_IKCONFIG?") | ||
32 | |||
33 | inf = gdb.inferiors()[0] | ||
34 | zconfig_buf = utils.read_memoryview(inf, py_config_ptr, | ||
35 | py_config_size).tobytes() | ||
36 | |||
37 | config_buf = zlib.decompress(zconfig_buf, 16) | ||
38 | with open(filename, 'wb') as f: | ||
39 | f.write(config_buf) | ||
40 | |||
41 | gdb.write("Dumped config to " + filename + "\n") | ||
42 | |||
43 | |||
44 | LxConfigDump() | ||
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in new file mode 100644 index 000000000..2efbec6b6 --- /dev/null +++ b/scripts/gdb/linux/constants.py.in | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * gdb helper commands and functions for Linux kernel debugging | ||
3 | * | ||
4 | * Kernel constants derived from include files. | ||
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 | |||
15 | #include <linux/clk-provider.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/hrtimer.h> | ||
18 | #include <linux/mount.h> | ||
19 | #include <linux/of_fdt.h> | ||
20 | #include <linux/threads.h> | ||
21 | |||
22 | /* We need to stringify expanded macros so that they can be parsed */ | ||
23 | |||
24 | #define STRING(x) #x | ||
25 | #define XSTRING(x) STRING(x) | ||
26 | |||
27 | #define LX_VALUE(x) LX_##x = x | ||
28 | #define LX_GDBPARSED(x) LX_##x = gdb.parse_and_eval(XSTRING(x)) | ||
29 | |||
30 | /* | ||
31 | * IS_ENABLED generates (a || b) which is not compatible with python | ||
32 | * We can only switch on configuration items we know are available | ||
33 | * Therefore - IS_BUILTIN() is more appropriate | ||
34 | */ | ||
35 | #define LX_CONFIG(x) LX_##x = IS_BUILTIN(x) | ||
36 | |||
37 | /* The build system will take care of deleting everything above this marker */ | ||
38 | <!-- end-c-headers --> | ||
39 | |||
40 | import gdb | ||
41 | |||
42 | /* linux/clk-provider.h */ | ||
43 | if IS_BUILTIN(CONFIG_COMMON_CLK): | ||
44 | LX_GDBPARSED(CLK_GET_RATE_NOCACHE) | ||
45 | |||
46 | /* linux/fs.h */ | ||
47 | LX_VALUE(SB_RDONLY) | ||
48 | LX_VALUE(SB_SYNCHRONOUS) | ||
49 | LX_VALUE(SB_MANDLOCK) | ||
50 | LX_VALUE(SB_DIRSYNC) | ||
51 | LX_VALUE(SB_NOATIME) | ||
52 | LX_VALUE(SB_NODIRATIME) | ||
53 | |||
54 | /* linux/htimer.h */ | ||
55 | LX_GDBPARSED(hrtimer_resolution) | ||
56 | |||
57 | /* linux/mount.h */ | ||
58 | LX_VALUE(MNT_NOSUID) | ||
59 | LX_VALUE(MNT_NODEV) | ||
60 | LX_VALUE(MNT_NOEXEC) | ||
61 | LX_VALUE(MNT_NOATIME) | ||
62 | LX_VALUE(MNT_NODIRATIME) | ||
63 | LX_VALUE(MNT_RELATIME) | ||
64 | |||
65 | /* linux/threads.h */ | ||
66 | LX_VALUE(NR_CPUS) | ||
67 | |||
68 | /* linux/of_fdt.h> */ | ||
69 | LX_VALUE(OF_DT_HEADER) | ||
70 | |||
71 | /* Kernel Configs */ | ||
72 | LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS) | ||
73 | LX_CONFIG(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) | ||
74 | LX_CONFIG(CONFIG_HIGH_RES_TIMERS) | ||
75 | LX_CONFIG(CONFIG_NR_CPUS) | ||
76 | LX_CONFIG(CONFIG_OF) | ||
77 | LX_CONFIG(CONFIG_TICK_ONESHOT) | ||
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py new file mode 100644 index 000000000..008e62f31 --- /dev/null +++ b/scripts/gdb/linux/cpus.py | |||
@@ -0,0 +1,174 @@ | |||
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 int(cpu) | ||
101 | |||
102 | |||
103 | def each_online_cpu(): | ||
104 | for cpu in cpu_list("__cpu_online_mask"): | ||
105 | yield cpu | ||
106 | |||
107 | |||
108 | def each_present_cpu(): | ||
109 | for cpu in cpu_list("__cpu_present_mask"): | ||
110 | yield cpu | ||
111 | |||
112 | |||
113 | def each_possible_cpu(): | ||
114 | for cpu in cpu_list("__cpu_possible_mask"): | ||
115 | yield cpu | ||
116 | |||
117 | |||
118 | def each_active_cpu(): | ||
119 | for cpu in cpu_list("__cpu_active_mask"): | ||
120 | yield cpu | ||
121 | |||
122 | |||
123 | class LxCpus(gdb.Command): | ||
124 | """List CPU status arrays | ||
125 | |||
126 | Displays the known state of each CPU based on the kernel masks | ||
127 | and can help identify the state of hotplugged CPUs""" | ||
128 | |||
129 | def __init__(self): | ||
130 | super(LxCpus, self).__init__("lx-cpus", gdb.COMMAND_DATA) | ||
131 | |||
132 | def invoke(self, arg, from_tty): | ||
133 | gdb.write("Possible CPUs : {}\n".format(list(each_possible_cpu()))) | ||
134 | gdb.write("Present CPUs : {}\n".format(list(each_present_cpu()))) | ||
135 | gdb.write("Online CPUs : {}\n".format(list(each_online_cpu()))) | ||
136 | gdb.write("Active CPUs : {}\n".format(list(each_active_cpu()))) | ||
137 | |||
138 | |||
139 | LxCpus() | ||
140 | |||
141 | |||
142 | class PerCpu(gdb.Function): | ||
143 | """Return per-cpu variable. | ||
144 | |||
145 | $lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the | ||
146 | given CPU number. If CPU is omitted, the CPU of the current context is used. | ||
147 | Note that VAR has to be quoted as string.""" | ||
148 | |||
149 | def __init__(self): | ||
150 | super(PerCpu, self).__init__("lx_per_cpu") | ||
151 | |||
152 | def invoke(self, var_name, cpu=-1): | ||
153 | var_ptr = gdb.parse_and_eval("&" + var_name.string()) | ||
154 | return per_cpu(var_ptr, cpu) | ||
155 | |||
156 | |||
157 | PerCpu() | ||
158 | |||
159 | |||
160 | class LxCurrentFunc(gdb.Function): | ||
161 | """Return current task. | ||
162 | |||
163 | $lx_current([CPU]): Return the per-cpu task variable for the given CPU | ||
164 | number. If CPU is omitted, the CPU of the current context is used.""" | ||
165 | |||
166 | def __init__(self): | ||
167 | super(LxCurrentFunc, self).__init__("lx_current") | ||
168 | |||
169 | def invoke(self, cpu=-1): | ||
170 | var_ptr = gdb.parse_and_eval("¤t_task") | ||
171 | return per_cpu(var_ptr, cpu).dereference() | ||
172 | |||
173 | |||
174 | LxCurrentFunc() | ||
diff --git a/scripts/gdb/linux/device.py b/scripts/gdb/linux/device.py new file mode 100644 index 000000000..16376c5cf --- /dev/null +++ b/scripts/gdb/linux/device.py | |||
@@ -0,0 +1,182 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright (c) NXP 2019 | ||
4 | |||
5 | import gdb | ||
6 | |||
7 | from linux.utils import CachedType | ||
8 | from linux.utils import container_of | ||
9 | from linux.lists import list_for_each_entry | ||
10 | |||
11 | |||
12 | device_private_type = CachedType('struct device_private') | ||
13 | device_type = CachedType('struct device') | ||
14 | |||
15 | subsys_private_type = CachedType('struct subsys_private') | ||
16 | kobject_type = CachedType('struct kobject') | ||
17 | kset_type = CachedType('struct kset') | ||
18 | |||
19 | bus_type = CachedType('struct bus_type') | ||
20 | class_type = CachedType('struct class') | ||
21 | |||
22 | |||
23 | def dev_name(dev): | ||
24 | dev_init_name = dev['init_name'] | ||
25 | if dev_init_name: | ||
26 | return dev_init_name.string() | ||
27 | return dev['kobj']['name'].string() | ||
28 | |||
29 | |||
30 | def kset_for_each_object(kset): | ||
31 | return list_for_each_entry(kset['list'], | ||
32 | kobject_type.get_type().pointer(), "entry") | ||
33 | |||
34 | |||
35 | def for_each_bus(): | ||
36 | for kobj in kset_for_each_object(gdb.parse_and_eval('bus_kset')): | ||
37 | subsys = container_of(kobj, kset_type.get_type().pointer(), 'kobj') | ||
38 | subsys_priv = container_of(subsys, subsys_private_type.get_type().pointer(), 'subsys') | ||
39 | yield subsys_priv['bus'] | ||
40 | |||
41 | |||
42 | def for_each_class(): | ||
43 | for kobj in kset_for_each_object(gdb.parse_and_eval('class_kset')): | ||
44 | subsys = container_of(kobj, kset_type.get_type().pointer(), 'kobj') | ||
45 | subsys_priv = container_of(subsys, subsys_private_type.get_type().pointer(), 'subsys') | ||
46 | yield subsys_priv['class'] | ||
47 | |||
48 | |||
49 | def get_bus_by_name(name): | ||
50 | for item in for_each_bus(): | ||
51 | if item['name'].string() == name: | ||
52 | return item | ||
53 | raise gdb.GdbError("Can't find bus type {!r}".format(name)) | ||
54 | |||
55 | |||
56 | def get_class_by_name(name): | ||
57 | for item in for_each_class(): | ||
58 | if item['name'].string() == name: | ||
59 | return item | ||
60 | raise gdb.GdbError("Can't find device class {!r}".format(name)) | ||
61 | |||
62 | |||
63 | klist_type = CachedType('struct klist') | ||
64 | klist_node_type = CachedType('struct klist_node') | ||
65 | |||
66 | |||
67 | def klist_for_each(klist): | ||
68 | return list_for_each_entry(klist['k_list'], | ||
69 | klist_node_type.get_type().pointer(), 'n_node') | ||
70 | |||
71 | |||
72 | def bus_for_each_device(bus): | ||
73 | for kn in klist_for_each(bus['p']['klist_devices']): | ||
74 | dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_bus') | ||
75 | yield dp['device'] | ||
76 | |||
77 | |||
78 | def class_for_each_device(cls): | ||
79 | for kn in klist_for_each(cls['p']['klist_devices']): | ||
80 | dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_class') | ||
81 | yield dp['device'] | ||
82 | |||
83 | |||
84 | def device_for_each_child(dev): | ||
85 | for kn in klist_for_each(dev['p']['klist_children']): | ||
86 | dp = container_of(kn, device_private_type.get_type().pointer(), 'knode_parent') | ||
87 | yield dp['device'] | ||
88 | |||
89 | |||
90 | def _show_device(dev, level=0, recursive=False): | ||
91 | gdb.write('{}dev {}:\t{}\n'.format('\t' * level, dev_name(dev), dev)) | ||
92 | if recursive: | ||
93 | for child in device_for_each_child(dev): | ||
94 | _show_device(child, level + 1, recursive) | ||
95 | |||
96 | |||
97 | class LxDeviceListBus(gdb.Command): | ||
98 | '''Print devices on a bus (or all buses if not specified)''' | ||
99 | |||
100 | def __init__(self): | ||
101 | super(LxDeviceListBus, self).__init__('lx-device-list-bus', gdb.COMMAND_DATA) | ||
102 | |||
103 | def invoke(self, arg, from_tty): | ||
104 | if not arg: | ||
105 | for bus in for_each_bus(): | ||
106 | gdb.write('bus {}:\t{}\n'.format(bus['name'].string(), bus)) | ||
107 | for dev in bus_for_each_device(bus): | ||
108 | _show_device(dev, level=1) | ||
109 | else: | ||
110 | bus = get_bus_by_name(arg) | ||
111 | if not bus: | ||
112 | raise gdb.GdbError("Can't find bus {!r}".format(arg)) | ||
113 | for dev in bus_for_each_device(bus): | ||
114 | _show_device(dev) | ||
115 | |||
116 | |||
117 | class LxDeviceListClass(gdb.Command): | ||
118 | '''Print devices in a class (or all classes if not specified)''' | ||
119 | |||
120 | def __init__(self): | ||
121 | super(LxDeviceListClass, self).__init__('lx-device-list-class', gdb.COMMAND_DATA) | ||
122 | |||
123 | def invoke(self, arg, from_tty): | ||
124 | if not arg: | ||
125 | for cls in for_each_class(): | ||
126 | gdb.write("class {}:\t{}\n".format(cls['name'].string(), cls)) | ||
127 | for dev in class_for_each_device(cls): | ||
128 | _show_device(dev, level=1) | ||
129 | else: | ||
130 | cls = get_class_by_name(arg) | ||
131 | for dev in class_for_each_device(cls): | ||
132 | _show_device(dev) | ||
133 | |||
134 | |||
135 | class LxDeviceListTree(gdb.Command): | ||
136 | '''Print a device and its children recursively''' | ||
137 | |||
138 | def __init__(self): | ||
139 | super(LxDeviceListTree, self).__init__('lx-device-list-tree', gdb.COMMAND_DATA) | ||
140 | |||
141 | def invoke(self, arg, from_tty): | ||
142 | if not arg: | ||
143 | raise gdb.GdbError('Please provide pointer to struct device') | ||
144 | dev = gdb.parse_and_eval(arg) | ||
145 | if dev.type != device_type.get_type().pointer(): | ||
146 | raise gdb.GdbError('Please provide pointer to struct device') | ||
147 | _show_device(dev, level=0, recursive=True) | ||
148 | |||
149 | |||
150 | class LxDeviceFindByBusName(gdb.Function): | ||
151 | '''Find struct device by bus and name (both strings)''' | ||
152 | |||
153 | def __init__(self): | ||
154 | super(LxDeviceFindByBusName, self).__init__('lx_device_find_by_bus_name') | ||
155 | |||
156 | def invoke(self, bus, name): | ||
157 | name = name.string() | ||
158 | bus = get_bus_by_name(bus.string()) | ||
159 | for dev in bus_for_each_device(bus): | ||
160 | if dev_name(dev) == name: | ||
161 | return dev | ||
162 | |||
163 | |||
164 | class LxDeviceFindByClassName(gdb.Function): | ||
165 | '''Find struct device by class and name (both strings)''' | ||
166 | |||
167 | def __init__(self): | ||
168 | super(LxDeviceFindByClassName, self).__init__('lx_device_find_by_class_name') | ||
169 | |||
170 | def invoke(self, cls, name): | ||
171 | name = name.string() | ||
172 | cls = get_class_by_name(cls.string()) | ||
173 | for dev in class_for_each_device(cls): | ||
174 | if dev_name(dev) == name: | ||
175 | return dev | ||
176 | |||
177 | |||
178 | LxDeviceListBus() | ||
179 | LxDeviceListClass() | ||
180 | LxDeviceListTree() | ||
181 | LxDeviceFindByBusName() | ||
182 | LxDeviceFindByClassName() | ||
diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py new file mode 100644 index 000000000..a92c55bd8 --- /dev/null +++ b/scripts/gdb/linux/dmesg.py | |||
@@ -0,0 +1,154 @@ | |||
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 sys | ||
16 | |||
17 | from linux import utils | ||
18 | |||
19 | printk_info_type = utils.CachedType("struct printk_info") | ||
20 | prb_data_blk_lpos_type = utils.CachedType("struct prb_data_blk_lpos") | ||
21 | prb_desc_type = utils.CachedType("struct prb_desc") | ||
22 | prb_desc_ring_type = utils.CachedType("struct prb_desc_ring") | ||
23 | prb_data_ring_type = utils.CachedType("struct prb_data_ring") | ||
24 | printk_ringbuffer_type = utils.CachedType("struct printk_ringbuffer") | ||
25 | atomic_long_type = utils.CachedType("atomic_long_t") | ||
26 | |||
27 | class LxDmesg(gdb.Command): | ||
28 | """Print Linux kernel log buffer.""" | ||
29 | |||
30 | def __init__(self): | ||
31 | super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) | ||
32 | |||
33 | def invoke(self, arg, from_tty): | ||
34 | inf = gdb.inferiors()[0] | ||
35 | |||
36 | # read in prb structure | ||
37 | prb_addr = int(str(gdb.parse_and_eval("(void *)'printk.c'::prb")).split()[0], 16) | ||
38 | sz = printk_ringbuffer_type.get_type().sizeof | ||
39 | prb = utils.read_memoryview(inf, prb_addr, sz).tobytes() | ||
40 | |||
41 | # read in descriptor ring structure | ||
42 | off = printk_ringbuffer_type.get_type()['desc_ring'].bitpos // 8 | ||
43 | addr = prb_addr + off | ||
44 | sz = prb_desc_ring_type.get_type().sizeof | ||
45 | desc_ring = utils.read_memoryview(inf, addr, sz).tobytes() | ||
46 | |||
47 | # read in descriptor array | ||
48 | off = prb_desc_ring_type.get_type()['count_bits'].bitpos // 8 | ||
49 | desc_ring_count = 1 << utils.read_u32(desc_ring, off) | ||
50 | desc_sz = prb_desc_type.get_type().sizeof | ||
51 | off = prb_desc_ring_type.get_type()['descs'].bitpos // 8 | ||
52 | addr = utils.read_ulong(desc_ring, off) | ||
53 | descs = utils.read_memoryview(inf, addr, desc_sz * desc_ring_count).tobytes() | ||
54 | |||
55 | # read in info array | ||
56 | info_sz = printk_info_type.get_type().sizeof | ||
57 | off = prb_desc_ring_type.get_type()['infos'].bitpos // 8 | ||
58 | addr = utils.read_ulong(desc_ring, off) | ||
59 | infos = utils.read_memoryview(inf, addr, info_sz * desc_ring_count).tobytes() | ||
60 | |||
61 | # read in text data ring structure | ||
62 | off = printk_ringbuffer_type.get_type()['text_data_ring'].bitpos // 8 | ||
63 | addr = prb_addr + off | ||
64 | sz = prb_data_ring_type.get_type().sizeof | ||
65 | text_data_ring = utils.read_memoryview(inf, addr, sz).tobytes() | ||
66 | |||
67 | # read in text data | ||
68 | off = prb_data_ring_type.get_type()['size_bits'].bitpos // 8 | ||
69 | text_data_sz = 1 << utils.read_u32(text_data_ring, off) | ||
70 | off = prb_data_ring_type.get_type()['data'].bitpos // 8 | ||
71 | addr = utils.read_ulong(text_data_ring, off) | ||
72 | text_data = utils.read_memoryview(inf, addr, text_data_sz).tobytes() | ||
73 | |||
74 | counter_off = atomic_long_type.get_type()['counter'].bitpos // 8 | ||
75 | |||
76 | sv_off = prb_desc_type.get_type()['state_var'].bitpos // 8 | ||
77 | |||
78 | off = prb_desc_type.get_type()['text_blk_lpos'].bitpos // 8 | ||
79 | begin_off = off + (prb_data_blk_lpos_type.get_type()['begin'].bitpos // 8) | ||
80 | next_off = off + (prb_data_blk_lpos_type.get_type()['next'].bitpos // 8) | ||
81 | |||
82 | ts_off = printk_info_type.get_type()['ts_nsec'].bitpos // 8 | ||
83 | len_off = printk_info_type.get_type()['text_len'].bitpos // 8 | ||
84 | |||
85 | # definitions from kernel/printk/printk_ringbuffer.h | ||
86 | desc_committed = 1 | ||
87 | desc_finalized = 2 | ||
88 | desc_sv_bits = utils.get_long_type().sizeof * 8 | ||
89 | desc_flags_shift = desc_sv_bits - 2 | ||
90 | desc_flags_mask = 3 << desc_flags_shift | ||
91 | desc_id_mask = ~desc_flags_mask | ||
92 | |||
93 | # read in tail and head descriptor ids | ||
94 | off = prb_desc_ring_type.get_type()['tail_id'].bitpos // 8 | ||
95 | tail_id = utils.read_u64(desc_ring, off + counter_off) | ||
96 | off = prb_desc_ring_type.get_type()['head_id'].bitpos // 8 | ||
97 | head_id = utils.read_u64(desc_ring, off + counter_off) | ||
98 | |||
99 | did = tail_id | ||
100 | while True: | ||
101 | ind = did % desc_ring_count | ||
102 | desc_off = desc_sz * ind | ||
103 | info_off = info_sz * ind | ||
104 | |||
105 | # skip non-committed record | ||
106 | state = 3 & (utils.read_u64(descs, desc_off + sv_off + | ||
107 | counter_off) >> desc_flags_shift) | ||
108 | if state != desc_committed and state != desc_finalized: | ||
109 | if did == head_id: | ||
110 | break | ||
111 | did = (did + 1) & desc_id_mask | ||
112 | continue | ||
113 | |||
114 | begin = utils.read_ulong(descs, desc_off + begin_off) % text_data_sz | ||
115 | end = utils.read_ulong(descs, desc_off + next_off) % text_data_sz | ||
116 | |||
117 | # handle data-less record | ||
118 | if begin & 1 == 1: | ||
119 | text = "" | ||
120 | else: | ||
121 | # handle wrapping data block | ||
122 | if begin > end: | ||
123 | begin = 0 | ||
124 | |||
125 | # skip over descriptor id | ||
126 | text_start = begin + utils.get_long_type().sizeof | ||
127 | |||
128 | text_len = utils.read_u16(infos, info_off + len_off) | ||
129 | |||
130 | # handle truncated message | ||
131 | if end - text_start < text_len: | ||
132 | text_len = end - text_start | ||
133 | |||
134 | text = text_data[text_start:text_start + text_len].decode( | ||
135 | encoding='utf8', errors='replace') | ||
136 | |||
137 | time_stamp = utils.read_u64(infos, info_off + ts_off) | ||
138 | |||
139 | for line in text.splitlines(): | ||
140 | msg = u"[{time:12.6f}] {line}\n".format( | ||
141 | time=time_stamp / 1000000000.0, | ||
142 | line=line) | ||
143 | # With python2 gdb.write will attempt to convert unicode to | ||
144 | # ascii and might fail so pass an utf8-encoded str instead. | ||
145 | if sys.hexversion < 0x03000000: | ||
146 | msg = msg.encode(encoding='utf8', errors='replace') | ||
147 | gdb.write(msg) | ||
148 | |||
149 | if did == head_id: | ||
150 | break | ||
151 | did = (did + 1) & desc_id_mask | ||
152 | |||
153 | |||
154 | LxDmesg() | ||
diff --git a/scripts/gdb/linux/genpd.py b/scripts/gdb/linux/genpd.py new file mode 100644 index 000000000..39cd1abd8 --- /dev/null +++ b/scripts/gdb/linux/genpd.py | |||
@@ -0,0 +1,83 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright (c) NXP 2019 | ||
4 | |||
5 | import gdb | ||
6 | import sys | ||
7 | |||
8 | from linux.utils import CachedType | ||
9 | from linux.lists import list_for_each_entry | ||
10 | |||
11 | generic_pm_domain_type = CachedType('struct generic_pm_domain') | ||
12 | pm_domain_data_type = CachedType('struct pm_domain_data') | ||
13 | device_link_type = CachedType('struct device_link') | ||
14 | |||
15 | |||
16 | def kobject_get_path(kobj): | ||
17 | path = kobj['name'].string() | ||
18 | parent = kobj['parent'] | ||
19 | if parent: | ||
20 | path = kobject_get_path(parent) + '/' + path | ||
21 | return path | ||
22 | |||
23 | |||
24 | def rtpm_status_str(dev): | ||
25 | if dev['power']['runtime_error']: | ||
26 | return 'error' | ||
27 | if dev['power']['disable_depth']: | ||
28 | return 'unsupported' | ||
29 | _RPM_STATUS_LOOKUP = [ | ||
30 | "active", | ||
31 | "resuming", | ||
32 | "suspended", | ||
33 | "suspending" | ||
34 | ] | ||
35 | return _RPM_STATUS_LOOKUP[dev['power']['runtime_status']] | ||
36 | |||
37 | |||
38 | class LxGenPDSummary(gdb.Command): | ||
39 | '''Print genpd summary | ||
40 | |||
41 | Output is similar to /sys/kernel/debug/pm_genpd/pm_genpd_summary''' | ||
42 | |||
43 | def __init__(self): | ||
44 | super(LxGenPDSummary, self).__init__('lx-genpd-summary', gdb.COMMAND_DATA) | ||
45 | |||
46 | def summary_one(self, genpd): | ||
47 | if genpd['status'] == 0: | ||
48 | status_string = 'on' | ||
49 | else: | ||
50 | status_string = 'off-{}'.format(genpd['state_idx']) | ||
51 | |||
52 | child_names = [] | ||
53 | for link in list_for_each_entry( | ||
54 | genpd['parent_links'], | ||
55 | device_link_type.get_type().pointer(), | ||
56 | 'parent_node'): | ||
57 | child_names.append(link['child']['name']) | ||
58 | |||
59 | gdb.write('%-30s %-15s %s\n' % ( | ||
60 | genpd['name'].string(), | ||
61 | status_string, | ||
62 | ', '.join(child_names))) | ||
63 | |||
64 | # Print devices in domain | ||
65 | for pm_data in list_for_each_entry(genpd['dev_list'], | ||
66 | pm_domain_data_type.get_type().pointer(), | ||
67 | 'list_node'): | ||
68 | dev = pm_data['dev'] | ||
69 | kobj_path = kobject_get_path(dev['kobj']) | ||
70 | gdb.write(' %-50s %s\n' % (kobj_path, rtpm_status_str(dev))) | ||
71 | |||
72 | def invoke(self, arg, from_tty): | ||
73 | gdb.write('domain status children\n'); | ||
74 | gdb.write(' /device runtime status\n'); | ||
75 | gdb.write('----------------------------------------------------------------------\n'); | ||
76 | for genpd in list_for_each_entry( | ||
77 | gdb.parse_and_eval('&gpd_list'), | ||
78 | generic_pm_domain_type.get_type().pointer(), | ||
79 | 'gpd_list_node'): | ||
80 | self.summary_one(genpd) | ||
81 | |||
82 | |||
83 | LxGenPDSummary() | ||
diff --git a/scripts/gdb/linux/lists.py b/scripts/gdb/linux/lists.py new file mode 100644 index 000000000..c487ddf09 --- /dev/null +++ b/scripts/gdb/linux/lists.py | |||
@@ -0,0 +1,131 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # list tools | ||
5 | # | ||
6 | # Copyright (c) Thiebaud Weksteen, 2015 | ||
7 | # | ||
8 | # Authors: | ||
9 | # Thiebaud Weksteen <thiebaud@weksteen.fr> | ||
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 | list_head = utils.CachedType("struct list_head") | ||
19 | hlist_head = utils.CachedType("struct hlist_head") | ||
20 | hlist_node = utils.CachedType("struct hlist_node") | ||
21 | |||
22 | |||
23 | def list_for_each(head): | ||
24 | if head.type == list_head.get_type().pointer(): | ||
25 | head = head.dereference() | ||
26 | elif head.type != list_head.get_type(): | ||
27 | raise TypeError("Must be struct list_head not {}" | ||
28 | .format(head.type)) | ||
29 | |||
30 | node = head['next'].dereference() | ||
31 | while node.address != head.address: | ||
32 | yield node.address | ||
33 | node = node['next'].dereference() | ||
34 | |||
35 | |||
36 | def list_for_each_entry(head, gdbtype, member): | ||
37 | for node in list_for_each(head): | ||
38 | yield utils.container_of(node, gdbtype, member) | ||
39 | |||
40 | |||
41 | def hlist_for_each(head): | ||
42 | if head.type == hlist_head.get_type().pointer(): | ||
43 | head = head.dereference() | ||
44 | elif head.type != hlist_head.get_type(): | ||
45 | raise TypeError("Must be struct hlist_head not {}" | ||
46 | .format(head.type)) | ||
47 | |||
48 | node = head['first'].dereference() | ||
49 | while node.address: | ||
50 | yield node.address | ||
51 | node = node['next'].dereference() | ||
52 | |||
53 | |||
54 | def hlist_for_each_entry(head, gdbtype, member): | ||
55 | for node in hlist_for_each(head): | ||
56 | yield utils.container_of(node, gdbtype, member) | ||
57 | |||
58 | |||
59 | def list_check(head): | ||
60 | nb = 0 | ||
61 | if (head.type == list_head.get_type().pointer()): | ||
62 | head = head.dereference() | ||
63 | elif (head.type != list_head.get_type()): | ||
64 | raise gdb.GdbError('argument must be of type (struct list_head [*])') | ||
65 | c = head | ||
66 | try: | ||
67 | gdb.write("Starting with: {}\n".format(c)) | ||
68 | except gdb.MemoryError: | ||
69 | gdb.write('head is not accessible\n') | ||
70 | return | ||
71 | while True: | ||
72 | p = c['prev'].dereference() | ||
73 | n = c['next'].dereference() | ||
74 | try: | ||
75 | if p['next'] != c.address: | ||
76 | gdb.write('prev.next != current: ' | ||
77 | 'current@{current_addr}={current} ' | ||
78 | 'prev@{p_addr}={p}\n'.format( | ||
79 | current_addr=c.address, | ||
80 | current=c, | ||
81 | p_addr=p.address, | ||
82 | p=p, | ||
83 | )) | ||
84 | return | ||
85 | except gdb.MemoryError: | ||
86 | gdb.write('prev is not accessible: ' | ||
87 | 'current@{current_addr}={current}\n'.format( | ||
88 | current_addr=c.address, | ||
89 | current=c | ||
90 | )) | ||
91 | return | ||
92 | try: | ||
93 | if n['prev'] != c.address: | ||
94 | gdb.write('next.prev != current: ' | ||
95 | 'current@{current_addr}={current} ' | ||
96 | 'next@{n_addr}={n}\n'.format( | ||
97 | current_addr=c.address, | ||
98 | current=c, | ||
99 | n_addr=n.address, | ||
100 | n=n, | ||
101 | )) | ||
102 | return | ||
103 | except gdb.MemoryError: | ||
104 | gdb.write('next is not accessible: ' | ||
105 | 'current@{current_addr}={current}\n'.format( | ||
106 | current_addr=c.address, | ||
107 | current=c | ||
108 | )) | ||
109 | return | ||
110 | c = n | ||
111 | nb += 1 | ||
112 | if c == head: | ||
113 | gdb.write("list is consistent: {} node(s)\n".format(nb)) | ||
114 | return | ||
115 | |||
116 | |||
117 | class LxListChk(gdb.Command): | ||
118 | """Verify a list consistency""" | ||
119 | |||
120 | def __init__(self): | ||
121 | super(LxListChk, self).__init__("lx-list-check", gdb.COMMAND_DATA, | ||
122 | gdb.COMPLETE_EXPRESSION) | ||
123 | |||
124 | def invoke(self, arg, from_tty): | ||
125 | argv = gdb.string_to_argv(arg) | ||
126 | if len(argv) != 1: | ||
127 | raise gdb.GdbError("lx-list-check takes one argument") | ||
128 | list_check(gdb.parse_and_eval(argv[0])) | ||
129 | |||
130 | |||
131 | LxListChk() | ||
diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py new file mode 100644 index 000000000..441b23239 --- /dev/null +++ b/scripts/gdb/linux/modules.py | |||
@@ -0,0 +1,95 @@ | |||
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, lists | ||
17 | |||
18 | |||
19 | module_type = utils.CachedType("struct module") | ||
20 | |||
21 | |||
22 | def module_list(): | ||
23 | global module_type | ||
24 | modules = utils.gdb_eval_or_none("modules") | ||
25 | if modules is None: | ||
26 | return | ||
27 | |||
28 | module_ptr_type = module_type.get_type().pointer() | ||
29 | |||
30 | for module in lists.list_for_each_entry(modules, module_ptr_type, "list"): | ||
31 | yield module | ||
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 | layout = module['core_layout'] | ||
77 | gdb.write("{address} {name:<19} {size:>8} {ref}".format( | ||
78 | address=str(layout['base']).split()[0], | ||
79 | name=module['name'].string(), | ||
80 | size=str(layout['size']), | ||
81 | ref=str(module['refcnt']['counter'] - 1))) | ||
82 | |||
83 | t = self._module_use_type.get_type().pointer() | ||
84 | first = True | ||
85 | sources = module['source_list'] | ||
86 | for use in lists.list_for_each_entry(sources, t, "source_list"): | ||
87 | gdb.write("{separator}{name}".format( | ||
88 | separator=" " if first else ",", | ||
89 | name=use['source']['name'].string())) | ||
90 | first = False | ||
91 | |||
92 | gdb.write("\n") | ||
93 | |||
94 | |||
95 | LxLsmod() | ||
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py new file mode 100644 index 000000000..09cd87192 --- /dev/null +++ b/scripts/gdb/linux/proc.py | |||
@@ -0,0 +1,275 @@ | |||
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 | ||
15 | from linux import constants | ||
16 | from linux import utils | ||
17 | from linux import tasks | ||
18 | from linux import lists | ||
19 | from struct import * | ||
20 | |||
21 | |||
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 | |||
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 | |||
33 | LxCmdLine() | ||
34 | |||
35 | |||
36 | class 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 | |||
48 | LxVersion() | ||
49 | |||
50 | |||
51 | # Resource Structure Printers | ||
52 | # /proc/iomem | ||
53 | # /proc/ioports | ||
54 | |||
55 | def 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 | |||
67 | def 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 | |||
80 | class LxIOMem(gdb.Command): | ||
81 | """Identify the IO memory resource locations defined by the kernel | ||
82 | |||
83 | Equivalent 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 | |||
92 | LxIOMem() | ||
93 | |||
94 | |||
95 | class LxIOPorts(gdb.Command): | ||
96 | """Identify the IO port resource locations defined by the kernel | ||
97 | |||
98 | Equivalent 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 | |||
107 | LxIOPorts() | ||
108 | |||
109 | |||
110 | # Mount namespace viewer | ||
111 | # /proc/mounts | ||
112 | |||
113 | def 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 | |||
121 | FS_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 | |||
127 | MNT_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 | |||
134 | mount_type = utils.CachedType("struct mount") | ||
135 | mount_ptr_type = mount_type.get_type().pointer() | ||
136 | |||
137 | |||
138 | class LxMounts(gdb.Command): | ||
139 | """Report the VFS mounts of the current process namespace. | ||
140 | |||
141 | Equivalent to cat /proc/mounts on a running target | ||
142 | An integer value can be supplied to display the mount | ||
143 | values 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 | |||
202 | LxMounts() | ||
203 | |||
204 | |||
205 | class 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 | |||
275 | LxFdtDump() | ||
diff --git a/scripts/gdb/linux/rbtree.py b/scripts/gdb/linux/rbtree.py new file mode 100644 index 000000000..fe462855e --- /dev/null +++ b/scripts/gdb/linux/rbtree.py | |||
@@ -0,0 +1,177 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright 2019 Google LLC. | ||
4 | |||
5 | import gdb | ||
6 | |||
7 | from linux import utils | ||
8 | |||
9 | rb_root_type = utils.CachedType("struct rb_root") | ||
10 | rb_node_type = utils.CachedType("struct rb_node") | ||
11 | |||
12 | |||
13 | def rb_first(root): | ||
14 | if root.type == rb_root_type.get_type(): | ||
15 | node = root.address.cast(rb_root_type.get_type().pointer()) | ||
16 | elif root.type != rb_root_type.get_type().pointer(): | ||
17 | raise gdb.GdbError("Must be struct rb_root not {}".format(root.type)) | ||
18 | |||
19 | node = root['rb_node'] | ||
20 | if node == 0: | ||
21 | return None | ||
22 | |||
23 | while node['rb_left']: | ||
24 | node = node['rb_left'] | ||
25 | |||
26 | return node | ||
27 | |||
28 | |||
29 | def rb_last(root): | ||
30 | if root.type == rb_root_type.get_type(): | ||
31 | node = root.address.cast(rb_root_type.get_type().pointer()) | ||
32 | elif root.type != rb_root_type.get_type().pointer(): | ||
33 | raise gdb.GdbError("Must be struct rb_root not {}".format(root.type)) | ||
34 | |||
35 | node = root['rb_node'] | ||
36 | if node == 0: | ||
37 | return None | ||
38 | |||
39 | while node['rb_right']: | ||
40 | node = node['rb_right'] | ||
41 | |||
42 | return node | ||
43 | |||
44 | |||
45 | def rb_parent(node): | ||
46 | parent = gdb.Value(node['__rb_parent_color'] & ~3) | ||
47 | return parent.cast(rb_node_type.get_type().pointer()) | ||
48 | |||
49 | |||
50 | def rb_empty_node(node): | ||
51 | return node['__rb_parent_color'] == node.address | ||
52 | |||
53 | |||
54 | def rb_next(node): | ||
55 | if node.type == rb_node_type.get_type(): | ||
56 | node = node.address.cast(rb_node_type.get_type().pointer()) | ||
57 | elif node.type != rb_node_type.get_type().pointer(): | ||
58 | raise gdb.GdbError("Must be struct rb_node not {}".format(node.type)) | ||
59 | |||
60 | if rb_empty_node(node): | ||
61 | return None | ||
62 | |||
63 | if node['rb_right']: | ||
64 | node = node['rb_right'] | ||
65 | while node['rb_left']: | ||
66 | node = node['rb_left'] | ||
67 | return node | ||
68 | |||
69 | parent = rb_parent(node) | ||
70 | while parent and node == parent['rb_right']: | ||
71 | node = parent | ||
72 | parent = rb_parent(node) | ||
73 | |||
74 | return parent | ||
75 | |||
76 | |||
77 | def rb_prev(node): | ||
78 | if node.type == rb_node_type.get_type(): | ||
79 | node = node.address.cast(rb_node_type.get_type().pointer()) | ||
80 | elif node.type != rb_node_type.get_type().pointer(): | ||
81 | raise gdb.GdbError("Must be struct rb_node not {}".format(node.type)) | ||
82 | |||
83 | if rb_empty_node(node): | ||
84 | return None | ||
85 | |||
86 | if node['rb_left']: | ||
87 | node = node['rb_left'] | ||
88 | while node['rb_right']: | ||
89 | node = node['rb_right'] | ||
90 | return node.dereference() | ||
91 | |||
92 | parent = rb_parent(node) | ||
93 | while parent and node == parent['rb_left'].dereference(): | ||
94 | node = parent | ||
95 | parent = rb_parent(node) | ||
96 | |||
97 | return parent | ||
98 | |||
99 | |||
100 | class LxRbFirst(gdb.Function): | ||
101 | """Lookup and return a node from an RBTree | ||
102 | |||
103 | $lx_rb_first(root): Return the node at the given index. | ||
104 | If index is omitted, the root node is dereferenced and returned.""" | ||
105 | |||
106 | def __init__(self): | ||
107 | super(LxRbFirst, self).__init__("lx_rb_first") | ||
108 | |||
109 | def invoke(self, root): | ||
110 | result = rb_first(root) | ||
111 | if result is None: | ||
112 | raise gdb.GdbError("No entry in tree") | ||
113 | |||
114 | return result | ||
115 | |||
116 | |||
117 | LxRbFirst() | ||
118 | |||
119 | |||
120 | class LxRbLast(gdb.Function): | ||
121 | """Lookup and return a node from an RBTree. | ||
122 | |||
123 | $lx_rb_last(root): Return the node at the given index. | ||
124 | If index is omitted, the root node is dereferenced and returned.""" | ||
125 | |||
126 | def __init__(self): | ||
127 | super(LxRbLast, self).__init__("lx_rb_last") | ||
128 | |||
129 | def invoke(self, root): | ||
130 | result = rb_last(root) | ||
131 | if result is None: | ||
132 | raise gdb.GdbError("No entry in tree") | ||
133 | |||
134 | return result | ||
135 | |||
136 | |||
137 | LxRbLast() | ||
138 | |||
139 | |||
140 | class LxRbNext(gdb.Function): | ||
141 | """Lookup and return a node from an RBTree. | ||
142 | |||
143 | $lx_rb_next(node): Return the node at the given index. | ||
144 | If index is omitted, the root node is dereferenced and returned.""" | ||
145 | |||
146 | def __init__(self): | ||
147 | super(LxRbNext, self).__init__("lx_rb_next") | ||
148 | |||
149 | def invoke(self, node): | ||
150 | result = rb_next(node) | ||
151 | if result is None: | ||
152 | raise gdb.GdbError("No entry in tree") | ||
153 | |||
154 | return result | ||
155 | |||
156 | |||
157 | LxRbNext() | ||
158 | |||
159 | |||
160 | class LxRbPrev(gdb.Function): | ||
161 | """Lookup and return a node from an RBTree. | ||
162 | |||
163 | $lx_rb_prev(node): Return the node at the given index. | ||
164 | If index is omitted, the root node is dereferenced and returned.""" | ||
165 | |||
166 | def __init__(self): | ||
167 | super(LxRbPrev, self).__init__("lx_rb_prev") | ||
168 | |||
169 | def invoke(self, node): | ||
170 | result = rb_prev(node) | ||
171 | if result is None: | ||
172 | raise gdb.GdbError("No entry in tree") | ||
173 | |||
174 | return result | ||
175 | |||
176 | |||
177 | LxRbPrev() | ||
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py new file mode 100644 index 000000000..1be9763cf --- /dev/null +++ b/scripts/gdb/linux/symbols.py | |||
@@ -0,0 +1,187 @@ | |||
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 | |||
18 | from linux import modules, utils | ||
19 | |||
20 | |||
21 | if hasattr(gdb, 'Breakpoint'): | ||
22 | class LoadModuleBreakpoint(gdb.Breakpoint): | ||
23 | def __init__(self, spec, gdb_command): | ||
24 | super(LoadModuleBreakpoint, self).__init__(spec, internal=True) | ||
25 | self.silent = True | ||
26 | self.gdb_command = gdb_command | ||
27 | |||
28 | def stop(self): | ||
29 | module = gdb.parse_and_eval("mod") | ||
30 | module_name = module['name'].string() | ||
31 | cmd = self.gdb_command | ||
32 | |||
33 | # enforce update if object file is not found | ||
34 | cmd.module_files_updated = False | ||
35 | |||
36 | # Disable pagination while reporting symbol (re-)loading. | ||
37 | # The console input is blocked in this context so that we would | ||
38 | # get stuck waiting for the user to acknowledge paged output. | ||
39 | show_pagination = gdb.execute("show pagination", to_string=True) | ||
40 | pagination = show_pagination.endswith("on.\n") | ||
41 | gdb.execute("set pagination off") | ||
42 | |||
43 | if module_name in cmd.loaded_modules: | ||
44 | gdb.write("refreshing all symbols to reload module " | ||
45 | "'{0}'\n".format(module_name)) | ||
46 | cmd.load_all_symbols() | ||
47 | else: | ||
48 | cmd.load_module_symbols(module) | ||
49 | |||
50 | # restore pagination state | ||
51 | gdb.execute("set pagination %s" % ("on" if pagination else "off")) | ||
52 | |||
53 | return False | ||
54 | |||
55 | |||
56 | class LxSymbols(gdb.Command): | ||
57 | """(Re-)load symbols of Linux kernel and currently loaded modules. | ||
58 | |||
59 | The kernel (vmlinux) is taken from the current working directly. Modules (.ko) | ||
60 | are scanned recursively, starting in the same directory. Optionally, the module | ||
61 | search path can be extended by a space separated list of paths passed to the | ||
62 | lx-symbols command.""" | ||
63 | |||
64 | module_paths = [] | ||
65 | module_files = [] | ||
66 | module_files_updated = False | ||
67 | loaded_modules = [] | ||
68 | breakpoint = None | ||
69 | |||
70 | def __init__(self): | ||
71 | super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, | ||
72 | gdb.COMPLETE_FILENAME) | ||
73 | |||
74 | def _update_module_files(self): | ||
75 | self.module_files = [] | ||
76 | for path in self.module_paths: | ||
77 | gdb.write("scanning for modules in {0}\n".format(path)) | ||
78 | for root, dirs, files in os.walk(path): | ||
79 | for name in files: | ||
80 | if name.endswith(".ko") or name.endswith(".ko.debug"): | ||
81 | self.module_files.append(root + "/" + name) | ||
82 | self.module_files_updated = True | ||
83 | |||
84 | def _get_module_file(self, module_name): | ||
85 | module_pattern = ".*/{0}\.ko(?:.debug)?$".format( | ||
86 | module_name.replace("_", r"[_\-]")) | ||
87 | for name in self.module_files: | ||
88 | if re.match(module_pattern, name) and os.path.exists(name): | ||
89 | return name | ||
90 | return None | ||
91 | |||
92 | def _section_arguments(self, module): | ||
93 | try: | ||
94 | sect_attrs = module['sect_attrs'].dereference() | ||
95 | except gdb.error: | ||
96 | return "" | ||
97 | attrs = sect_attrs['attrs'] | ||
98 | section_name_to_address = { | ||
99 | attrs[n]['battr']['attr']['name'].string(): attrs[n]['address'] | ||
100 | for n in range(int(sect_attrs['nsections']))} | ||
101 | args = [] | ||
102 | for section_name in [".data", ".data..read_mostly", ".rodata", ".bss", | ||
103 | ".text", ".text.hot", ".text.unlikely"]: | ||
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['core_layout']['base']).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 | if utils.is_target_arch('s390'): | ||
121 | # Module text is preceded by PLT stubs on s390. | ||
122 | module_arch = module['arch'] | ||
123 | plt_offset = int(module_arch['plt_offset']) | ||
124 | plt_size = int(module_arch['plt_size']) | ||
125 | module_addr = hex(int(module_addr, 0) + plt_offset + plt_size) | ||
126 | gdb.write("loading @{addr}: {filename}\n".format( | ||
127 | addr=module_addr, filename=module_file)) | ||
128 | cmdline = "add-symbol-file {filename} {addr}{sections}".format( | ||
129 | filename=module_file, | ||
130 | addr=module_addr, | ||
131 | sections=self._section_arguments(module)) | ||
132 | gdb.execute(cmdline, to_string=True) | ||
133 | if module_name not in self.loaded_modules: | ||
134 | self.loaded_modules.append(module_name) | ||
135 | else: | ||
136 | gdb.write("no module object found for '{0}'\n".format(module_name)) | ||
137 | |||
138 | def load_all_symbols(self): | ||
139 | gdb.write("loading vmlinux\n") | ||
140 | |||
141 | # Dropping symbols will disable all breakpoints. So save their states | ||
142 | # and restore them afterward. | ||
143 | saved_states = [] | ||
144 | if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: | ||
145 | for bp in gdb.breakpoints(): | ||
146 | saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) | ||
147 | |||
148 | # drop all current symbols and reload vmlinux | ||
149 | orig_vmlinux = 'vmlinux' | ||
150 | for obj in gdb.objfiles(): | ||
151 | if obj.filename.endswith('vmlinux'): | ||
152 | orig_vmlinux = obj.filename | ||
153 | gdb.execute("symbol-file", to_string=True) | ||
154 | gdb.execute("symbol-file {0}".format(orig_vmlinux)) | ||
155 | |||
156 | self.loaded_modules = [] | ||
157 | module_list = modules.module_list() | ||
158 | if not module_list: | ||
159 | gdb.write("no modules found\n") | ||
160 | else: | ||
161 | [self.load_module_symbols(module) for module in module_list] | ||
162 | |||
163 | for saved_state in saved_states: | ||
164 | saved_state['breakpoint'].enabled = saved_state['enabled'] | ||
165 | |||
166 | def invoke(self, arg, from_tty): | ||
167 | self.module_paths = [os.path.expanduser(p) for p in arg.split()] | ||
168 | self.module_paths.append(os.getcwd()) | ||
169 | |||
170 | # enforce update | ||
171 | self.module_files = [] | ||
172 | self.module_files_updated = False | ||
173 | |||
174 | self.load_all_symbols() | ||
175 | |||
176 | if hasattr(gdb, 'Breakpoint'): | ||
177 | if self.breakpoint is not None: | ||
178 | self.breakpoint.delete() | ||
179 | self.breakpoint = None | ||
180 | self.breakpoint = LoadModuleBreakpoint( | ||
181 | "kernel/module.c:do_init_module", self) | ||
182 | else: | ||
183 | gdb.write("Note: symbol update on module loading not supported " | ||
184 | "with this gdb version\n") | ||
185 | |||
186 | |||
187 | LxSymbols() | ||
diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py new file mode 100644 index 000000000..17ec19e9b --- /dev/null +++ b/scripts/gdb/linux/tasks.py | |||
@@ -0,0 +1,140 @@ | |||
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 | |||
22 | def task_lists(): | ||
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 | |||
42 | def get_task_by_pid(pid): | ||
43 | for task in task_lists(): | ||
44 | if int(task['pid']) == pid: | ||
45 | return task | ||
46 | return None | ||
47 | |||
48 | |||
49 | class LxTaskByPidFunc(gdb.Function): | ||
50 | """Find Linux task by PID and return the task_struct variable. | ||
51 | |||
52 | $lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and | ||
53 | return that task_struct variable which PID matches.""" | ||
54 | |||
55 | def __init__(self): | ||
56 | super(LxTaskByPidFunc, self).__init__("lx_task_by_pid") | ||
57 | |||
58 | def invoke(self, pid): | ||
59 | task = get_task_by_pid(pid) | ||
60 | if task: | ||
61 | return task.dereference() | ||
62 | else: | ||
63 | raise gdb.GdbError("No task of PID " + str(pid)) | ||
64 | |||
65 | |||
66 | LxTaskByPidFunc() | ||
67 | |||
68 | |||
69 | class LxPs(gdb.Command): | ||
70 | """Dump Linux tasks.""" | ||
71 | |||
72 | def __init__(self): | ||
73 | super(LxPs, self).__init__("lx-ps", gdb.COMMAND_DATA) | ||
74 | |||
75 | def invoke(self, arg, from_tty): | ||
76 | gdb.write("{:>10} {:>12} {:>7}\n".format("TASK", "PID", "COMM")) | ||
77 | for task in task_lists(): | ||
78 | gdb.write("{} {:^5} {}\n".format( | ||
79 | task.format_string().split()[0], | ||
80 | task["pid"].format_string(), | ||
81 | task["comm"].string())) | ||
82 | |||
83 | |||
84 | LxPs() | ||
85 | |||
86 | |||
87 | thread_info_type = utils.CachedType("struct thread_info") | ||
88 | |||
89 | ia64_task_size = None | ||
90 | |||
91 | |||
92 | def get_thread_info(task): | ||
93 | thread_info_ptr_type = thread_info_type.get_type().pointer() | ||
94 | if utils.is_target_arch("ia64"): | ||
95 | global ia64_task_size | ||
96 | if ia64_task_size is None: | ||
97 | ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)") | ||
98 | thread_info_addr = task.address + ia64_task_size | ||
99 | thread_info = thread_info_addr.cast(thread_info_ptr_type) | ||
100 | else: | ||
101 | if task.type.fields()[0].type == thread_info_type.get_type(): | ||
102 | return task['thread_info'] | ||
103 | thread_info = task['stack'].cast(thread_info_ptr_type) | ||
104 | return thread_info.dereference() | ||
105 | |||
106 | |||
107 | class LxThreadInfoFunc (gdb.Function): | ||
108 | """Calculate Linux thread_info from task variable. | ||
109 | |||
110 | $lx_thread_info(TASK): Given TASK, return the corresponding thread_info | ||
111 | variable.""" | ||
112 | |||
113 | def __init__(self): | ||
114 | super(LxThreadInfoFunc, self).__init__("lx_thread_info") | ||
115 | |||
116 | def invoke(self, task): | ||
117 | return get_thread_info(task) | ||
118 | |||
119 | |||
120 | LxThreadInfoFunc() | ||
121 | |||
122 | |||
123 | class LxThreadInfoByPidFunc (gdb.Function): | ||
124 | """Calculate Linux thread_info from task variable found by pid | ||
125 | |||
126 | $lx_thread_info_by_pid(PID): Given PID, return the corresponding thread_info | ||
127 | variable.""" | ||
128 | |||
129 | def __init__(self): | ||
130 | super(LxThreadInfoByPidFunc, self).__init__("lx_thread_info_by_pid") | ||
131 | |||
132 | def invoke(self, pid): | ||
133 | task = get_task_by_pid(pid) | ||
134 | if task: | ||
135 | return get_thread_info(task.dereference()) | ||
136 | else: | ||
137 | raise gdb.GdbError("No task of PID " + str(pid)) | ||
138 | |||
139 | |||
140 | LxThreadInfoByPidFunc() | ||
diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py new file mode 100644 index 000000000..071d0dd5a --- /dev/null +++ b/scripts/gdb/linux/timerlist.py | |||
@@ -0,0 +1,219 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | # | ||
3 | # Copyright 2019 Google LLC. | ||
4 | |||
5 | import binascii | ||
6 | import gdb | ||
7 | |||
8 | from linux import constants | ||
9 | from linux import cpus | ||
10 | from linux import rbtree | ||
11 | from linux import utils | ||
12 | |||
13 | timerqueue_node_type = utils.CachedType("struct timerqueue_node").get_type() | ||
14 | hrtimer_type = utils.CachedType("struct hrtimer").get_type() | ||
15 | |||
16 | |||
17 | def ktime_get(): | ||
18 | """Returns the current time, but not very accurately | ||
19 | |||
20 | We can't read the hardware timer itself to add any nanoseconds | ||
21 | that need to be added since we last stored the time in the | ||
22 | timekeeper. But this is probably good enough for debug purposes.""" | ||
23 | tk_core = gdb.parse_and_eval("&tk_core") | ||
24 | |||
25 | return tk_core['timekeeper']['tkr_mono']['base'] | ||
26 | |||
27 | |||
28 | def print_timer(rb_node, idx): | ||
29 | timerqueue = utils.container_of(rb_node, timerqueue_node_type.pointer(), | ||
30 | "node") | ||
31 | timer = utils.container_of(timerqueue, hrtimer_type.pointer(), "node") | ||
32 | |||
33 | function = str(timer['function']).split(" ")[1].strip("<>") | ||
34 | softexpires = timer['_softexpires'] | ||
35 | expires = timer['node']['expires'] | ||
36 | now = ktime_get() | ||
37 | |||
38 | text = " #{}: <{}>, {}, ".format(idx, timer, function) | ||
39 | text += "S:{:02x}\n".format(int(timer['state'])) | ||
40 | text += " # expires at {}-{} nsecs [in {} to {} nsecs]\n".format( | ||
41 | softexpires, expires, softexpires - now, expires - now) | ||
42 | return text | ||
43 | |||
44 | |||
45 | def print_active_timers(base): | ||
46 | curr = base['active']['next']['node'] | ||
47 | curr = curr.address.cast(rbtree.rb_node_type.get_type().pointer()) | ||
48 | idx = 0 | ||
49 | while curr: | ||
50 | yield print_timer(curr, idx) | ||
51 | curr = rbtree.rb_next(curr) | ||
52 | idx += 1 | ||
53 | |||
54 | |||
55 | def print_base(base): | ||
56 | text = " .base: {}\n".format(base.address) | ||
57 | text += " .index: {}\n".format(base['index']) | ||
58 | |||
59 | text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution) | ||
60 | |||
61 | text += " .get_time: {}\n".format(base['get_time']) | ||
62 | if constants.LX_CONFIG_HIGH_RES_TIMERS: | ||
63 | text += " .offset: {} nsecs\n".format(base['offset']) | ||
64 | text += "active timers:\n" | ||
65 | text += "".join([x for x in print_active_timers(base)]) | ||
66 | return text | ||
67 | |||
68 | |||
69 | def print_cpu(hrtimer_bases, cpu, max_clock_bases): | ||
70 | cpu_base = cpus.per_cpu(hrtimer_bases, cpu) | ||
71 | jiffies = gdb.parse_and_eval("jiffies_64") | ||
72 | tick_sched_ptr = gdb.parse_and_eval("&tick_cpu_sched") | ||
73 | ts = cpus.per_cpu(tick_sched_ptr, cpu) | ||
74 | |||
75 | text = "cpu: {}\n".format(cpu) | ||
76 | for i in xrange(max_clock_bases): | ||
77 | text += " clock {}:\n".format(i) | ||
78 | text += print_base(cpu_base['clock_base'][i]) | ||
79 | |||
80 | if constants.LX_CONFIG_HIGH_RES_TIMERS: | ||
81 | fmts = [(" .{} : {} nsecs", 'expires_next'), | ||
82 | (" .{} : {}", 'hres_active'), | ||
83 | (" .{} : {}", 'nr_events'), | ||
84 | (" .{} : {}", 'nr_retries'), | ||
85 | (" .{} : {}", 'nr_hangs'), | ||
86 | (" .{} : {}", 'max_hang_time')] | ||
87 | text += "\n".join([s.format(f, cpu_base[f]) for s, f in fmts]) | ||
88 | text += "\n" | ||
89 | |||
90 | if constants.LX_CONFIG_TICK_ONESHOT: | ||
91 | fmts = [(" .{} : {}", 'nohz_mode'), | ||
92 | (" .{} : {} nsecs", 'last_tick'), | ||
93 | (" .{} : {}", 'tick_stopped'), | ||
94 | (" .{} : {}", 'idle_jiffies'), | ||
95 | (" .{} : {}", 'idle_calls'), | ||
96 | (" .{} : {}", 'idle_sleeps'), | ||
97 | (" .{} : {} nsecs", 'idle_entrytime'), | ||
98 | (" .{} : {} nsecs", 'idle_waketime'), | ||
99 | (" .{} : {} nsecs", 'idle_exittime'), | ||
100 | (" .{} : {} nsecs", 'idle_sleeptime'), | ||
101 | (" .{}: {} nsecs", 'iowait_sleeptime'), | ||
102 | (" .{} : {}", 'last_jiffies'), | ||
103 | (" .{} : {}", 'next_timer'), | ||
104 | (" .{} : {} nsecs", 'idle_expires')] | ||
105 | text += "\n".join([s.format(f, ts[f]) for s, f in fmts]) | ||
106 | text += "\njiffies: {}\n".format(jiffies) | ||
107 | |||
108 | text += "\n" | ||
109 | |||
110 | return text | ||
111 | |||
112 | |||
113 | def print_tickdevice(td, cpu): | ||
114 | dev = td['evtdev'] | ||
115 | text = "Tick Device: mode: {}\n".format(td['mode']) | ||
116 | if cpu < 0: | ||
117 | text += "Broadcast device\n" | ||
118 | else: | ||
119 | text += "Per CPU device: {}\n".format(cpu) | ||
120 | |||
121 | text += "Clock Event Device: " | ||
122 | if dev == 0: | ||
123 | text += "<NULL>\n" | ||
124 | return text | ||
125 | |||
126 | text += "{}\n".format(dev['name']) | ||
127 | text += " max_delta_ns: {}\n".format(dev['max_delta_ns']) | ||
128 | text += " min_delta_ns: {}\n".format(dev['min_delta_ns']) | ||
129 | text += " mult: {}\n".format(dev['mult']) | ||
130 | text += " shift: {}\n".format(dev['shift']) | ||
131 | text += " mode: {}\n".format(dev['state_use_accessors']) | ||
132 | text += " next_event: {} nsecs\n".format(dev['next_event']) | ||
133 | |||
134 | text += " set_next_event: {}\n".format(dev['set_next_event']) | ||
135 | |||
136 | members = [('set_state_shutdown', " shutdown: {}\n"), | ||
137 | ('set_state_periodic', " periodic: {}\n"), | ||
138 | ('set_state_oneshot', " oneshot: {}\n"), | ||
139 | ('set_state_oneshot_stopped', " oneshot stopped: {}\n"), | ||
140 | ('tick_resume', " resume: {}\n")] | ||
141 | for member, fmt in members: | ||
142 | if dev[member]: | ||
143 | text += fmt.format(dev[member]) | ||
144 | |||
145 | text += " event_handler: {}\n".format(dev['event_handler']) | ||
146 | text += " retries: {}\n".format(dev['retries']) | ||
147 | |||
148 | return text | ||
149 | |||
150 | |||
151 | def pr_cpumask(mask): | ||
152 | nr_cpu_ids = 1 | ||
153 | if constants.LX_NR_CPUS > 1: | ||
154 | nr_cpu_ids = gdb.parse_and_eval("nr_cpu_ids") | ||
155 | |||
156 | inf = gdb.inferiors()[0] | ||
157 | bits = mask['bits'] | ||
158 | num_bytes = (nr_cpu_ids + 7) / 8 | ||
159 | buf = utils.read_memoryview(inf, bits, num_bytes).tobytes() | ||
160 | buf = binascii.b2a_hex(buf) | ||
161 | |||
162 | chunks = [] | ||
163 | i = num_bytes | ||
164 | while i > 0: | ||
165 | i -= 1 | ||
166 | start = i * 2 | ||
167 | end = start + 2 | ||
168 | chunks.append(buf[start:end]) | ||
169 | if i != 0 and i % 4 == 0: | ||
170 | chunks.append(',') | ||
171 | |||
172 | extra = nr_cpu_ids % 8 | ||
173 | if 0 < extra <= 4: | ||
174 | chunks[0] = chunks[0][0] # Cut off the first 0 | ||
175 | |||
176 | return "".join(chunks) | ||
177 | |||
178 | |||
179 | class LxTimerList(gdb.Command): | ||
180 | """Print /proc/timer_list""" | ||
181 | |||
182 | def __init__(self): | ||
183 | super(LxTimerList, self).__init__("lx-timerlist", gdb.COMMAND_DATA) | ||
184 | |||
185 | def invoke(self, arg, from_tty): | ||
186 | hrtimer_bases = gdb.parse_and_eval("&hrtimer_bases") | ||
187 | max_clock_bases = gdb.parse_and_eval("HRTIMER_MAX_CLOCK_BASES") | ||
188 | |||
189 | text = "Timer List Version: gdb scripts\n" | ||
190 | text += "HRTIMER_MAX_CLOCK_BASES: {}\n".format(max_clock_bases) | ||
191 | text += "now at {} nsecs\n".format(ktime_get()) | ||
192 | |||
193 | for cpu in cpus.each_online_cpu(): | ||
194 | text += print_cpu(hrtimer_bases, cpu, max_clock_bases) | ||
195 | |||
196 | if constants.LX_CONFIG_GENERIC_CLOCKEVENTS: | ||
197 | if constants.LX_CONFIG_GENERIC_CLOCKEVENTS_BROADCAST: | ||
198 | bc_dev = gdb.parse_and_eval("&tick_broadcast_device") | ||
199 | text += print_tickdevice(bc_dev, -1) | ||
200 | text += "\n" | ||
201 | mask = gdb.parse_and_eval("tick_broadcast_mask") | ||
202 | mask = pr_cpumask(mask) | ||
203 | text += "tick_broadcast_mask: {}\n".format(mask) | ||
204 | if constants.LX_CONFIG_TICK_ONESHOT: | ||
205 | mask = gdb.parse_and_eval("tick_broadcast_oneshot_mask") | ||
206 | mask = pr_cpumask(mask) | ||
207 | text += "tick_broadcast_oneshot_mask: {}\n".format(mask) | ||
208 | text += "\n" | ||
209 | |||
210 | tick_cpu_devices = gdb.parse_and_eval("&tick_cpu_device") | ||
211 | for cpu in cpus.each_online_cpu(): | ||
212 | tick_dev = cpus.per_cpu(tick_cpu_devices, cpu) | ||
213 | text += print_tickdevice(tick_dev, cpu) | ||
214 | text += "\n" | ||
215 | |||
216 | gdb.write(text) | ||
217 | |||
218 | |||
219 | LxTimerList() | ||
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py new file mode 100644 index 000000000..ff7c1799d --- /dev/null +++ b/scripts/gdb/linux/utils.py | |||
@@ -0,0 +1,193 @@ | |||
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 | |||
70 | ContainerOf() | ||
71 | |||
72 | |||
73 | BIG_ENDIAN = 0 | ||
74 | LITTLE_ENDIAN = 1 | ||
75 | target_endianness = None | ||
76 | |||
77 | |||
78 | def get_target_endianness(): | ||
79 | global target_endianness | ||
80 | if target_endianness is None: | ||
81 | endian = gdb.execute("show endian", to_string=True) | ||
82 | if "little endian" in endian: | ||
83 | target_endianness = LITTLE_ENDIAN | ||
84 | elif "big endian" in endian: | ||
85 | target_endianness = BIG_ENDIAN | ||
86 | else: | ||
87 | raise gdb.GdbError("unknown endianness '{0}'".format(str(endian))) | ||
88 | return target_endianness | ||
89 | |||
90 | |||
91 | def read_memoryview(inf, start, length): | ||
92 | return memoryview(inf.read_memory(start, length)) | ||
93 | |||
94 | |||
95 | def read_u16(buffer, offset): | ||
96 | buffer_val = buffer[offset:offset + 2] | ||
97 | value = [0, 0] | ||
98 | |||
99 | if type(buffer_val[0]) is str: | ||
100 | value[0] = ord(buffer_val[0]) | ||
101 | value[1] = ord(buffer_val[1]) | ||
102 | else: | ||
103 | value[0] = buffer_val[0] | ||
104 | value[1] = buffer_val[1] | ||
105 | |||
106 | if get_target_endianness() == LITTLE_ENDIAN: | ||
107 | return value[0] + (value[1] << 8) | ||
108 | else: | ||
109 | return value[1] + (value[0] << 8) | ||
110 | |||
111 | |||
112 | def read_u32(buffer, offset): | ||
113 | if get_target_endianness() == LITTLE_ENDIAN: | ||
114 | return read_u16(buffer, offset) + (read_u16(buffer, offset + 2) << 16) | ||
115 | else: | ||
116 | return read_u16(buffer, offset + 2) + (read_u16(buffer, offset) << 16) | ||
117 | |||
118 | |||
119 | def read_u64(buffer, offset): | ||
120 | if get_target_endianness() == LITTLE_ENDIAN: | ||
121 | return read_u32(buffer, offset) + (read_u32(buffer, offset + 4) << 32) | ||
122 | else: | ||
123 | return read_u32(buffer, offset + 4) + (read_u32(buffer, offset) << 32) | ||
124 | |||
125 | |||
126 | def read_ulong(buffer, offset): | ||
127 | if get_long_type().sizeof == 8: | ||
128 | return read_u64(buffer, offset) | ||
129 | else: | ||
130 | return read_u32(buffer, offset) | ||
131 | |||
132 | |||
133 | target_arch = None | ||
134 | |||
135 | |||
136 | def is_target_arch(arch): | ||
137 | if hasattr(gdb.Frame, 'architecture'): | ||
138 | return arch in gdb.newest_frame().architecture().name() | ||
139 | else: | ||
140 | global target_arch | ||
141 | if target_arch is None: | ||
142 | target_arch = gdb.execute("show architecture", to_string=True) | ||
143 | return arch in target_arch | ||
144 | |||
145 | |||
146 | GDBSERVER_QEMU = 0 | ||
147 | GDBSERVER_KGDB = 1 | ||
148 | gdbserver_type = None | ||
149 | |||
150 | |||
151 | def get_gdbserver_type(): | ||
152 | def exit_handler(event): | ||
153 | global gdbserver_type | ||
154 | gdbserver_type = None | ||
155 | gdb.events.exited.disconnect(exit_handler) | ||
156 | |||
157 | def probe_qemu(): | ||
158 | try: | ||
159 | return gdb.execute("monitor info version", to_string=True) != "" | ||
160 | except gdb.error: | ||
161 | return False | ||
162 | |||
163 | def probe_kgdb(): | ||
164 | try: | ||
165 | thread_info = gdb.execute("info thread 2", to_string=True) | ||
166 | return "shadowCPU0" in thread_info | ||
167 | except gdb.error: | ||
168 | return False | ||
169 | |||
170 | global gdbserver_type | ||
171 | if gdbserver_type is None: | ||
172 | if probe_qemu(): | ||
173 | gdbserver_type = GDBSERVER_QEMU | ||
174 | elif probe_kgdb(): | ||
175 | gdbserver_type = GDBSERVER_KGDB | ||
176 | if gdbserver_type is not None and hasattr(gdb, 'events'): | ||
177 | gdb.events.exited.connect(exit_handler) | ||
178 | return gdbserver_type | ||
179 | |||
180 | |||
181 | def gdb_eval_or_none(expresssion): | ||
182 | try: | ||
183 | return gdb.parse_and_eval(expresssion) | ||
184 | except gdb.error: | ||
185 | return None | ||
186 | |||
187 | |||
188 | def dentry_name(d): | ||
189 | parent = d['d_parent'] | ||
190 | if parent == d or parent == 0: | ||
191 | return "" | ||
192 | p = dentry_name(d['d_parent']) + "/" | ||
193 | return p + d['d_iname'].string() | ||
diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py new file mode 100644 index 000000000..4136dc2c5 --- /dev/null +++ b/scripts/gdb/vmlinux-gdb.py | |||
@@ -0,0 +1,39 @@ | |||
1 | # | ||
2 | # gdb helper commands and functions for Linux kernel debugging | ||
3 | # | ||
4 | # loader module | ||
5 | # | ||
6 | # Copyright (c) Siemens AG, 2012, 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 os | ||
15 | |||
16 | sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb") | ||
17 | |||
18 | try: | ||
19 | gdb.parse_and_eval("0") | ||
20 | gdb.execute("", to_string=True) | ||
21 | except: | ||
22 | gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to " | ||
23 | "work.\n") | ||
24 | else: | ||
25 | import linux.utils | ||
26 | import linux.symbols | ||
27 | import linux.modules | ||
28 | import linux.dmesg | ||
29 | import linux.tasks | ||
30 | import linux.config | ||
31 | import linux.cpus | ||
32 | import linux.lists | ||
33 | import linux.rbtree | ||
34 | import linux.proc | ||
35 | import linux.constants | ||
36 | import linux.timerlist | ||
37 | import linux.clk | ||
38 | import linux.genpd | ||
39 | import linux.device | ||