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