diff options
author | Wang Nan <wangnan0@huawei.com> | 2015-06-03 04:52:21 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-06-03 09:02:38 -0400 |
commit | 1f121b03d058dd07199d8924373d3c52a207f63b (patch) | |
tree | f8fe8788f068105ea71719e0c942b2ac6d1d599d | |
parent | 4fc62a89dc607cee4f75f6dbb102cd6215fd0d64 (diff) |
perf tools: Deal with kernel module names in '[]' correctly
Before patch ba92732e9808 ('perf kmaps: Check kmaps to make code more
robust'), 'perf report' and 'perf annotate' will segfault if trace data
contains kernel module information like this:
# perf report -D -i ./perf.data
...
0 0 0x188 [0x50]: PERF_RECORD_MMAP -1/0: [0xffffffbff1018000(0xf068000) @ 0]: x [test_module]
...
# perf report -i ./perf.data --objdump=/path/to/objdump --kallsyms=/path/to/kallsyms
perf: Segmentation fault
-------- backtrace --------
/path/to/perf[0x503478]
/lib64/libc.so.6(+0x3545f)[0x7fb201f3745f]
/path/to/perf[0x499b56]
/path/to/perf(dso__load_kallsyms+0x13c)[0x49b56c]
/path/to/perf(dso__load+0x72e)[0x49c21e]
/path/to/perf(map__load+0x6e)[0x4ae9ee]
/path/to/perf(thread__find_addr_map+0x24c)[0x47deec]
/path/to/perf(perf_event__preprocess_sample+0x88)[0x47e238]
/path/to/perf[0x43ad02]
/path/to/perf[0x4b55bc]
/path/to/perf(ordered_events__flush+0xca)[0x4b57ea]
/path/to/perf[0x4b1a01]
/path/to/perf(perf_session__process_events+0x3be)[0x4b428e]
/path/to/perf(cmd_report+0xf11)[0x43bfc1]
/path/to/perf[0x474702]
/path/to/perf(main+0x5f5)[0x42de95]
/lib64/libc.so.6(__libc_start_main+0xf4)[0x7fb201f23bd4]
/path/to/perf[0x42dfc4]
This is because __kmod_path__parse treats '[' leading names as kernel
name instead of names of kernel module.
If perf.data contains build information and the buildid of such modules
can be found, the dso->kernel of it will be set to DSO_TYPE_KERNEL by
__event_process_build_id(), not kernel module.
It will then be passed to dso__load() -> dso__load_kernel_sym() ->
dso__load_kcore() if --kallsyms is provided.
The refered patch adds NULL pointer checker to avoid segfault. However,
such kernel modules are still processed incorrectly.
This patch fixes __kmod_path__parse, makes it treat names like
'[test_module]' as kernel modules.
kmod-path.c is also update to reflect the above changes.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Link: http://lkml.kernel.org/r/1433321541-170245-1-git-send-email-wangnan0@huawei.com
[ Fixed the merged with 0443f36b0de0 ("perf machine: Fix the search
for the kernel DSO on the unified list" ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/tests/kmod-path.c | 72 | ||||
-rw-r--r-- | tools/perf/util/dso.c | 47 | ||||
-rw-r--r-- | tools/perf/util/dso.h | 2 | ||||
-rw-r--r-- | tools/perf/util/header.c | 8 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 22 |
5 files changed, 140 insertions, 11 deletions
diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c index e8d7cbb9320c..08c433b4bf4f 100644 --- a/tools/perf/tests/kmod-path.c +++ b/tools/perf/tests/kmod-path.c | |||
@@ -34,9 +34,21 @@ static int test(const char *path, bool alloc_name, bool alloc_ext, | |||
34 | return 0; | 34 | return 0; |
35 | } | 35 | } |
36 | 36 | ||
37 | static int test_is_kernel_module(const char *path, int cpumode, bool expect) | ||
38 | { | ||
39 | TEST_ASSERT_VAL("is_kernel_module", | ||
40 | (!!is_kernel_module(path, cpumode)) == (!!expect)); | ||
41 | pr_debug("%s (cpumode: %d) - is_kernel_module: %s\n", | ||
42 | path, cpumode, expect ? "true" : "false"); | ||
43 | return 0; | ||
44 | } | ||
45 | |||
37 | #define T(path, an, ae, k, c, n, e) \ | 46 | #define T(path, an, ae, k, c, n, e) \ |
38 | TEST_ASSERT_VAL("failed", !test(path, an, ae, k, c, n, e)) | 47 | TEST_ASSERT_VAL("failed", !test(path, an, ae, k, c, n, e)) |
39 | 48 | ||
49 | #define M(path, c, e) \ | ||
50 | TEST_ASSERT_VAL("failed", !test_is_kernel_module(path, c, e)) | ||
51 | |||
40 | int test__kmod_path__parse(void) | 52 | int test__kmod_path__parse(void) |
41 | { | 53 | { |
42 | /* path alloc_name alloc_ext kmod comp name ext */ | 54 | /* path alloc_name alloc_ext kmod comp name ext */ |
@@ -44,30 +56,90 @@ int test__kmod_path__parse(void) | |||
44 | T("/xxxx/xxxx/x-x.ko", false , true , true, false, NULL , NULL); | 56 | T("/xxxx/xxxx/x-x.ko", false , true , true, false, NULL , NULL); |
45 | T("/xxxx/xxxx/x-x.ko", true , false , true, false, "[x_x]", NULL); | 57 | T("/xxxx/xxxx/x-x.ko", true , false , true, false, "[x_x]", NULL); |
46 | T("/xxxx/xxxx/x-x.ko", false , false , true, false, NULL , NULL); | 58 | T("/xxxx/xxxx/x-x.ko", false , false , true, false, NULL , NULL); |
59 | M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true); | ||
60 | M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_KERNEL, true); | ||
61 | M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_USER, false); | ||
47 | 62 | ||
48 | /* path alloc_name alloc_ext kmod comp name ext */ | 63 | /* path alloc_name alloc_ext kmod comp name ext */ |
49 | T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz"); | 64 | T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz"); |
50 | T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz"); | 65 | T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz"); |
51 | T("/xxxx/xxxx/x.ko.gz", true , false , true, true, "[x]", NULL); | 66 | T("/xxxx/xxxx/x.ko.gz", true , false , true, true, "[x]", NULL); |
52 | T("/xxxx/xxxx/x.ko.gz", false , false , true, true, NULL , NULL); | 67 | T("/xxxx/xxxx/x.ko.gz", false , false , true, true, NULL , NULL); |
68 | M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true); | ||
69 | M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_KERNEL, true); | ||
70 | M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_USER, false); | ||
53 | 71 | ||
54 | /* path alloc_name alloc_ext kmod comp name ext */ | 72 | /* path alloc_name alloc_ext kmod comp name ext */ |
55 | T("/xxxx/xxxx/x.gz", true , true , false, true, "x.gz" ,"gz"); | 73 | T("/xxxx/xxxx/x.gz", true , true , false, true, "x.gz" ,"gz"); |
56 | T("/xxxx/xxxx/x.gz", false , true , false, true, NULL ,"gz"); | 74 | T("/xxxx/xxxx/x.gz", false , true , false, true, NULL ,"gz"); |
57 | T("/xxxx/xxxx/x.gz", true , false , false, true, "x.gz" , NULL); | 75 | T("/xxxx/xxxx/x.gz", true , false , false, true, "x.gz" , NULL); |
58 | T("/xxxx/xxxx/x.gz", false , false , false, true, NULL , NULL); | 76 | T("/xxxx/xxxx/x.gz", false , false , false, true, NULL , NULL); |
77 | M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false); | ||
78 | M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_KERNEL, false); | ||
79 | M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_USER, false); | ||
59 | 80 | ||
60 | /* path alloc_name alloc_ext kmod comp name ext */ | 81 | /* path alloc_name alloc_ext kmod comp name ext */ |
61 | T("x.gz", true , true , false, true, "x.gz", "gz"); | 82 | T("x.gz", true , true , false, true, "x.gz", "gz"); |
62 | T("x.gz", false , true , false, true, NULL , "gz"); | 83 | T("x.gz", false , true , false, true, NULL , "gz"); |
63 | T("x.gz", true , false , false, true, "x.gz", NULL); | 84 | T("x.gz", true , false , false, true, "x.gz", NULL); |
64 | T("x.gz", false , false , false, true, NULL , NULL); | 85 | T("x.gz", false , false , false, true, NULL , NULL); |
86 | M("x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false); | ||
87 | M("x.gz", PERF_RECORD_MISC_KERNEL, false); | ||
88 | M("x.gz", PERF_RECORD_MISC_USER, false); | ||
65 | 89 | ||
66 | /* path alloc_name alloc_ext kmod comp name ext */ | 90 | /* path alloc_name alloc_ext kmod comp name ext */ |
67 | T("x.ko.gz", true , true , true, true, "[x]", "gz"); | 91 | T("x.ko.gz", true , true , true, true, "[x]", "gz"); |
68 | T("x.ko.gz", false , true , true, true, NULL , "gz"); | 92 | T("x.ko.gz", false , true , true, true, NULL , "gz"); |
69 | T("x.ko.gz", true , false , true, true, "[x]", NULL); | 93 | T("x.ko.gz", true , false , true, true, "[x]", NULL); |
70 | T("x.ko.gz", false , false , true, true, NULL , NULL); | 94 | T("x.ko.gz", false , false , true, true, NULL , NULL); |
95 | M("x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true); | ||
96 | M("x.ko.gz", PERF_RECORD_MISC_KERNEL, true); | ||
97 | M("x.ko.gz", PERF_RECORD_MISC_USER, false); | ||
98 | |||
99 | /* path alloc_name alloc_ext kmod comp name ext */ | ||
100 | T("[test_module]", true , true , true, false, "[test_module]", NULL); | ||
101 | T("[test_module]", false , true , true, false, NULL , NULL); | ||
102 | T("[test_module]", true , false , true, false, "[test_module]", NULL); | ||
103 | T("[test_module]", false , false , true, false, NULL , NULL); | ||
104 | M("[test_module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true); | ||
105 | M("[test_module]", PERF_RECORD_MISC_KERNEL, true); | ||
106 | M("[test_module]", PERF_RECORD_MISC_USER, false); | ||
107 | |||
108 | /* path alloc_name alloc_ext kmod comp name ext */ | ||
109 | T("[test.module]", true , true , true, false, "[test.module]", NULL); | ||
110 | T("[test.module]", false , true , true, false, NULL , NULL); | ||
111 | T("[test.module]", true , false , true, false, "[test.module]", NULL); | ||
112 | T("[test.module]", false , false , true, false, NULL , NULL); | ||
113 | M("[test.module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true); | ||
114 | M("[test.module]", PERF_RECORD_MISC_KERNEL, true); | ||
115 | M("[test.module]", PERF_RECORD_MISC_USER, false); | ||
116 | |||
117 | /* path alloc_name alloc_ext kmod comp name ext */ | ||
118 | T("[vdso]", true , true , false, false, "[vdso]", NULL); | ||
119 | T("[vdso]", false , true , false, false, NULL , NULL); | ||
120 | T("[vdso]", true , false , false, false, "[vdso]", NULL); | ||
121 | T("[vdso]", false , false , false, false, NULL , NULL); | ||
122 | M("[vdso]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false); | ||
123 | M("[vdso]", PERF_RECORD_MISC_KERNEL, false); | ||
124 | M("[vdso]", PERF_RECORD_MISC_USER, false); | ||
125 | |||
126 | /* path alloc_name alloc_ext kmod comp name ext */ | ||
127 | T("[vsyscall]", true , true , false, false, "[vsyscall]", NULL); | ||
128 | T("[vsyscall]", false , true , false, false, NULL , NULL); | ||
129 | T("[vsyscall]", true , false , false, false, "[vsyscall]", NULL); | ||
130 | T("[vsyscall]", false , false , false, false, NULL , NULL); | ||
131 | M("[vsyscall]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false); | ||
132 | M("[vsyscall]", PERF_RECORD_MISC_KERNEL, false); | ||
133 | M("[vsyscall]", PERF_RECORD_MISC_USER, false); | ||
134 | |||
135 | /* path alloc_name alloc_ext kmod comp name ext */ | ||
136 | T("[kernel.kallsyms]", true , true , false, false, "[kernel.kallsyms]", NULL); | ||
137 | T("[kernel.kallsyms]", false , true , false, false, NULL , NULL); | ||
138 | T("[kernel.kallsyms]", true , false , false, false, "[kernel.kallsyms]", NULL); | ||
139 | T("[kernel.kallsyms]", false , false , false, false, NULL , NULL); | ||
140 | M("[kernel.kallsyms]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false); | ||
141 | M("[kernel.kallsyms]", PERF_RECORD_MISC_KERNEL, false); | ||
142 | M("[kernel.kallsyms]", PERF_RECORD_MISC_USER, false); | ||
71 | 143 | ||
72 | return 0; | 144 | return 0; |
73 | } | 145 | } |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index b335db3532a2..5ec9e892c89b 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -166,12 +166,28 @@ bool is_supported_compression(const char *ext) | |||
166 | return false; | 166 | return false; |
167 | } | 167 | } |
168 | 168 | ||
169 | bool is_kernel_module(const char *pathname) | 169 | bool is_kernel_module(const char *pathname, int cpumode) |
170 | { | 170 | { |
171 | struct kmod_path m; | 171 | struct kmod_path m; |
172 | 172 | int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK; | |
173 | if (kmod_path__parse(&m, pathname)) | 173 | |
174 | return NULL; | 174 | WARN_ONCE(mode != cpumode, |
175 | "Internal error: passing unmasked cpumode (%x) to is_kernel_module", | ||
176 | cpumode); | ||
177 | |||
178 | switch (mode) { | ||
179 | case PERF_RECORD_MISC_USER: | ||
180 | case PERF_RECORD_MISC_HYPERVISOR: | ||
181 | case PERF_RECORD_MISC_GUEST_USER: | ||
182 | return false; | ||
183 | /* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */ | ||
184 | default: | ||
185 | if (kmod_path__parse(&m, pathname)) { | ||
186 | pr_err("Failed to check whether %s is a kernel module or not. Assume it is.", | ||
187 | pathname); | ||
188 | return true; | ||
189 | } | ||
190 | } | ||
175 | 191 | ||
176 | return m.kmod; | 192 | return m.kmod; |
177 | } | 193 | } |
@@ -215,12 +231,33 @@ int __kmod_path__parse(struct kmod_path *m, const char *path, | |||
215 | { | 231 | { |
216 | const char *name = strrchr(path, '/'); | 232 | const char *name = strrchr(path, '/'); |
217 | const char *ext = strrchr(path, '.'); | 233 | const char *ext = strrchr(path, '.'); |
234 | bool is_simple_name = false; | ||
218 | 235 | ||
219 | memset(m, 0x0, sizeof(*m)); | 236 | memset(m, 0x0, sizeof(*m)); |
220 | name = name ? name + 1 : path; | 237 | name = name ? name + 1 : path; |
221 | 238 | ||
239 | /* | ||
240 | * '.' is also a valid character for module name. For example: | ||
241 | * [aaa.bbb] is a valid module name. '[' should have higher | ||
242 | * priority than '.ko' suffix. | ||
243 | * | ||
244 | * The kernel names are from machine__mmap_name. Such | ||
245 | * name should belong to kernel itself, not kernel module. | ||
246 | */ | ||
247 | if (name[0] == '[') { | ||
248 | is_simple_name = true; | ||
249 | if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) || | ||
250 | (strncmp(name, "[guest.kernel.kallsyms", 22) == 0) || | ||
251 | (strncmp(name, "[vdso]", 6) == 0) || | ||
252 | (strncmp(name, "[vsyscall]", 10) == 0)) { | ||
253 | m->kmod = false; | ||
254 | |||
255 | } else | ||
256 | m->kmod = true; | ||
257 | } | ||
258 | |||
222 | /* No extension, just return name. */ | 259 | /* No extension, just return name. */ |
223 | if (ext == NULL) { | 260 | if ((ext == NULL) || is_simple_name) { |
224 | if (alloc_name) { | 261 | if (alloc_name) { |
225 | m->name = strdup(name); | 262 | m->name = strdup(name); |
226 | return m->name ? 0 : -ENOMEM; | 263 | return m->name ? 0 : -ENOMEM; |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 24a507a54147..ba2d90ed881f 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -220,7 +220,7 @@ char dso__symtab_origin(const struct dso *dso); | |||
220 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, | 220 | int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, |
221 | char *root_dir, char *filename, size_t size); | 221 | char *root_dir, char *filename, size_t size); |
222 | bool is_supported_compression(const char *ext); | 222 | bool is_supported_compression(const char *ext); |
223 | bool is_kernel_module(const char *pathname); | 223 | bool is_kernel_module(const char *pathname, int cpumode); |
224 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); | 224 | bool decompress_to_file(const char *ext, const char *filename, int output_fd); |
225 | bool dso__needs_decompress(struct dso *dso); | 225 | bool dso__needs_decompress(struct dso *dso); |
226 | 226 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 851143a7988d..ac5aaaeed7ff 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1239,7 +1239,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1239 | { | 1239 | { |
1240 | int err = -1; | 1240 | int err = -1; |
1241 | struct machine *machine; | 1241 | struct machine *machine; |
1242 | u16 misc; | 1242 | u16 cpumode; |
1243 | struct dso *dso; | 1243 | struct dso *dso; |
1244 | enum dso_kernel_type dso_type; | 1244 | enum dso_kernel_type dso_type; |
1245 | 1245 | ||
@@ -1247,9 +1247,9 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1247 | if (!machine) | 1247 | if (!machine) |
1248 | goto out; | 1248 | goto out; |
1249 | 1249 | ||
1250 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 1250 | cpumode = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
1251 | 1251 | ||
1252 | switch (misc) { | 1252 | switch (cpumode) { |
1253 | case PERF_RECORD_MISC_KERNEL: | 1253 | case PERF_RECORD_MISC_KERNEL: |
1254 | dso_type = DSO_TYPE_KERNEL; | 1254 | dso_type = DSO_TYPE_KERNEL; |
1255 | break; | 1255 | break; |
@@ -1270,7 +1270,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
1270 | 1270 | ||
1271 | dso__set_build_id(dso, &bev->build_id); | 1271 | dso__set_build_id(dso, &bev->build_id); |
1272 | 1272 | ||
1273 | if (!is_kernel_module(filename)) | 1273 | if (!is_kernel_module(filename, cpumode)) |
1274 | dso->kernel = dso_type; | 1274 | dso->kernel = dso_type; |
1275 | 1275 | ||
1276 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1276 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 4e29e80932e5..9e02c86f39f5 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1149,9 +1149,29 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
1149 | struct dso *dso; | 1149 | struct dso *dso; |
1150 | 1150 | ||
1151 | list_for_each_entry(dso, &machine->dsos.head, node) { | 1151 | list_for_each_entry(dso, &machine->dsos.head, node) { |
1152 | if (!dso->kernel || is_kernel_module(dso->long_name)) | 1152 | |
1153 | /* | ||
1154 | * The cpumode passed to is_kernel_module is not the | ||
1155 | * cpumode of *this* event. If we insist on passing | ||
1156 | * correct cpumode to is_kernel_module, we should | ||
1157 | * record the cpumode when we adding this dso to the | ||
1158 | * linked list. | ||
1159 | * | ||
1160 | * However we don't really need passing correct | ||
1161 | * cpumode. We know the correct cpumode must be kernel | ||
1162 | * mode (if not, we should not link it onto kernel_dsos | ||
1163 | * list). | ||
1164 | * | ||
1165 | * Therefore, we pass PERF_RECORD_MISC_CPUMODE_UNKNOWN. | ||
1166 | * is_kernel_module() treats it as a kernel cpumode. | ||
1167 | */ | ||
1168 | |||
1169 | if (!dso->kernel || | ||
1170 | is_kernel_module(dso->long_name, | ||
1171 | PERF_RECORD_MISC_CPUMODE_UNKNOWN)) | ||
1153 | continue; | 1172 | continue; |
1154 | 1173 | ||
1174 | |||
1155 | kernel = dso; | 1175 | kernel = dso; |
1156 | break; | 1176 | break; |
1157 | } | 1177 | } |