18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/compiler.h>
38c2ecf20Sopenharmony_ci#include <linux/types.h>
48c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
58c2ecf20Sopenharmony_ci#include <inttypes.h>
68c2ecf20Sopenharmony_ci#include <limits.h>
78c2ecf20Sopenharmony_ci#include <unistd.h>
88c2ecf20Sopenharmony_ci#include "tests.h"
98c2ecf20Sopenharmony_ci#include "debug.h"
108c2ecf20Sopenharmony_ci#include "machine.h"
118c2ecf20Sopenharmony_ci#include "event.h"
128c2ecf20Sopenharmony_ci#include "../util/unwind.h"
138c2ecf20Sopenharmony_ci#include "perf_regs.h"
148c2ecf20Sopenharmony_ci#include "map.h"
158c2ecf20Sopenharmony_ci#include "symbol.h"
168c2ecf20Sopenharmony_ci#include "thread.h"
178c2ecf20Sopenharmony_ci#include "callchain.h"
188c2ecf20Sopenharmony_ci#include "util/synthetic-events.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__)
218c2ecf20Sopenharmony_ci#include "arch-tests.h"
228c2ecf20Sopenharmony_ci#endif
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* For bsearch. We try to unwind functions in shared object. */
258c2ecf20Sopenharmony_ci#include <stdlib.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int mmap_handler(struct perf_tool *tool __maybe_unused,
288c2ecf20Sopenharmony_ci			union perf_event *event,
298c2ecf20Sopenharmony_ci			struct perf_sample *sample,
308c2ecf20Sopenharmony_ci			struct machine *machine)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	return machine__process_mmap2_event(machine, event, sample);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int init_live_machine(struct machine *machine)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	union perf_event event;
388c2ecf20Sopenharmony_ci	pid_t pid = getpid();
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	memset(&event, 0, sizeof(event));
418c2ecf20Sopenharmony_ci	return perf_event__synthesize_mmap_events(NULL, &event, pid, pid,
428c2ecf20Sopenharmony_ci						  mmap_handler, machine, true);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * We need to keep these functions global, despite the
478c2ecf20Sopenharmony_ci * fact that they are used only locally in this object,
488c2ecf20Sopenharmony_ci * in order to keep them around even if the binary is
498c2ecf20Sopenharmony_ci * stripped. If they are gone, the unwind check for
508c2ecf20Sopenharmony_ci * symbol fails.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ciint test_dwarf_unwind__thread(struct thread *thread);
538c2ecf20Sopenharmony_ciint test_dwarf_unwind__compare(void *p1, void *p2);
548c2ecf20Sopenharmony_ciint test_dwarf_unwind__krava_3(struct thread *thread);
558c2ecf20Sopenharmony_ciint test_dwarf_unwind__krava_2(struct thread *thread);
568c2ecf20Sopenharmony_ciint test_dwarf_unwind__krava_1(struct thread *thread);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define MAX_STACK 8
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int unwind_entry(struct unwind_entry *entry, void *arg)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned long *cnt = (unsigned long *) arg;
638c2ecf20Sopenharmony_ci	char *symbol = entry->ms.sym ? entry->ms.sym->name : NULL;
648c2ecf20Sopenharmony_ci	static const char *funcs[MAX_STACK] = {
658c2ecf20Sopenharmony_ci		"test__arch_unwind_sample",
668c2ecf20Sopenharmony_ci		"test_dwarf_unwind__thread",
678c2ecf20Sopenharmony_ci		"test_dwarf_unwind__compare",
688c2ecf20Sopenharmony_ci		"bsearch",
698c2ecf20Sopenharmony_ci		"test_dwarf_unwind__krava_3",
708c2ecf20Sopenharmony_ci		"test_dwarf_unwind__krava_2",
718c2ecf20Sopenharmony_ci		"test_dwarf_unwind__krava_1",
728c2ecf20Sopenharmony_ci		"test__dwarf_unwind"
738c2ecf20Sopenharmony_ci	};
748c2ecf20Sopenharmony_ci	/*
758c2ecf20Sopenharmony_ci	 * The funcs[MAX_STACK] array index, based on the
768c2ecf20Sopenharmony_ci	 * callchain order setup.
778c2ecf20Sopenharmony_ci	 */
788c2ecf20Sopenharmony_ci	int idx = callchain_param.order == ORDER_CALLER ?
798c2ecf20Sopenharmony_ci		  MAX_STACK - *cnt - 1 : *cnt;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (*cnt >= MAX_STACK) {
828c2ecf20Sopenharmony_ci		pr_debug("failed: crossed the max stack value %d\n", MAX_STACK);
838c2ecf20Sopenharmony_ci		return -1;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (!symbol) {
878c2ecf20Sopenharmony_ci		pr_debug("failed: got unresolved address 0x%" PRIx64 "\n",
888c2ecf20Sopenharmony_ci			 entry->ip);
898c2ecf20Sopenharmony_ci		return -1;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	(*cnt)++;
938c2ecf20Sopenharmony_ci	pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n",
948c2ecf20Sopenharmony_ci		 symbol, entry->ip, funcs[idx]);
958c2ecf20Sopenharmony_ci	return strcmp((const char *) symbol, funcs[idx]);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cinoinline int test_dwarf_unwind__thread(struct thread *thread)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct perf_sample sample;
1018c2ecf20Sopenharmony_ci	unsigned long cnt = 0;
1028c2ecf20Sopenharmony_ci	int err = -1;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	memset(&sample, 0, sizeof(sample));
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (test__arch_unwind_sample(&sample, thread)) {
1078c2ecf20Sopenharmony_ci		pr_debug("failed to get unwind sample\n");
1088c2ecf20Sopenharmony_ci		goto out;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	err = unwind__get_entries(unwind_entry, &cnt, thread,
1128c2ecf20Sopenharmony_ci				  &sample, MAX_STACK);
1138c2ecf20Sopenharmony_ci	if (err)
1148c2ecf20Sopenharmony_ci		pr_debug("unwind failed\n");
1158c2ecf20Sopenharmony_ci	else if (cnt != MAX_STACK) {
1168c2ecf20Sopenharmony_ci		pr_debug("got wrong number of stack entries %lu != %d\n",
1178c2ecf20Sopenharmony_ci			 cnt, MAX_STACK);
1188c2ecf20Sopenharmony_ci		err = -1;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci out:
1228c2ecf20Sopenharmony_ci	zfree(&sample.user_stack.data);
1238c2ecf20Sopenharmony_ci	zfree(&sample.user_regs.regs);
1248c2ecf20Sopenharmony_ci	return err;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int global_unwind_retval = -INT_MAX;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cinoinline int test_dwarf_unwind__compare(void *p1, void *p2)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	/* Any possible value should be 'thread' */
1328c2ecf20Sopenharmony_ci	struct thread *thread = *(struct thread **)p1;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (global_unwind_retval == -INT_MAX) {
1358c2ecf20Sopenharmony_ci		/* Call unwinder twice for both callchain orders. */
1368c2ecf20Sopenharmony_ci		callchain_param.order = ORDER_CALLER;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		global_unwind_retval = test_dwarf_unwind__thread(thread);
1398c2ecf20Sopenharmony_ci		if (!global_unwind_retval) {
1408c2ecf20Sopenharmony_ci			callchain_param.order = ORDER_CALLEE;
1418c2ecf20Sopenharmony_ci			global_unwind_retval = test_dwarf_unwind__thread(thread);
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return p1 - p2;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cinoinline int test_dwarf_unwind__krava_3(struct thread *thread)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct thread *array[2] = {thread, thread};
1518c2ecf20Sopenharmony_ci	void *fp = &bsearch;
1528c2ecf20Sopenharmony_ci	/*
1538c2ecf20Sopenharmony_ci	 * make _bsearch a volatile function pointer to
1548c2ecf20Sopenharmony_ci	 * prevent potential optimization, which may expand
1558c2ecf20Sopenharmony_ci	 * bsearch and call compare directly from this function,
1568c2ecf20Sopenharmony_ci	 * instead of libc shared object.
1578c2ecf20Sopenharmony_ci	 */
1588c2ecf20Sopenharmony_ci	void *(*volatile _bsearch)(void *, void *, size_t,
1598c2ecf20Sopenharmony_ci			size_t, int (*)(void *, void *));
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	_bsearch = fp;
1628c2ecf20Sopenharmony_ci	_bsearch(array, &thread, 2, sizeof(struct thread **),
1638c2ecf20Sopenharmony_ci		 test_dwarf_unwind__compare);
1648c2ecf20Sopenharmony_ci	return global_unwind_retval;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cinoinline int test_dwarf_unwind__krava_2(struct thread *thread)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	return test_dwarf_unwind__krava_3(thread);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cinoinline int test_dwarf_unwind__krava_1(struct thread *thread)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	return test_dwarf_unwind__krava_2(thread);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciint test__dwarf_unwind(struct test *test __maybe_unused, int subtest __maybe_unused)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct machine *machine;
1808c2ecf20Sopenharmony_ci	struct thread *thread;
1818c2ecf20Sopenharmony_ci	int err = -1;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	machine = machine__new_host();
1848c2ecf20Sopenharmony_ci	if (!machine) {
1858c2ecf20Sopenharmony_ci		pr_err("Could not get machine\n");
1868c2ecf20Sopenharmony_ci		return -1;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (machine__create_kernel_maps(machine)) {
1908c2ecf20Sopenharmony_ci		pr_err("Failed to create kernel maps\n");
1918c2ecf20Sopenharmony_ci		return -1;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	callchain_param.record_mode = CALLCHAIN_DWARF;
1958c2ecf20Sopenharmony_ci	dwarf_callchain_users = true;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (init_live_machine(machine)) {
1988c2ecf20Sopenharmony_ci		pr_err("Could not init machine\n");
1998c2ecf20Sopenharmony_ci		goto out;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (verbose > 1)
2038c2ecf20Sopenharmony_ci		machine__fprintf(machine, stderr);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	thread = machine__find_thread(machine, getpid(), getpid());
2068c2ecf20Sopenharmony_ci	if (!thread) {
2078c2ecf20Sopenharmony_ci		pr_err("Could not get thread\n");
2088c2ecf20Sopenharmony_ci		goto out;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	err = test_dwarf_unwind__krava_1(thread);
2128c2ecf20Sopenharmony_ci	thread__put(thread);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci out:
2158c2ecf20Sopenharmony_ci	machine__delete_threads(machine);
2168c2ecf20Sopenharmony_ci	machine__delete(machine);
2178c2ecf20Sopenharmony_ci	return err;
2188c2ecf20Sopenharmony_ci}
219