File Index
Files and directories
Sign in
FILE

dwarf-unwind.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/tests/dwarf-unwind.c C
  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 "env.h"
 11#include "machine.h"
 12#include "event.h"
 13#include "../util/unwind.h"
 14#include "perf_regs.h"
 15#include "map.h"
 16#include "symbol.h"
 17#include "thread.h"
 18#include "callchain.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/*
 41 * We need to keep these functions global, despite the
 42 * fact that they are used only locally in this object,
 43 * in order to keep them around even if the binary is
 44 * stripped. If they are gone, the unwind check for
 45 * symbol fails.
 46 */
 47int test_dwarf_unwind__thread(struct thread *thread);
 48int test_dwarf_unwind__compare(void *p1, void *p2);
 49int test_dwarf_unwind__krava_3(struct thread *thread);
 50int test_dwarf_unwind__krava_2(struct thread *thread);
 51int test_dwarf_unwind__krava_1(struct thread *thread);
 52int test__dwarf_unwind(struct test_suite *test, int subtest);
 53
 54#define MAX_STACK 8
 55
 56static int unwind_entry(struct unwind_entry *entry, void *arg)
 57{
 58	unsigned long *cnt = (unsigned long *) arg;
 59	char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL;
 60	static const char *funcs[MAX_STACK] = {
 61		"test__arch_unwind_sample",
 62		"test_dwarf_unwind__thread",
 63		"test_dwarf_unwind__compare",
 64		"bsearch",
 65		"test_dwarf_unwind__krava_3",
 66		"test_dwarf_unwind__krava_2",
 67		"test_dwarf_unwind__krava_1",
 68		"test__dwarf_unwind"
 69	};
 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;
 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
 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]);
 92}
 93
 94NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__thread(struct thread *thread)
 95{
 96	struct perf_sample sample;
 97	unsigned long cnt = 0;
 98	int err = -1;
 99
100	perf_sample__init(&sample, /*all=*/true);
101	if (test__arch_unwind_sample(&sample, thread)) {
102		pr_debug("failed to get unwind sample\n");
103		goto out;
104	}
105
106	err = unwind__get_entries(unwind_entry, &cnt, thread,
107				  &sample, MAX_STACK, false);
108	if (err)
109		pr_debug("unwind failed\n");
110	else if (cnt != MAX_STACK) {
111		pr_debug("got wrong number of stack entries %lu != %d\n",
112			 cnt, MAX_STACK);
113		err = -1;
114	}
115
116 out:
117	zfree(&sample.user_stack.data);
118	zfree(&sample.user_regs->regs);
119	perf_sample__exit(&sample);
120	return err;
121}
122
123static int global_unwind_retval = -INT_MAX;
124
125NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__compare(void *p1, void *p2)
126{
127	/* Any possible value should be 'thread' */
128	struct thread *thread = *(struct thread **)p1;
129
130	if (global_unwind_retval == -INT_MAX) {
131		/* Call unwinder twice for both callchain orders. */
132		callchain_param.order = ORDER_CALLER;
133
134		global_unwind_retval = test_dwarf_unwind__thread(thread);
135		if (!global_unwind_retval) {
136			callchain_param.order = ORDER_CALLEE;
137			global_unwind_retval = test_dwarf_unwind__thread(thread);
138		}
139	}
140
141	return p1 - p2;
142}
143
144NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_3(struct thread *thread)
145{
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;
158	_bsearch(array, &thread, 2, sizeof(struct thread **),
159		 test_dwarf_unwind__compare);
160	return global_unwind_retval;
161}
162
163NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_2(struct thread *thread)
164{
165	int ret;
166
167	ret =  test_dwarf_unwind__krava_3(thread);
168	NO_TAIL_CALL_BARRIER;
169	return ret;
170}
171
172NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_1(struct thread *thread)
173{
174	int ret;
175
176	ret =  test_dwarf_unwind__krava_2(thread);
177	NO_TAIL_CALL_BARRIER;
178	return ret;
179}
180
181noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused,
182				int subtest __maybe_unused)
183{
184	struct perf_env host_env;
185	struct machine *machine;
186	struct thread *thread;
187	int err = -1;
188	pid_t pid = getpid();
189
190	callchain_param.record_mode = CALLCHAIN_DWARF;
191	dwarf_callchain_users = true;
192
193	perf_env__init(&host_env);
194	machine = machine__new_live(&host_env, /*kernel_maps=*/true, pid);
195	if (!machine) {
196		pr_err("Could not get machine\n");
197		goto out;
198	}
199
200	if (machine__create_kernel_maps(machine)) {
201		pr_err("Failed to create kernel maps\n");
202		goto out;
203	}
204
205	if (verbose > 1)
206		machine__fprintf(machine, stderr);
207
208	thread = machine__find_thread(machine, pid, pid);
209	if (!thread) {
210		pr_err("Could not get thread\n");
211		goto out;
212	}
213
214	err = test_dwarf_unwind__krava_1(thread);
215	thread__put(thread);
216
217 out:
218	machine__delete(machine);
219	perf_env__exit(&host_env);
220	return err;
221}
222
223DEFINE_SUITE("Test dwarf unwind", dwarf_unwind);
~/ Read-only file preview.