diff options
Diffstat (limited to 'tools/perf/arch/arm64')
27 files changed, 1791 insertions, 507 deletions
diff --git a/tools/perf/arch/arm64/Build b/tools/perf/arch/arm64/Build index a7dd46a5b678..12ebc65ea7a3 100644 --- a/tools/perf/arch/arm64/Build +++ b/tools/perf/arch/arm64/Build @@ -1,2 +1,2 @@ -perf-y += util/ -perf-y += tests/ +perf-util-y += util/ +perf-test-y += tests/ diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile index dbef716a1913..087e099fb453 100644 --- a/tools/perf/arch/arm64/Makefile +++ b/tools/perf/arch/arm64/Makefile @@ -1,28 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -ifndef NO_DWARF -PERF_HAVE_DWARF_REGS := 1 -endif PERF_HAVE_JITDUMP := 1 -PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1 - -# -# Syscall table generation for perf -# - -out := $(OUTPUT)arch/arm64/include/generated/asm -header := $(out)/syscalls.c -incpath := $(srctree)/tools -sysdef := $(srctree)/tools/arch/arm64/include/uapi/asm/unistd.h -sysprf := $(srctree)/tools/perf/arch/arm64/entry/syscalls/ -systbl := $(sysprf)/mksyscalltbl - -# Create output directory if not already present -_dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)') - -$(header): $(sysdef) $(systbl) - $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@ - -clean:: - $(call QUIET_CLEAN, arm64) $(RM) $(header) - -archheaders: $(header) +HAVE_KVM_STAT_SUPPORT := 1 diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index 037e292ecd8e..d465d093e7eb 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c @@ -11,7 +11,8 @@ struct arm64_annotate { static int arm64_mov__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, - struct map_symbol *ms __maybe_unused) + struct map_symbol *ms __maybe_unused, + struct disasm_line *dl __maybe_unused) { char *s = strchr(ops->raw, ','), *target, *endptr; @@ -102,7 +103,7 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) if (err) goto out_free_arm; /* b, b.cond, br, cbz/cbnz, tbz/tbnz */ - err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl)?n?z?$", + err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|hs|le|lo|ls|lt|mi|ne|pl|vc|vs)?n?z?$", REG_EXTENDED); if (err) goto out_free_call; @@ -112,6 +113,8 @@ static int arm64__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->associate_instruction_ops = arm64__associate_instruction_ops; arch->objdump.comment_char = '/'; arch->objdump.skip_functions_char = '+'; + arch->e_machine = EM_AARCH64; + arch->e_flags = 0; return 0; out_free_call: diff --git a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl b/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl deleted file mode 100755 index 459469b7222c..000000000000 --- a/tools/perf/arch/arm64/entry/syscalls/mksyscalltbl +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-2.0 -# -# Generate system call table for perf. Derived from -# powerpc script. -# -# Copyright IBM Corp. 2017 -# Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> -# Changed by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com> -# Changed by: Kim Phillips <kim.phillips@arm.com> - -gcc=$1 -hostcc=$2 -incpath=$3 -input=$4 - -if ! test -r $input; then - echo "Could not read input file" >&2 - exit 1 -fi - -create_table_from_c() -{ - local sc nr last_sc - - create_table_exe=`mktemp ${TMPDIR:-/tmp}/create-table-XXXXXX` - - { - - cat <<-_EoHEADER - #include <stdio.h> - #include "$input" - int main(int argc, char *argv[]) - { - _EoHEADER - - while read sc nr; do - printf "%s\n" " printf(\"\\t[%d] = \\\"$sc\\\",\\n\", __NR_$sc);" - last_sc=$sc - done - - printf "%s\n" " printf(\"#define SYSCALLTBL_ARM64_MAX_ID %d\\n\", __NR_$last_sc);" - printf "}\n" - - } | $hostcc -I $incpath/include/uapi -o $create_table_exe -x c - - - $create_table_exe - - rm -f $create_table_exe -} - -create_table() -{ - echo "static const char *syscalltbl_arm64[] = {" - create_table_from_c - echo "};" -} - -$gcc -E -dM -x c -I $incpath/include/uapi $input \ - |sed -ne 's/^#define __NR_//p' \ - |sort -t' ' -k2 -nu \ - |create_table diff --git a/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl b/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl new file mode 100644 index 000000000000..9a37930d4e26 --- /dev/null +++ b/tools/perf/arch/arm64/entry/syscalls/syscall_32.tbl @@ -0,0 +1,476 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# AArch32 (compat) system call definitions. +# +# Copyright (C) 2001-2005 Russell King +# Copyright (C) 2012 ARM Ltd. +# +# This file corresponds to arch/arm/tools/syscall.tbl +# for the native EABI syscalls and should be kept in sync +# Instead of the OABI syscalls, it contains pointers to +# the compat entry points where they differ from the native +# syscalls. +# +0 common restart_syscall sys_restart_syscall +1 common exit sys_exit +2 common fork sys_fork +3 common read sys_read +4 common write sys_write +5 common open sys_open compat_sys_open +6 common close sys_close +# 7 was sys_waitpid +8 common creat sys_creat +9 common link sys_link +10 common unlink sys_unlink +11 common execve sys_execve compat_sys_execve +12 common chdir sys_chdir +# 13 was sys_time +14 common mknod sys_mknod +15 common chmod sys_chmod +16 common lchown sys_lchown16 +# 17 was sys_break +# 18 was sys_stat +19 common lseek sys_lseek compat_sys_lseek +20 common getpid sys_getpid +21 common mount sys_mount +# 22 was sys_umount +23 common setuid sys_setuid16 +24 common getuid sys_getuid16 +# 25 was sys_stime +26 common ptrace sys_ptrace compat_sys_ptrace +# 27 was sys_alarm +# 28 was sys_fstat +29 common pause sys_pause +# 30 was sys_utime +# 31 was sys_stty +# 32 was sys_gtty +33 common access sys_access +34 common nice sys_nice +# 35 was sys_ftime +36 common sync sys_sync +37 common kill sys_kill +38 common rename sys_rename +39 common mkdir sys_mkdir +40 common rmdir sys_rmdir +41 common dup sys_dup +42 common pipe sys_pipe +43 common times sys_times compat_sys_times +# 44 was sys_prof +45 common brk sys_brk +46 common setgid sys_setgid16 +47 common getgid sys_getgid16 +# 48 was sys_signal +49 common geteuid sys_geteuid16 +50 common getegid sys_getegid16 +51 common acct sys_acct +52 common umount2 sys_umount +# 53 was sys_lock +54 common ioctl sys_ioctl compat_sys_ioctl +55 common fcntl sys_fcntl compat_sys_fcntl +# 56 was sys_mpx +57 common setpgid sys_setpgid +# 58 was sys_ulimit +# 59 was sys_olduname +60 common umask sys_umask +61 common chroot sys_chroot +62 common ustat sys_ustat compat_sys_ustat +63 common dup2 sys_dup2 +64 common getppid sys_getppid +65 common getpgrp sys_getpgrp +66 common setsid sys_setsid +67 common sigaction sys_sigaction compat_sys_sigaction +# 68 was sys_sgetmask +# 69 was sys_ssetmask +70 common setreuid sys_setreuid16 +71 common setregid sys_setregid16 +72 common sigsuspend sys_sigsuspend +73 common sigpending sys_sigpending compat_sys_sigpending +74 common sethostname sys_sethostname +75 common setrlimit sys_setrlimit compat_sys_setrlimit +# 76 was compat_sys_getrlimit +77 common getrusage sys_getrusage compat_sys_getrusage +78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 common settimeofday sys_settimeofday compat_sys_settimeofday +80 common getgroups sys_getgroups16 +81 common setgroups sys_setgroups16 +# 82 was compat_sys_select +83 common symlink sys_symlink +# 84 was sys_lstat +85 common readlink sys_readlink +86 common uselib sys_uselib +87 common swapon sys_swapon +88 common reboot sys_reboot +# 89 was sys_readdir +# 90 was sys_mmap +91 common munmap sys_munmap +92 common truncate sys_truncate compat_sys_truncate +93 common ftruncate sys_ftruncate compat_sys_ftruncate +94 common fchmod sys_fchmod +95 common fchown sys_fchown16 +96 common getpriority sys_getpriority +97 common setpriority sys_setpriority +# 98 was sys_profil +99 common statfs sys_statfs compat_sys_statfs +100 common fstatfs sys_fstatfs compat_sys_fstatfs +# 101 was sys_ioperm +# 102 was sys_socketcall +103 common syslog sys_syslog +104 common setitimer sys_setitimer compat_sys_setitimer +105 common getitimer sys_getitimer compat_sys_getitimer +106 common stat sys_newstat compat_sys_newstat +107 common lstat sys_newlstat compat_sys_newlstat +108 common fstat sys_newfstat compat_sys_newfstat +# 109 was sys_uname +# 110 was sys_iopl +111 common vhangup sys_vhangup +# 112 was sys_idle +# 113 was sys_syscall +114 common wait4 sys_wait4 compat_sys_wait4 +115 common swapoff sys_swapoff +116 common sysinfo sys_sysinfo compat_sys_sysinfo +# 117 was sys_ipc +118 common fsync sys_fsync +119 common sigreturn sys_sigreturn_wrapper compat_sys_sigreturn +120 common clone sys_clone +121 common setdomainname sys_setdomainname +122 common uname sys_newuname +# 123 was sys_modify_ldt +124 common adjtimex sys_adjtimex_time32 +125 common mprotect sys_mprotect +126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask +# 127 was sys_create_module +128 common init_module sys_init_module +129 common delete_module sys_delete_module +# 130 was sys_get_kernel_syms +131 common quotactl sys_quotactl +132 common getpgid sys_getpgid +133 common fchdir sys_fchdir +134 common bdflush sys_ni_syscall +135 common sysfs sys_sysfs +136 common personality sys_personality +# 137 was sys_afs_syscall +138 common setfsuid sys_setfsuid16 +139 common setfsgid sys_setfsgid16 +140 common _llseek sys_llseek +141 common getdents sys_getdents compat_sys_getdents +142 common _newselect sys_select compat_sys_select +143 common flock sys_flock +144 common msync sys_msync +145 common readv sys_readv +146 common writev sys_writev +147 common getsid sys_getsid +148 common fdatasync sys_fdatasync +149 common _sysctl sys_ni_syscall +150 common mlock sys_mlock +151 common munlock sys_munlock +152 common mlockall sys_mlockall +153 common munlockall sys_munlockall +154 common sched_setparam sys_sched_setparam +155 common sched_getparam sys_sched_getparam +156 common sched_setscheduler sys_sched_setscheduler +157 common sched_getscheduler sys_sched_getscheduler +158 common sched_yield sys_sched_yield +159 common sched_get_priority_max sys_sched_get_priority_max +160 common sched_get_priority_min sys_sched_get_priority_min +161 common sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 common nanosleep sys_nanosleep_time32 +163 common mremap sys_mremap +164 common setresuid sys_setresuid16 +165 common getresuid sys_getresuid16 +# 166 was sys_vm86 +# 167 was sys_query_module +168 common poll sys_poll +169 common nfsservctl sys_ni_syscall +170 common setresgid sys_setresgid16 +171 common getresgid sys_getresgid16 +172 common prctl sys_prctl +173 common rt_sigreturn sys_rt_sigreturn_wrapper compat_sys_rt_sigreturn +174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +177 common rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +180 common pread64 sys_pread64 compat_sys_aarch32_pread64 +181 common pwrite64 sys_pwrite64 compat_sys_aarch32_pwrite64 +182 common chown sys_chown16 +183 common getcwd sys_getcwd +184 common capget sys_capget +185 common capset sys_capset +186 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +187 common sendfile sys_sendfile compat_sys_sendfile +# 188 reserved +# 189 reserved +190 common vfork sys_vfork +# SuS compliant getrlimit +191 common ugetrlimit sys_getrlimit compat_sys_getrlimit +192 common mmap2 sys_mmap2 compat_sys_aarch32_mmap2 +193 common truncate64 sys_truncate64 compat_sys_aarch32_truncate64 +194 common ftruncate64 sys_ftruncate64 compat_sys_aarch32_ftruncate64 +195 common stat64 sys_stat64 +196 common lstat64 sys_lstat64 +197 common fstat64 sys_fstat64 +198 common lchown32 sys_lchown +199 common getuid32 sys_getuid +200 common getgid32 sys_getgid +201 common geteuid32 sys_geteuid +202 common getegid32 sys_getegid +203 common setreuid32 sys_setreuid +204 common setregid32 sys_setregid +205 common getgroups32 sys_getgroups +206 common setgroups32 sys_setgroups +207 common fchown32 sys_fchown +208 common setresuid32 sys_setresuid +209 common getresuid32 sys_getresuid +210 common setresgid32 sys_setresgid +211 common getresgid32 sys_getresgid +212 common chown32 sys_chown +213 common setuid32 sys_setuid +214 common setgid32 sys_setgid +215 common setfsuid32 sys_setfsuid +216 common setfsgid32 sys_setfsgid +217 common getdents64 sys_getdents64 +218 common pivot_root sys_pivot_root +219 common mincore sys_mincore +220 common madvise sys_madvise +221 common fcntl64 sys_fcntl64 compat_sys_fcntl64 +# 222 for tux +# 223 is unused +224 common gettid sys_gettid +225 common readahead sys_readahead compat_sys_aarch32_readahead +226 common setxattr sys_setxattr +227 common lsetxattr sys_lsetxattr +228 common fsetxattr sys_fsetxattr +229 common getxattr sys_getxattr +230 common lgetxattr sys_lgetxattr +231 common fgetxattr sys_fgetxattr +232 common listxattr sys_listxattr +233 common llistxattr sys_llistxattr +234 common flistxattr sys_flistxattr +235 common removexattr sys_removexattr +236 common lremovexattr sys_lremovexattr +237 common fremovexattr sys_fremovexattr +238 common tkill sys_tkill +239 common sendfile64 sys_sendfile64 +240 common futex sys_futex_time32 +241 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +242 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +243 common io_setup sys_io_setup compat_sys_io_setup +244 common io_destroy sys_io_destroy +245 common io_getevents sys_io_getevents_time32 +246 common io_submit sys_io_submit compat_sys_io_submit +247 common io_cancel sys_io_cancel +248 common exit_group sys_exit_group +249 common lookup_dcookie sys_ni_syscall +250 common epoll_create sys_epoll_create +251 common epoll_ctl sys_epoll_ctl +252 common epoll_wait sys_epoll_wait +253 common remap_file_pages sys_remap_file_pages +# 254 for set_thread_area +# 255 for get_thread_area +256 common set_tid_address sys_set_tid_address +257 common timer_create sys_timer_create compat_sys_timer_create +258 common timer_settime sys_timer_settime32 +259 common timer_gettime sys_timer_gettime32 +260 common timer_getoverrun sys_timer_getoverrun +261 common timer_delete sys_timer_delete +262 common clock_settime sys_clock_settime32 +263 common clock_gettime sys_clock_gettime32 +264 common clock_getres sys_clock_getres_time32 +265 common clock_nanosleep sys_clock_nanosleep_time32 +266 common statfs64 sys_statfs64_wrapper compat_sys_aarch32_statfs64 +267 common fstatfs64 sys_fstatfs64_wrapper compat_sys_aarch32_fstatfs64 +268 common tgkill sys_tgkill +269 common utimes sys_utimes_time32 +270 common arm_fadvise64_64 sys_arm_fadvise64_64 compat_sys_aarch32_fadvise64_64 +271 common pciconfig_iobase sys_pciconfig_iobase +272 common pciconfig_read sys_pciconfig_read +273 common pciconfig_write sys_pciconfig_write +274 common mq_open sys_mq_open compat_sys_mq_open +275 common mq_unlink sys_mq_unlink +276 common mq_timedsend sys_mq_timedsend_time32 +277 common mq_timedreceive sys_mq_timedreceive_time32 +278 common mq_notify sys_mq_notify compat_sys_mq_notify +279 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +280 common waitid sys_waitid compat_sys_waitid +281 common socket sys_socket +282 common bind sys_bind +283 common connect sys_connect +284 common listen sys_listen +285 common accept sys_accept +286 common getsockname sys_getsockname +287 common getpeername sys_getpeername +288 common socketpair sys_socketpair +289 common send sys_send +290 common sendto sys_sendto +291 common recv sys_recv compat_sys_recv +292 common recvfrom sys_recvfrom compat_sys_recvfrom +293 common shutdown sys_shutdown +294 common setsockopt sys_setsockopt +295 common getsockopt sys_getsockopt +296 common sendmsg sys_sendmsg compat_sys_sendmsg +297 common recvmsg sys_recvmsg compat_sys_recvmsg +298 common semop sys_semop +299 common semget sys_semget +300 common semctl sys_old_semctl compat_sys_old_semctl +301 common msgsnd sys_msgsnd compat_sys_msgsnd +302 common msgrcv sys_msgrcv compat_sys_msgrcv +303 common msgget sys_msgget +304 common msgctl sys_old_msgctl compat_sys_old_msgctl +305 common shmat sys_shmat compat_sys_shmat +306 common shmdt sys_shmdt +307 common shmget sys_shmget +308 common shmctl sys_old_shmctl compat_sys_old_shmctl +309 common add_key sys_add_key +310 common request_key sys_request_key +311 common keyctl sys_keyctl compat_sys_keyctl +312 common semtimedop sys_semtimedop_time32 +313 common vserver sys_ni_syscall +314 common ioprio_set sys_ioprio_set +315 common ioprio_get sys_ioprio_get +316 common inotify_init sys_inotify_init +317 common inotify_add_watch sys_inotify_add_watch +318 common inotify_rm_watch sys_inotify_rm_watch +319 common mbind sys_mbind +320 common get_mempolicy sys_get_mempolicy +321 common set_mempolicy sys_set_mempolicy +322 common openat sys_openat compat_sys_openat +323 common mkdirat sys_mkdirat +324 common mknodat sys_mknodat +325 common fchownat sys_fchownat +326 common futimesat sys_futimesat_time32 +327 common fstatat64 sys_fstatat64 +328 common unlinkat sys_unlinkat +329 common renameat sys_renameat +330 common linkat sys_linkat +331 common symlinkat sys_symlinkat +332 common readlinkat sys_readlinkat +333 common fchmodat sys_fchmodat +334 common faccessat sys_faccessat +335 common pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +336 common ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +337 common unshare sys_unshare +338 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +339 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +340 common splice sys_splice +341 common arm_sync_file_range sys_sync_file_range2 compat_sys_aarch32_sync_file_range2 +342 common tee sys_tee +343 common vmsplice sys_vmsplice +344 common move_pages sys_move_pages +345 common getcpu sys_getcpu +346 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +347 common kexec_load sys_kexec_load compat_sys_kexec_load +348 common utimensat sys_utimensat_time32 +349 common signalfd sys_signalfd compat_sys_signalfd +350 common timerfd_create sys_timerfd_create +351 common eventfd sys_eventfd +352 common fallocate sys_fallocate compat_sys_aarch32_fallocate +353 common timerfd_settime sys_timerfd_settime32 +354 common timerfd_gettime sys_timerfd_gettime32 +355 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +356 common eventfd2 sys_eventfd2 +357 common epoll_create1 sys_epoll_create1 +358 common dup3 sys_dup3 +359 common pipe2 sys_pipe2 +360 common inotify_init1 sys_inotify_init1 +361 common preadv sys_preadv compat_sys_preadv +362 common pwritev sys_pwritev compat_sys_pwritev +363 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +364 common perf_event_open sys_perf_event_open +365 common recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +366 common accept4 sys_accept4 +367 common fanotify_init sys_fanotify_init +368 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +369 common prlimit64 sys_prlimit64 +370 common name_to_handle_at sys_name_to_handle_at +371 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +372 common clock_adjtime sys_clock_adjtime32 +373 common syncfs sys_syncfs +374 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +375 common setns sys_setns +376 common process_vm_readv sys_process_vm_readv +377 common process_vm_writev sys_process_vm_writev +378 common kcmp sys_kcmp +379 common finit_module sys_finit_module +380 common sched_setattr sys_sched_setattr +381 common sched_getattr sys_sched_getattr +382 common renameat2 sys_renameat2 +383 common seccomp sys_seccomp +384 common getrandom sys_getrandom +385 common memfd_create sys_memfd_create +386 common bpf sys_bpf +387 common execveat sys_execveat compat_sys_execveat +388 common userfaultfd sys_userfaultfd +389 common membarrier sys_membarrier +390 common mlock2 sys_mlock2 +391 common copy_file_range sys_copy_file_range +392 common preadv2 sys_preadv2 compat_sys_preadv2 +393 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +394 common pkey_mprotect sys_pkey_mprotect +395 common pkey_alloc sys_pkey_alloc +396 common pkey_free sys_pkey_free +397 common statx sys_statx +398 common rseq sys_rseq +399 common io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +400 common migrate_pages sys_migrate_pages +401 common kexec_file_load sys_kexec_file_load +# 402 is unused +403 common clock_gettime64 sys_clock_gettime +404 common clock_settime64 sys_clock_settime +405 common clock_adjtime64 sys_clock_adjtime +406 common clock_getres_time64 sys_clock_getres +407 common clock_nanosleep_time64 sys_clock_nanosleep +408 common timer_gettime64 sys_timer_gettime +409 common timer_settime64 sys_timer_settime +410 common timerfd_gettime64 sys_timerfd_gettime +411 common timerfd_settime64 sys_timerfd_settime +412 common utimensat_time64 sys_utimensat +413 common pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 common ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 common io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 common recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 common mq_timedsend_time64 sys_mq_timedsend +419 common mq_timedreceive_time64 sys_mq_timedreceive +420 common semtimedop_time64 sys_semtimedop +421 common rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 common futex_time64 sys_futex +423 common sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/tools/perf/arch/arm64/entry/syscalls/syscall_64.tbl b/tools/perf/arch/arm64/entry/syscalls/syscall_64.tbl new file mode 120000 index 000000000000..4fdd58f10c15 --- /dev/null +++ b/tools/perf/arch/arm64/entry/syscalls/syscall_64.tbl @@ -0,0 +1 @@ +../../../../../scripts/syscall.tbl
\ No newline at end of file diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h index 90ec4c8cb880..474d7cf5afbd 100644 --- a/tools/perf/arch/arm64/include/arch-tests.h +++ b/tools/perf/arch/arm64/include/arch-tests.h @@ -2,11 +2,9 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H -#ifdef HAVE_DWARF_UNWIND_SUPPORT -struct thread; -struct perf_sample; -#endif +struct test_suite; -extern struct test arch_tests[]; +int test__cpuid_match(struct test_suite *test, int subtest); +extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index baaa5e64a3fb..58639ee9f7ea 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -4,7 +4,9 @@ #include <stdlib.h> #include <linux/types.h> +#define perf_event_arm_regs perf_event_arm64_regs #include <asm/perf_regs.h> +#undef perf_event_arm_regs void perf_regs_load(u64 *regs); @@ -12,83 +14,4 @@ void perf_regs_load(u64 *regs); #define PERF_REGS_MAX PERF_REG_ARM64_MAX #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64 -#define PERF_REG_IP PERF_REG_ARM64_PC -#define PERF_REG_SP PERF_REG_ARM64_SP - -static inline const char *perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_ARM64_X0: - return "x0"; - case PERF_REG_ARM64_X1: - return "x1"; - case PERF_REG_ARM64_X2: - return "x2"; - case PERF_REG_ARM64_X3: - return "x3"; - case PERF_REG_ARM64_X4: - return "x4"; - case PERF_REG_ARM64_X5: - return "x5"; - case PERF_REG_ARM64_X6: - return "x6"; - case PERF_REG_ARM64_X7: - return "x7"; - case PERF_REG_ARM64_X8: - return "x8"; - case PERF_REG_ARM64_X9: - return "x9"; - case PERF_REG_ARM64_X10: - return "x10"; - case PERF_REG_ARM64_X11: - return "x11"; - case PERF_REG_ARM64_X12: - return "x12"; - case PERF_REG_ARM64_X13: - return "x13"; - case PERF_REG_ARM64_X14: - return "x14"; - case PERF_REG_ARM64_X15: - return "x15"; - case PERF_REG_ARM64_X16: - return "x16"; - case PERF_REG_ARM64_X17: - return "x17"; - case PERF_REG_ARM64_X18: - return "x18"; - case PERF_REG_ARM64_X19: - return "x19"; - case PERF_REG_ARM64_X20: - return "x20"; - case PERF_REG_ARM64_X21: - return "x21"; - case PERF_REG_ARM64_X22: - return "x22"; - case PERF_REG_ARM64_X23: - return "x23"; - case PERF_REG_ARM64_X24: - return "x24"; - case PERF_REG_ARM64_X25: - return "x25"; - case PERF_REG_ARM64_X26: - return "x26"; - case PERF_REG_ARM64_X27: - return "x27"; - case PERF_REG_ARM64_X28: - return "x28"; - case PERF_REG_ARM64_X29: - return "x29"; - case PERF_REG_ARM64_SP: - return "sp"; - case PERF_REG_ARM64_LR: - return "lr"; - case PERF_REG_ARM64_PC: - return "pc"; - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build index a61c06bdb757..d44c9de92d42 100644 --- a/tools/perf/arch/arm64/tests/Build +++ b/tools/perf/arch/arm64/tests/Build @@ -1,4 +1,5 @@ -perf-y += regs_load.o -perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o +perf-test-y += regs_load.o +perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o -perf-y += arch-tests.o +perf-test-y += arch-tests.o +perf-test-y += cpuid-match.o diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c index 5b1543c98022..74932e72c727 100644 --- a/tools/perf/arch/arm64/tests/arch-tests.c +++ b/tools/perf/arch/arm64/tests/arch-tests.c @@ -3,14 +3,13 @@ #include "tests/tests.h" #include "arch-tests.h" -struct test arch_tests[] = { + +DEFINE_SUITE("arm64 CPUID matching", cpuid_match); + +struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT - { - .desc = "DWARF unwind", - .func = test__dwarf_unwind, - }, + &suite__dwarf_unwind, #endif - { - .func = NULL, - }, + &suite__cpuid_match, + NULL, }; diff --git a/tools/perf/arch/arm64/tests/cpuid-match.c b/tools/perf/arch/arm64/tests/cpuid-match.c new file mode 100644 index 000000000000..e8e3947cca18 --- /dev/null +++ b/tools/perf/arch/arm64/tests/cpuid-match.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/compiler.h> + +#include "arch-tests.h" +#include "tests/tests.h" +#include "util/header.h" + +int test__cpuid_match(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + /* midr with no leading zeros matches */ + if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410fd0c0")) + return -1; + /* Upper case matches */ + if (strcmp_cpuid_str("0x410fd0c0", "0x00000000410FD0C0")) + return -1; + /* r0p0 = r0p0 matches */ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd480")) + return -1; + /* r0p1 > r0p0 matches */ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000410fd481")) + return -1; + /* r1p0 > r0p0 matches*/ + if (strcmp_cpuid_str("0x00000000410fd480", "0x00000000411fd480")) + return -1; + /* r0p0 < r0p1 doesn't match */ + if (!strcmp_cpuid_str("0x00000000410fd481", "0x00000000410fd480")) + return -1; + /* r0p0 < r1p0 doesn't match */ + if (!strcmp_cpuid_str("0x00000000411fd480", "0x00000000410fd480")) + return -1; + /* Different CPU doesn't match */ + if (!strcmp_cpuid_str("0x00000000410fd4c0", "0x00000000430f0af0")) + return -1; + + return 0; +} diff --git a/tools/perf/arch/arm64/tests/dwarf-unwind.c b/tools/perf/arch/arm64/tests/dwarf-unwind.c index 46147a483049..440d00f0de14 100644 --- a/tools/perf/arch/arm64/tests/dwarf-unwind.c +++ b/tools/perf/arch/arm64/tests/dwarf-unwind.c @@ -26,14 +26,14 @@ static int sample_ustack(struct perf_sample *sample, sp = (unsigned long) regs[PERF_REG_ARM64_SP]; - map = maps__find(thread->maps, (u64)sp); + map = maps__find(thread__maps(thread), (u64)sp); if (!map) { pr_debug("failed to get stack map\n"); free(buf); return -1; } - stack_size = map->end - sp; + stack_size = map__end(map) - sp; stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size; memcpy(buf, (void *) sp, stack_size); @@ -45,7 +45,7 @@ static int sample_ustack(struct perf_sample *sample, int test__arch_unwind_sample(struct perf_sample *sample, struct thread *thread) { - struct regs_dump *regs = &sample->user_regs; + struct regs_dump *regs = perf_sample__user_regs(sample); u64 *buf; buf = calloc(1, sizeof(u64) * PERF_REGS_MAX); diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index 5c13438c7bd4..a74521b79eaa 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -1,11 +1,13 @@ -perf-y += header.o -perf-y += machine.o -perf-y += perf_regs.o -perf-$(CONFIG_DWARF) += dwarf-regs.o -perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o -perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o +perf-util-y += header.o +perf-util-y += machine.o +perf-util-y += perf_regs.o +perf-util-y += tsc.o +perf-util-y += pmu.o +perf-util-$(CONFIG_LIBTRACEEVENT) += kvm-stat.o +perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o +perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o -perf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \ +perf-util-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \ ../../arm/util/auxtrace.o \ ../../arm/util/cs-etm.o \ - arm-spe.o + arm-spe.o mem-events.o hisi-ptt.o diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index e3593063b3d1..4f2833b62ff5 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -8,12 +8,14 @@ #include <linux/types.h> #include <linux/bitops.h> #include <linux/log2.h> +#include <linux/string.h> #include <linux/zalloc.h> #include <time.h> #include "../../../util/cpumap.h" #include "../../../util/event.h" #include "../../../util/evsel.h" +#include "../../../util/evsel_config.h" #include "../../../util/evlist.h" #include "../../../util/session.h" #include <internal/lib.h> // page_size @@ -21,7 +23,11 @@ #include "../../../util/debug.h" #include "../../../util/auxtrace.h" #include "../../../util/record.h" +#include "../../../util/header.h" #include "../../../util/arm-spe.h" +#include <tools/libc_compat.h> // reallocarray + +#define ARM_SPE_CPU_MAGIC 0x1010101010101010ULL #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) @@ -30,13 +36,101 @@ struct arm_spe_recording { struct auxtrace_record itr; struct perf_pmu *arm_spe_pmu; struct evlist *evlist; + int wrapped_cnt; + bool *wrapped; }; +/* Iterate config list to detect if the "freq" parameter is set */ +static bool arm_spe_is_set_freq(struct evsel *evsel) +{ + struct evsel_config_term *term; + + list_for_each_entry(term, &evsel->config_terms, list) { + if (term->type == EVSEL__CONFIG_TERM_FREQ) + return true; + } + + return false; +} + +/* + * arm_spe_find_cpus() returns a new cpu map, and the caller should invoke + * perf_cpu_map__put() to release the map after use. + */ +static struct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist) +{ + struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus; + struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus(); + struct perf_cpu_map *intersect_cpus; + + /* cpu map is not "any" CPU , we have specific CPUs to work with */ + if (!perf_cpu_map__has_any_cpu(event_cpus)) { + intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus); + perf_cpu_map__put(online_cpus); + /* Event can be "any" CPU so count all CPUs. */ + } else { + intersect_cpus = online_cpus; + } + + return intersect_cpus; +} + static size_t arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused, - struct evlist *evlist __maybe_unused) + struct evlist *evlist) +{ + struct perf_cpu_map *cpu_map = arm_spe_find_cpus(evlist); + size_t size; + + if (!cpu_map) + return 0; + + size = ARM_SPE_AUXTRACE_PRIV_MAX + + ARM_SPE_CPU_PRIV_MAX * perf_cpu_map__nr(cpu_map); + size *= sizeof(u64); + + perf_cpu_map__put(cpu_map); + return size; +} + +static int arm_spe_save_cpu_header(struct auxtrace_record *itr, + struct perf_cpu cpu, __u64 data[]) { - return ARM_SPE_AUXTRACE_PRIV_SIZE; + struct arm_spe_recording *sper = + container_of(itr, struct arm_spe_recording, itr); + struct perf_pmu *pmu = NULL; + char *cpuid = NULL; + u64 val; + + /* Read CPU MIDR */ + cpuid = get_cpuid_allow_env_override(cpu); + if (!cpuid) + return -ENOMEM; + val = strtol(cpuid, NULL, 16); + + data[ARM_SPE_MAGIC] = ARM_SPE_CPU_MAGIC; + data[ARM_SPE_CPU] = cpu.cpu; + data[ARM_SPE_CPU_NR_PARAMS] = ARM_SPE_CPU_PRIV_MAX - ARM_SPE_CPU_MIDR; + data[ARM_SPE_CPU_MIDR] = val; + + /* Find the associate Arm SPE PMU for the CPU */ + if (perf_cpu_map__has(sper->arm_spe_pmu->cpus, cpu)) + pmu = sper->arm_spe_pmu; + + if (!pmu) { + /* No Arm SPE PMU is found */ + data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX; + data[ARM_SPE_CAP_MIN_IVAL] = 0; + } else { + data[ARM_SPE_CPU_PMU_TYPE] = pmu->type; + + if (perf_pmu__scan_file(pmu, "caps/min_interval", "%lu", &val) != 1) + val = 0; + data[ARM_SPE_CAP_MIN_IVAL] = val; + } + + free(cpuid); + return ARM_SPE_CPU_PRIV_MAX; } static int arm_spe_info_fill(struct auxtrace_record *itr, @@ -44,54 +138,194 @@ static int arm_spe_info_fill(struct auxtrace_record *itr, struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size) { + int i, ret; + size_t offset; struct arm_spe_recording *sper = container_of(itr, struct arm_spe_recording, itr); struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; + struct perf_cpu_map *cpu_map; + struct perf_cpu cpu; + __u64 *data; - if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE) + if (priv_size != arm_spe_info_priv_size(itr, session->evlist)) return -EINVAL; if (!session->evlist->core.nr_mmaps) return -EINVAL; + cpu_map = arm_spe_find_cpus(session->evlist); + if (!cpu_map) + return -EINVAL; + auxtrace_info->type = PERF_AUXTRACE_ARM_SPE; - auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type; + auxtrace_info->priv[ARM_SPE_HEADER_VERSION] = ARM_SPE_HEADER_CURRENT_VERSION; + auxtrace_info->priv[ARM_SPE_HEADER_SIZE] = + ARM_SPE_AUXTRACE_PRIV_MAX - ARM_SPE_HEADER_VERSION; + auxtrace_info->priv[ARM_SPE_PMU_TYPE_V2] = arm_spe_pmu->type; + auxtrace_info->priv[ARM_SPE_CPUS_NUM] = perf_cpu_map__nr(cpu_map); + + offset = ARM_SPE_AUXTRACE_PRIV_MAX; + perf_cpu_map__for_each_cpu(cpu, i, cpu_map) { + assert(offset < priv_size); + data = &auxtrace_info->priv[offset]; + ret = arm_spe_save_cpu_header(itr, cpu, data); + if (ret < 0) + goto out; + offset += ret; + } - return 0; + ret = 0; +out: + perf_cpu_map__put(cpu_map); + return ret; } -static int arm_spe_recording_options(struct auxtrace_record *itr, - struct evlist *evlist, - struct record_opts *opts) +static void +arm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts, + bool privileged) +{ + /* + * The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor + * snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for + * unprivileged users. + * + * The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for + * unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages + * will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the + * user is likely to get an error as they exceed their mlock limmit. + */ + + /* + * No size were given to '-S' or '-m,', so go with the default + */ + if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { + if (privileged) { + opts->auxtrace_mmap_pages = MiB(4) / page_size; + } else { + opts->auxtrace_mmap_pages = KiB(128) / page_size; + if (opts->mmap_pages == UINT_MAX) + opts->mmap_pages = KiB(256) / page_size; + } + } else if (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) { + opts->mmap_pages = KiB(256) / page_size; + } + + /* + * '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the + * auxtrace mmap area. + */ + if (!opts->auxtrace_snapshot_size) + opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size; + + /* + * '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big + * enough to fit the requested snapshot size. + */ + if (!opts->auxtrace_mmap_pages) { + size_t sz = opts->auxtrace_snapshot_size; + + sz = round_up(sz, page_size) / page_size; + opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); + } +} + +static __u64 arm_spe_pmu__sample_period(const struct perf_pmu *arm_spe_pmu) +{ + static __u64 sample_period; + + if (sample_period) + return sample_period; + + /* + * If kernel driver doesn't advertise a minimum, + * use max allowable by PMSIDR_EL1.INTERVAL + */ + if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", + &sample_period) != 1) { + pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); + sample_period = 4096; + } + return sample_period; +} + +static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus) +{ + u64 bit; + + evsel->core.attr.freq = 0; + evsel->core.attr.sample_period = arm_spe_pmu__sample_period(evsel->pmu); + evsel->needs_auxtrace_mmap = true; + + /* + * To obtain the auxtrace buffer file descriptor, the auxtrace event + * must come first. + */ + evlist__to_front(evsel->evlist, evsel); + + /* + * In the case of per-cpu mmaps, sample CPU for AUX event; + * also enable the timestamp tracing for samples correlation. + */ + if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) { + evsel__set_sample_bit(evsel, CPU); + evsel__set_config_if_unset(evsel->pmu, evsel, "ts_enable", 1); + } + + /* + * Set this only so that perf report knows that SPE generates memory info. It has no effect + * on the opening of the event or the SPE data produced. + */ + evsel__set_sample_bit(evsel, DATA_SRC); + + /* + * The PHYS_ADDR flag does not affect the driver behaviour, it is used to + * inform that the resulting output's SPE samples contain physical addresses + * where applicable. + */ + bit = perf_pmu__format_bits(evsel->pmu, "pa_enable"); + if (evsel->core.attr.config & bit) + evsel__set_sample_bit(evsel, PHYS_ADDR); +} + +static int arm_spe_setup_aux_buffer(struct record_opts *opts) { - struct arm_spe_recording *sper = - container_of(itr, struct arm_spe_recording, itr); - struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; - struct evsel *evsel, *arm_spe_evsel = NULL; bool privileged = perf_event_paranoid_check(-1); - struct evsel *tracking_evsel; - int err; - sper->evlist = evlist; + /* + * we are in snapshot mode. + */ + if (opts->auxtrace_snapshot_mode) { + /* + * Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with + * default values. + */ + if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) + arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged); + + /* + * Snapshot size can't be bigger than the auxtrace area. + */ + if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) { + pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", + opts->auxtrace_snapshot_size, + opts->auxtrace_mmap_pages * (size_t)page_size); + return -EINVAL; + } - evlist__for_each_entry(evlist, evsel) { - if (evsel->core.attr.type == arm_spe_pmu->type) { - if (arm_spe_evsel) { - pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n"); - return -EINVAL; - } - evsel->core.attr.freq = 0; - evsel->core.attr.sample_period = 1; - arm_spe_evsel = evsel; - opts->full_auxtrace = true; + /* + * Something went wrong somewhere - this shouldn't happen. + */ + if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) { + pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); + return -EINVAL; } - } - if (!opts->full_auxtrace) - return 0; + pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME, + opts->auxtrace_snapshot_size); + } /* We are in full trace mode but '-m,xyz' wasn't specified */ - if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { + if (!opts->auxtrace_mmap_pages) { if (privileged) { opts->auxtrace_mmap_pages = MiB(4) / page_size; } else { @@ -113,30 +347,283 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, } } + return 0; +} - /* - * To obtain the auxtrace buffer file descriptor, the auxtrace event - * must come first. - */ - perf_evlist__to_front(evlist, arm_spe_evsel); - - evsel__set_sample_bit(arm_spe_evsel, CPU); - evsel__set_sample_bit(arm_spe_evsel, TIME); - evsel__set_sample_bit(arm_spe_evsel, TID); +static int arm_spe_setup_tracking_event(struct evlist *evlist, + struct record_opts *opts) +{ + int err; + struct evsel *tracking_evsel; + struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; /* Add dummy event to keep tracking */ - err = parse_events(evlist, "dummy:u", NULL); + err = parse_event(evlist, "dummy:u"); if (err) return err; tracking_evsel = evlist__last(evlist); - perf_evlist__set_tracking_event(evlist, tracking_evsel); + evlist__set_tracking_event(evlist, tracking_evsel); tracking_evsel->core.attr.freq = 0; tracking_evsel->core.attr.sample_period = 1; - evsel__set_sample_bit(tracking_evsel, TIME); - evsel__set_sample_bit(tracking_evsel, CPU); - evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK); + + /* In per-cpu case, always need the time of mmap events etc */ + if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) { + evsel__set_sample_bit(tracking_evsel, TIME); + evsel__set_sample_bit(tracking_evsel, CPU); + + /* also track task context switch */ + if (!record_opts__no_switch_events(opts)) + tracking_evsel->core.attr.context_switch = 1; + } + + return 0; +} + +static int arm_spe_recording_options(struct auxtrace_record *itr, + struct evlist *evlist, + struct record_opts *opts) +{ + struct arm_spe_recording *sper = + container_of(itr, struct arm_spe_recording, itr); + struct evsel *evsel, *tmp; + struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; + bool discard = false; + int err; + + sper->evlist = evlist; + + evlist__for_each_entry(evlist, evsel) { + if (evsel__is_aux_event(evsel)) { + if (!strstarts(evsel->pmu->name, ARM_SPE_PMU_NAME)) { + pr_err("Found unexpected auxtrace event: %s\n", + evsel->pmu->name); + return -EINVAL; + } + opts->full_auxtrace = true; + + if (opts->user_freq != UINT_MAX || + arm_spe_is_set_freq(evsel)) { + pr_err("Arm SPE: Frequency is not supported. " + "Set period with -c option or PMU parameter (-e %s/period=NUM/).\n", + evsel->pmu->name); + return -EINVAL; + } + } + } + + if (!opts->full_auxtrace) + return 0; + + evlist__for_each_entry_safe(evlist, tmp, evsel) { + if (evsel__is_aux_event(evsel)) { + arm_spe_setup_evsel(evsel, cpus); + if (evsel->core.attr.config & + perf_pmu__format_bits(evsel->pmu, "discard")) + discard = true; + } + } + + if (discard) + return 0; + + err = arm_spe_setup_aux_buffer(opts); + if (err) + return err; + + return arm_spe_setup_tracking_event(evlist, opts); +} + +static int arm_spe_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused, + struct record_opts *opts, + const char *str) +{ + unsigned long long snapshot_size = 0; + char *endptr; + + if (str) { + snapshot_size = strtoull(str, &endptr, 0); + if (*endptr || snapshot_size > SIZE_MAX) + return -1; + } + + opts->auxtrace_snapshot_mode = true; + opts->auxtrace_snapshot_size = snapshot_size; + + return 0; +} + +static int arm_spe_snapshot_start(struct auxtrace_record *itr) +{ + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + struct evsel *evsel; + int ret = -EINVAL; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel__is_aux_event(evsel)) { + ret = evsel__disable(evsel); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int arm_spe_snapshot_finish(struct auxtrace_record *itr) +{ + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + struct evsel *evsel; + int ret = -EINVAL; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel__is_aux_event(evsel)) { + ret = evsel__enable(evsel); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int arm_spe_alloc_wrapped_array(struct arm_spe_recording *ptr, int idx) +{ + bool *wrapped; + int cnt = ptr->wrapped_cnt, new_cnt, i; + + /* + * No need to allocate, so return early. + */ + if (idx < cnt) + return 0; + + /* + * Make ptr->wrapped as big as idx. + */ + new_cnt = idx + 1; + + /* + * Free'ed in arm_spe_recording_free(). + */ + wrapped = reallocarray(ptr->wrapped, new_cnt, sizeof(bool)); + if (!wrapped) + return -ENOMEM; + + /* + * init new allocated values. + */ + for (i = cnt; i < new_cnt; i++) + wrapped[i] = false; + + ptr->wrapped_cnt = new_cnt; + ptr->wrapped = wrapped; + + return 0; +} + +static bool arm_spe_buffer_has_wrapped(unsigned char *buffer, + size_t buffer_size, u64 head) +{ + u64 i, watermark; + u64 *buf = (u64 *)buffer; + size_t buf_size = buffer_size; + + /* + * Defensively handle the case where head might be continually increasing - if its value is + * equal or greater than the size of the ring buffer, then we can safely determine it has + * wrapped around. Otherwise, continue to detect if head might have wrapped. + */ + if (head >= buffer_size) + return true; + + /* + * We want to look the very last 512 byte (chosen arbitrarily) in the ring buffer. + */ + watermark = buf_size - 512; + + /* + * The value of head is somewhere within the size of the ring buffer. This can be that there + * hasn't been enough data to fill the ring buffer yet or the trace time was so long that + * head has numerically wrapped around. To find we need to check if we have data at the + * very end of the ring buffer. We can reliably do this because mmap'ed pages are zeroed + * out and there is a fresh mapping with every new session. + */ + + /* + * head is less than 512 byte from the end of the ring buffer. + */ + if (head > watermark) + watermark = head; + + /* + * Speed things up by using 64 bit transactions (see "u64 *buf" above) + */ + watermark /= sizeof(u64); + buf_size /= sizeof(u64); + + /* + * If we find trace data at the end of the ring buffer, head has been there and has + * numerically wrapped around at least once. + */ + for (i = watermark; i < buf_size; i++) + if (buf[i]) + return true; + + return false; +} + +static int arm_spe_find_snapshot(struct auxtrace_record *itr, int idx, + struct auxtrace_mmap *mm, unsigned char *data, + u64 *head, u64 *old) +{ + int err; + bool wrapped; + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + + /* + * Allocate memory to keep track of wrapping if this is the first + * time we deal with this *mm. + */ + if (idx >= ptr->wrapped_cnt) { + err = arm_spe_alloc_wrapped_array(ptr, idx); + if (err) + return err; + } + + /* + * Check to see if *head has wrapped around. If it hasn't only the + * amount of data between *head and *old is snapshot'ed to avoid + * bloating the perf.data file with zeros. But as soon as *head has + * wrapped around the entire size of the AUX ring buffer it taken. + */ + wrapped = ptr->wrapped[idx]; + if (!wrapped && arm_spe_buffer_has_wrapped(data, mm->len, *head)) { + wrapped = true; + ptr->wrapped[idx] = true; + } + + pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", + __func__, idx, (size_t)*old, (size_t)*head, mm->len); + + /* + * No wrap has occurred, we can just use *head and *old. + */ + if (!wrapped) + return 0; + + /* + * *head has wrapped around - adjust *head and *old to pickup the + * entire content of the AUX buffer. + */ + if (*head >= mm->len) { + *old = *head - mm->len; + } else { + *head += mm->len; + *old = *head - mm->len; + } return 0; } @@ -155,6 +642,7 @@ static void arm_spe_recording_free(struct auxtrace_record *itr) struct arm_spe_recording *sper = container_of(itr, struct arm_spe_recording, itr); + zfree(&sper->wrapped); free(sper); } @@ -175,7 +663,10 @@ struct auxtrace_record *arm_spe_recording_init(int *err, } sper->arm_spe_pmu = arm_spe_pmu; - sper->itr.pmu = arm_spe_pmu; + sper->itr.snapshot_start = arm_spe_snapshot_start; + sper->itr.snapshot_finish = arm_spe_snapshot_finish; + sper->itr.find_snapshot = arm_spe_find_snapshot; + sper->itr.parse_snapshot_options = arm_spe_parse_snapshot_options; sper->itr.recording_options = arm_spe_recording_options; sper->itr.info_priv_size = arm_spe_info_priv_size; sper->itr.info_fill = arm_spe_info_fill; @@ -188,29 +679,8 @@ struct auxtrace_record *arm_spe_recording_init(int *err, return &sper->itr; } -struct perf_event_attr -*arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu) +void +arm_spe_pmu_default_config(const struct perf_pmu *arm_spe_pmu, struct perf_event_attr *attr) { - struct perf_event_attr *attr; - - attr = zalloc(sizeof(struct perf_event_attr)); - if (!attr) { - pr_err("arm_spe default config cannot allocate a perf_event_attr\n"); - return NULL; - } - - /* - * If kernel driver doesn't advertise a minimum, - * use max allowable by PMSIDR_EL1.INTERVAL - */ - if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", - &attr->sample_period) != 1) { - pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); - attr->sample_period = 4096; - } - - arm_spe_pmu->selectable = true; - arm_spe_pmu->is_uncore = false; - - return attr; + attr->sample_period = arm_spe_pmu__sample_period(arm_spe_pmu); } diff --git a/tools/perf/arch/arm64/util/arm64_exception_types.h b/tools/perf/arch/arm64/util/arm64_exception_types.h new file mode 100644 index 000000000000..27c981ebe401 --- /dev/null +++ b/tools/perf/arch/arm64/util/arm64_exception_types.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef ARCH_PERF_ARM64_EXCEPTION_TYPES_H +#define ARCH_PERF_ARM64_EXCEPTION_TYPES_H + +/* Per asm/virt.h */ +#define HVC_STUB_ERR 0xbadca11 + +/* Per asm/kvm_asm.h */ +#define ARM_EXCEPTION_IRQ 0 +#define ARM_EXCEPTION_EL1_SERROR 1 +#define ARM_EXCEPTION_TRAP 2 +#define ARM_EXCEPTION_IL 3 +/* The hyp-stub will return this for any kvm_call_hyp() call */ +#define ARM_EXCEPTION_HYP_GONE HVC_STUB_ERR + +#define kvm_arm_exception_type \ + {ARM_EXCEPTION_IRQ, "IRQ" }, \ + {ARM_EXCEPTION_EL1_SERROR, "SERROR" }, \ + {ARM_EXCEPTION_TRAP, "TRAP" }, \ + {ARM_EXCEPTION_IL, "ILLEGAL" }, \ + {ARM_EXCEPTION_HYP_GONE, "HYP_GONE" } + +/* Per asm/esr.h */ +#define ESR_ELx_EC_UNKNOWN (0x00) +#define ESR_ELx_EC_WFx (0x01) +/* Unallocated EC: 0x02 */ +#define ESR_ELx_EC_CP15_32 (0x03) +#define ESR_ELx_EC_CP15_64 (0x04) +#define ESR_ELx_EC_CP14_MR (0x05) +#define ESR_ELx_EC_CP14_LS (0x06) +#define ESR_ELx_EC_FP_ASIMD (0x07) +#define ESR_ELx_EC_CP10_ID (0x08) /* EL2 only */ +#define ESR_ELx_EC_PAC (0x09) /* EL2 and above */ +/* Unallocated EC: 0x0A - 0x0B */ +#define ESR_ELx_EC_CP14_64 (0x0C) +/* Unallocated EC: 0x0d */ +#define ESR_ELx_EC_ILL (0x0E) +/* Unallocated EC: 0x0F - 0x10 */ +#define ESR_ELx_EC_SVC32 (0x11) +#define ESR_ELx_EC_HVC32 (0x12) /* EL2 only */ +#define ESR_ELx_EC_SMC32 (0x13) /* EL2 and above */ +/* Unallocated EC: 0x14 */ +#define ESR_ELx_EC_SVC64 (0x15) +#define ESR_ELx_EC_HVC64 (0x16) /* EL2 and above */ +#define ESR_ELx_EC_SMC64 (0x17) /* EL2 and above */ +#define ESR_ELx_EC_SYS64 (0x18) +#define ESR_ELx_EC_SVE (0x19) +#define ESR_ELx_EC_ERET (0x1a) /* EL2 only */ +/* Unallocated EC: 0x1b - 0x1E */ +#define ESR_ELx_EC_IMP_DEF (0x1f) /* EL3 only */ +#define ESR_ELx_EC_IABT_LOW (0x20) +#define ESR_ELx_EC_IABT_CUR (0x21) +#define ESR_ELx_EC_PC_ALIGN (0x22) +/* Unallocated EC: 0x23 */ +#define ESR_ELx_EC_DABT_LOW (0x24) +#define ESR_ELx_EC_DABT_CUR (0x25) +#define ESR_ELx_EC_SP_ALIGN (0x26) +/* Unallocated EC: 0x27 */ +#define ESR_ELx_EC_FP_EXC32 (0x28) +/* Unallocated EC: 0x29 - 0x2B */ +#define ESR_ELx_EC_FP_EXC64 (0x2C) +/* Unallocated EC: 0x2D - 0x2E */ +#define ESR_ELx_EC_SERROR (0x2F) +#define ESR_ELx_EC_BREAKPT_LOW (0x30) +#define ESR_ELx_EC_BREAKPT_CUR (0x31) +#define ESR_ELx_EC_SOFTSTP_LOW (0x32) +#define ESR_ELx_EC_SOFTSTP_CUR (0x33) +#define ESR_ELx_EC_WATCHPT_LOW (0x34) +#define ESR_ELx_EC_WATCHPT_CUR (0x35) +/* Unallocated EC: 0x36 - 0x37 */ +#define ESR_ELx_EC_BKPT32 (0x38) +/* Unallocated EC: 0x39 */ +#define ESR_ELx_EC_VECTOR32 (0x3A) /* EL2 only */ +/* Unallocated EC: 0x3B */ +#define ESR_ELx_EC_BRK64 (0x3C) +/* Unallocated EC: 0x3D - 0x3F */ +#define ESR_ELx_EC_MAX (0x3F) + +#define ECN(x) { ESR_ELx_EC_##x, #x } + +#define kvm_arm_exception_class \ + ECN(UNKNOWN), ECN(WFx), ECN(CP15_32), ECN(CP15_64), ECN(CP14_MR), \ + ECN(CP14_LS), ECN(FP_ASIMD), ECN(CP10_ID), ECN(PAC), ECN(CP14_64), \ + ECN(SVC64), ECN(HVC64), ECN(SMC64), ECN(SYS64), ECN(SVE), \ + ECN(IMP_DEF), ECN(IABT_LOW), ECN(IABT_CUR), \ + ECN(PC_ALIGN), ECN(DABT_LOW), ECN(DABT_CUR), \ + ECN(SP_ALIGN), ECN(FP_EXC32), ECN(FP_EXC64), ECN(SERROR), \ + ECN(BREAKPT_LOW), ECN(BREAKPT_CUR), ECN(SOFTSTP_LOW), \ + ECN(SOFTSTP_CUR), ECN(WATCHPT_LOW), ECN(WATCHPT_CUR), \ + ECN(BKPT32), ECN(VECTOR32), ECN(BRK64) + +#endif /* ARCH_PERF_ARM64_EXCEPTION_TYPES_H */ diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c deleted file mode 100644 index 917b97d7c5d3..000000000000 --- a/tools/perf/arch/arm64/util/dwarf-regs.c +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Mapping of DWARF debug register numbers into register names. - * - * Copyright (C) 2010 Will Deacon, ARM Ltd. - */ - -#include <errno.h> -#include <stddef.h> -#include <string.h> -#include <dwarf-regs.h> -#include <linux/ptrace.h> /* for struct user_pt_regs */ -#include <linux/stringify.h> - -struct pt_regs_dwarfnum { - const char *name; - unsigned int dwarfnum; -}; - -#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} -#define GPR_DWARFNUM_NAME(num) \ - {.name = __stringify(%x##num), .dwarfnum = num} -#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} -#define DWARFNUM2OFFSET(index) \ - (index * sizeof((struct user_pt_regs *)0)->regs[0]) - -/* - * Reference: - * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf - */ -static const struct pt_regs_dwarfnum regdwarfnum_table[] = { - GPR_DWARFNUM_NAME(0), - GPR_DWARFNUM_NAME(1), - GPR_DWARFNUM_NAME(2), - GPR_DWARFNUM_NAME(3), - GPR_DWARFNUM_NAME(4), - GPR_DWARFNUM_NAME(5), - GPR_DWARFNUM_NAME(6), - GPR_DWARFNUM_NAME(7), - GPR_DWARFNUM_NAME(8), - GPR_DWARFNUM_NAME(9), - GPR_DWARFNUM_NAME(10), - GPR_DWARFNUM_NAME(11), - GPR_DWARFNUM_NAME(12), - GPR_DWARFNUM_NAME(13), - GPR_DWARFNUM_NAME(14), - GPR_DWARFNUM_NAME(15), - GPR_DWARFNUM_NAME(16), - GPR_DWARFNUM_NAME(17), - GPR_DWARFNUM_NAME(18), - GPR_DWARFNUM_NAME(19), - GPR_DWARFNUM_NAME(20), - GPR_DWARFNUM_NAME(21), - GPR_DWARFNUM_NAME(22), - GPR_DWARFNUM_NAME(23), - GPR_DWARFNUM_NAME(24), - GPR_DWARFNUM_NAME(25), - GPR_DWARFNUM_NAME(26), - GPR_DWARFNUM_NAME(27), - GPR_DWARFNUM_NAME(28), - GPR_DWARFNUM_NAME(29), - REG_DWARFNUM_NAME("%lr", 30), - REG_DWARFNUM_NAME("%sp", 31), - REG_DWARFNUM_END, -}; - -/** - * get_arch_regstr() - lookup register name from it's DWARF register number - * @n: the DWARF register number - * - * get_arch_regstr() returns the name of the register in struct - * regdwarfnum_table from it's DWARF register number. If the register is not - * found in the table, this returns NULL; - */ -const char *get_arch_regstr(unsigned int n) -{ - const struct pt_regs_dwarfnum *roff; - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (roff->dwarfnum == n) - return roff->name; - return NULL; -} - -int regs_query_register_offset(const char *name) -{ - const struct pt_regs_dwarfnum *roff; - - for (roff = regdwarfnum_table; roff->name != NULL; roff++) - if (!strcmp(roff->name, name)) - return DWARFNUM2OFFSET(roff->dwarfnum); - return -EINVAL; -} diff --git a/tools/perf/arch/arm64/util/header.c b/tools/perf/arch/arm64/util/header.c index d730666ab95d..f445a2dd6293 100644 --- a/tools/perf/arch/arm64/util/header.c +++ b/tools/perf/arch/arm64/util/header.c @@ -1,8 +1,9 @@ +#include <linux/kernel.h> +#include <linux/bits.h> +#include <linux/bitfield.h> #include <stdio.h> #include <stdlib.h> #include <perf/cpumap.h> -#include <util/cpumap.h> -#include <internal/cpumap.h> #include <api/fs/fs.h> #include <errno.h> #include "debug.h" @@ -10,92 +11,116 @@ #define MIDR "/regs/identification/midr_el1" #define MIDR_SIZE 19 -#define MIDR_REVISION_MASK 0xf -#define MIDR_VARIANT_SHIFT 20 -#define MIDR_VARIANT_MASK (0xf << MIDR_VARIANT_SHIFT) +#define MIDR_REVISION_MASK GENMASK(3, 0) +#define MIDR_VARIANT_MASK GENMASK(23, 20) -static int _get_cpuid(char *buf, size_t sz, struct perf_cpu_map *cpus) +static int _get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) { + char path[PATH_MAX]; + FILE *file; const char *sysfs = sysfs__mountpoint(); - u64 midr = 0; - int cpu; + assert(cpu.cpu != -1); if (!sysfs || sz < MIDR_SIZE) return EINVAL; - cpus = perf_cpu_map__get(cpus); + scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d" MIDR, sysfs, cpu.cpu); - for (cpu = 0; cpu < perf_cpu_map__nr(cpus); cpu++) { - char path[PATH_MAX]; - FILE *file; - - scnprintf(path, PATH_MAX, "%s/devices/system/cpu/cpu%d"MIDR, - sysfs, cpus->map[cpu]); - - file = fopen(path, "r"); - if (!file) { - pr_debug("fopen failed for file %s\n", path); - continue; - } - - if (!fgets(buf, MIDR_SIZE, file)) { - fclose(file); - continue; - } - fclose(file); - - /* Ignore/clear Variant[23:20] and - * Revision[3:0] of MIDR - */ - midr = strtoul(buf, NULL, 16); - midr &= (~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK)); - scnprintf(buf, MIDR_SIZE, "0x%016lx", midr); - /* got midr break loop */ - break; + file = fopen(path, "r"); + if (!file) { + pr_debug("fopen failed for file %s\n", path); + return EINVAL; } - perf_cpu_map__put(cpus); - - if (!midr) + if (!fgets(buf, MIDR_SIZE, file)) { + pr_debug("Failed to read file %s\n", path); + fclose(file); return EINVAL; - + } + fclose(file); return 0; } -int get_cpuid(char *buf, size_t sz) +int get_cpuid(char *buf, size_t sz, struct perf_cpu cpu) { - struct perf_cpu_map *cpus = perf_cpu_map__new(NULL); - int ret; + struct perf_cpu_map *cpus; + int idx; + + if (cpu.cpu != -1) + return _get_cpuid(buf, sz, cpu); + cpus = perf_cpu_map__new_online_cpus(); if (!cpus) return EINVAL; - ret = _get_cpuid(buf, sz, cpus); - - perf_cpu_map__put(cpus); + perf_cpu_map__for_each_cpu(cpu, idx, cpus) { + int ret = _get_cpuid(buf, sz, cpu); - return ret; + if (ret == 0) + return 0; + } + return EINVAL; } -char *get_cpuid_str(struct perf_pmu *pmu) +char *get_cpuid_str(struct perf_cpu cpu) { - char *buf = NULL; + char *buf = malloc(MIDR_SIZE); int res; - if (!pmu || !pmu->cpus) - return NULL; - - buf = malloc(MIDR_SIZE); if (!buf) return NULL; /* read midr from list of cpus mapped to this pmu */ - res = _get_cpuid(buf, MIDR_SIZE, pmu->cpus); + res = get_cpuid(buf, MIDR_SIZE, cpu); if (res) { - pr_err("failed to get cpuid string for PMU %s\n", pmu->name); + pr_err("failed to get cpuid string for CPU %d\n", cpu.cpu); free(buf); buf = NULL; } return buf; } + +/* + * Return 0 if idstr is a higher or equal to version of the same part as + * mapcpuid. Therefore, if mapcpuid has 0 for revision and variant then any + * version of idstr will match as long as it's the same CPU type. + * + * Return 1 if the CPU type is different or the version of idstr is lower. + */ +int strcmp_cpuid_str(const char *mapcpuid, const char *idstr) +{ + u64 map_id = strtoull(mapcpuid, NULL, 16); + char map_id_variant = FIELD_GET(MIDR_VARIANT_MASK, map_id); + char map_id_revision = FIELD_GET(MIDR_REVISION_MASK, map_id); + u64 id = strtoull(idstr, NULL, 16); + char id_variant = FIELD_GET(MIDR_VARIANT_MASK, id); + char id_revision = FIELD_GET(MIDR_REVISION_MASK, id); + u64 id_fields = ~(MIDR_VARIANT_MASK | MIDR_REVISION_MASK); + + /* Compare without version first */ + if ((map_id & id_fields) != (id & id_fields)) + return 1; + + /* + * ID matches, now compare version. + * + * Arm revisions (like r0p0) are compared here like two digit semver + * values eg. 1.3 < 2.0 < 2.1 < 2.2. + * + * r = high value = 'Variant' field in MIDR + * p = low value = 'Revision' field in MIDR + * + */ + if (id_variant > map_id_variant) + return 0; + + if (id_variant == map_id_variant && id_revision >= map_id_revision) + return 0; + + /* + * variant is less than mapfile variant or variants are the same but + * the revision doesn't match. Return no match. + */ + return 1; +} diff --git a/tools/perf/arch/arm64/util/hisi-ptt.c b/tools/perf/arch/arm64/util/hisi-ptt.c new file mode 100644 index 000000000000..eac9739c87e6 --- /dev/null +++ b/tools/perf/arch/arm64/util/hisi-ptt.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HiSilicon PCIe Trace and Tuning (PTT) support + * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/log2.h> +#include <linux/zalloc.h> +#include <time.h> + +#include <internal/lib.h> // page_size +#include "../../../util/auxtrace.h" +#include "../../../util/cpumap.h" +#include "../../../util/debug.h" +#include "../../../util/event.h" +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" +#include "../../../util/hisi-ptt.h" +#include "../../../util/pmu.h" +#include "../../../util/record.h" +#include "../../../util/session.h" +#include "../../../util/tsc.h" + +#define KiB(x) ((x) * 1024) +#define MiB(x) ((x) * 1024 * 1024) + +struct hisi_ptt_recording { + struct auxtrace_record itr; + struct perf_pmu *hisi_ptt_pmu; + struct evlist *evlist; +}; + +static size_t +hisi_ptt_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct evlist *evlist __maybe_unused) +{ + return HISI_PTT_AUXTRACE_PRIV_SIZE; +} + +static int hisi_ptt_info_fill(struct auxtrace_record *itr, + struct perf_session *session, + struct perf_record_auxtrace_info *auxtrace_info, + size_t priv_size) +{ + struct hisi_ptt_recording *pttr = + container_of(itr, struct hisi_ptt_recording, itr); + struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu; + + if (priv_size != HISI_PTT_AUXTRACE_PRIV_SIZE) + return -EINVAL; + + if (!session->evlist->core.nr_mmaps) + return -EINVAL; + + auxtrace_info->type = PERF_AUXTRACE_HISI_PTT; + auxtrace_info->priv[0] = hisi_ptt_pmu->type; + + return 0; +} + +static int hisi_ptt_set_auxtrace_mmap_page(struct record_opts *opts) +{ + bool privileged = perf_event_paranoid_check(-1); + + if (!opts->full_auxtrace) + return 0; + + if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { + if (privileged) { + opts->auxtrace_mmap_pages = MiB(16) / page_size; + } else { + opts->auxtrace_mmap_pages = KiB(128) / page_size; + if (opts->mmap_pages == UINT_MAX) + opts->mmap_pages = KiB(256) / page_size; + } + } + + /* Validate auxtrace_mmap_pages */ + if (opts->auxtrace_mmap_pages) { + size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; + size_t min_sz = KiB(8); + + if (sz < min_sz || !is_power_of_2(sz)) { + pr_err("Invalid mmap size for HISI PTT: must be at least %zuKiB and a power of 2\n", + min_sz / 1024); + return -EINVAL; + } + } + + return 0; +} + +static int hisi_ptt_recording_options(struct auxtrace_record *itr, + struct evlist *evlist, + struct record_opts *opts) +{ + struct hisi_ptt_recording *pttr = + container_of(itr, struct hisi_ptt_recording, itr); + struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu; + struct evsel *evsel, *hisi_ptt_evsel = NULL; + struct evsel *tracking_evsel; + int err; + + pttr->evlist = evlist; + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.type == hisi_ptt_pmu->type) { + if (hisi_ptt_evsel) { + pr_err("There may be only one " HISI_PTT_PMU_NAME "x event\n"); + return -EINVAL; + } + evsel->core.attr.freq = 0; + evsel->core.attr.sample_period = 1; + evsel->needs_auxtrace_mmap = true; + hisi_ptt_evsel = evsel; + opts->full_auxtrace = true; + } + } + + err = hisi_ptt_set_auxtrace_mmap_page(opts); + if (err) + return err; + /* + * To obtain the auxtrace buffer file descriptor, the auxtrace event + * must come first. + */ + evlist__to_front(evlist, hisi_ptt_evsel); + evsel__set_sample_bit(hisi_ptt_evsel, TIME); + + /* Add dummy event to keep tracking */ + err = parse_event(evlist, "dummy:u"); + if (err) + return err; + + tracking_evsel = evlist__last(evlist); + evlist__set_tracking_event(evlist, tracking_evsel); + + tracking_evsel->core.attr.freq = 0; + tracking_evsel->core.attr.sample_period = 1; + evsel__set_sample_bit(tracking_evsel, TIME); + + return 0; +} + +static u64 hisi_ptt_reference(struct auxtrace_record *itr __maybe_unused) +{ + return rdtsc(); +} + +static void hisi_ptt_recording_free(struct auxtrace_record *itr) +{ + struct hisi_ptt_recording *pttr = + container_of(itr, struct hisi_ptt_recording, itr); + + free(pttr); +} + +struct auxtrace_record *hisi_ptt_recording_init(int *err, + struct perf_pmu *hisi_ptt_pmu) +{ + struct hisi_ptt_recording *pttr; + + if (!hisi_ptt_pmu) { + *err = -ENODEV; + return NULL; + } + + pttr = zalloc(sizeof(*pttr)); + if (!pttr) { + *err = -ENOMEM; + return NULL; + } + + pttr->hisi_ptt_pmu = hisi_ptt_pmu; + pttr->itr.recording_options = hisi_ptt_recording_options; + pttr->itr.info_priv_size = hisi_ptt_info_priv_size; + pttr->itr.info_fill = hisi_ptt_info_fill; + pttr->itr.free = hisi_ptt_recording_free; + pttr->itr.reference = hisi_ptt_reference; + pttr->itr.read_finish = auxtrace_record__read_finish; + pttr->itr.alignment = 0; + + *err = 0; + return &pttr->itr; +} diff --git a/tools/perf/arch/arm64/util/kvm-stat.c b/tools/perf/arch/arm64/util/kvm-stat.c new file mode 100644 index 000000000000..6611aa21cba9 --- /dev/null +++ b/tools/perf/arch/arm64/util/kvm-stat.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <errno.h> +#include <memory.h> +#include "../../../util/evsel.h" +#include "../../../util/kvm-stat.h" +#include "arm64_exception_types.h" +#include "debug.h" + +define_exit_reasons_table(arm64_exit_reasons, kvm_arm_exception_type); +define_exit_reasons_table(arm64_trap_exit_reasons, kvm_arm_exception_class); + +const char *kvm_trap_exit_reason = "esr_ec"; +const char *vcpu_id_str = "id"; +const char *kvm_exit_reason = "ret"; +const char *kvm_entry_trace = "kvm:kvm_entry"; +const char *kvm_exit_trace = "kvm:kvm_exit"; + +const char *kvm_events_tp[] = { + "kvm:kvm_entry", + "kvm:kvm_exit", + NULL, +}; + +static void event_get_key(struct evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + key->info = 0; + key->key = evsel__intval(evsel, sample, kvm_exit_reason); + key->exit_reasons = arm64_exit_reasons; + + /* + * TRAP exceptions carry exception class info in esr_ec field + * and, hence, we need to use a different exit_reasons table to + * properly decode event's est_ec. + */ + if (key->key == ARM_EXCEPTION_TRAP) { + key->key = evsel__intval(evsel, sample, kvm_trap_exit_reason); + key->exit_reasons = arm64_trap_exit_reasons; + } +} + +static bool event_begin(struct evsel *evsel, + struct perf_sample *sample __maybe_unused, + struct event_key *key __maybe_unused) +{ + return evsel__name_is(evsel, kvm_entry_trace); +} + +static bool event_end(struct evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + if (evsel__name_is(evsel, kvm_exit_trace)) { + event_get_key(evsel, sample, key); + return true; + } + return false; +} + +static struct kvm_events_ops exit_events = { + .is_begin_event = event_begin, + .is_end_event = event_end, + .decode_key = exit_event_decode_key, + .name = "VM-EXIT" +}; + +struct kvm_reg_events_ops kvm_reg_events_ops[] = { + { + .name = "vmexit", + .ops = &exit_events, + }, + { NULL, NULL }, +}; + +const char * const kvm_skip_events[] = { + NULL, +}; + +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused) +{ + kvm->exit_reasons_isa = "arm64"; + return 0; +} diff --git a/tools/perf/arch/arm64/util/machine.c b/tools/perf/arch/arm64/util/machine.c index d41b27e781d3..aab1cc2bc283 100644 --- a/tools/perf/arch/arm64/util/machine.c +++ b/tools/perf/arch/arm64/util/machine.c @@ -1,27 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 +#include <inttypes.h> #include <stdio.h> #include <string.h> #include "debug.h" #include "symbol.h" +#include "callchain.h" +#include "perf_regs.h" +#include "record.h" +#include "util/perf_regs.h" -/* On arm64, kernel text segment start at high memory address, - * for example 0xffff 0000 8xxx xxxx. Modules start at a low memory - * address, like 0xffff 0000 00ax xxxx. When only samll amount of - * memory is used by modules, gap between end of module's text segment - * and start of kernel text segment may be reach 2G. - * Therefore do not fill this gap and do not assign it to the kernel dso map. - */ - -#define SYMBOL_LIMIT (1 << 12) /* 4K */ - -void arch__symbols__fixup_end(struct symbol *p, struct symbol *c) +void arch__add_leaf_frame_record_opts(struct record_opts *opts) { - if ((strchr(p->name, '[') && strchr(c->name, '[') == NULL) || - (strchr(p->name, '[') == NULL && strchr(c->name, '['))) - /* Limit range of last symbol in module and kernel */ - p->end += SYMBOL_LIMIT; - else - p->end = c->start; - pr_debug4("%s sym:%s end:%#lx\n", __func__, p->name, p->end); + const struct sample_reg *sample_reg_masks = arch__sample_reg_masks(); + + opts->sample_user_regs |= sample_reg_masks[PERF_REG_ARM64_LR].mask; } diff --git a/tools/perf/arch/arm64/util/mem-events.c b/tools/perf/arch/arm64/util/mem-events.c new file mode 100644 index 000000000000..9f8da7937255 --- /dev/null +++ b/tools/perf/arch/arm64/util/mem-events.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "util/map_symbol.h" +#include "util/mem-events.h" +#include "mem-events.h" + +#define E(t, n, s, l, a) { .tag = t, .name = n, .event_name = s, .ldlat = l, .aux_event = a } + +struct perf_mem_event perf_mem_events_arm[PERF_MEM_EVENTS__MAX] = { + E("spe-load", "%s/ts_enable=1,pa_enable=1,load_filter=1,store_filter=0,min_latency=%u/", NULL, true, 0), + E("spe-store", "%s/ts_enable=1,pa_enable=1,load_filter=0,store_filter=1/", NULL, false, 0), + E("spe-ldst", "%s/ts_enable=1,pa_enable=1,load_filter=1,store_filter=1,min_latency=%u/", NULL, true, 0), +}; diff --git a/tools/perf/arch/arm64/util/mem-events.h b/tools/perf/arch/arm64/util/mem-events.h new file mode 100644 index 000000000000..5fc50be4be38 --- /dev/null +++ b/tools/perf/arch/arm64/util/mem-events.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ARM64_MEM_EVENTS_H +#define _ARM64_MEM_EVENTS_H + +extern struct perf_mem_event perf_mem_events_arm[PERF_MEM_EVENTS__MAX]; + +#endif /* _ARM64_MEM_EVENTS_H */ diff --git a/tools/perf/arch/arm64/util/perf_regs.c b/tools/perf/arch/arm64/util/perf_regs.c index 2833e101a7c6..09308665e28a 100644 --- a/tools/perf/arch/arm64/util/perf_regs.c +++ b/tools/perf/arch/arm64/util/perf_regs.c @@ -1,6 +1,182 @@ // SPDX-License-Identifier: GPL-2.0 +#include <errno.h> +#include <regex.h> +#include <string.h> +#include <sys/auxv.h> +#include <linux/kernel.h> +#include <linux/zalloc.h> + +#include "perf_regs.h" +#include "../../../perf-sys.h" +#include "../../../util/debug.h" +#include "../../../util/event.h" #include "../../../util/perf_regs.h" -const struct sample_reg sample_reg_masks[] = { +#ifndef HWCAP_SVE +#define HWCAP_SVE (1 << 22) +#endif + +static const struct sample_reg sample_reg_masks[] = { + SMPL_REG(x0, PERF_REG_ARM64_X0), + SMPL_REG(x1, PERF_REG_ARM64_X1), + SMPL_REG(x2, PERF_REG_ARM64_X2), + SMPL_REG(x3, PERF_REG_ARM64_X3), + SMPL_REG(x4, PERF_REG_ARM64_X4), + SMPL_REG(x5, PERF_REG_ARM64_X5), + SMPL_REG(x6, PERF_REG_ARM64_X6), + SMPL_REG(x7, PERF_REG_ARM64_X7), + SMPL_REG(x8, PERF_REG_ARM64_X8), + SMPL_REG(x9, PERF_REG_ARM64_X9), + SMPL_REG(x10, PERF_REG_ARM64_X10), + SMPL_REG(x11, PERF_REG_ARM64_X11), + SMPL_REG(x12, PERF_REG_ARM64_X12), + SMPL_REG(x13, PERF_REG_ARM64_X13), + SMPL_REG(x14, PERF_REG_ARM64_X14), + SMPL_REG(x15, PERF_REG_ARM64_X15), + SMPL_REG(x16, PERF_REG_ARM64_X16), + SMPL_REG(x17, PERF_REG_ARM64_X17), + SMPL_REG(x18, PERF_REG_ARM64_X18), + SMPL_REG(x19, PERF_REG_ARM64_X19), + SMPL_REG(x20, PERF_REG_ARM64_X20), + SMPL_REG(x21, PERF_REG_ARM64_X21), + SMPL_REG(x22, PERF_REG_ARM64_X22), + SMPL_REG(x23, PERF_REG_ARM64_X23), + SMPL_REG(x24, PERF_REG_ARM64_X24), + SMPL_REG(x25, PERF_REG_ARM64_X25), + SMPL_REG(x26, PERF_REG_ARM64_X26), + SMPL_REG(x27, PERF_REG_ARM64_X27), + SMPL_REG(x28, PERF_REG_ARM64_X28), + SMPL_REG(x29, PERF_REG_ARM64_X29), + SMPL_REG(lr, PERF_REG_ARM64_LR), + SMPL_REG(sp, PERF_REG_ARM64_SP), + SMPL_REG(pc, PERF_REG_ARM64_PC), + SMPL_REG(vg, PERF_REG_ARM64_VG), SMPL_REG_END }; + +/* %xNUM */ +#define SDT_OP_REGEX1 "^(x[1-2]?[0-9]|3[0-1])$" + +/* [sp], [sp, NUM] */ +#define SDT_OP_REGEX2 "^\\[sp(, )?([0-9]+)?\\]$" + +static regex_t sdt_op_regex1, sdt_op_regex2; + +static int sdt_init_op_regex(void) +{ + static int initialized; + int ret = 0; + + if (initialized) + return 0; + + ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED); + if (ret) + goto error; + + ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED); + if (ret) + goto free_regex1; + + initialized = 1; + return 0; + +free_regex1: + regfree(&sdt_op_regex1); +error: + pr_debug4("Regex compilation error.\n"); + return ret; +} + +/* + * SDT marker arguments on Arm64 uses %xREG or [sp, NUM], currently + * support these two formats. + */ +int arch_sdt_arg_parse_op(char *old_op, char **new_op) +{ + int ret, new_len; + regmatch_t rm[5]; + + ret = sdt_init_op_regex(); + if (ret < 0) + return ret; + + if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) { + /* Extract xNUM */ + new_len = 2; /* % NULL */ + new_len += (int)(rm[1].rm_eo - rm[1].rm_so); + + *new_op = zalloc(new_len); + if (!*new_op) + return -ENOMEM; + + scnprintf(*new_op, new_len, "%%%.*s", + (int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so); + } else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) { + /* [sp], [sp, NUM] or [sp,NUM] */ + new_len = 7; /* + ( % s p ) NULL */ + + /* If the argument is [sp], need to fill offset '0' */ + if (rm[2].rm_so == -1) + new_len += 1; + else + new_len += (int)(rm[2].rm_eo - rm[2].rm_so); + + *new_op = zalloc(new_len); + if (!*new_op) + return -ENOMEM; + + if (rm[2].rm_so == -1) + scnprintf(*new_op, new_len, "+0(%%sp)"); + else + scnprintf(*new_op, new_len, "+%.*s(%%sp)", + (int)(rm[2].rm_eo - rm[2].rm_so), + old_op + rm[2].rm_so); + } else { + pr_debug4("Skipping unsupported SDT argument: %s\n", old_op); + return SDT_ARG_SKIP; + } + + return SDT_ARG_VALID; +} + +uint64_t arch__intr_reg_mask(void) +{ + return PERF_REGS_MASK; +} + +uint64_t arch__user_reg_mask(void) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .sample_type = PERF_SAMPLE_REGS_USER, + .disabled = 1, + .exclude_kernel = 1, + .sample_period = 1, + .sample_regs_user = PERF_REGS_MASK + }; + int fd; + + if (getauxval(AT_HWCAP) & HWCAP_SVE) + attr.sample_regs_user |= SMPL_REG_MASK(PERF_REG_ARM64_VG); + + /* + * Check if the pmu supports perf extended regs, before + * returning the register mask to sample. + */ + if (attr.sample_regs_user != PERF_REGS_MASK) { + event_attr_init(&attr); + fd = sys_perf_event_open(&attr, 0, -1, -1, 0); + if (fd != -1) { + close(fd); + return attr.sample_regs_user; + } + } + return PERF_REGS_MASK; +} + +const struct sample_reg *arch__sample_reg_masks(void) +{ + return sample_reg_masks; +} diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c new file mode 100644 index 000000000000..895fb0d0610c --- /dev/null +++ b/tools/perf/arch/arm64/util/pmu.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "../../../util/pmu.h" +#include "../../../util/pmus.h" +#include "../../../util/tool_pmu.h" +#include <api/fs/fs.h> + +u64 tool_pmu__cpu_slots_per_cycle(void) +{ + char path[PATH_MAX]; + unsigned long long slots = 0; + struct perf_pmu *pmu = perf_pmus__find_core_pmu(); + + if (pmu) { + perf_pmu__pathname_scnprintf(path, sizeof(path), + pmu->name, "caps/slots"); + /* + * The value of slots is not greater than 32 bits, but + * filename__read_int can't read value with 0x prefix, + * so use filename__read_ull instead. + */ + filename__read_ull(path, &slots); + } + + return slots; +} diff --git a/tools/perf/arch/arm64/util/tsc.c b/tools/perf/arch/arm64/util/tsc.c new file mode 100644 index 000000000000..cc85bd9e73f1 --- /dev/null +++ b/tools/perf/arch/arm64/util/tsc.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/types.h> + +#include "../../../util/tsc.h" + +u64 rdtsc(void) +{ + u64 val; + + /* + * According to ARM DDI 0487F.c, from Armv8.0 to Armv8.5 inclusive, the + * system counter is at least 56 bits wide; from Armv8.6, the counter + * must be 64 bits wide. So the system counter could be less than 64 + * bits wide and it is attributed with the flag 'cap_user_time_short' + * is true. + */ + asm volatile("mrs %0, cntvct_el0" : "=r" (val)); + + return val; +} diff --git a/tools/perf/arch/arm64/util/unwind-libdw.c b/tools/perf/arch/arm64/util/unwind-libdw.c index a50941629649..b89b0a7e5ad9 100644 --- a/tools/perf/arch/arm64/util/unwind-libdw.c +++ b/tools/perf/arch/arm64/util/unwind-libdw.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 #include <elfutils/libdwfl.h> +#include "perf_regs.h" #include "../../../util/unwind-libdw.h" #include "../../../util/perf_regs.h" -#include "../../../util/event.h" +#include "../../../util/sample.h" bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) { struct unwind_info *ui = arg; - struct regs_dump *user_regs = &ui->sample->user_regs; + struct regs_dump *user_regs = perf_sample__user_regs(ui->sample); Dwarf_Word dwarf_regs[PERF_REG_ARM64_MAX], dwarf_pc; #define REG(r) ({ \ diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c index 1495a9523a23..871af5992298 100644 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c @@ -4,83 +4,14 @@ #ifndef REMOTE_UNWIND_LIBUNWIND #include <libunwind.h> #include "perf_regs.h" -#include "../../util/unwind.h" +#include "../../../util/unwind.h" #endif -#include "../../util/debug.h" +#include "../../../util/debug.h" int LIBUNWIND__ARCH_REG_ID(int regnum) { - switch (regnum) { - case UNW_AARCH64_X0: - return PERF_REG_ARM64_X0; - case UNW_AARCH64_X1: - return PERF_REG_ARM64_X1; - case UNW_AARCH64_X2: - return PERF_REG_ARM64_X2; - case UNW_AARCH64_X3: - return PERF_REG_ARM64_X3; - case UNW_AARCH64_X4: - return PERF_REG_ARM64_X4; - case UNW_AARCH64_X5: - return PERF_REG_ARM64_X5; - case UNW_AARCH64_X6: - return PERF_REG_ARM64_X6; - case UNW_AARCH64_X7: - return PERF_REG_ARM64_X7; - case UNW_AARCH64_X8: - return PERF_REG_ARM64_X8; - case UNW_AARCH64_X9: - return PERF_REG_ARM64_X9; - case UNW_AARCH64_X10: - return PERF_REG_ARM64_X10; - case UNW_AARCH64_X11: - return PERF_REG_ARM64_X11; - case UNW_AARCH64_X12: - return PERF_REG_ARM64_X12; - case UNW_AARCH64_X13: - return PERF_REG_ARM64_X13; - case UNW_AARCH64_X14: - return PERF_REG_ARM64_X14; - case UNW_AARCH64_X15: - return PERF_REG_ARM64_X15; - case UNW_AARCH64_X16: - return PERF_REG_ARM64_X16; - case UNW_AARCH64_X17: - return PERF_REG_ARM64_X17; - case UNW_AARCH64_X18: - return PERF_REG_ARM64_X18; - case UNW_AARCH64_X19: - return PERF_REG_ARM64_X19; - case UNW_AARCH64_X20: - return PERF_REG_ARM64_X20; - case UNW_AARCH64_X21: - return PERF_REG_ARM64_X21; - case UNW_AARCH64_X22: - return PERF_REG_ARM64_X22; - case UNW_AARCH64_X23: - return PERF_REG_ARM64_X23; - case UNW_AARCH64_X24: - return PERF_REG_ARM64_X24; - case UNW_AARCH64_X25: - return PERF_REG_ARM64_X25; - case UNW_AARCH64_X26: - return PERF_REG_ARM64_X26; - case UNW_AARCH64_X27: - return PERF_REG_ARM64_X27; - case UNW_AARCH64_X28: - return PERF_REG_ARM64_X28; - case UNW_AARCH64_X29: - return PERF_REG_ARM64_X29; - case UNW_AARCH64_X30: - return PERF_REG_ARM64_LR; - case UNW_AARCH64_SP: - return PERF_REG_ARM64_SP; - case UNW_AARCH64_PC: - return PERF_REG_ARM64_PC; - default: - pr_err("unwind: invalid reg id %d\n", regnum); + if (regnum < 0 || regnum >= PERF_REG_ARM64_EXTENDED_MAX) return -EINVAL; - } - return -EINVAL; + return regnum; } |