]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
aa16b81f | 2 | #include <linux/compiler.h> |
d944c4ee | 3 | #include <linux/types.h> |
d8f9da24 | 4 | #include <linux/zalloc.h> |
fd20e811 | 5 | #include <inttypes.h> |
4a3cec84 | 6 | #include <limits.h> |
aa16b81f JO |
7 | #include <unistd.h> |
8 | #include "tests.h" | |
9 | #include "debug.h" | |
10 | #include "machine.h" | |
11 | #include "event.h" | |
df90cc41 | 12 | #include "../util/unwind.h" |
aa16b81f JO |
13 | #include "perf_regs.h" |
14 | #include "map.h" | |
daecf9e0 | 15 | #include "symbol.h" |
aa16b81f | 16 | #include "thread.h" |
0cdccac6 | 17 | #include "callchain.h" |
ea49e01c | 18 | #include "util/synthetic-events.h" |
aa16b81f | 19 | |
b93b0967 WN |
20 | /* For bsearch. We try to unwind functions in shared object. */ |
21 | #include <stdlib.h> | |
22 | ||
aa16b81f JO |
23 | static int mmap_handler(struct perf_tool *tool __maybe_unused, |
24 | union perf_event *event, | |
eb9f0323 | 25 | struct perf_sample *sample, |
aa16b81f JO |
26 | struct machine *machine) |
27 | { | |
eb9f0323 | 28 | return machine__process_mmap2_event(machine, event, sample); |
aa16b81f JO |
29 | } |
30 | ||
31 | static int init_live_machine(struct machine *machine) | |
32 | { | |
33 | union perf_event event; | |
34 | pid_t pid = getpid(); | |
35 | ||
0fb0d615 | 36 | memset(&event, 0, sizeof(event)); |
aa16b81f | 37 | return perf_event__synthesize_mmap_events(NULL, &event, pid, pid, |
3fcb10e4 | 38 | mmap_handler, machine, true); |
aa16b81f JO |
39 | } |
40 | ||
fdf7c49c JO |
41 | /* |
42 | * We need to keep these functions global, despite the | |
43 | * fact that they are used only locally in this object, | |
44 | * in order to keep them around even if the binary is | |
45 | * stripped. If they are gone, the unwind check for | |
46 | * symbol fails. | |
47 | */ | |
48 | int test_dwarf_unwind__thread(struct thread *thread); | |
49 | int test_dwarf_unwind__compare(void *p1, void *p2); | |
50 | int test_dwarf_unwind__krava_3(struct thread *thread); | |
51 | int test_dwarf_unwind__krava_2(struct thread *thread); | |
52 | int test_dwarf_unwind__krava_1(struct thread *thread); | |
53 | ||
b93b0967 | 54 | #define MAX_STACK 8 |
aa16b81f JO |
55 | |
56 | static int unwind_entry(struct unwind_entry *entry, void *arg) | |
57 | { | |
58 | unsigned long *cnt = (unsigned long *) arg; | |
c1529738 | 59 | char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL; |
aa16b81f JO |
60 | static const char *funcs[MAX_STACK] = { |
61 | "test__arch_unwind_sample", | |
fdf7c49c JO |
62 | "test_dwarf_unwind__thread", |
63 | "test_dwarf_unwind__compare", | |
b93b0967 | 64 | "bsearch", |
fdf7c49c JO |
65 | "test_dwarf_unwind__krava_3", |
66 | "test_dwarf_unwind__krava_2", | |
67 | "test_dwarf_unwind__krava_1", | |
aa16b81f JO |
68 | "test__dwarf_unwind" |
69 | }; | |
8dc0564d JO |
70 | /* |
71 | * The funcs[MAX_STACK] array index, based on the | |
72 | * callchain order setup. | |
73 | */ | |
74 | int idx = callchain_param.order == ORDER_CALLER ? | |
75 | MAX_STACK - *cnt - 1 : *cnt; | |
aa16b81f JO |
76 | |
77 | if (*cnt >= MAX_STACK) { | |
78 | pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); | |
79 | return -1; | |
80 | } | |
81 | ||
82 | if (!symbol) { | |
83 | pr_debug("failed: got unresolved address 0x%" PRIx64 "\n", | |
84 | entry->ip); | |
85 | return -1; | |
86 | } | |
87 | ||
8dc0564d JO |
88 | (*cnt)++; |
89 | pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n", | |
90 | symbol, entry->ip, funcs[idx]); | |
91 | return strcmp((const char *) symbol, funcs[idx]); | |
aa16b81f JO |
92 | } |
93 | ||
9ae1e990 | 94 | noinline int test_dwarf_unwind__thread(struct thread *thread) |
aa16b81f JO |
95 | { |
96 | struct perf_sample sample; | |
97 | unsigned long cnt = 0; | |
98 | int err = -1; | |
99 | ||
100 | memset(&sample, 0, sizeof(sample)); | |
101 | ||
102 | if (test__arch_unwind_sample(&sample, thread)) { | |
103 | pr_debug("failed to get unwind sample\n"); | |
104 | goto out; | |
105 | } | |
106 | ||
dd8c17a5 | 107 | err = unwind__get_entries(unwind_entry, &cnt, thread, |
352ea45a | 108 | &sample, MAX_STACK); |
aa16b81f JO |
109 | if (err) |
110 | pr_debug("unwind failed\n"); | |
111 | else if (cnt != MAX_STACK) { | |
112 | pr_debug("got wrong number of stack entries %lu != %d\n", | |
113 | cnt, MAX_STACK); | |
114 | err = -1; | |
115 | } | |
116 | ||
117 | out: | |
d8f9da24 ACM |
118 | zfree(&sample.user_stack.data); |
119 | zfree(&sample.user_regs.regs); | |
aa16b81f JO |
120 | return err; |
121 | } | |
122 | ||
b93b0967 WN |
123 | static int global_unwind_retval = -INT_MAX; |
124 | ||
9ae1e990 | 125 | noinline int test_dwarf_unwind__compare(void *p1, void *p2) |
b93b0967 WN |
126 | { |
127 | /* Any possible value should be 'thread' */ | |
128 | struct thread *thread = *(struct thread **)p1; | |
129 | ||
8dc0564d JO |
130 | if (global_unwind_retval == -INT_MAX) { |
131 | /* Call unwinder twice for both callchain orders. */ | |
132 | callchain_param.order = ORDER_CALLER; | |
133 | ||
fdf7c49c | 134 | global_unwind_retval = test_dwarf_unwind__thread(thread); |
8dc0564d JO |
135 | if (!global_unwind_retval) { |
136 | callchain_param.order = ORDER_CALLEE; | |
fdf7c49c | 137 | global_unwind_retval = test_dwarf_unwind__thread(thread); |
8dc0564d JO |
138 | } |
139 | } | |
b93b0967 WN |
140 | |
141 | return p1 - p2; | |
142 | } | |
143 | ||
9ae1e990 | 144 | noinline int test_dwarf_unwind__krava_3(struct thread *thread) |
aa16b81f | 145 | { |
b93b0967 WN |
146 | struct thread *array[2] = {thread, thread}; |
147 | void *fp = &bsearch; | |
148 | /* | |
149 | * make _bsearch a volatile function pointer to | |
150 | * prevent potential optimization, which may expand | |
151 | * bsearch and call compare directly from this function, | |
152 | * instead of libc shared object. | |
153 | */ | |
154 | void *(*volatile _bsearch)(void *, void *, size_t, | |
155 | size_t, int (*)(void *, void *)); | |
156 | ||
157 | _bsearch = fp; | |
fdf7c49c JO |
158 | _bsearch(array, &thread, 2, sizeof(struct thread **), |
159 | test_dwarf_unwind__compare); | |
b93b0967 | 160 | return global_unwind_retval; |
aa16b81f JO |
161 | } |
162 | ||
9ae1e990 | 163 | noinline int test_dwarf_unwind__krava_2(struct thread *thread) |
aa16b81f | 164 | { |
fdf7c49c | 165 | return test_dwarf_unwind__krava_3(thread); |
aa16b81f JO |
166 | } |
167 | ||
9ae1e990 | 168 | noinline int test_dwarf_unwind__krava_1(struct thread *thread) |
aa16b81f | 169 | { |
fdf7c49c | 170 | return test_dwarf_unwind__krava_2(thread); |
aa16b81f JO |
171 | } |
172 | ||
81f17c90 | 173 | int test__dwarf_unwind(struct test *test __maybe_unused, int subtest __maybe_unused) |
aa16b81f | 174 | { |
aa16b81f JO |
175 | struct machine *machine; |
176 | struct thread *thread; | |
177 | int err = -1; | |
178 | ||
bdaba8ae | 179 | machine = machine__new_host(); |
aa16b81f JO |
180 | if (!machine) { |
181 | pr_err("Could not get machine\n"); | |
182 | return -1; | |
183 | } | |
184 | ||
9bdcede5 JO |
185 | if (machine__create_kernel_maps(machine)) { |
186 | pr_err("Failed to create kernel maps\n"); | |
187 | return -1; | |
188 | } | |
189 | ||
0cdccac6 | 190 | callchain_param.record_mode = CALLCHAIN_DWARF; |
eabad8c6 | 191 | dwarf_callchain_users = true; |
0cdccac6 | 192 | |
aa16b81f JO |
193 | if (init_live_machine(machine)) { |
194 | pr_err("Could not init machine\n"); | |
195 | goto out; | |
196 | } | |
197 | ||
198 | if (verbose > 1) | |
199 | machine__fprintf(machine, stderr); | |
200 | ||
d75e6097 | 201 | thread = machine__find_thread(machine, getpid(), getpid()); |
aa16b81f JO |
202 | if (!thread) { |
203 | pr_err("Could not get thread\n"); | |
204 | goto out; | |
205 | } | |
206 | ||
fdf7c49c | 207 | err = test_dwarf_unwind__krava_1(thread); |
b91fc39f | 208 | thread__put(thread); |
aa16b81f JO |
209 | |
210 | out: | |
211 | machine__delete_threads(machine); | |
bdaba8ae | 212 | machine__delete(machine); |
aa16b81f JO |
213 | return err; |
214 | } |