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