Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
perf-util-y += util/
perf-test-y += tests/
ifdef SHELLCHECK
SHELL_TEST_LOGS := $(SHELL_TESTS:%=%.shellcheck_log)
else
SHELL_TESTS :=
SHELL_TEST_LOGS :=
endif
$(OUTPUT)%.shellcheck_log: %
$(call rule_mkdir)
$(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false)
perf-test-y += $(SHELL_TEST_LOGS)
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
PERF_HAVE_JITDUMP := 1
@@ -0,0 +1,479 @@
# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
#
# 32-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point> [<compat entry point> [noreturn]]
#
# The __ia32_sys and __ia32_compat_sys stubs are created on-the-fly for
# sys_*() system calls and compat_sys_*() compat system calls if
# IA32_EMULATION is defined, and expect struct pt_regs *regs as their only
# parameter.
#
# The abi is always "i386" for this file.
#
0 i386 restart_syscall sys_restart_syscall
1 i386 exit sys_exit - noreturn
2 i386 fork sys_fork
3 i386 read sys_read
4 i386 write sys_write
5 i386 open sys_open compat_sys_open
6 i386 close sys_close
7 i386 waitpid sys_waitpid
8 i386 creat sys_creat
9 i386 link sys_link
10 i386 unlink sys_unlink
11 i386 execve sys_execve compat_sys_execve
12 i386 chdir sys_chdir
13 i386 time sys_time32
14 i386 mknod sys_mknod
15 i386 chmod sys_chmod
16 i386 lchown sys_lchown16
17 i386 break
18 i386 oldstat sys_stat
19 i386 lseek sys_lseek compat_sys_lseek
20 i386 getpid sys_getpid
21 i386 mount sys_mount
22 i386 umount sys_oldumount
23 i386 setuid sys_setuid16
24 i386 getuid sys_getuid16
25 i386 stime sys_stime32
26 i386 ptrace sys_ptrace compat_sys_ptrace
27 i386 alarm sys_alarm
28 i386 oldfstat sys_fstat
29 i386 pause sys_pause
30 i386 utime sys_utime32
31 i386 stty
32 i386 gtty
33 i386 access sys_access
34 i386 nice sys_nice
35 i386 ftime
36 i386 sync sys_sync
37 i386 kill sys_kill
38 i386 rename sys_rename
39 i386 mkdir sys_mkdir
40 i386 rmdir sys_rmdir
41 i386 dup sys_dup
42 i386 pipe sys_pipe
43 i386 times sys_times compat_sys_times
44 i386 prof
45 i386 brk sys_brk
46 i386 setgid sys_setgid16
47 i386 getgid sys_getgid16
48 i386 signal sys_signal
49 i386 geteuid sys_geteuid16
50 i386 getegid sys_getegid16
51 i386 acct sys_acct
52 i386 umount2 sys_umount
53 i386 lock
54 i386 ioctl sys_ioctl compat_sys_ioctl
55 i386 fcntl sys_fcntl compat_sys_fcntl64
56 i386 mpx
57 i386 setpgid sys_setpgid
58 i386 ulimit
59 i386 oldolduname sys_olduname
60 i386 umask sys_umask
61 i386 chroot sys_chroot
62 i386 ustat sys_ustat compat_sys_ustat
63 i386 dup2 sys_dup2
64 i386 getppid sys_getppid
65 i386 getpgrp sys_getpgrp
66 i386 setsid sys_setsid
67 i386 sigaction sys_sigaction compat_sys_sigaction
68 i386 sgetmask sys_sgetmask
69 i386 ssetmask sys_ssetmask
70 i386 setreuid sys_setreuid16
71 i386 setregid sys_setregid16
72 i386 sigsuspend sys_sigsuspend
73 i386 sigpending sys_sigpending compat_sys_sigpending
74 i386 sethostname sys_sethostname
75 i386 setrlimit sys_setrlimit compat_sys_setrlimit
76 i386 getrlimit sys_old_getrlimit compat_sys_old_getrlimit
77 i386 getrusage sys_getrusage compat_sys_getrusage
78 i386 gettimeofday sys_gettimeofday compat_sys_gettimeofday
79 i386 settimeofday sys_settimeofday compat_sys_settimeofday
80 i386 getgroups sys_getgroups16
81 i386 setgroups sys_setgroups16
82 i386 select sys_old_select compat_sys_old_select
83 i386 symlink sys_symlink
84 i386 oldlstat sys_lstat
85 i386 readlink sys_readlink
86 i386 uselib sys_uselib
87 i386 swapon sys_swapon
88 i386 reboot sys_reboot
89 i386 readdir sys_old_readdir compat_sys_old_readdir
90 i386 mmap sys_old_mmap compat_sys_ia32_mmap
91 i386 munmap sys_munmap
92 i386 truncate sys_truncate compat_sys_truncate
93 i386 ftruncate sys_ftruncate compat_sys_ftruncate
94 i386 fchmod sys_fchmod
95 i386 fchown sys_fchown16
96 i386 getpriority sys_getpriority
97 i386 setpriority sys_setpriority
98 i386 profil
99 i386 statfs sys_statfs compat_sys_statfs
100 i386 fstatfs sys_fstatfs compat_sys_fstatfs
101 i386 ioperm sys_ioperm
102 i386 socketcall sys_socketcall compat_sys_socketcall
103 i386 syslog sys_syslog
104 i386 setitimer sys_setitimer compat_sys_setitimer
105 i386 getitimer sys_getitimer compat_sys_getitimer
106 i386 stat sys_newstat compat_sys_newstat
107 i386 lstat sys_newlstat compat_sys_newlstat
108 i386 fstat sys_newfstat compat_sys_newfstat
109 i386 olduname sys_uname
110 i386 iopl sys_iopl
111 i386 vhangup sys_vhangup
112 i386 idle
113 i386 vm86old sys_vm86old sys_ni_syscall
114 i386 wait4 sys_wait4 compat_sys_wait4
115 i386 swapoff sys_swapoff
116 i386 sysinfo sys_sysinfo compat_sys_sysinfo
117 i386 ipc sys_ipc compat_sys_ipc
118 i386 fsync sys_fsync
119 i386 sigreturn sys_sigreturn compat_sys_sigreturn
120 i386 clone sys_clone compat_sys_ia32_clone
121 i386 setdomainname sys_setdomainname
122 i386 uname sys_newuname
123 i386 modify_ldt sys_modify_ldt
124 i386 adjtimex sys_adjtimex_time32
125 i386 mprotect sys_mprotect
126 i386 sigprocmask sys_sigprocmask compat_sys_sigprocmask
127 i386 create_module
128 i386 init_module sys_init_module
129 i386 delete_module sys_delete_module
130 i386 get_kernel_syms
131 i386 quotactl sys_quotactl
132 i386 getpgid sys_getpgid
133 i386 fchdir sys_fchdir
134 i386 bdflush sys_ni_syscall
135 i386 sysfs sys_sysfs
136 i386 personality sys_personality
137 i386 afs_syscall
138 i386 setfsuid sys_setfsuid16
139 i386 setfsgid sys_setfsgid16
140 i386 _llseek sys_llseek
141 i386 getdents sys_getdents compat_sys_getdents
142 i386 _newselect sys_select compat_sys_select
143 i386 flock sys_flock
144 i386 msync sys_msync
145 i386 readv sys_readv
146 i386 writev sys_writev
147 i386 getsid sys_getsid
148 i386 fdatasync sys_fdatasync
149 i386 _sysctl sys_ni_syscall
150 i386 mlock sys_mlock
151 i386 munlock sys_munlock
152 i386 mlockall sys_mlockall
153 i386 munlockall sys_munlockall
154 i386 sched_setparam sys_sched_setparam
155 i386 sched_getparam sys_sched_getparam
156 i386 sched_setscheduler sys_sched_setscheduler
157 i386 sched_getscheduler sys_sched_getscheduler
158 i386 sched_yield sys_sched_yield
159 i386 sched_get_priority_max sys_sched_get_priority_max
160 i386 sched_get_priority_min sys_sched_get_priority_min
161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32
162 i386 nanosleep sys_nanosleep_time32
163 i386 mremap sys_mremap
164 i386 setresuid sys_setresuid16
165 i386 getresuid sys_getresuid16
166 i386 vm86 sys_vm86 sys_ni_syscall
167 i386 query_module
168 i386 poll sys_poll
169 i386 nfsservctl
170 i386 setresgid sys_setresgid16
171 i386 getresgid sys_getresgid16
172 i386 prctl sys_prctl
173 i386 rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn
174 i386 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction
175 i386 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask
176 i386 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending
177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32
178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo
179 i386 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend
180 i386 pread64 sys_ia32_pread64
181 i386 pwrite64 sys_ia32_pwrite64
182 i386 chown sys_chown16
183 i386 getcwd sys_getcwd
184 i386 capget sys_capget
185 i386 capset sys_capset
186 i386 sigaltstack sys_sigaltstack compat_sys_sigaltstack
187 i386 sendfile sys_sendfile compat_sys_sendfile
188 i386 getpmsg
189 i386 putpmsg
190 i386 vfork sys_vfork
191 i386 ugetrlimit sys_getrlimit compat_sys_getrlimit
192 i386 mmap2 sys_mmap_pgoff
193 i386 truncate64 sys_ia32_truncate64
194 i386 ftruncate64 sys_ia32_ftruncate64
195 i386 stat64 sys_stat64 compat_sys_ia32_stat64
196 i386 lstat64 sys_lstat64 compat_sys_ia32_lstat64
197 i386 fstat64 sys_fstat64 compat_sys_ia32_fstat64
198 i386 lchown32 sys_lchown
199 i386 getuid32 sys_getuid
200 i386 getgid32 sys_getgid
201 i386 geteuid32 sys_geteuid
202 i386 getegid32 sys_getegid
203 i386 setreuid32 sys_setreuid
204 i386 setregid32 sys_setregid
205 i386 getgroups32 sys_getgroups
206 i386 setgroups32 sys_setgroups
207 i386 fchown32 sys_fchown
208 i386 setresuid32 sys_setresuid
209 i386 getresuid32 sys_getresuid
210 i386 setresgid32 sys_setresgid
211 i386 getresgid32 sys_getresgid
212 i386 chown32 sys_chown
213 i386 setuid32 sys_setuid
214 i386 setgid32 sys_setgid
215 i386 setfsuid32 sys_setfsuid
216 i386 setfsgid32 sys_setfsgid
217 i386 pivot_root sys_pivot_root
218 i386 mincore sys_mincore
219 i386 madvise sys_madvise
220 i386 getdents64 sys_getdents64
221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64
# 222 is unused
# 223 is unused
224 i386 gettid sys_gettid
225 i386 readahead sys_ia32_readahead
226 i386 setxattr sys_setxattr
227 i386 lsetxattr sys_lsetxattr
228 i386 fsetxattr sys_fsetxattr
229 i386 getxattr sys_getxattr
230 i386 lgetxattr sys_lgetxattr
231 i386 fgetxattr sys_fgetxattr
232 i386 listxattr sys_listxattr
233 i386 llistxattr sys_llistxattr
234 i386 flistxattr sys_flistxattr
235 i386 removexattr sys_removexattr
236 i386 lremovexattr sys_lremovexattr
237 i386 fremovexattr sys_fremovexattr
238 i386 tkill sys_tkill
239 i386 sendfile64 sys_sendfile64
240 i386 futex sys_futex_time32
241 i386 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity
242 i386 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity
243 i386 set_thread_area sys_set_thread_area
244 i386 get_thread_area sys_get_thread_area
245 i386 io_setup sys_io_setup compat_sys_io_setup
246 i386 io_destroy sys_io_destroy
247 i386 io_getevents sys_io_getevents_time32
248 i386 io_submit sys_io_submit compat_sys_io_submit
249 i386 io_cancel sys_io_cancel
250 i386 fadvise64 sys_ia32_fadvise64
# 251 is available for reuse (was briefly sys_set_zone_reclaim)
252 i386 exit_group sys_exit_group - noreturn
253 i386 lookup_dcookie
254 i386 epoll_create sys_epoll_create
255 i386 epoll_ctl sys_epoll_ctl
256 i386 epoll_wait sys_epoll_wait
257 i386 remap_file_pages sys_remap_file_pages
258 i386 set_tid_address sys_set_tid_address
259 i386 timer_create sys_timer_create compat_sys_timer_create
260 i386 timer_settime sys_timer_settime32
261 i386 timer_gettime sys_timer_gettime32
262 i386 timer_getoverrun sys_timer_getoverrun
263 i386 timer_delete sys_timer_delete
264 i386 clock_settime sys_clock_settime32
265 i386 clock_gettime sys_clock_gettime32
266 i386 clock_getres sys_clock_getres_time32
267 i386 clock_nanosleep sys_clock_nanosleep_time32
268 i386 statfs64 sys_statfs64 compat_sys_statfs64
269 i386 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64
270 i386 tgkill sys_tgkill
271 i386 utimes sys_utimes_time32
272 i386 fadvise64_64 sys_ia32_fadvise64_64
273 i386 vserver
274 i386 mbind sys_mbind
275 i386 get_mempolicy sys_get_mempolicy
276 i386 set_mempolicy sys_set_mempolicy
277 i386 mq_open sys_mq_open compat_sys_mq_open
278 i386 mq_unlink sys_mq_unlink
279 i386 mq_timedsend sys_mq_timedsend_time32
280 i386 mq_timedreceive sys_mq_timedreceive_time32
281 i386 mq_notify sys_mq_notify compat_sys_mq_notify
282 i386 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr
283 i386 kexec_load sys_kexec_load compat_sys_kexec_load
284 i386 waitid sys_waitid compat_sys_waitid
# 285 sys_setaltroot
286 i386 add_key sys_add_key
287 i386 request_key sys_request_key
288 i386 keyctl sys_keyctl compat_sys_keyctl
289 i386 ioprio_set sys_ioprio_set
290 i386 ioprio_get sys_ioprio_get
291 i386 inotify_init sys_inotify_init
292 i386 inotify_add_watch sys_inotify_add_watch
293 i386 inotify_rm_watch sys_inotify_rm_watch
294 i386 migrate_pages sys_migrate_pages
295 i386 openat sys_openat compat_sys_openat
296 i386 mkdirat sys_mkdirat
297 i386 mknodat sys_mknodat
298 i386 fchownat sys_fchownat
299 i386 futimesat sys_futimesat_time32
300 i386 fstatat64 sys_fstatat64 compat_sys_ia32_fstatat64
301 i386 unlinkat sys_unlinkat
302 i386 renameat sys_renameat
303 i386 linkat sys_linkat
304 i386 symlinkat sys_symlinkat
305 i386 readlinkat sys_readlinkat
306 i386 fchmodat sys_fchmodat
307 i386 faccessat sys_faccessat
308 i386 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32
309 i386 ppoll sys_ppoll_time32 compat_sys_ppoll_time32
310 i386 unshare sys_unshare
311 i386 set_robust_list sys_set_robust_list compat_sys_set_robust_list
312 i386 get_robust_list sys_get_robust_list compat_sys_get_robust_list
313 i386 splice sys_splice
314 i386 sync_file_range sys_ia32_sync_file_range
315 i386 tee sys_tee
316 i386 vmsplice sys_vmsplice
317 i386 move_pages sys_move_pages
318 i386 getcpu sys_getcpu
319 i386 epoll_pwait sys_epoll_pwait
320 i386 utimensat sys_utimensat_time32
321 i386 signalfd sys_signalfd compat_sys_signalfd
322 i386 timerfd_create sys_timerfd_create
323 i386 eventfd sys_eventfd
324 i386 fallocate sys_ia32_fallocate
325 i386 timerfd_settime sys_timerfd_settime32
326 i386 timerfd_gettime sys_timerfd_gettime32
327 i386 signalfd4 sys_signalfd4 compat_sys_signalfd4
328 i386 eventfd2 sys_eventfd2
329 i386 epoll_create1 sys_epoll_create1
330 i386 dup3 sys_dup3
331 i386 pipe2 sys_pipe2
332 i386 inotify_init1 sys_inotify_init1
333 i386 preadv sys_preadv compat_sys_preadv
334 i386 pwritev sys_pwritev compat_sys_pwritev
335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
336 i386 perf_event_open sys_perf_event_open
337 i386 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32
338 i386 fanotify_init sys_fanotify_init
339 i386 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark
340 i386 prlimit64 sys_prlimit64
341 i386 name_to_handle_at sys_name_to_handle_at
342 i386 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at
343 i386 clock_adjtime sys_clock_adjtime32
344 i386 syncfs sys_syncfs
345 i386 sendmmsg sys_sendmmsg compat_sys_sendmmsg
346 i386 setns sys_setns
347 i386 process_vm_readv sys_process_vm_readv
348 i386 process_vm_writev sys_process_vm_writev
349 i386 kcmp sys_kcmp
350 i386 finit_module sys_finit_module
351 i386 sched_setattr sys_sched_setattr
352 i386 sched_getattr sys_sched_getattr
353 i386 renameat2 sys_renameat2
354 i386 seccomp sys_seccomp
355 i386 getrandom sys_getrandom
356 i386 memfd_create sys_memfd_create
357 i386 bpf sys_bpf
358 i386 execveat sys_execveat compat_sys_execveat
359 i386 socket sys_socket
360 i386 socketpair sys_socketpair
361 i386 bind sys_bind
362 i386 connect sys_connect
363 i386 listen sys_listen
364 i386 accept4 sys_accept4
365 i386 getsockopt sys_getsockopt sys_getsockopt
366 i386 setsockopt sys_setsockopt sys_setsockopt
367 i386 getsockname sys_getsockname
368 i386 getpeername sys_getpeername
369 i386 sendto sys_sendto
370 i386 sendmsg sys_sendmsg compat_sys_sendmsg
371 i386 recvfrom sys_recvfrom compat_sys_recvfrom
372 i386 recvmsg sys_recvmsg compat_sys_recvmsg
373 i386 shutdown sys_shutdown
374 i386 userfaultfd sys_userfaultfd
375 i386 membarrier sys_membarrier
376 i386 mlock2 sys_mlock2
377 i386 copy_file_range sys_copy_file_range
378 i386 preadv2 sys_preadv2 compat_sys_preadv2
379 i386 pwritev2 sys_pwritev2 compat_sys_pwritev2
380 i386 pkey_mprotect sys_pkey_mprotect
381 i386 pkey_alloc sys_pkey_alloc
382 i386 pkey_free sys_pkey_free
383 i386 statx sys_statx
384 i386 arch_prctl sys_arch_prctl
385 i386 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents
386 i386 rseq sys_rseq
393 i386 semget sys_semget
394 i386 semctl sys_semctl compat_sys_semctl
395 i386 shmget sys_shmget
396 i386 shmctl sys_shmctl compat_sys_shmctl
397 i386 shmat sys_shmat compat_sys_shmat
398 i386 shmdt sys_shmdt
399 i386 msgget sys_msgget
400 i386 msgsnd sys_msgsnd compat_sys_msgsnd
401 i386 msgrcv sys_msgrcv compat_sys_msgrcv
402 i386 msgctl sys_msgctl compat_sys_msgctl
403 i386 clock_gettime64 sys_clock_gettime
404 i386 clock_settime64 sys_clock_settime
405 i386 clock_adjtime64 sys_clock_adjtime
406 i386 clock_getres_time64 sys_clock_getres
407 i386 clock_nanosleep_time64 sys_clock_nanosleep
408 i386 timer_gettime64 sys_timer_gettime
409 i386 timer_settime64 sys_timer_settime
410 i386 timerfd_gettime64 sys_timerfd_gettime
411 i386 timerfd_settime64 sys_timerfd_settime
412 i386 utimensat_time64 sys_utimensat
413 i386 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64
414 i386 ppoll_time64 sys_ppoll compat_sys_ppoll_time64
416 i386 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64
417 i386 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64
418 i386 mq_timedsend_time64 sys_mq_timedsend
419 i386 mq_timedreceive_time64 sys_mq_timedreceive
420 i386 semtimedop_time64 sys_semtimedop
421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64
422 i386 futex_time64 sys_futex
423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval
424 i386 pidfd_send_signal sys_pidfd_send_signal
425 i386 io_uring_setup sys_io_uring_setup
426 i386 io_uring_enter sys_io_uring_enter
427 i386 io_uring_register sys_io_uring_register
428 i386 open_tree sys_open_tree
429 i386 move_mount sys_move_mount
430 i386 fsopen sys_fsopen
431 i386 fsconfig sys_fsconfig
432 i386 fsmount sys_fsmount
433 i386 fspick sys_fspick
434 i386 pidfd_open sys_pidfd_open
435 i386 clone3 sys_clone3
436 i386 close_range sys_close_range
437 i386 openat2 sys_openat2
438 i386 pidfd_getfd sys_pidfd_getfd
439 i386 faccessat2 sys_faccessat2
440 i386 process_madvise sys_process_madvise
441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
442 i386 mount_setattr sys_mount_setattr
443 i386 quotactl_fd sys_quotactl_fd
444 i386 landlock_create_ruleset sys_landlock_create_ruleset
445 i386 landlock_add_rule sys_landlock_add_rule
446 i386 landlock_restrict_self sys_landlock_restrict_self
447 i386 memfd_secret sys_memfd_secret
448 i386 process_mrelease sys_process_mrelease
449 i386 futex_waitv sys_futex_waitv
450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node
451 i386 cachestat sys_cachestat
452 i386 fchmodat2 sys_fchmodat2
453 i386 map_shadow_stack sys_map_shadow_stack
454 i386 futex_wake sys_futex_wake
455 i386 futex_wait sys_futex_wait
456 i386 futex_requeue sys_futex_requeue
457 i386 statmount sys_statmount
458 i386 listmount sys_listmount
459 i386 lsm_get_self_attr sys_lsm_get_self_attr
460 i386 lsm_set_self_attr sys_lsm_set_self_attr
461 i386 lsm_list_modules sys_lsm_list_modules
462 i386 mseal sys_mseal
463 i386 setxattrat sys_setxattrat
464 i386 getxattrat sys_getxattrat
465 i386 listxattrat sys_listxattrat
466 i386 removexattrat sys_removexattrat
467 i386 open_tree_attr sys_open_tree_attr
468 i386 file_getattr sys_file_getattr
469 i386 file_setattr sys_file_setattr
470 i386 listns sys_listns
471 i386 rseq_slice_yield sys_rseq_slice_yield
@@ -0,0 +1,443 @@
# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
#
# 64-bit system call numbers and entry vectors
#
# The format is:
# <number> <abi> <name> <entry point> [<compat entry point> [noreturn]]
#
# The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls
#
# The abi is "common", "64" or "x32" for this file.
#
0 common read sys_read
1 common write sys_write
2 common open sys_open
3 common close sys_close
4 common stat sys_newstat
5 common fstat sys_newfstat
6 common lstat sys_newlstat
7 common poll sys_poll
8 common lseek sys_lseek
9 common mmap sys_mmap
10 common mprotect sys_mprotect
11 common munmap sys_munmap
12 common brk sys_brk
13 64 rt_sigaction sys_rt_sigaction
14 common rt_sigprocmask sys_rt_sigprocmask
15 64 rt_sigreturn sys_rt_sigreturn
16 64 ioctl sys_ioctl
17 common pread64 sys_pread64
18 common pwrite64 sys_pwrite64
19 64 readv sys_readv
20 64 writev sys_writev
21 common access sys_access
22 common pipe sys_pipe
23 common select sys_select
24 common sched_yield sys_sched_yield
25 common mremap sys_mremap
26 common msync sys_msync
27 common mincore sys_mincore
28 common madvise sys_madvise
29 common shmget sys_shmget
30 common shmat sys_shmat
31 common shmctl sys_shmctl
32 common dup sys_dup
33 common dup2 sys_dup2
34 common pause sys_pause
35 common nanosleep sys_nanosleep
36 common getitimer sys_getitimer
37 common alarm sys_alarm
38 common setitimer sys_setitimer
39 common getpid sys_getpid
40 common sendfile sys_sendfile64
41 common socket sys_socket
42 common connect sys_connect
43 common accept sys_accept
44 common sendto sys_sendto
45 64 recvfrom sys_recvfrom
46 64 sendmsg sys_sendmsg
47 64 recvmsg sys_recvmsg
48 common shutdown sys_shutdown
49 common bind sys_bind
50 common listen sys_listen
51 common getsockname sys_getsockname
52 common getpeername sys_getpeername
53 common socketpair sys_socketpair
54 64 setsockopt sys_setsockopt
55 64 getsockopt sys_getsockopt
56 common clone sys_clone
57 common fork sys_fork
58 common vfork sys_vfork
59 64 execve sys_execve
60 common exit sys_exit - noreturn
61 common wait4 sys_wait4
62 common kill sys_kill
63 common uname sys_newuname
64 common semget sys_semget
65 common semop sys_semop
66 common semctl sys_semctl
67 common shmdt sys_shmdt
68 common msgget sys_msgget
69 common msgsnd sys_msgsnd
70 common msgrcv sys_msgrcv
71 common msgctl sys_msgctl
72 common fcntl sys_fcntl
73 common flock sys_flock
74 common fsync sys_fsync
75 common fdatasync sys_fdatasync
76 common truncate sys_truncate
77 common ftruncate sys_ftruncate
78 common getdents sys_getdents
79 common getcwd sys_getcwd
80 common chdir sys_chdir
81 common fchdir sys_fchdir
82 common rename sys_rename
83 common mkdir sys_mkdir
84 common rmdir sys_rmdir
85 common creat sys_creat
86 common link sys_link
87 common unlink sys_unlink
88 common symlink sys_symlink
89 common readlink sys_readlink
90 common chmod sys_chmod
91 common fchmod sys_fchmod
92 common chown sys_chown
93 common fchown sys_fchown
94 common lchown sys_lchown
95 common umask sys_umask
96 common gettimeofday sys_gettimeofday
97 common getrlimit sys_getrlimit
98 common getrusage sys_getrusage
99 common sysinfo sys_sysinfo
100 common times sys_times
101 64 ptrace sys_ptrace
102 common getuid sys_getuid
103 common syslog sys_syslog
104 common getgid sys_getgid
105 common setuid sys_setuid
106 common setgid sys_setgid
107 common geteuid sys_geteuid
108 common getegid sys_getegid
109 common setpgid sys_setpgid
110 common getppid sys_getppid
111 common getpgrp sys_getpgrp
112 common setsid sys_setsid
113 common setreuid sys_setreuid
114 common setregid sys_setregid
115 common getgroups sys_getgroups
116 common setgroups sys_setgroups
117 common setresuid sys_setresuid
118 common getresuid sys_getresuid
119 common setresgid sys_setresgid
120 common getresgid sys_getresgid
121 common getpgid sys_getpgid
122 common setfsuid sys_setfsuid
123 common setfsgid sys_setfsgid
124 common getsid sys_getsid
125 common capget sys_capget
126 common capset sys_capset
127 64 rt_sigpending sys_rt_sigpending
128 64 rt_sigtimedwait sys_rt_sigtimedwait
129 64 rt_sigqueueinfo sys_rt_sigqueueinfo
130 common rt_sigsuspend sys_rt_sigsuspend
131 64 sigaltstack sys_sigaltstack
132 common utime sys_utime
133 common mknod sys_mknod
134 64 uselib
135 common personality sys_personality
136 common ustat sys_ustat
137 common statfs sys_statfs
138 common fstatfs sys_fstatfs
139 common sysfs sys_sysfs
140 common getpriority sys_getpriority
141 common setpriority sys_setpriority
142 common sched_setparam sys_sched_setparam
143 common sched_getparam sys_sched_getparam
144 common sched_setscheduler sys_sched_setscheduler
145 common sched_getscheduler sys_sched_getscheduler
146 common sched_get_priority_max sys_sched_get_priority_max
147 common sched_get_priority_min sys_sched_get_priority_min
148 common sched_rr_get_interval sys_sched_rr_get_interval
149 common mlock sys_mlock
150 common munlock sys_munlock
151 common mlockall sys_mlockall
152 common munlockall sys_munlockall
153 common vhangup sys_vhangup
154 common modify_ldt sys_modify_ldt
155 common pivot_root sys_pivot_root
156 64 _sysctl sys_ni_syscall
157 common prctl sys_prctl
158 common arch_prctl sys_arch_prctl
159 common adjtimex sys_adjtimex
160 common setrlimit sys_setrlimit
161 common chroot sys_chroot
162 common sync sys_sync
163 common acct sys_acct
164 common settimeofday sys_settimeofday
165 common mount sys_mount
166 common umount2 sys_umount
167 common swapon sys_swapon
168 common swapoff sys_swapoff
169 common reboot sys_reboot
170 common sethostname sys_sethostname
171 common setdomainname sys_setdomainname
172 common iopl sys_iopl
173 common ioperm sys_ioperm
174 64 create_module
175 common init_module sys_init_module
176 common delete_module sys_delete_module
177 64 get_kernel_syms
178 64 query_module
179 common quotactl sys_quotactl
180 64 nfsservctl
181 common getpmsg
182 common putpmsg
183 common afs_syscall
184 common tuxcall
185 common security
186 common gettid sys_gettid
187 common readahead sys_readahead
188 common setxattr sys_setxattr
189 common lsetxattr sys_lsetxattr
190 common fsetxattr sys_fsetxattr
191 common getxattr sys_getxattr
192 common lgetxattr sys_lgetxattr
193 common fgetxattr sys_fgetxattr
194 common listxattr sys_listxattr
195 common llistxattr sys_llistxattr
196 common flistxattr sys_flistxattr
197 common removexattr sys_removexattr
198 common lremovexattr sys_lremovexattr
199 common fremovexattr sys_fremovexattr
200 common tkill sys_tkill
201 common time sys_time
202 common futex sys_futex
203 common sched_setaffinity sys_sched_setaffinity
204 common sched_getaffinity sys_sched_getaffinity
205 64 set_thread_area
206 64 io_setup sys_io_setup
207 common io_destroy sys_io_destroy
208 common io_getevents sys_io_getevents
209 64 io_submit sys_io_submit
210 common io_cancel sys_io_cancel
211 64 get_thread_area
212 common lookup_dcookie
213 common epoll_create sys_epoll_create
214 64 epoll_ctl_old
215 64 epoll_wait_old
216 common remap_file_pages sys_remap_file_pages
217 common getdents64 sys_getdents64
218 common set_tid_address sys_set_tid_address
219 common restart_syscall sys_restart_syscall
220 common semtimedop sys_semtimedop
221 common fadvise64 sys_fadvise64
222 64 timer_create sys_timer_create
223 common timer_settime sys_timer_settime
224 common timer_gettime sys_timer_gettime
225 common timer_getoverrun sys_timer_getoverrun
226 common timer_delete sys_timer_delete
227 common clock_settime sys_clock_settime
228 common clock_gettime sys_clock_gettime
229 common clock_getres sys_clock_getres
230 common clock_nanosleep sys_clock_nanosleep
231 common exit_group sys_exit_group - noreturn
232 common epoll_wait sys_epoll_wait
233 common epoll_ctl sys_epoll_ctl
234 common tgkill sys_tgkill
235 common utimes sys_utimes
236 64 vserver
237 common mbind sys_mbind
238 common set_mempolicy sys_set_mempolicy
239 common get_mempolicy sys_get_mempolicy
240 common mq_open sys_mq_open
241 common mq_unlink sys_mq_unlink
242 common mq_timedsend sys_mq_timedsend
243 common mq_timedreceive sys_mq_timedreceive
244 64 mq_notify sys_mq_notify
245 common mq_getsetattr sys_mq_getsetattr
246 64 kexec_load sys_kexec_load
247 64 waitid sys_waitid
248 common add_key sys_add_key
249 common request_key sys_request_key
250 common keyctl sys_keyctl
251 common ioprio_set sys_ioprio_set
252 common ioprio_get sys_ioprio_get
253 common inotify_init sys_inotify_init
254 common inotify_add_watch sys_inotify_add_watch
255 common inotify_rm_watch sys_inotify_rm_watch
256 common migrate_pages sys_migrate_pages
257 common openat sys_openat
258 common mkdirat sys_mkdirat
259 common mknodat sys_mknodat
260 common fchownat sys_fchownat
261 common futimesat sys_futimesat
262 common newfstatat sys_newfstatat
263 common unlinkat sys_unlinkat
264 common renameat sys_renameat
265 common linkat sys_linkat
266 common symlinkat sys_symlinkat
267 common readlinkat sys_readlinkat
268 common fchmodat sys_fchmodat
269 common faccessat sys_faccessat
270 common pselect6 sys_pselect6
271 common ppoll sys_ppoll
272 common unshare sys_unshare
273 64 set_robust_list sys_set_robust_list
274 64 get_robust_list sys_get_robust_list
275 common splice sys_splice
276 common tee sys_tee
277 common sync_file_range sys_sync_file_range
278 64 vmsplice sys_vmsplice
279 64 move_pages sys_move_pages
280 common utimensat sys_utimensat
281 common epoll_pwait sys_epoll_pwait
282 common signalfd sys_signalfd
283 common timerfd_create sys_timerfd_create
284 common eventfd sys_eventfd
285 common fallocate sys_fallocate
286 common timerfd_settime sys_timerfd_settime
287 common timerfd_gettime sys_timerfd_gettime
288 common accept4 sys_accept4
289 common signalfd4 sys_signalfd4
290 common eventfd2 sys_eventfd2
291 common epoll_create1 sys_epoll_create1
292 common dup3 sys_dup3
293 common pipe2 sys_pipe2
294 common inotify_init1 sys_inotify_init1
295 64 preadv sys_preadv
296 64 pwritev sys_pwritev
297 64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo
298 common perf_event_open sys_perf_event_open
299 64 recvmmsg sys_recvmmsg
300 common fanotify_init sys_fanotify_init
301 common fanotify_mark sys_fanotify_mark
302 common prlimit64 sys_prlimit64
303 common name_to_handle_at sys_name_to_handle_at
304 common open_by_handle_at sys_open_by_handle_at
305 common clock_adjtime sys_clock_adjtime
306 common syncfs sys_syncfs
307 64 sendmmsg sys_sendmmsg
308 common setns sys_setns
309 common getcpu sys_getcpu
310 64 process_vm_readv sys_process_vm_readv
311 64 process_vm_writev sys_process_vm_writev
312 common kcmp sys_kcmp
313 common finit_module sys_finit_module
314 common sched_setattr sys_sched_setattr
315 common sched_getattr sys_sched_getattr
316 common renameat2 sys_renameat2
317 common seccomp sys_seccomp
318 common getrandom sys_getrandom
319 common memfd_create sys_memfd_create
320 common kexec_file_load sys_kexec_file_load
321 common bpf sys_bpf
322 64 execveat sys_execveat
323 common userfaultfd sys_userfaultfd
324 common membarrier sys_membarrier
325 common mlock2 sys_mlock2
326 common copy_file_range sys_copy_file_range
327 64 preadv2 sys_preadv2
328 64 pwritev2 sys_pwritev2
329 common pkey_mprotect sys_pkey_mprotect
330 common pkey_alloc sys_pkey_alloc
331 common pkey_free sys_pkey_free
332 common statx sys_statx
333 common io_pgetevents sys_io_pgetevents
334 common rseq sys_rseq
335 common uretprobe sys_uretprobe
336 common uprobe sys_uprobe
# don't use numbers 387 through 423, add new calls after the last
# 'common' entry
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
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 common memfd_secret sys_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
463 common setxattrat sys_setxattrat
464 common getxattrat sys_getxattrat
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
470 common listns sys_listns
471 common rseq_slice_yield sys_rseq_slice_yield
#
# Due to a historical design error, certain syscalls are numbered differently
# in x32 as compared to native x86_64. These syscalls have numbers 512-547.
# Do not add new syscalls to this range. Numbers 548 and above are available
# for non-x32 use.
#
512 x32 rt_sigaction compat_sys_rt_sigaction
513 x32 rt_sigreturn compat_sys_x32_rt_sigreturn
514 x32 ioctl compat_sys_ioctl
515 x32 readv sys_readv
516 x32 writev sys_writev
517 x32 recvfrom compat_sys_recvfrom
518 x32 sendmsg compat_sys_sendmsg
519 x32 recvmsg compat_sys_recvmsg
520 x32 execve compat_sys_execve
521 x32 ptrace compat_sys_ptrace
522 x32 rt_sigpending compat_sys_rt_sigpending
523 x32 rt_sigtimedwait compat_sys_rt_sigtimedwait_time64
524 x32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo
525 x32 sigaltstack compat_sys_sigaltstack
526 x32 timer_create compat_sys_timer_create
527 x32 mq_notify compat_sys_mq_notify
528 x32 kexec_load compat_sys_kexec_load
529 x32 waitid compat_sys_waitid
530 x32 set_robust_list compat_sys_set_robust_list
531 x32 get_robust_list compat_sys_get_robust_list
532 x32 vmsplice sys_vmsplice
533 x32 move_pages sys_move_pages
534 x32 preadv compat_sys_preadv64
535 x32 pwritev compat_sys_pwritev64
536 x32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
537 x32 recvmmsg compat_sys_recvmmsg_time64
538 x32 sendmmsg compat_sys_sendmmsg
539 x32 process_vm_readv sys_process_vm_readv
540 x32 process_vm_writev sys_process_vm_writev
541 x32 setsockopt sys_setsockopt
542 x32 getsockopt sys_getsockopt
543 x32 io_setup compat_sys_io_setup
544 x32 io_submit compat_sys_io_submit
545 x32 execveat compat_sys_execveat
546 x32 preadv2 compat_sys_preadv64v2
547 x32 pwritev2 compat_sys_pwritev64v2
# This is the end of the legacy x32 range. Numbers 548 and above are
# not special and are not to be used for x32-specific syscalls.
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_TESTS_H
#define ARCH_TESTS_H
#include "tests/tests.h"
struct test_suite;
/* Tests */
int test__rdpmc(struct test_suite *test, int subtest);
#ifdef HAVE_EXTRA_TESTS
int test__insn_x86(struct test_suite *test, int subtest);
#endif
int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest);
int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest);
int test__bp_modify(struct test_suite *test, int subtest);
int test__amd_ibs_via_core_pmu(struct test_suite *test, int subtest);
int test__amd_ibs_period(struct test_suite *test, int subtest);
int test__hybrid(struct test_suite *test, int subtest);
DECLARE_SUITE(x86_topdown);
extern struct test_suite *arch_tests[];
#endif
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifdef DEFINE_DWARF_REGSTR_TABLE
/* This is included in perf/util/dwarf-regs.c */
static const char * const x86_32_regstr_tbl[] = {
"%ax", "%cx", "%dx", "%bx", "$stack",/* Stack address instead of %sp */
"%bp", "%si", "%di",
};
static const char * const x86_64_regstr_tbl[] = {
"%ax", "%dx", "%cx", "%bx", "%si", "%di",
"%bp", "%sp", "%r8", "%r9", "%r10", "%r11",
"%r12", "%r13", "%r14", "%r15",
};
#endif
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_PERF_REGS_H
#define ARCH_PERF_REGS_H
#include <stdlib.h>
#include <linux/types.h>
#include "../../../../arch/x86/include/uapi/asm/perf_regs.h"
void perf_regs_load(u64 *regs);
#define PERF_REGS_MAX PERF_REG_X86_XMM_MAX
#ifndef HAVE_ARCH_X86_64_SUPPORT
#define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1)
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32
#else
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
(1ULL << PERF_REG_X86_ES) | \
(1ULL << PERF_REG_X86_FS) | \
(1ULL << PERF_REG_X86_GS))
#define PERF_REGS_MASK (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~REG_NOSUPPORT)
#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64
#endif
#endif /* ARCH_PERF_REGS_H */
@@ -0,0 +1,27 @@
perf-test-$(CONFIG_DWARF_UNWIND) += regs_load.o
perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
perf-test-y += arch-tests.o
perf-test-y += hybrid.o
perf-test-y += intel-pt-test.o
ifeq ($(CONFIG_EXTRA_TESTS),y)
perf-test-y += insn-x86.o
endif
perf-test-$(CONFIG_X86_64) += bp-modify.o
perf-test-y += amd-ibs-via-core-pmu.o
perf-test-y += amd-ibs-period.o
perf-test-y += topdown.o
ifdef SHELLCHECK
SHELL_TESTS := gen-insn-x86-dat.sh
SHELL_TEST_LOGS := $(SHELL_TESTS:%=%.shellcheck_log)
else
SHELL_TESTS :=
SHELL_TEST_LOGS :=
endif
$(OUTPUT)%.shellcheck_log: %
$(call rule_mkdir)
$(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false)
perf-test-y += $(SHELL_TEST_LOGS)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
#include "arch-tests.h"
#include "linux/perf_event.h"
#include "tests/tests.h"
#include "pmu.h"
#include "pmus.h"
#include "../perf-sys.h"
#include "debug.h"
#define NR_SUB_TESTS 5
static struct sub_tests {
int type;
unsigned long config;
bool valid;
} sub_tests[NR_SUB_TESTS] = {
{ PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, true },
{ PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, false },
{ PERF_TYPE_RAW, 0x076, true },
{ PERF_TYPE_RAW, 0x0C1, true },
{ PERF_TYPE_RAW, 0x012, false },
};
static int event_open(int type, unsigned long config)
{
struct perf_event_attr attr;
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.type = type;
attr.size = sizeof(struct perf_event_attr);
attr.config = config;
attr.disabled = 1;
attr.precise_ip = 1;
attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
attr.sample_period = 100000;
return sys_perf_event_open(&attr, -1, 0, -1, 0);
}
int test__amd_ibs_via_core_pmu(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_pmu *ibs_pmu;
int ret = TEST_OK;
int fd, i;
ibs_pmu = perf_pmus__find("ibs_op");
if (!ibs_pmu)
return TEST_SKIP;
for (i = 0; i < NR_SUB_TESTS; i++) {
fd = event_open(sub_tests[i].type, sub_tests[i].config);
pr_debug("type: 0x%x, config: 0x%lx, fd: %d - ", sub_tests[i].type,
sub_tests[i].config, fd);
if ((sub_tests[i].valid && fd == -1) ||
(!sub_tests[i].valid && fd > 0)) {
pr_debug("Fail\n");
ret = TEST_FAIL;
} else {
pr_debug("Pass\n");
}
if (fd > 0)
close(fd);
}
return ret;
}
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "tests/tests.h"
#include "arch-tests.h"
#ifdef HAVE_EXTRA_TESTS
DEFINE_SUITE("x86 instruction decoder - new instructions", insn_x86);
#endif
static struct test_case intel_pt_tests[] = {
TEST_CASE("Intel PT packet decoder", intel_pt_pkt_decoder),
TEST_CASE("Intel PT hybrid CPU compatibility", intel_pt_hybrid_compat),
{ .name = NULL, }
};
struct test_suite suite__intel_pt = {
.desc = "Intel PT",
.test_cases = intel_pt_tests,
};
#if defined(__x86_64__)
DEFINE_SUITE("x86 bp modify", bp_modify);
#endif
DEFINE_SUITE("AMD IBS via core pmu", amd_ibs_via_core_pmu);
DEFINE_SUITE_EXCLUSIVE("AMD IBS sample period", amd_ibs_period);
static struct test_case hybrid_tests[] = {
TEST_CASE_REASON("x86 hybrid event parsing", hybrid, "not hybrid"),
{ .name = NULL, }
};
struct test_suite suite__hybrid = {
.desc = "x86 hybrid",
.test_cases = hybrid_tests,
};
struct test_suite *arch_tests[] = {
#ifdef HAVE_DWARF_UNWIND_SUPPORT
&suite__dwarf_unwind,
#endif
#ifdef HAVE_EXTRA_TESTS
&suite__insn_x86,
#endif
&suite__intel_pt,
#if defined(__x86_64__)
&suite__bp_modify,
#endif
&suite__amd_ibs_via_core_pmu,
&suite__amd_ibs_period,
&suite__hybrid,
&suite__x86_topdown,
NULL,
};
@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <errno.h>
#include "debug.h"
#include "tests/tests.h"
#include "arch-tests.h"
static noinline int bp_1(void)
{
pr_debug("in %s\n", __func__);
return 0;
}
static noinline int bp_2(void)
{
pr_debug("in %s\n", __func__);
return 0;
}
static int spawn_child(void)
{
int child = fork();
if (child == 0) {
/*
* The child sets itself for as tracee and
* waits in signal for parent to trace it,
* then it calls bp_1 and quits.
*/
int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if (err) {
pr_debug("failed to PTRACE_TRACEME\n");
exit(1);
}
raise(SIGCONT);
bp_1();
exit(0);
}
return child;
}
/*
* This tests creates HW breakpoint, tries to
* change it and checks it was properly changed.
*/
static int bp_modify1(void)
{
pid_t child;
int status;
unsigned long rip = 0, dr7 = 1;
child = spawn_child();
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
pr_debug("tracee exited prematurely 1\n");
return TEST_FAIL;
}
/*
* The parent does following steps:
* - creates a new breakpoint (id 0) for bp_2 function
* - changes that breakpoint to bp_1 function
* - waits for the breakpoint to hit and checks
* it has proper rip of bp_1 function
* - detaches the child
*/
if (ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[0]), bp_2)) {
pr_debug("failed to set breakpoint, 1st time: %m\n");
goto out;
}
if (ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[0]), bp_1)) {
pr_debug("failed to set breakpoint, 2nd time: %m\n");
goto out;
}
if (ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[7]), dr7)) {
pr_debug("failed to set dr7: %m\n");
goto out;
}
if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
pr_debug("failed to PTRACE_CONT: %m\n");
goto out;
}
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
pr_debug("tracee exited prematurely 2\n");
return TEST_FAIL;
}
rip = ptrace(PTRACE_PEEKUSER, child,
offsetof(struct user_regs_struct, rip), NULL);
if (rip == (unsigned long) -1) {
pr_debug("failed to PTRACE_PEEKUSER: %m\n");
goto out;
}
pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
out:
if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
pr_debug("failed to PTRACE_DETACH: %m\n");
return TEST_FAIL;
}
return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
}
/*
* This tests creates HW breakpoint, tries to
* change it to bogus value and checks the original
* breakpoint is hit.
*/
static int bp_modify2(void)
{
pid_t child;
int status;
unsigned long rip = 0, dr7 = 1;
child = spawn_child();
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
pr_debug("tracee exited prematurely 1\n");
return TEST_FAIL;
}
/*
* The parent does following steps:
* - creates a new breakpoint (id 0) for bp_1 function
* - tries to change that breakpoint to (-1) address
* - waits for the breakpoint to hit and checks
* it has proper rip of bp_1 function
* - detaches the child
*/
if (ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[0]), bp_1)) {
pr_debug("failed to set breakpoint: %m\n");
goto out;
}
if (ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[7]), dr7)) {
pr_debug("failed to set dr7: %m\n");
goto out;
}
if (!ptrace(PTRACE_POKEUSER, child,
offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
pr_debug("failed, breakpoint set to bogus address\n");
goto out;
}
if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
pr_debug("failed to PTRACE_CONT: %m\n");
goto out;
}
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
pr_debug("tracee exited prematurely 2\n");
return TEST_FAIL;
}
rip = ptrace(PTRACE_PEEKUSER, child,
offsetof(struct user_regs_struct, rip), NULL);
if (rip == (unsigned long) -1) {
pr_debug("failed to PTRACE_PEEKUSER: %m\n");
goto out;
}
pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
out:
if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
pr_debug("failed to PTRACE_DETACH: %m\n");
return TEST_FAIL;
}
return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
}
int test__bp_modify(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
return 0;
}
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "perf_regs.h"
#include "thread.h"
#include "map.h"
#include "maps.h"
#include "event.h"
#include "debug.h"
#include "tests/tests.h"
#define STACK_SIZE 8192
static int sample_ustack(struct perf_sample *sample,
struct thread *thread, u64 *regs)
{
struct stack_dump *stack = &sample->user_stack;
struct map *map;
unsigned long sp;
u64 stack_size, *buf;
buf = malloc(STACK_SIZE);
if (!buf) {
pr_debug("failed to allocate sample uregs data\n");
return -1;
}
sp = (unsigned long) regs[PERF_REG_X86_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(map) - sp;
map__put(map);
stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
memcpy(buf, (void *) sp, stack_size);
#ifdef MEMORY_SANITIZER
/*
* Copying the stack may copy msan poison, avoid false positives in the
* unwinder by removing the poison here.
*/
__msan_unpoison(buf, stack_size);
#endif
stack->data = (char *) buf;
stack->size = stack_size;
return 0;
}
int test__arch_unwind_sample(struct perf_sample *sample,
struct thread *thread)
{
struct regs_dump *regs = perf_sample__user_regs(sample);
u64 *buf = calloc(PERF_REGS_MAX, sizeof(u64));
if (!buf) {
pr_debug("failed to allocate sample uregs data\n");
return -1;
}
perf_regs_load(buf);
regs->abi = PERF_SAMPLE_REGS_ABI;
regs->regs = buf;
regs->mask = PERF_REGS_MASK;
return sample_ustack(sample, thread, buf);
}
@@ -0,0 +1,68 @@
#!/bin/awk -f
# SPDX-License-Identifier: GPL-2.0-only
# gen-insn-x86-dat.awk: script to convert data for the insn-x86 test
# Copyright (c) 2015, Intel Corporation.
#
BEGIN {
print "/*"
print " * Generated by gen-insn-x86-dat.sh and gen-insn-x86-dat.awk"
print " * from insn-x86-dat-src.c for inclusion by insn-x86.c"
print " * Do not change this code."
print "*/\n"
op = ""
branch = ""
rel = 0
going = 0
}
/ Start here / {
going = 1
}
/ Stop here / {
going = 0
}
/^\s*[0-9a-fA-F]+\:/ {
if (going) {
colon_pos = index($0, ":")
useful_line = substr($0, colon_pos + 1)
first_pos = match(useful_line, "[0-9a-fA-F]")
useful_line = substr(useful_line, first_pos)
gsub("\t", "\\t", useful_line)
printf "{{"
len = 0
for (i = 2; i <= NF; i++) {
if (match($i, "^[0-9a-fA-F][0-9a-fA-F]$")) {
printf "0x%s, ", $i
len += 1
} else {
break
}
}
printf "}, %d, %s, \"%s\", \"%s\",", len, rel, op, branch
printf "\n\"%s\",},\n", useful_line
op = ""
branch = ""
rel = 0
}
}
/ Expecting: / {
expecting_str = " Expecting: "
expecting_len = length(expecting_str)
expecting_pos = index($0, expecting_str)
useful_line = substr($0, expecting_pos + expecting_len)
for (i = 1; i <= NF; i++) {
if ($i == "Expecting:") {
i++
op = $i
i++
branch = $i
i++
rel = $i
break
}
}
}
+36
View File
@@ -0,0 +1,36 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# gen-insn-x86-dat: generate data for the insn-x86 test
# Copyright (c) 2015, Intel Corporation.
#
set -e
if [ "$(uname -m)" != "x86_64" ]; then
echo "ERROR: This script only works on x86_64"
exit 1
fi
cd "$(dirname $0)"
trap 'echo "Might need a more recent version of binutils"' EXIT
echo "Compiling insn-x86-dat-src.c to 64-bit object"
gcc -g -c insn-x86-dat-src.c
objdump -dSw insn-x86-dat-src.o | awk -f gen-insn-x86-dat.awk > insn-x86-dat-64.c
rm -f insn-x86-dat-src.o
echo "Compiling insn-x86-dat-src.c to 32-bit object"
gcc -g -c -m32 insn-x86-dat-src.c
objdump -dSw insn-x86-dat-src.o | awk -f gen-insn-x86-dat.awk > insn-x86-dat-32.c
rm -f insn-x86-dat-src.o
trap - EXIT
echo "Done (use git diff to see the changes)"
@@ -0,0 +1,310 @@
// SPDX-License-Identifier: GPL-2.0
#include "arch-tests.h"
#include "debug.h"
#include "evlist.h"
#include "evsel.h"
#include "pmu.h"
#include "pmus.h"
#include "tests/tests.h"
static bool test_config(const struct evsel *evsel, __u64 expected_config)
{
return (evsel->core.attr.config & PERF_HW_EVENT_MASK) == expected_config;
}
static bool test_perf_config(const struct perf_evsel *evsel, __u64 expected_config)
{
return (evsel->attr.config & PERF_HW_EVENT_MASK) == expected_config;
}
static bool test_hybrid_type(const struct evsel *evsel, __u64 expected_config)
{
return (evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT) == expected_config;
}
static int test__hybrid_hw_event_with_pmu(struct evlist *evlist)
{
struct evsel *evsel = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
return TEST_OK;
}
static int test__hybrid_hw_group_event(struct evlist *evlist)
{
struct evsel *evsel, *leader;
evsel = leader = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
evsel = evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_INSTRUCTIONS));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
return TEST_OK;
}
static int test__hybrid_sw_hw_group_event(struct evlist *evlist)
{
struct evsel *evsel, *leader;
evsel = leader = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
evsel = evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
return TEST_OK;
}
static int test__hybrid_hw_sw_group_event(struct evlist *evlist)
{
struct evsel *evsel, *leader;
evsel = leader = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
evsel = evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
return TEST_OK;
}
static int test__hybrid_group_modifier1(struct evlist *evlist)
{
struct evsel *evsel, *leader;
evsel = leader = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
TEST_ASSERT_VAL("wrong exclude_user", evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->core.attr.exclude_kernel);
evsel = evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_BRANCH_INSTRUCTIONS));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
TEST_ASSERT_VAL("wrong exclude_user", !evsel->core.attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->core.attr.exclude_kernel);
return TEST_OK;
}
static int test__hybrid_raw1(struct evlist *evlist)
{
struct perf_evsel *evsel;
perf_evlist__for_each_evsel(&evlist->core, evsel) {
struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->attr.type);
TEST_ASSERT_VAL("missing pmu", pmu);
TEST_ASSERT_VAL("unexpected pmu", !strncmp(pmu->name, "cpu_", 4));
TEST_ASSERT_VAL("wrong config", test_perf_config(evsel, 0x1a));
}
return TEST_OK;
}
static int test__hybrid_raw2(struct evlist *evlist)
{
struct evsel *evsel = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config", test_config(evsel, 0x1a));
return TEST_OK;
}
static int test__hybrid_cache_event(struct evlist *evlist)
{
struct evsel *evsel = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config", 0x2 == (evsel->core.attr.config & 0xffffffff));
return TEST_OK;
}
static int test__checkevent_pmu(struct evlist *evlist)
{
struct evsel *evsel = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config", 10 == evsel->core.attr.config);
TEST_ASSERT_VAL("wrong config1", 1 == evsel->core.attr.config1);
TEST_ASSERT_VAL("wrong config2", 3 == evsel->core.attr.config2);
TEST_ASSERT_VAL("wrong config3", 0 == evsel->core.attr.config3);
/*
* The period value gets configured within evlist__config,
* while this test executes only parse events method.
*/
TEST_ASSERT_VAL("wrong period", 0 == evsel->core.attr.sample_period);
return TEST_OK;
}
static int test__hybrid_hw_group_event_2(struct evlist *evlist)
{
struct evsel *evsel, *leader;
evsel = leader = evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->core.nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong hybrid type", test_hybrid_type(evsel, PERF_TYPE_RAW));
TEST_ASSERT_VAL("wrong config", test_config(evsel, PERF_COUNT_HW_CPU_CYCLES));
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
evsel = evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->core.attr.type);
TEST_ASSERT_VAL("wrong config", evsel->core.attr.config == 0x3c);
TEST_ASSERT_VAL("wrong leader", evsel__has_leader(evsel, leader));
return TEST_OK;
}
struct evlist_test {
const char *name;
bool (*valid)(void);
int (*check)(struct evlist *evlist);
};
static const struct evlist_test test__hybrid_events[] = {
{
.name = "cpu_core/cycles/",
.check = test__hybrid_hw_event_with_pmu,
/* 0 */
},
{
.name = "{cpu_core/cycles/,cpu_core/branches/}",
.check = test__hybrid_hw_group_event,
/* 1 */
},
{
.name = "{cpu-clock,cpu_core/cycles/}",
.check = test__hybrid_sw_hw_group_event,
/* 2 */
},
{
.name = "{cpu_core/cycles/,cpu-clock}",
.check = test__hybrid_hw_sw_group_event,
/* 3 */
},
{
.name = "{cpu_core/cycles/k,cpu_core/branches/u}",
.check = test__hybrid_group_modifier1,
/* 4 */
},
{
.name = "r1a",
.check = test__hybrid_raw1,
/* 5 */
},
{
.name = "cpu_core/r1a/",
.check = test__hybrid_raw2,
/* 6 */
},
{
.name = "cpu_core/config=10,config1,config2=3,period=1000/u",
.check = test__checkevent_pmu,
/* 7 */
},
{
.name = "cpu_core/LLC-loads/",
.check = test__hybrid_cache_event,
/* 8 */
},
{
.name = "{cpu_core/cycles/,cpu_core/cpu-cycles/}",
.check = test__hybrid_hw_group_event_2,
/* 9 */
},
};
static int test_event(const struct evlist_test *e)
{
struct parse_events_error err;
struct evlist *evlist;
int ret;
if (e->valid && !e->valid()) {
pr_debug("... SKIP\n");
return TEST_OK;
}
evlist = evlist__new();
if (evlist == NULL) {
pr_err("Failed allocation");
return TEST_FAIL;
}
parse_events_error__init(&err);
ret = parse_events(evlist, e->name, &err);
if (ret) {
pr_debug("failed to parse event '%s', err %d\n", e->name, ret);
parse_events_error__print(&err, e->name);
ret = TEST_FAIL;
if (parse_events_error__contains(&err, "can't access trace events"))
ret = TEST_SKIP;
} else {
ret = e->check(evlist);
}
parse_events_error__exit(&err);
evlist__delete(evlist);
return ret;
}
static int combine_test_results(int existing, int latest)
{
if (existing == TEST_FAIL)
return TEST_FAIL;
if (existing == TEST_SKIP)
return latest == TEST_OK ? TEST_SKIP : latest;
return latest;
}
static int test_events(const struct evlist_test *events, int cnt)
{
int ret = TEST_OK;
for (int i = 0; i < cnt; i++) {
const struct evlist_test *e = &events[i];
int test_ret;
pr_debug("running test %d '%s'\n", i, e->name);
test_ret = test_event(e);
if (test_ret != TEST_OK) {
pr_debug("Event test failure: test %d '%s'", i, e->name);
ret = combine_test_results(ret, test_ret);
}
}
return ret;
}
int test__hybrid(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
if (perf_pmus__num_core_pmus() == 1)
return TEST_SKIP;
return test_events(test__hybrid_events, ARRAY_SIZE(test__hybrid_events));
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <string.h>
#include "debug.h"
#include "tests/tests.h"
#include "arch-tests.h"
#include "../../../../arch/x86/include/asm/insn.h"
#include "intel-pt-decoder/intel-pt-insn-decoder.h"
struct test_data {
u8 data[MAX_INSN_SIZE];
int expected_length;
int expected_rel;
const char *expected_op_str;
const char *expected_branch_str;
const char *asm_rep;
};
const struct test_data test_data_32[] = {
#include "insn-x86-dat-32.c"
{{0x0f, 0x01, 0xee}, 3, 0, NULL, NULL, "0f 01 ee \trdpkru"},
{{0x0f, 0x01, 0xef}, 3, 0, NULL, NULL, "0f 01 ef \twrpkru"},
{{0}, 0, 0, NULL, NULL, NULL},
};
const struct test_data test_data_64[] = {
#include "insn-x86-dat-64.c"
{{0x0f, 0x01, 0xee}, 3, 0, NULL, NULL, "0f 01 ee \trdpkru"},
{{0x0f, 0x01, 0xef}, 3, 0, NULL, NULL, "0f 01 ef \twrpkru"},
{{0xf2, 0x0f, 0x01, 0xca}, 4, 0, "erets", "indirect", "f2 0f 01 ca \terets"},
{{0xf3, 0x0f, 0x01, 0xca}, 4, 0, "eretu", "indirect", "f3 0f 01 ca \teretu"},
{{0}, 0, 0, NULL, NULL, NULL},
};
static int get_op(const char *op_str)
{
struct val_data {
const char *name;
int val;
} vals[] = {
{"other", INTEL_PT_OP_OTHER},
{"call", INTEL_PT_OP_CALL},
{"ret", INTEL_PT_OP_RET},
{"jcc", INTEL_PT_OP_JCC},
{"jmp", INTEL_PT_OP_JMP},
{"loop", INTEL_PT_OP_LOOP},
{"iret", INTEL_PT_OP_IRET},
{"int", INTEL_PT_OP_INT},
{"syscall", INTEL_PT_OP_SYSCALL},
{"sysret", INTEL_PT_OP_SYSRET},
{"vmentry", INTEL_PT_OP_VMENTRY},
{"erets", INTEL_PT_OP_ERETS},
{"eretu", INTEL_PT_OP_ERETU},
{NULL, 0},
};
struct val_data *val;
if (!op_str || !strlen(op_str))
return 0;
for (val = vals; val->name; val++) {
if (!strcmp(val->name, op_str))
return val->val;
}
pr_debug("Failed to get op\n");
return -1;
}
static int get_branch(const char *branch_str)
{
struct val_data {
const char *name;
int val;
} vals[] = {
{"no_branch", INTEL_PT_BR_NO_BRANCH},
{"indirect", INTEL_PT_BR_INDIRECT},
{"conditional", INTEL_PT_BR_CONDITIONAL},
{"unconditional", INTEL_PT_BR_UNCONDITIONAL},
{NULL, 0},
};
struct val_data *val;
if (!branch_str || !strlen(branch_str))
return 0;
for (val = vals; val->name; val++) {
if (!strcmp(val->name, branch_str))
return val->val;
}
pr_debug("Failed to get branch\n");
return -1;
}
static int test_data_item(const struct test_data *dat, int x86_64)
{
struct intel_pt_insn intel_pt_insn;
int op, branch, ret;
struct insn insn;
ret = insn_decode(&insn, dat->data, MAX_INSN_SIZE,
x86_64 ? INSN_MODE_64 : INSN_MODE_32);
if (ret < 0) {
pr_debug("Failed to decode: %s\n", dat->asm_rep);
return -1;
}
if (insn.length != dat->expected_length) {
pr_debug("Failed to decode length (%d vs expected %d): %s\n",
insn.length, dat->expected_length, dat->asm_rep);
return -1;
}
op = get_op(dat->expected_op_str);
branch = get_branch(dat->expected_branch_str);
if (intel_pt_get_insn(dat->data, MAX_INSN_SIZE, x86_64, &intel_pt_insn)) {
pr_debug("Intel PT failed to decode: %s\n", dat->asm_rep);
return -1;
}
if ((int)intel_pt_insn.op != op) {
pr_debug("Failed to decode 'op' value (%d vs expected %d): %s\n",
intel_pt_insn.op, op, dat->asm_rep);
return -1;
}
if ((int)intel_pt_insn.branch != branch) {
pr_debug("Failed to decode 'branch' value (%d vs expected %d): %s\n",
intel_pt_insn.branch, branch, dat->asm_rep);
return -1;
}
if (intel_pt_insn.rel != dat->expected_rel) {
pr_debug("Failed to decode 'rel' value (%#x vs expected %#x): %s\n",
intel_pt_insn.rel, dat->expected_rel, dat->asm_rep);
return -1;
}
pr_debug("Decoded ok: %s\n", dat->asm_rep);
return 0;
}
static int test_data_set(const struct test_data *dat_set, int x86_64)
{
const struct test_data *dat;
int ret = 0;
for (dat = dat_set; dat->expected_length; dat++) {
if (test_data_item(dat, x86_64))
ret = -1;
}
return ret;
}
/**
* test__insn_x86 - test x86 instruction decoder - new instructions.
*
* This function implements a test that decodes a selection of instructions and
* checks the results. The Intel PT function that further categorizes
* instructions (i.e. intel_pt_get_insn()) is also checked.
*
* The instructions are originally in insn-x86-dat-src.c which has been
* processed by scripts gen-insn-x86-dat.sh and gen-insn-x86-dat.awk to produce
* insn-x86-dat-32.c and insn-x86-dat-64.c which are included into this program.
* i.e. to add new instructions to the test, edit insn-x86-dat-src.c, run the
* gen-insn-x86-dat.sh script, make perf, and then run the test.
*
* If the test passes %0 is returned, otherwise %-1 is returned. Use the
* verbose (-v) option to see all the instructions and whether or not they
* decoded successfully.
*/
int test__insn_x86(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
int ret = 0;
if (test_data_set(test_data_32, 0))
ret = -1;
if (test_data_set(test_data_64, 1))
ret = -1;
return ret;
}
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/bits.h>
#include <string.h>
#include <sched.h>
#include "intel-pt-decoder/intel-pt-pkt-decoder.h"
#include "debug.h"
#include "tests/tests.h"
#include "arch-tests.h"
#include "../util/cpuid.h"
#include "cpumap.h"
/**
* struct test_data - Test data.
* @len: number of bytes to decode
* @bytes: bytes to decode
* @ctx: packet context to decode
* @packet: expected packet
* @new_ctx: expected new packet context
* @ctx_unchanged: the packet context must not change
*/
static const struct test_data {
int len;
u8 bytes[INTEL_PT_PKT_MAX_SZ];
enum intel_pt_pkt_ctx ctx;
struct intel_pt_pkt packet;
enum intel_pt_pkt_ctx new_ctx;
int ctx_unchanged;
} data[] = {
/* Padding Packet */
{1, {0}, 0, {INTEL_PT_PAD, 0, 0}, 0, 1 },
/* Short Taken/Not Taken Packet */
{1, {4}, 0, {INTEL_PT_TNT, 1, 0}, 0, 0 },
{1, {6}, 0, {INTEL_PT_TNT, 1, 0x20ULL << 58}, 0, 0 },
{1, {0x80}, 0, {INTEL_PT_TNT, 6, 0}, 0, 0 },
{1, {0xfe}, 0, {INTEL_PT_TNT, 6, 0x3fULL << 58}, 0, 0 },
/* Long Taken/Not Taken Packet */
{8, {0x02, 0xa3, 2}, 0, {INTEL_PT_TNT, 1, 0xa302ULL << 47}, 0, 0 },
{8, {0x02, 0xa3, 3}, 0, {INTEL_PT_TNT, 1, 0x1a302ULL << 47}, 0, 0 },
{8, {0x02, 0xa3, 0, 0, 0, 0, 0, 0x80}, 0, {INTEL_PT_TNT, 47, 0xa302ULL << 1}, 0, 0 },
{8, {0x02, 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, {INTEL_PT_TNT, 47, 0xffffffffffffa302ULL << 1}, 0, 0 },
/* Target IP Packet */
{1, {0x0d}, 0, {INTEL_PT_TIP, 0, 0}, 0, 0 },
{3, {0x2d, 1, 2}, 0, {INTEL_PT_TIP, 1, 0x201}, 0, 0 },
{5, {0x4d, 1, 2, 3, 4}, 0, {INTEL_PT_TIP, 2, 0x4030201}, 0, 0 },
{7, {0x6d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP, 3, 0x60504030201}, 0, 0 },
{7, {0x8d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP, 4, 0x60504030201}, 0, 0 },
{9, {0xcd, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP, 6, 0x807060504030201}, 0, 0 },
/* Packet Generation Enable */
{1, {0x11}, 0, {INTEL_PT_TIP_PGE, 0, 0}, 0, 0 },
{3, {0x31, 1, 2}, 0, {INTEL_PT_TIP_PGE, 1, 0x201}, 0, 0 },
{5, {0x51, 1, 2, 3, 4}, 0, {INTEL_PT_TIP_PGE, 2, 0x4030201}, 0, 0 },
{7, {0x71, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGE, 3, 0x60504030201}, 0, 0 },
{7, {0x91, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGE, 4, 0x60504030201}, 0, 0 },
{9, {0xd1, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP_PGE, 6, 0x807060504030201}, 0, 0 },
/* Packet Generation Disable */
{1, {0x01}, 0, {INTEL_PT_TIP_PGD, 0, 0}, 0, 0 },
{3, {0x21, 1, 2}, 0, {INTEL_PT_TIP_PGD, 1, 0x201}, 0, 0 },
{5, {0x41, 1, 2, 3, 4}, 0, {INTEL_PT_TIP_PGD, 2, 0x4030201}, 0, 0 },
{7, {0x61, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGD, 3, 0x60504030201}, 0, 0 },
{7, {0x81, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_TIP_PGD, 4, 0x60504030201}, 0, 0 },
{9, {0xc1, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_TIP_PGD, 6, 0x807060504030201}, 0, 0 },
/* Flow Update Packet */
{1, {0x1d}, 0, {INTEL_PT_FUP, 0, 0}, 0, 0 },
{3, {0x3d, 1, 2}, 0, {INTEL_PT_FUP, 1, 0x201}, 0, 0 },
{5, {0x5d, 1, 2, 3, 4}, 0, {INTEL_PT_FUP, 2, 0x4030201}, 0, 0 },
{7, {0x7d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_FUP, 3, 0x60504030201}, 0, 0 },
{7, {0x9d, 1, 2, 3, 4, 5, 6}, 0, {INTEL_PT_FUP, 4, 0x60504030201}, 0, 0 },
{9, {0xdd, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_FUP, 6, 0x807060504030201}, 0, 0 },
/* Paging Information Packet */
{8, {0x02, 0x43, 2, 4, 6, 8, 10, 12}, 0, {INTEL_PT_PIP, 0, 0xC0A08060402}, 0, 0 },
{8, {0x02, 0x43, 3, 4, 6, 8, 10, 12}, 0, {INTEL_PT_PIP, 0, 0xC0A08060403}, 0, 0 },
/* Mode Exec Packet */
{2, {0x99, 0x00}, 0, {INTEL_PT_MODE_EXEC, 0, 16}, 0, 0 },
{2, {0x99, 0x01}, 0, {INTEL_PT_MODE_EXEC, 1, 64}, 0, 0 },
{2, {0x99, 0x02}, 0, {INTEL_PT_MODE_EXEC, 2, 32}, 0, 0 },
{2, {0x99, 0x04}, 0, {INTEL_PT_MODE_EXEC, 4, 16}, 0, 0 },
{2, {0x99, 0x05}, 0, {INTEL_PT_MODE_EXEC, 5, 64}, 0, 0 },
{2, {0x99, 0x06}, 0, {INTEL_PT_MODE_EXEC, 6, 32}, 0, 0 },
/* Mode TSX Packet */
{2, {0x99, 0x20}, 0, {INTEL_PT_MODE_TSX, 0, 0}, 0, 0 },
{2, {0x99, 0x21}, 0, {INTEL_PT_MODE_TSX, 0, 1}, 0, 0 },
{2, {0x99, 0x22}, 0, {INTEL_PT_MODE_TSX, 0, 2}, 0, 0 },
/* Trace Stop Packet */
{2, {0x02, 0x83}, 0, {INTEL_PT_TRACESTOP, 0, 0}, 0, 0 },
/* Core:Bus Ratio Packet */
{4, {0x02, 0x03, 0x12, 0}, 0, {INTEL_PT_CBR, 0, 0x12}, 0, 1 },
/* Timestamp Counter Packet */
{8, {0x19, 1, 2, 3, 4, 5, 6, 7}, 0, {INTEL_PT_TSC, 0, 0x7060504030201}, 0, 1 },
/* Mini Time Counter Packet */
{2, {0x59, 0x12}, 0, {INTEL_PT_MTC, 0, 0x12}, 0, 1 },
/* TSC / MTC Alignment Packet */
{7, {0x02, 0x73}, 0, {INTEL_PT_TMA, 0, 0}, 0, 1 },
{7, {0x02, 0x73, 1, 2}, 0, {INTEL_PT_TMA, 0, 0x201}, 0, 1 },
{7, {0x02, 0x73, 0, 0, 0, 0xff, 1}, 0, {INTEL_PT_TMA, 0x1ff, 0}, 0, 1 },
{7, {0x02, 0x73, 0x80, 0xc0, 0, 0xff, 1}, 0, {INTEL_PT_TMA, 0x1ff, 0xc080}, 0, 1 },
/* Cycle Count Packet */
{1, {0x03}, 0, {INTEL_PT_CYC, 0, 0}, 0, 1 },
{1, {0x0b}, 0, {INTEL_PT_CYC, 0, 1}, 0, 1 },
{1, {0xfb}, 0, {INTEL_PT_CYC, 0, 0x1f}, 0, 1 },
{2, {0x07, 2}, 0, {INTEL_PT_CYC, 0, 0x20}, 0, 1 },
{2, {0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0xfff}, 0, 1 },
{3, {0x07, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x1000}, 0, 1 },
{3, {0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x7ffff}, 0, 1 },
{4, {0x07, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x80000}, 0, 1 },
{4, {0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x3ffffff}, 0, 1 },
{5, {0x07, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x4000000}, 0, 1 },
{5, {0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x1ffffffff}, 0, 1 },
{6, {0x07, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x200000000}, 0, 1 },
{6, {0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0xffffffffff}, 0, 1 },
{7, {0x07, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x10000000000}, 0, 1 },
{7, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x7fffffffffff}, 0, 1 },
{8, {0x07, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x800000000000}, 0, 1 },
{8, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x3fffffffffffff}, 0, 1 },
{9, {0x07, 1, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x40000000000000}, 0, 1 },
{9, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}, 0, {INTEL_PT_CYC, 0, 0x1fffffffffffffff}, 0, 1 },
{10, {0x07, 1, 1, 1, 1, 1, 1, 1, 1, 2}, 0, {INTEL_PT_CYC, 0, 0x2000000000000000}, 0, 1 },
{10, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe}, 0, {INTEL_PT_CYC, 0, 0xffffffffffffffff}, 0, 1 },
/* Virtual-Machine Control Structure Packet */
{7, {0x02, 0xc8, 1, 2, 3, 4, 5}, 0, {INTEL_PT_VMCS, 5, 0x504030201}, 0, 0 },
/* Overflow Packet */
{2, {0x02, 0xf3}, 0, {INTEL_PT_OVF, 0, 0}, 0, 0 },
{2, {0x02, 0xf3}, INTEL_PT_BLK_4_CTX, {INTEL_PT_OVF, 0, 0}, 0, 0 },
{2, {0x02, 0xf3}, INTEL_PT_BLK_8_CTX, {INTEL_PT_OVF, 0, 0}, 0, 0 },
/* Packet Stream Boundary*/
{16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, 0, {INTEL_PT_PSB, 0, 0}, 0, 0 },
{16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, INTEL_PT_BLK_4_CTX, {INTEL_PT_PSB, 0, 0}, 0, 0 },
{16, {0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82, 0x02, 0x82}, INTEL_PT_BLK_8_CTX, {INTEL_PT_PSB, 0, 0}, 0, 0 },
/* PSB End Packet */
{2, {0x02, 0x23}, 0, {INTEL_PT_PSBEND, 0, 0}, 0, 0 },
/* Maintenance Packet */
{11, {0x02, 0xc3, 0x88, 1, 2, 3, 4, 5, 6, 7}, 0, {INTEL_PT_MNT, 0, 0x7060504030201}, 0, 1 },
/* Write Data to PT Packet */
{6, {0x02, 0x12, 1, 2, 3, 4}, 0, {INTEL_PT_PTWRITE, 0, 0x4030201}, 0, 0 },
{10, {0x02, 0x32, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_PTWRITE, 1, 0x807060504030201}, 0, 0 },
{6, {0x02, 0x92, 1, 2, 3, 4}, 0, {INTEL_PT_PTWRITE_IP, 0, 0x4030201}, 0, 0 },
{10, {0x02, 0xb2, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_PTWRITE_IP, 1, 0x807060504030201}, 0, 0 },
/* Execution Stop Packet */
{2, {0x02, 0x62}, 0, {INTEL_PT_EXSTOP, 0, 0}, 0, 1 },
{2, {0x02, 0xe2}, 0, {INTEL_PT_EXSTOP_IP, 0, 0}, 0, 1 },
/* Monitor Wait Packet */
{10, {0x02, 0xc2}, 0, {INTEL_PT_MWAIT, 0, 0}, 0, 0 },
{10, {0x02, 0xc2, 1, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_MWAIT, 0, 0x807060504030201}, 0, 0 },
{10, {0x02, 0xc2, 0xff, 2, 3, 4, 7, 6, 7, 8}, 0, {INTEL_PT_MWAIT, 0, 0x8070607040302ff}, 0, 0 },
/* Power Entry Packet */
{4, {0x02, 0x22}, 0, {INTEL_PT_PWRE, 0, 0}, 0, 1 },
{4, {0x02, 0x22, 1, 2}, 0, {INTEL_PT_PWRE, 0, 0x0201}, 0, 1 },
{4, {0x02, 0x22, 0x80, 0x34}, 0, {INTEL_PT_PWRE, 0, 0x3480}, 0, 1 },
{4, {0x02, 0x22, 0x00, 0x56}, 0, {INTEL_PT_PWRE, 0, 0x5600}, 0, 1 },
/* Power Exit Packet */
{7, {0x02, 0xa2}, 0, {INTEL_PT_PWRX, 0, 0}, 0, 1 },
{7, {0x02, 0xa2, 1, 2, 3, 4, 5}, 0, {INTEL_PT_PWRX, 0, 0x504030201}, 0, 1 },
{7, {0x02, 0xa2, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, {INTEL_PT_PWRX, 0, 0xffffffffff}, 0, 1 },
/* Block Begin Packet */
{3, {0x02, 0x63, 0x00}, 0, {INTEL_PT_BBP, 0, 0}, INTEL_PT_BLK_8_CTX, 0 },
{3, {0x02, 0x63, 0x80}, 0, {INTEL_PT_BBP, 1, 0}, INTEL_PT_BLK_4_CTX, 0 },
{3, {0x02, 0x63, 0x1f}, 0, {INTEL_PT_BBP, 0, 0x1f}, INTEL_PT_BLK_8_CTX, 0 },
{3, {0x02, 0x63, 0x9f}, 0, {INTEL_PT_BBP, 1, 0x1f}, INTEL_PT_BLK_4_CTX, 0 },
/* 4-byte Block Item Packet */
{5, {0x04}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0, 0}, INTEL_PT_BLK_4_CTX, 0 },
{5, {0xfc}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0x1f, 0}, INTEL_PT_BLK_4_CTX, 0 },
{5, {0x04, 1, 2, 3, 4}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0, 0x04030201}, INTEL_PT_BLK_4_CTX, 0 },
{5, {0xfc, 1, 2, 3, 4}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BIP, 0x1f, 0x04030201}, INTEL_PT_BLK_4_CTX, 0 },
/* 8-byte Block Item Packet */
{9, {0x04}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0, 0}, INTEL_PT_BLK_8_CTX, 0 },
{9, {0xfc}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0x1f, 0}, INTEL_PT_BLK_8_CTX, 0 },
{9, {0x04, 1, 2, 3, 4, 5, 6, 7, 8}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0, 0x0807060504030201}, INTEL_PT_BLK_8_CTX, 0 },
{9, {0xfc, 1, 2, 3, 4, 5, 6, 7, 8}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BIP, 0x1f, 0x0807060504030201}, INTEL_PT_BLK_8_CTX, 0 },
/* Block End Packet */
{2, {0x02, 0x33}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BEP, 0, 0}, 0, 0 },
{2, {0x02, 0xb3}, INTEL_PT_BLK_4_CTX, {INTEL_PT_BEP_IP, 0, 0}, 0, 0 },
{2, {0x02, 0x33}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BEP, 0, 0}, 0, 0 },
{2, {0x02, 0xb3}, INTEL_PT_BLK_8_CTX, {INTEL_PT_BEP_IP, 0, 0}, 0, 0 },
/* Control Flow Event Packet */
{4, {0x02, 0x13, 0x01, 0x03}, 0, {INTEL_PT_CFE, 1, 3}, 0, 0 },
{4, {0x02, 0x13, 0x81, 0x03}, 0, {INTEL_PT_CFE_IP, 1, 3}, 0, 0 },
{4, {0x02, 0x13, 0x1f, 0x00}, 0, {INTEL_PT_CFE, 0x1f, 0}, 0, 0 },
{4, {0x02, 0x13, 0x9f, 0xff}, 0, {INTEL_PT_CFE_IP, 0x1f, 0xff}, 0, 0 },
/* */
{11, {0x02, 0x53, 0x09, 1, 2, 3, 4, 5, 6, 7}, 0, {INTEL_PT_EVD, 0x09, 0x7060504030201}, 0, 0 },
{11, {0x02, 0x53, 0x3f, 2, 3, 4, 5, 6, 7, 8}, 0, {INTEL_PT_EVD, 0x3f, 0x8070605040302}, 0, 0 },
/* Terminator */
{0, {0}, 0, {0, 0, 0}, 0, 0 },
};
static int dump_packet(const struct intel_pt_pkt *packet, const u8 *bytes, int len)
{
char desc[INTEL_PT_PKT_DESC_MAX];
int ret, i;
for (i = 0; i < len; i++)
pr_debug(" %02x", bytes[i]);
for (; i < INTEL_PT_PKT_MAX_SZ; i++)
pr_debug(" ");
pr_debug(" ");
ret = intel_pt_pkt_desc(packet, desc, INTEL_PT_PKT_DESC_MAX);
if (ret < 0) {
pr_debug("intel_pt_pkt_desc failed!\n");
return TEST_FAIL;
}
pr_debug("%s\n", desc);
return TEST_OK;
}
static void decoding_failed(const struct test_data *d)
{
pr_debug("Decoding failed!\n");
pr_debug("Decoding: ");
dump_packet(&d->packet, d->bytes, d->len);
}
static int fail(const struct test_data *d, struct intel_pt_pkt *packet, int len,
enum intel_pt_pkt_ctx new_ctx)
{
decoding_failed(d);
if (len != d->len)
pr_debug("Expected length: %d Decoded length %d\n",
d->len, len);
if (packet->type != d->packet.type)
pr_debug("Expected type: %d Decoded type %d\n",
d->packet.type, packet->type);
if (packet->count != d->packet.count)
pr_debug("Expected count: %d Decoded count %d\n",
d->packet.count, packet->count);
if (packet->payload != d->packet.payload)
pr_debug("Expected payload: 0x%llx Decoded payload 0x%llx\n",
(unsigned long long)d->packet.payload,
(unsigned long long)packet->payload);
if (new_ctx != d->new_ctx)
pr_debug("Expected packet context: %d Decoded packet context %d\n",
d->new_ctx, new_ctx);
return TEST_FAIL;
}
static int test_ctx_unchanged(const struct test_data *d, struct intel_pt_pkt *packet,
enum intel_pt_pkt_ctx ctx)
{
enum intel_pt_pkt_ctx old_ctx = ctx;
intel_pt_upd_pkt_ctx(packet, &ctx);
if (ctx != old_ctx) {
decoding_failed(d);
pr_debug("Packet context changed!\n");
return TEST_FAIL;
}
return TEST_OK;
}
static int test_one(const struct test_data *d)
{
struct intel_pt_pkt packet;
enum intel_pt_pkt_ctx ctx = d->ctx;
int ret;
memset(&packet, 0xff, sizeof(packet));
/* Decode a packet */
ret = intel_pt_get_packet(d->bytes, d->len, &packet, &ctx);
if (ret < 0 || ret > INTEL_PT_PKT_MAX_SZ) {
decoding_failed(d);
pr_debug("intel_pt_get_packet returned %d\n", ret);
return TEST_FAIL;
}
/* Some packets must always leave the packet context unchanged */
if (d->ctx_unchanged) {
int err;
err = test_ctx_unchanged(d, &packet, INTEL_PT_NO_CTX);
if (err)
return err;
err = test_ctx_unchanged(d, &packet, INTEL_PT_BLK_4_CTX);
if (err)
return err;
err = test_ctx_unchanged(d, &packet, INTEL_PT_BLK_8_CTX);
if (err)
return err;
}
/* Compare to the expected values */
if (ret != d->len || packet.type != d->packet.type ||
packet.count != d->packet.count ||
packet.payload != d->packet.payload || ctx != d->new_ctx)
return fail(d, &packet, ret, ctx);
pr_debug("Decoded ok:");
ret = dump_packet(&d->packet, d->bytes, d->len);
return ret;
}
/*
* This test feeds byte sequences to the Intel PT packet decoder and checks the
* results. Changes to the packet context are also checked.
*/
int test__intel_pt_pkt_decoder(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
const struct test_data *d = data;
int ret;
for (d = data; d->len; d++) {
ret = test_one(d);
if (ret)
return ret;
}
return TEST_OK;
}
static int setaffinity(int cpu)
{
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(cpu, &cpu_set);
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set)) {
pr_debug("sched_setaffinity() failed for CPU %d\n", cpu);
return -1;
}
return 0;
}
#define INTEL_PT_ADDR_FILT_CNT_MASK GENMASK(2, 0)
#define INTEL_PT_SUBLEAF_CNT 2
#define CPUID_REG_CNT 4
struct cpuid_result {
union {
struct {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
};
unsigned int reg[CPUID_REG_CNT];
};
};
struct pt_caps {
struct cpuid_result subleaf[INTEL_PT_SUBLEAF_CNT];
};
static int get_pt_caps(int cpu, struct pt_caps *caps)
{
struct cpuid_result r;
int i;
if (setaffinity(cpu))
return -1;
memset(caps, 0, sizeof(*caps));
for (i = 0; i < INTEL_PT_SUBLEAF_CNT; i++) {
cpuid(20, i, &r.eax, &r.ebx, &r.ecx, &r.edx);
pr_debug("CPU %d CPUID leaf 20 subleaf %d\n", cpu, i);
pr_debug("eax = 0x%08x\n", r.eax);
pr_debug("ebx = 0x%08x\n", r.ebx);
pr_debug("ecx = 0x%08x\n", r.ecx);
pr_debug("edx = 0x%08x\n", r.edx);
caps->subleaf[i] = r;
}
return 0;
}
static bool is_hybrid(void)
{
unsigned int eax, ebx, ecx, edx = 0;
bool result;
cpuid(7, 0, &eax, &ebx, &ecx, &edx);
result = edx & BIT(15);
pr_debug("Is %shybrid : CPUID leaf 7 subleaf 0 edx %#x (bit-15 indicates hybrid)\n",
result ? "" : "not ", edx);
return result;
}
static int compare_caps(int cpu, struct pt_caps *caps, struct pt_caps *caps0)
{
struct pt_caps mask = { /* Mask of bits to check*/
.subleaf = {
[0] = {
.ebx = GENMASK(8, 0),
.ecx = GENMASK(3, 0),
},
[1] = {
.eax = GENMASK(31, 16),
.ebx = GENMASK(31, 0),
}
}
};
unsigned int m, reg, reg0;
int ret = 0;
int i, j;
for (i = 0; i < INTEL_PT_SUBLEAF_CNT; i++) {
for (j = 0; j < CPUID_REG_CNT; j++) {
m = mask.subleaf[i].reg[j];
reg = m & caps->subleaf[i].reg[j];
reg0 = m & caps0->subleaf[i].reg[j];
if ((reg & reg0) != reg0) {
pr_debug("CPU %d subleaf %d reg %d FAIL %#x vs %#x\n",
cpu, i, j, reg, reg0);
ret = -1;
}
}
}
m = INTEL_PT_ADDR_FILT_CNT_MASK;
reg = m & caps->subleaf[1].eax;
reg0 = m & caps0->subleaf[1].eax;
if (reg < reg0) {
pr_debug("CPU %d subleaf 1 reg 0 FAIL address filter count %#x vs %#x\n",
cpu, reg, reg0);
ret = -1;
}
if (!ret)
pr_debug("CPU %d OK\n", cpu);
return ret;
}
int test__intel_pt_hybrid_compat(struct test_suite *test, int subtest)
{
int max_cpu = cpu__max_cpu().cpu;
struct pt_caps last_caps;
struct pt_caps caps0;
int ret = TEST_OK;
int cpu;
if (!is_hybrid()) {
test->test_cases[subtest].skip_reason = "not hybrid";
return TEST_SKIP;
}
if (get_pt_caps(0, &caps0))
return TEST_FAIL;
for (cpu = 1, last_caps = caps0; cpu < max_cpu; cpu++) {
struct pt_caps caps;
if (get_pt_caps(cpu, &caps)) {
pr_debug("CPU %d not found\n", cpu);
continue;
}
if (!memcmp(&caps, &last_caps, sizeof(caps))) {
pr_debug("CPU %d same caps as previous CPU\n", cpu);
continue;
}
if (compare_caps(cpu, &caps, &caps0))
ret = TEST_FAIL;
last_caps = caps;
}
return ret;
}
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#define AX 0
#define BX 1 * 8
#define CX 2 * 8
#define DX 3 * 8
#define SI 4 * 8
#define DI 5 * 8
#define BP 6 * 8
#define SP 7 * 8
#define IP 8 * 8
#define FLAGS 9 * 8
#define CS 10 * 8
#define SS 11 * 8
#define DS 12 * 8
#define ES 13 * 8
#define FS 14 * 8
#define GS 15 * 8
#define R8 16 * 8
#define R9 17 * 8
#define R10 18 * 8
#define R11 19 * 8
#define R12 20 * 8
#define R13 21 * 8
#define R14 22 * 8
#define R15 23 * 8
.text
#ifdef HAVE_ARCH_X86_64_SUPPORT
SYM_FUNC_START(perf_regs_load)
movq %rax, AX(%rdi)
movq %rbx, BX(%rdi)
movq %rcx, CX(%rdi)
movq %rdx, DX(%rdi)
movq %rsi, SI(%rdi)
movq %rdi, DI(%rdi)
movq %rbp, BP(%rdi)
leaq 8(%rsp), %rax /* exclude this call. */
movq %rax, SP(%rdi)
movq 0(%rsp), %rax
movq %rax, IP(%rdi)
movq $0, FLAGS(%rdi)
movq $0, CS(%rdi)
movq $0, SS(%rdi)
movq $0, DS(%rdi)
movq $0, ES(%rdi)
movq $0, FS(%rdi)
movq $0, GS(%rdi)
movq %r8, R8(%rdi)
movq %r9, R9(%rdi)
movq %r10, R10(%rdi)
movq %r11, R11(%rdi)
movq %r12, R12(%rdi)
movq %r13, R13(%rdi)
movq %r14, R14(%rdi)
movq %r15, R15(%rdi)
ret
SYM_FUNC_END(perf_regs_load)
#else
SYM_FUNC_START(perf_regs_load)
push %edi
movl 8(%esp), %edi
movl %eax, AX(%edi)
movl %ebx, BX(%edi)
movl %ecx, CX(%edi)
movl %edx, DX(%edi)
movl %esi, SI(%edi)
pop %eax
movl %eax, DI(%edi)
movl %ebp, BP(%edi)
leal 4(%esp), %eax /* exclude this call. */
movl %eax, SP(%edi)
movl 0(%esp), %eax
movl %eax, IP(%edi)
movl $0, FLAGS(%edi)
movl $0, CS(%edi)
movl $0, SS(%edi)
movl $0, DS(%edi)
movl $0, ES(%edi)
movl $0, FS(%edi)
movl $0, GS(%edi)
ret
SYM_FUNC_END(perf_regs_load)
#endif
/*
* We need to provide note.GNU-stack section, saying that we want
* NOT executable stack. Otherwise the final linking will assume that
* the ELF stack should not be restricted at all and set it RWX.
*/
.section .note.GNU-stack,"",@progbits
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include "arch-tests.h"
#include "../util/topdown.h"
#include "debug.h"
#include "evlist.h"
#include "parse-events.h"
#include "pmu.h"
#include "pmus.h"
static int event_cb(void *state, struct pmu_event_info *info)
{
char buf[256];
struct parse_events_error parse_err;
int *ret = state, err;
struct evlist *evlist = evlist__new();
struct evsel *evsel;
if (!evlist)
return -ENOMEM;
parse_events_error__init(&parse_err);
snprintf(buf, sizeof(buf), "%s/%s/", info->pmu->name, info->name);
err = parse_events(evlist, buf, &parse_err);
if (err) {
parse_events_error__print(&parse_err, buf);
*ret = TEST_FAIL;
}
parse_events_error__exit(&parse_err);
evlist__for_each_entry(evlist, evsel) {
bool fail = false;
bool p_core_pmu = evsel->pmu->type == PERF_TYPE_RAW;
const char *name = evsel__name(evsel);
if (strcasestr(name, "uops_retired.slots") ||
strcasestr(name, "topdown.backend_bound_slots") ||
strcasestr(name, "topdown.br_mispredict_slots") ||
strcasestr(name, "topdown.memory_bound_slots") ||
strcasestr(name, "topdown.bad_spec_slots") ||
strcasestr(name, "topdown.slots_p")) {
if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel))
fail = true;
} else if (strcasestr(name, "slots")) {
if (arch_is_topdown_slots(evsel) != p_core_pmu ||
arch_is_topdown_metrics(evsel))
fail = true;
} else if (strcasestr(name, "topdown")) {
if (arch_is_topdown_slots(evsel) ||
arch_is_topdown_metrics(evsel) != p_core_pmu)
fail = true;
} else if (arch_is_topdown_slots(evsel) || arch_is_topdown_metrics(evsel)) {
fail = true;
}
if (fail) {
pr_debug("Broken topdown information for '%s'\n", evsel__name(evsel));
*ret = TEST_FAIL;
}
}
evlist__delete(evlist);
return 0;
}
static int test__x86_topdown(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
int ret = TEST_OK;
struct perf_pmu *pmu = NULL;
if (!topdown_sys_has_perf_metrics())
return TEST_OK;
while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
if (perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/false, &ret, event_cb))
break;
}
return ret;
}
#define CHECK_COND(cond, text) \
do { \
if (!(cond)) { \
pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
ret = TEST_FAIL; \
goto out_err; \
} \
} while (0)
#define CHECK_EQUAL(val, expected, text) \
do { \
if ((val) != (expected)) { \
pr_debug("FAILED %s:%d %s (%d != %d)\n", \
__FILE__, __LINE__, text, (val), (expected)); \
ret = TEST_FAIL; \
goto out_err; \
} \
} while (0)
static int test_sort(const char *str, int expected_slots_group_size,
int expected_instructions_group_size)
{
struct evlist *evlist = NULL;
struct parse_events_error err;
struct evsel *evsel;
int ret = TEST_FAIL;
bool slots_seen = false;
parse_events_error__init(&err);
evlist = evlist__new();
if (!evlist)
goto out_err;
if (parse_events(evlist, str, &err)) {
pr_debug("parse_events failed for %s\n", str);
goto out_err;
}
evlist__for_each_entry(evlist, evsel) {
if (!evsel__is_group_leader(evsel))
continue;
if (strstr(evsel__name(evsel), "slots")) {
/*
* Slots as a leader means the PMU is for a perf metric
* group as the slots event isn't present when not.
*/
slots_seen = true;
CHECK_EQUAL(evsel->core.nr_members, expected_slots_group_size,
"slots group size");
if (expected_slots_group_size == 3) {
struct evsel *next = evsel__next(evsel);
struct evsel *next2 = evsel__next(next);
CHECK_COND(strstr(evsel__name(next), "instructions") != NULL,
"slots second event is instructions");
CHECK_COND(strstr(evsel__name(next2), "topdown-retiring") != NULL,
"slots third event is topdown-retiring");
} else if (expected_slots_group_size == 2) {
struct evsel *next = evsel__next(evsel);
CHECK_COND(strstr(evsel__name(next), "topdown-retiring") != NULL,
"slots second event is topdown-retiring");
}
} else if (strstr(evsel__name(evsel), "instructions")) {
CHECK_EQUAL(evsel->core.nr_members, expected_instructions_group_size,
"instructions group size");
if (expected_instructions_group_size == 2) {
/*
* On Intel hybrid CPUs (e.g., Alder Lake/
* Raptor Lake), E-cores (cpu_atom) do not
* support/enforce the slots event. When
* parsing event groups containing slots
* across all PMUs, slots is automatically
* filtered out from cpu_atom, leaving
* {cpu_atom/instructions/,
* cpu_atom/topdown-retiring/}. On cpu_atom,
* instructions correctly leads this group of
* 2 without slots reordering.
*/
struct evsel *next = evsel__next(evsel);
CHECK_COND(strstr(evsel__name(next), "topdown-retiring") != NULL,
"instructions second event is topdown-retiring");
}
} else if (strstr(evsel__name(evsel), "topdown-retiring")) {
/*
* A perf metric event where the PMU doesn't require
* slots as a leader.
*/
CHECK_EQUAL(evsel->core.nr_members, 1, "topdown-retiring group size");
} else if (strstr(evsel__name(evsel), "cycles")) {
CHECK_EQUAL(evsel->core.nr_members, 1, "cycles group size");
}
}
CHECK_COND(slots_seen, "slots seen");
ret = TEST_OK;
out_err:
evlist__delete(evlist);
parse_events_error__exit(&err);
return ret;
}
static int test__x86_topdown_sorting(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
int ret;
if (!topdown_sys_has_perf_metrics())
return TEST_OK;
ret = test_sort("{instructions,topdown-retiring,slots}", 3, 2);
TEST_ASSERT_EQUAL("all events in a group", ret, TEST_OK);
ret = test_sort("instructions,topdown-retiring,slots", 2, 1);
TEST_ASSERT_EQUAL("all events not in a group", ret, TEST_OK);
ret = test_sort("{instructions,slots},topdown-retiring", 2, 1);
TEST_ASSERT_EQUAL("slots event in a group but topdown metrics events outside the group",
ret, TEST_OK);
ret = test_sort("{instructions,slots},{topdown-retiring}", 2, 1);
TEST_ASSERT_EQUAL("slots event and topdown metrics events in two groups",
ret, TEST_OK);
ret = test_sort("{instructions,slots},cycles,topdown-retiring", 2, 1);
TEST_ASSERT_EQUAL("slots event and metrics event are not in a group and not adjacent",
ret, TEST_OK);
return TEST_OK;
}
static int test__x86_topdown_slots_injection(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
int ret;
if (!topdown_sys_has_perf_metrics())
return TEST_OK;
ret = test_sort("{instructions,topdown-retiring}", 3, 2);
TEST_ASSERT_EQUAL("all events in a group", ret, TEST_OK);
ret = test_sort("instructions,topdown-retiring", 2, 1);
TEST_ASSERT_EQUAL("all events not in a group", ret, TEST_OK);
ret = test_sort("{instructions},topdown-retiring", 2, 1);
TEST_ASSERT_EQUAL("event in a group but topdown metrics events outside the group",
ret, TEST_OK);
ret = test_sort("{instructions},{topdown-retiring}", 2, 1);
TEST_ASSERT_EQUAL("event and topdown metrics events in two groups",
ret, TEST_OK);
ret = test_sort("{instructions},cycles,topdown-retiring", 2, 1);
TEST_ASSERT_EQUAL("event and metrics event are not in a group and not adjacent",
ret, TEST_OK);
return TEST_OK;
}
static struct test_case x86_topdown_tests[] = {
TEST_CASE("topdown events", x86_topdown),
TEST_CASE("topdown sorting", x86_topdown_sorting),
TEST_CASE("topdown slots injection", x86_topdown_slots_injection),
{ .name = NULL, }
};
struct test_suite suite__x86_topdown = {
.desc = "x86 topdown",
.test_cases = x86_topdown_tests,
};
@@ -0,0 +1,13 @@
perf-util-y += header.o
perf-util-y += tsc.o
perf-util-y += pmu.o
perf-util-y += topdown.o
perf-util-y += machine.o
perf-util-y += event.o
perf-util-y += evlist.o
perf-util-y += mem-events.o
perf-util-y += evsel.o
perf-util-y += iostat.o
perf-util-y += auxtrace.o
perf-util-y += intel-pt.o
perf-util-y += intel-bts.o
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* auxtrace.c: AUX area tracing support
* Copyright (c) 2013-2014, Intel Corporation.
*/
#include <errno.h>
#include <stdbool.h>
#include "../../../util/header.h"
#include "../../../util/debug.h"
#include "../../../util/pmu.h"
#include "../../../util/pmus.h"
#include "../../../util/auxtrace.h"
#include "../../../util/intel-pt.h"
#include "../../../util/intel-bts.h"
#include "../../../util/evlist.h"
static
struct auxtrace_record *auxtrace_record__init_intel(struct evlist *evlist,
int *err)
{
struct perf_pmu *intel_pt_pmu;
struct perf_pmu *intel_bts_pmu;
struct evsel *evsel;
bool found_pt = false;
bool found_bts = false;
intel_pt_pmu = perf_pmus__find(INTEL_PT_PMU_NAME);
intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME);
evlist__for_each_entry(evlist, evsel) {
if (intel_pt_pmu && evsel->core.attr.type == intel_pt_pmu->type)
found_pt = true;
if (intel_bts_pmu && evsel->core.attr.type == intel_bts_pmu->type)
found_bts = true;
}
if (found_pt && found_bts) {
pr_err("intel_pt and intel_bts may not be used together\n");
*err = -EINVAL;
return NULL;
}
if (found_pt)
return intel_pt_recording_init(err);
if (found_bts)
return intel_bts_recording_init(err);
return NULL;
}
struct auxtrace_record *auxtrace_record__init(struct evlist *evlist,
int *err)
{
char buffer[64];
struct perf_cpu cpu = perf_cpu_map__min(evlist->core.all_cpus);
int ret;
*err = 0;
ret = get_cpuid(buffer, sizeof(buffer), cpu);
if (ret) {
*err = ret;
return NULL;
}
if (!strncmp(buffer, "GenuineIntel,", 13))
return auxtrace_record__init_intel(evlist, err);
return NULL;
}
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef PERF_CPUID_H
#define PERF_CPUID_H 1
static inline void
cpuid(unsigned int op, unsigned int op2, unsigned int *a, unsigned int *b,
unsigned int *c, unsigned int *d)
{
/*
* Preserve %ebx/%rbx register by either placing it in %rdi or saving it
* on the stack - x86-64 needs to avoid the stack red zone. In PIC
* compilations %ebx contains the address of the global offset
* table. %rbx is occasionally used to address stack variables in
* presence of dynamic allocas.
*/
asm(
#if defined(__x86_64__)
"mov %%rbx, %%rdi\n"
"cpuid\n"
"xchg %%rdi, %%rbx\n"
#else
"pushl %%ebx\n"
"cpuid\n"
"movl %%ebx, %%edi\n"
"popl %%ebx\n"
#endif
: "=a"(*a), "=D"(*b), "=c"(*c), "=d"(*d)
: "a"(op), "2"(op2));
}
void get_cpuid_0(char *vendor, unsigned int *lvl);
#endif
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/string.h>
#include <linux/zalloc.h>
#include <stdlib.h>
#include "../../../util/event.h"
#include "../../../util/synthetic-events.h"
#include "../../../util/machine.h"
#include "../../../util/tool.h"
#include "../../../util/map.h"
#include "../../../util/debug.h"
#include "util/sample.h"
#if defined(__x86_64__)
struct perf_event__synthesize_extra_kmaps_cb_args {
const struct perf_tool *tool;
perf_event__handler_t process;
struct machine *machine;
union perf_event *event;
};
static int perf_event__synthesize_extra_kmaps_cb(struct map *map, void *data)
{
struct perf_event__synthesize_extra_kmaps_cb_args *args = data;
union perf_event *event = args->event;
struct kmap *kmap;
size_t size;
if (!__map__is_extra_kernel_map(map))
return 0;
kmap = map__kmap(map);
size = sizeof(event->mmap) - sizeof(event->mmap.filename) +
PERF_ALIGN(strlen(kmap->name) + 1, sizeof(u64)) +
args->machine->id_hdr_size;
memset(event, 0, size);
event->mmap.header.type = PERF_RECORD_MMAP;
/*
* kernel uses 0 for user space maps, see kernel/perf_event.c
* __perf_event_mmap
*/
if (machine__is_host(args->machine))
event->header.misc = PERF_RECORD_MISC_KERNEL;
else
event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
event->mmap.header.size = size;
event->mmap.start = map__start(map);
event->mmap.len = map__size(map);
event->mmap.pgoff = map__pgoff(map);
event->mmap.pid = args->machine->pid;
strlcpy(event->mmap.filename, kmap->name, PATH_MAX);
if (perf_tool__process_synth_event(args->tool, event, args->machine, args->process) != 0)
return -1;
return 0;
}
int perf_event__synthesize_extra_kmaps(const struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
int rc;
struct maps *kmaps = machine__kernel_maps(machine);
struct perf_event__synthesize_extra_kmaps_cb_args args = {
.tool = tool,
.process = process,
.machine = machine,
.event = zalloc(sizeof(args.event->mmap) + machine->id_hdr_size),
};
if (!args.event) {
pr_debug("Not enough memory synthesizing mmap event "
"for extra kernel maps\n");
return -1;
}
rc = maps__for_each_map(kmaps, perf_event__synthesize_extra_kmaps_cb, &args);
free(args.event);
return rc;
}
#endif
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include "../../../util/evlist.h"
#include "../../../util/evsel.h"
#include "topdown.h"
#include "evsel.h"
int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
{
/*
* Currently the following topdown events sequence are supported to
* move and regroup correctly.
*
* a. all events in a group
* perf stat -e "{instructions,topdown-retiring,slots}" -C0 sleep 1
* WARNING: events were regrouped to match PMUs
* Performance counter stats for 'CPU(s) 0':
* 15,066,240 slots
* 1,899,760 instructions
* 2,126,998 topdown-retiring
* b. all events not in a group
* perf stat -e "instructions,topdown-retiring,slots" -C0 sleep 1
* WARNING: events were regrouped to match PMUs
* Performance counter stats for 'CPU(s) 0':
* 2,045,561 instructions
* 17,108,370 slots
* 2,281,116 topdown-retiring
* c. slots event in a group but topdown metrics events outside the group
* perf stat -e "{instructions,slots},topdown-retiring" -C0 sleep 1
* WARNING: events were regrouped to match PMUs
* Performance counter stats for 'CPU(s) 0':
* 20,323,878 slots
* 2,634,884 instructions
* 3,028,656 topdown-retiring
* d. slots event and topdown metrics events in two groups
* perf stat -e "{instructions,slots},{topdown-retiring}" -C0 sleep 1
* WARNING: events were regrouped to match PMUs
* Performance counter stats for 'CPU(s) 0':
* 26,319,024 slots
* 2,427,791 instructions
* 2,683,508 topdown-retiring
* e. slots event and metrics event are not in a group and not adjacent
* perf stat -e "{instructions,slots},cycles,topdown-retiring" -C0 sleep 1
* WARNING: events were regrouped to match PMUs
* 68,433,522 slots
* 8,856,102 topdown-retiring
* 7,791,494 instructions
* 11,469,513 cycles
*/
if (topdown_sys_has_perf_metrics() &&
(arch_evsel__must_be_in_group(lhs) || arch_evsel__must_be_in_group(rhs))) {
/* Ensure the topdown slots comes first. */
if (arch_is_topdown_slots(lhs))
return -1;
if (arch_is_topdown_slots(rhs))
return 1;
/*
* Move topdown metrics events forward only when topdown metrics
* events are not in same group with previous slots event. If
* topdown metrics events are already in same group with slots
* event, do nothing.
*/
if (lhs->core.leader != rhs->core.leader) {
bool lhs_topdown = arch_is_topdown_metrics(lhs);
bool rhs_topdown = arch_is_topdown_metrics(rhs);
if (lhs_topdown && !rhs_topdown)
return -1;
if (!lhs_topdown && rhs_topdown)
return 1;
}
}
/* Retire latency event should not be group leader*/
if (lhs->retire_lat && !rhs->retire_lat)
return 1;
if (!lhs->retire_lat && rhs->retire_lat)
return -1;
/* Default ordering by insertion index. */
return lhs->core.idx - rhs->core.idx;
}
int arch_evlist__add_required_events(struct list_head *list)
{
struct evsel *pos, *metric_event = NULL;
int idx = 0;
if (!topdown_sys_has_perf_metrics())
return 0;
list_for_each_entry(pos, list, core.node) {
if (arch_is_topdown_slots(pos)) {
/* Slots event already present, nothing to do. */
return 0;
}
if (metric_event == NULL && arch_is_topdown_metrics(pos))
metric_event = pos;
idx++;
}
if (metric_event == NULL) {
/* No topdown metric events, nothing to do. */
return 0;
}
return topdown_insert_slots_event(list, idx + 1, metric_event);
}
@@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/evsel_config.h"
#include "util/env.h"
#include "util/pmu.h"
#include "util/pmus.h"
#include "util/stat.h"
#include "util/strbuf.h"
#include "linux/string.h"
#include "topdown.h"
#include "evsel.h"
#include "util/debug.h"
#include "env.h"
#define IBS_FETCH_L3MISSONLY (1ULL << 59)
#define IBS_OP_L3MISSONLY (1ULL << 16)
void arch_evsel__set_sample_weight(struct evsel *evsel)
{
evsel__set_sample_bit(evsel, WEIGHT_STRUCT);
}
/* Check whether the evsel's PMU supports the perf metrics */
bool evsel__sys_has_perf_metrics(const struct evsel *evsel)
{
struct perf_pmu *pmu;
if (!topdown_sys_has_perf_metrics())
return false;
/*
* The PERF_TYPE_RAW type is the core PMU type, e.g., "cpu" PMU on a
* non-hybrid machine, "cpu_core" PMU on a hybrid machine. The
* topdown_sys_has_perf_metrics checks the slots event is only available
* for the core PMU, which supports the perf metrics feature. Checking
* both the PERF_TYPE_RAW type and the slots event should be good enough
* to detect the perf metrics feature.
*/
pmu = evsel__find_pmu(evsel);
return pmu && pmu->type == PERF_TYPE_RAW;
}
bool arch_evsel__must_be_in_group(const struct evsel *evsel)
{
if (!evsel__sys_has_perf_metrics(evsel))
return false;
return arch_is_topdown_metrics(evsel) || arch_is_topdown_slots(evsel);
}
int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
{
u64 event = evsel->core.attr.config & PERF_HW_EVENT_MASK;
u64 pmu = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT;
const char *event_name;
if (event < PERF_COUNT_HW_MAX && evsel__hw_names[event])
event_name = evsel__hw_names[event];
else
event_name = "unknown-hardware";
/* The PMU type is not required for the non-hybrid platform. */
if (!pmu)
return scnprintf(bf, size, "%s", event_name);
return scnprintf(bf, size, "%s/%s/",
evsel->pmu ? evsel->pmu->name : "cpu",
event_name);
}
void arch_evsel__apply_ratio_to_prev(struct evsel *evsel,
struct perf_event_attr *attr)
{
struct perf_event_attr *prev_attr = NULL;
struct evsel *evsel_prev = NULL;
const char *name = "acr_mask";
int evsel_idx = 0;
__u64 ev_mask, pr_ev_mask;
if (!perf_pmu__has_format(evsel->pmu, name)) {
pr_err("'%s' does not have acr_mask format support\n", evsel->pmu->name);
return;
}
if (perf_pmu__format_type(evsel->pmu, name) !=
PERF_PMU_FORMAT_VALUE_CONFIG2) {
pr_err("'%s' does not have config2 format support\n", evsel->pmu->name);
return;
}
evsel_prev = evsel__prev(evsel);
if (!evsel_prev) {
pr_err("Previous event does not exist.\n");
return;
}
prev_attr = &evsel_prev->core.attr;
if (prev_attr->config2) {
pr_err("'%s' has set config2 (acr_mask?) already, configuration not supported\n", evsel_prev->name);
return;
}
/*
* acr_mask (config2) is calculated using the event's index in
* the event group. The first event will use the index of the
* second event as its mask (e.g., 0x2), indicating that the
* second event counter will be reset and a sample taken for
* the first event if its counter overflows. The second event
* will use the mask consisting of the first and second bits
* (e.g., 0x3), meaning both counters will be reset if the
* second event counter overflows.
*/
evsel_idx = evsel__group_idx(evsel);
ev_mask = 1ull << evsel_idx;
pr_ev_mask = 1ull << (evsel_idx - 1);
prev_attr->config2 = ev_mask;
attr->config2 = ev_mask | pr_ev_mask;
}
static void ibs_l3miss_warn(void)
{
pr_warning(
"WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled\n"
"and tagged operation does not cause L3 Miss. This causes sampling period skew.\n");
}
void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr)
{
struct perf_pmu *evsel_pmu, *ibs_fetch_pmu, *ibs_op_pmu;
static int warned_once;
if (warned_once || !x86__is_amd_cpu())
return;
evsel_pmu = evsel__find_pmu(evsel);
if (!evsel_pmu)
return;
ibs_fetch_pmu = perf_pmus__find("ibs_fetch");
ibs_op_pmu = perf_pmus__find("ibs_op");
if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) {
if (attr->config & IBS_FETCH_L3MISSONLY) {
ibs_l3miss_warn();
warned_once = 1;
}
} else if (ibs_op_pmu && ibs_op_pmu->type == evsel_pmu->type) {
if (attr->config & IBS_OP_L3MISSONLY) {
ibs_l3miss_warn();
warned_once = 1;
}
}
}
static int amd_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size)
{
struct perf_pmu *pmu;
if (evsel->core.attr.precise_ip == 0)
return 0;
pmu = evsel__find_pmu(evsel);
if (!pmu || strncmp(pmu->name, "ibs", 3))
return 0;
/* More verbose IBS errors. */
if (evsel->core.attr.exclude_kernel || evsel->core.attr.exclude_user ||
evsel->core.attr.exclude_hv || evsel->core.attr.exclude_idle ||
evsel->core.attr.exclude_host || evsel->core.attr.exclude_guest) {
return scnprintf(msg, size, "AMD IBS doesn't support privilege filtering. Try "
"again without the privilege modifiers (like 'k') at the end.");
}
return 0;
}
static int intel_evsel__open_strerror(struct evsel *evsel, int err, char *msg, size_t size)
{
struct strbuf sb = STRBUF_INIT;
int ret;
if (err != EINVAL)
return 0;
if (!topdown_sys_has_perf_metrics())
return 0;
if (arch_is_topdown_slots(evsel)) {
if (!evsel__is_group_leader(evsel)) {
evlist__uniquify_evsel_names(evsel->evlist, &stat_config);
evlist__format_evsels(evsel->evlist, &sb, 2048);
ret = scnprintf(msg, size, "Topdown slots event can only be group leader "
"in '%s'.", sb.buf);
strbuf_release(&sb);
return ret;
}
} else if (arch_is_topdown_metrics(evsel)) {
struct evsel *pos;
evlist__for_each_entry(evsel->evlist, pos) {
if (pos == evsel || !arch_is_topdown_metrics(pos))
continue;
if (pos->core.attr.config != evsel->core.attr.config)
continue;
evlist__uniquify_evsel_names(evsel->evlist, &stat_config);
evlist__format_evsels(evsel->evlist, &sb, 2048);
ret = scnprintf(msg, size, "Perf metric event '%s' is duplicated "
"in the same group (only one event is allowed) in '%s'.",
evsel__name(evsel), sb.buf);
strbuf_release(&sb);
return ret;
}
}
return 0;
}
int arch_evsel__open_strerror(struct evsel *evsel, int err, char *msg, size_t size)
{
return x86__is_amd_cpu()
? amd_evsel__open_strerror(evsel, msg, size)
: intel_evsel__open_strerror(evsel, err, msg, size);
}
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _EVSEL_H
#define _EVSEL_H 1
bool evsel__sys_has_perf_metrics(const struct evsel *evsel);
#endif
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include "../../../util/debug.h"
#include "../../../util/header.h"
#include "cpuid.h"
void get_cpuid_0(char *vendor, unsigned int *lvl)
{
unsigned int b, c, d;
cpuid(0, 0, lvl, &b, &c, &d);
strncpy(&vendor[0], (char *)(&b), 4);
strncpy(&vendor[4], (char *)(&d), 4);
strncpy(&vendor[8], (char *)(&c), 4);
vendor[12] = '\0';
}
static int
__get_cpuid(char *buffer, size_t sz, const char *fmt)
{
unsigned int a, b, c, d, lvl;
int family = -1, model = -1, step = -1;
int nb;
char vendor[16];
get_cpuid_0(vendor, &lvl);
if (lvl >= 1) {
cpuid(1, 0, &a, &b, &c, &d);
family = (a >> 8) & 0xf; /* bits 11 - 8 */
model = (a >> 4) & 0xf; /* Bits 7 - 4 */
step = a & 0xf;
/* extended family */
if (family == 0xf)
family += (a >> 20) & 0xff;
/* extended model */
if (family >= 0x6)
model += ((a >> 16) & 0xf) << 4;
}
nb = scnprintf(buffer, sz, fmt, vendor, family, model, step);
/* look for end marker to ensure the entire data fit */
if (strchr(buffer, '$')) {
buffer[nb-1] = '\0';
return 0;
}
return ENOBUFS;
}
int
get_cpuid(char *buffer, size_t sz, struct perf_cpu cpu __maybe_unused)
{
return __get_cpuid(buffer, sz, "%s,%u,%u,%u$");
}
char *get_cpuid_str(struct perf_cpu cpu __maybe_unused)
{
char *buf = malloc(128);
if (buf && __get_cpuid(buf, 128, "%s-%u-%X-%X$") < 0) {
free(buf);
return NULL;
}
return buf;
}
/* Full CPUID format for x86 is vendor-family-model-stepping */
static bool is_full_cpuid(const char *id)
{
const char *tmp = id;
int count = 0;
while ((tmp = strchr(tmp, '-')) != NULL) {
count++;
tmp++;
}
if (count == 3)
return true;
return false;
}
int strcmp_cpuid_str(const char *mapcpuid, const char *id)
{
regex_t re;
regmatch_t pmatch[1];
int match;
bool full_mapcpuid = is_full_cpuid(mapcpuid);
bool full_cpuid = is_full_cpuid(id);
/*
* Full CPUID format is required to identify a platform.
* Error out if the cpuid string is incomplete.
*/
if (full_mapcpuid && !full_cpuid) {
pr_info("Invalid CPUID %s. Full CPUID is required, "
"vendor-family-model-stepping\n", id);
return 1;
}
if (regcomp(&re, mapcpuid, REG_EXTENDED) != 0) {
/* Warn unable to generate match particular string. */
pr_info("Invalid regular expression %s\n", mapcpuid);
return 1;
}
match = !regexec(&re, id, 1, pmatch, 0);
regfree(&re);
if (match) {
size_t match_len = (pmatch[0].rm_eo - pmatch[0].rm_so);
size_t cpuid_len;
/* If the full CPUID format isn't required,
* ignoring the stepping.
*/
if (!full_mapcpuid && full_cpuid)
cpuid_len = strrchr(id, '-') - id;
else
cpuid_len = strlen(id);
/* Verify the entire string matched. */
if (match_len == cpuid_len)
return 0;
}
return 1;
}
@@ -0,0 +1,453 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* intel-bts.c: Intel Processor Trace support
* Copyright (c) 2013-2015, Intel Corporation.
*/
#include "../../../util/intel-bts.h"
#include <errno.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/types.h>
#include <linux/zalloc.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/mmap.h"
#include "../../../util/pmu.h"
#include "../../../util/pmus.h"
#include "../../../util/record.h"
#include "../../../util/session.h"
#include "../../../util/tsc.h"
#define KiB(x) ((x) * 1024)
#define MiB(x) ((x) * 1024 * 1024)
#define KiB_MASK(x) (KiB(x) - 1)
#define MiB_MASK(x) (MiB(x) - 1)
struct intel_bts_snapshot_ref {
void *ref_buf;
size_t ref_offset;
bool wrapped;
};
struct intel_bts_recording {
struct auxtrace_record itr;
struct perf_pmu *intel_bts_pmu;
struct evlist *evlist;
bool snapshot_mode;
size_t snapshot_size;
int snapshot_ref_cnt;
struct intel_bts_snapshot_ref *snapshot_refs;
};
struct branch {
u64 from;
u64 to;
u64 misc;
};
static size_t
intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused,
struct evlist *evlist __maybe_unused)
{
return INTEL_BTS_AUXTRACE_PRIV_SIZE;
}
static int intel_bts_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
struct perf_event_mmap_page *pc;
struct perf_tsc_conversion tc = { .time_mult = 0, };
bool cap_user_time_zero = false;
int err;
if (priv_size != INTEL_BTS_AUXTRACE_PRIV_SIZE)
return -EINVAL;
if (!session->evlist->core.nr_mmaps)
return -EINVAL;
pc = session->evlist->mmap[0].core.base;
if (pc) {
err = perf_read_tsc_conversion(pc, &tc);
if (err) {
if (err != -EOPNOTSUPP)
return err;
} else {
cap_user_time_zero = tc.time_mult != 0;
}
if (!cap_user_time_zero)
ui__warning("Intel BTS: TSC not available\n");
}
auxtrace_info->type = PERF_AUXTRACE_INTEL_BTS;
auxtrace_info->priv[INTEL_BTS_PMU_TYPE] = intel_bts_pmu->type;
auxtrace_info->priv[INTEL_BTS_TIME_SHIFT] = tc.time_shift;
auxtrace_info->priv[INTEL_BTS_TIME_MULT] = tc.time_mult;
auxtrace_info->priv[INTEL_BTS_TIME_ZERO] = tc.time_zero;
auxtrace_info->priv[INTEL_BTS_CAP_USER_TIME_ZERO] = cap_user_time_zero;
auxtrace_info->priv[INTEL_BTS_SNAPSHOT_MODE] = btsr->snapshot_mode;
return 0;
}
static int intel_bts_recording_options(struct auxtrace_record *itr,
struct evlist *evlist,
struct record_opts *opts)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
struct evsel *evsel, *intel_bts_evsel = NULL;
const struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
bool privileged = perf_event_paranoid_check(-1);
if (opts->auxtrace_sample_mode) {
pr_err("Intel BTS does not support AUX area sampling\n");
return -EINVAL;
}
btsr->evlist = evlist;
btsr->snapshot_mode = opts->auxtrace_snapshot_mode;
evlist__for_each_entry(evlist, evsel) {
if (evsel->core.attr.type == intel_bts_pmu->type) {
if (intel_bts_evsel) {
pr_err("There may be only one " INTEL_BTS_PMU_NAME " event\n");
return -EINVAL;
}
evsel->core.attr.freq = 0;
evsel->core.attr.sample_period = 1;
evsel->needs_auxtrace_mmap = true;
intel_bts_evsel = evsel;
opts->full_auxtrace = true;
}
}
if (opts->auxtrace_snapshot_mode && !opts->full_auxtrace) {
pr_err("Snapshot mode (-S option) requires " INTEL_BTS_PMU_NAME " PMU event (-e " INTEL_BTS_PMU_NAME ")\n");
return -EINVAL;
}
if (!opts->full_auxtrace)
return 0;
if (opts->full_auxtrace && !perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
pr_err(INTEL_BTS_PMU_NAME " does not support per-cpu recording\n");
return -EINVAL;
}
/* Set default sizes for snapshot mode */
if (opts->auxtrace_snapshot_mode) {
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;
}
if (!opts->auxtrace_snapshot_size)
opts->auxtrace_snapshot_size =
opts->auxtrace_mmap_pages * (size_t)page_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);
}
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;
}
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;
}
pr_debug2("Intel BTS snapshot size: %zu\n",
opts->auxtrace_snapshot_size);
}
/* Set default sizes for full trace mode */
if (opts->full_auxtrace && !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;
}
}
/* Validate auxtrace_mmap_pages */
if (opts->auxtrace_mmap_pages) {
size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
size_t min_sz;
if (opts->auxtrace_snapshot_mode)
min_sz = KiB(4);
else
min_sz = KiB(8);
if (sz < min_sz || !is_power_of_2(sz)) {
pr_err("Invalid mmap size for Intel BTS: must be at least %zuKiB and a power of 2\n",
min_sz / 1024);
return -EINVAL;
}
}
if (intel_bts_evsel) {
/*
* To obtain the auxtrace buffer file descriptor, the auxtrace event
* must come first.
*/
evlist__to_front(evlist, intel_bts_evsel);
/*
* In the case of per-cpu mmaps, we need the CPU on the
* AUX event.
*/
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus))
evsel__set_sample_bit(intel_bts_evsel, CPU);
}
/* Add dummy event to keep tracking */
if (opts->full_auxtrace) {
struct evsel *tracking_evsel;
int err;
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;
}
return 0;
}
static int intel_bts_parse_snapshot_options(struct auxtrace_record *itr,
struct record_opts *opts,
const char *str)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
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;
btsr->snapshot_size = snapshot_size;
return 0;
}
static u64 intel_bts_reference(struct auxtrace_record *itr __maybe_unused)
{
return rdtsc();
}
static int intel_bts_alloc_snapshot_refs(struct intel_bts_recording *btsr,
int idx)
{
const size_t sz = sizeof(struct intel_bts_snapshot_ref);
int cnt = btsr->snapshot_ref_cnt, new_cnt = cnt * 2;
struct intel_bts_snapshot_ref *refs;
if (!new_cnt)
new_cnt = 16;
while (new_cnt <= idx)
new_cnt *= 2;
refs = calloc(new_cnt, sz);
if (!refs)
return -ENOMEM;
memcpy(refs, btsr->snapshot_refs, cnt * sz);
btsr->snapshot_refs = refs;
btsr->snapshot_ref_cnt = new_cnt;
return 0;
}
static void intel_bts_free_snapshot_refs(struct intel_bts_recording *btsr)
{
int i;
for (i = 0; i < btsr->snapshot_ref_cnt; i++)
zfree(&btsr->snapshot_refs[i].ref_buf);
zfree(&btsr->snapshot_refs);
}
static void intel_bts_recording_free(struct auxtrace_record *itr)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
intel_bts_free_snapshot_refs(btsr);
free(btsr);
}
static int intel_bts_snapshot_start(struct auxtrace_record *itr)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
struct evsel *evsel;
evlist__for_each_entry(btsr->evlist, evsel) {
if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
return evsel__disable(evsel);
}
return -EINVAL;
}
static int intel_bts_snapshot_finish(struct auxtrace_record *itr)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
struct evsel *evsel;
evlist__for_each_entry(btsr->evlist, evsel) {
if (evsel->core.attr.type == btsr->intel_bts_pmu->type)
return evsel__enable(evsel);
}
return -EINVAL;
}
static bool intel_bts_first_wrap(u64 *data, size_t buf_size)
{
int i, a, b;
b = buf_size >> 3;
a = b - 512;
if (a < 0)
a = 0;
for (i = a; i < b; i++) {
if (data[i])
return true;
}
return false;
}
static int intel_bts_find_snapshot(struct auxtrace_record *itr, int idx,
struct auxtrace_mmap *mm, unsigned char *data,
u64 *head, u64 *old)
{
struct intel_bts_recording *btsr =
container_of(itr, struct intel_bts_recording, itr);
bool wrapped;
int err;
pr_debug3("%s: mmap index %d old head %zu new head %zu\n",
__func__, idx, (size_t)*old, (size_t)*head);
if (idx >= btsr->snapshot_ref_cnt) {
err = intel_bts_alloc_snapshot_refs(btsr, idx);
if (err)
goto out_err;
}
wrapped = btsr->snapshot_refs[idx].wrapped;
if (!wrapped && intel_bts_first_wrap((u64 *)data, mm->len)) {
btsr->snapshot_refs[idx].wrapped = true;
wrapped = true;
}
/*
* In full trace mode 'head' continually increases. However in snapshot
* mode 'head' is an offset within the buffer. Here 'old' and 'head'
* are adjusted to match the full trace case which expects that 'old' is
* always less than 'head'.
*/
if (wrapped) {
*old = *head;
*head += mm->len;
} else {
if (mm->mask)
*old &= mm->mask;
else
*old %= mm->len;
if (*old > *head)
*head += mm->len;
}
pr_debug3("%s: wrap-around %sdetected, adjusted old head %zu adjusted new head %zu\n",
__func__, wrapped ? "" : "not ", (size_t)*old, (size_t)*head);
return 0;
out_err:
pr_err("%s: failed, error %d\n", __func__, err);
return err;
}
struct auxtrace_record *intel_bts_recording_init(int *err)
{
struct perf_pmu *intel_bts_pmu = perf_pmus__find(INTEL_BTS_PMU_NAME);
struct intel_bts_recording *btsr;
if (!intel_bts_pmu)
return NULL;
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
*err = -errno;
return NULL;
}
btsr = zalloc(sizeof(struct intel_bts_recording));
if (!btsr) {
*err = -ENOMEM;
return NULL;
}
btsr->intel_bts_pmu = intel_bts_pmu;
btsr->itr.recording_options = intel_bts_recording_options;
btsr->itr.info_priv_size = intel_bts_info_priv_size;
btsr->itr.info_fill = intel_bts_info_fill;
btsr->itr.free = intel_bts_recording_free;
btsr->itr.snapshot_start = intel_bts_snapshot_start;
btsr->itr.snapshot_finish = intel_bts_snapshot_finish;
btsr->itr.find_snapshot = intel_bts_find_snapshot;
btsr->itr.parse_snapshot_options = intel_bts_parse_snapshot_options;
btsr->itr.reference = intel_bts_reference;
btsr->itr.read_finish = auxtrace_record__read_finish;
btsr->itr.alignment = sizeof(struct branch);
return &btsr->itr;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,475 @@
// SPDX-License-Identifier: GPL-2.0
/*
* perf iostat
*
* Copyright (C) 2020, Intel Corporation
*
* Authors: Alexander Antonov <alexander.antonov@linux.intel.com>
*/
#include <api/fs/fs.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/zalloc.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <regex.h>
#include "util/cpumap.h"
#include "util/debug.h"
#include "util/iostat.h"
#include "util/counts.h"
#include "path.h"
#ifndef MAX_PATH
#define MAX_PATH 1024
#endif
#define UNCORE_IIO_PMU_PATH "bus/event_source/devices/uncore_iio_%d"
#define SYSFS_UNCORE_PMU_PATH "%s/"UNCORE_IIO_PMU_PATH
#define PLATFORM_MAPPING_PATH UNCORE_IIO_PMU_PATH"/die%d"
/*
* Each metric requiries one IIO event which increments at every 4B transfer
* in corresponding direction. The formulas to compute metrics are generic:
* #EventCount * 4B / (1024 * 1024)
*/
static const char * const iostat_metrics[] = {
"Inbound Read(MB)",
"Inbound Write(MB)",
"Outbound Read(MB)",
"Outbound Write(MB)",
};
static inline int iostat_metrics_count(void)
{
return sizeof(iostat_metrics) / sizeof(char *);
}
static const char *iostat_metric_by_idx(int idx)
{
return *(iostat_metrics + idx % iostat_metrics_count());
}
struct iio_root_port {
u32 domain;
u8 bus;
u8 die;
u8 pmu_idx;
int idx;
};
struct iio_root_ports_list {
struct iio_root_port **rps;
int nr_entries;
};
static struct iio_root_ports_list *root_ports;
static void iio_root_port_show(FILE *output,
const struct iio_root_port * const rp)
{
if (output && rp)
fprintf(output, "S%d-uncore_iio_%d<%04x:%02x>\n",
rp->die, rp->pmu_idx, rp->domain, rp->bus);
}
static struct iio_root_port *iio_root_port_new(u32 domain, u8 bus,
u8 die, u8 pmu_idx)
{
struct iio_root_port *p = calloc(1, sizeof(*p));
if (p) {
p->domain = domain;
p->bus = bus;
p->die = die;
p->pmu_idx = pmu_idx;
}
return p;
}
static void iio_root_ports_list_free(struct iio_root_ports_list *list)
{
int idx;
if (list) {
for (idx = 0; idx < list->nr_entries; idx++)
zfree(&list->rps[idx]);
zfree(&list->rps);
free(list);
}
}
static struct iio_root_port *iio_root_port_find_by_notation(
const struct iio_root_ports_list * const list, u32 domain, u8 bus)
{
int idx;
struct iio_root_port *rp;
if (list) {
for (idx = 0; idx < list->nr_entries; idx++) {
rp = list->rps[idx];
if (rp && rp->domain == domain && rp->bus == bus)
return rp;
}
}
return NULL;
}
static int iio_root_ports_list_insert(struct iio_root_ports_list *list,
struct iio_root_port * const rp)
{
struct iio_root_port **tmp_buf;
if (list && rp) {
rp->idx = list->nr_entries++;
tmp_buf = realloc(list->rps,
list->nr_entries * sizeof(*list->rps));
if (!tmp_buf) {
pr_err("Failed to realloc memory\n");
return -ENOMEM;
}
tmp_buf[rp->idx] = rp;
list->rps = tmp_buf;
}
return 0;
}
static int iio_mapping(u8 pmu_idx, struct iio_root_ports_list * const list)
{
char *buf;
char path[MAX_PATH];
u32 domain;
u8 bus;
struct iio_root_port *rp;
size_t size;
int ret;
for (int die = 0; die < cpu__max_node(); die++) {
scnprintf(path, MAX_PATH, PLATFORM_MAPPING_PATH, pmu_idx, die);
if (sysfs__read_str(path, &buf, &size) < 0) {
if (pmu_idx)
goto out;
pr_err("Mode iostat is not supported\n");
return -1;
}
ret = sscanf(buf, "%04x:%02hhx", &domain, &bus);
free(buf);
if (ret != 2) {
pr_err("Invalid mapping data: iio_%d; die%d\n",
pmu_idx, die);
return -1;
}
rp = iio_root_port_new(domain, bus, die, pmu_idx);
if (!rp || iio_root_ports_list_insert(list, rp)) {
free(rp);
return -ENOMEM;
}
}
out:
return 0;
}
static u8 iio_pmu_count(void)
{
u8 pmu_idx = 0;
char path[MAX_PATH];
const char *sysfs = sysfs__mountpoint();
if (sysfs) {
for (;; pmu_idx++) {
snprintf(path, sizeof(path), SYSFS_UNCORE_PMU_PATH,
sysfs, pmu_idx);
if (access(path, F_OK) != 0)
break;
}
}
return pmu_idx;
}
static int iio_root_ports_scan(struct iio_root_ports_list **list)
{
int ret = -ENOMEM;
struct iio_root_ports_list *tmp_list;
u8 pmu_count = iio_pmu_count();
if (!pmu_count) {
pr_err("Unsupported uncore pmu configuration\n");
return -1;
}
tmp_list = calloc(1, sizeof(*tmp_list));
if (!tmp_list)
goto err;
for (u8 pmu_idx = 0; pmu_idx < pmu_count; pmu_idx++) {
ret = iio_mapping(pmu_idx, tmp_list);
if (ret)
break;
}
err:
if (!ret)
*list = tmp_list;
else
iio_root_ports_list_free(tmp_list);
return ret;
}
static int iio_root_port_parse_str(u32 *domain, u8 *bus, char *str)
{
int ret;
regex_t regex;
/*
* Expected format domain:bus:
* Valid domain range [0:ffff]
* Valid bus range [0:ff]
* Example: 0000:af, 0:3d, 01:7
*/
regcomp(&regex, "^([a-f0-9A-F]{1,}):([a-f0-9A-F]{1,2})", REG_EXTENDED);
ret = regexec(&regex, str, 0, NULL, 0);
if (ret || sscanf(str, "%08x:%02hhx", domain, bus) != 2)
pr_warning("Unrecognized root port format: %s\n"
"Please use the following format:\n"
"\t [domain]:[bus]\n"
"\t for example: 0000:3d\n", str);
regfree(&regex);
return ret;
}
static int iio_root_ports_list_filter(struct iio_root_ports_list **list,
const char *filter)
{
char *tok, *tmp, *filter_copy = NULL;
struct iio_root_port *rp;
u32 domain;
u8 bus;
int ret = -ENOMEM;
struct iio_root_ports_list *tmp_list = calloc(1, sizeof(*tmp_list));
if (!tmp_list)
goto err;
filter_copy = strdup(filter);
if (!filter_copy)
goto err;
for (tok = strtok_r(filter_copy, ",", &tmp); tok;
tok = strtok_r(NULL, ",", &tmp)) {
if (!iio_root_port_parse_str(&domain, &bus, tok)) {
rp = iio_root_port_find_by_notation(*list, domain, bus);
if (rp) {
(*list)->rps[rp->idx] = NULL;
ret = iio_root_ports_list_insert(tmp_list, rp);
if (ret) {
free(rp);
goto err;
}
} else if (!iio_root_port_find_by_notation(tmp_list,
domain, bus))
pr_warning("Root port %04x:%02x were not found\n",
domain, bus);
}
}
if (tmp_list->nr_entries == 0) {
pr_err("Requested root ports were not found\n");
ret = -EINVAL;
}
err:
iio_root_ports_list_free(*list);
if (ret)
iio_root_ports_list_free(tmp_list);
else
*list = tmp_list;
free(filter_copy);
return ret;
}
static int iostat_event_group(struct evlist *evl,
struct iio_root_ports_list *list)
{
int ret;
int idx;
const char *iostat_cmd_template =
"{uncore_iio_%x/event=0x83,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
uncore_iio_%x/event=0x83,umask=0x01,ch_mask=0xF,fc_mask=0x07/,\
uncore_iio_%x/event=0xc0,umask=0x04,ch_mask=0xF,fc_mask=0x07/,\
uncore_iio_%x/event=0xc0,umask=0x01,ch_mask=0xF,fc_mask=0x07/}";
const int len_template = strlen(iostat_cmd_template) + 1;
struct evsel *evsel = NULL;
int metrics_count = iostat_metrics_count();
char *iostat_cmd = calloc(len_template, 1);
if (!iostat_cmd)
return -ENOMEM;
for (idx = 0; idx < list->nr_entries; idx++) {
sprintf(iostat_cmd, iostat_cmd_template,
list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx,
list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx);
ret = parse_event(evl, iostat_cmd);
if (ret)
goto err;
}
evlist__for_each_entry(evl, evsel) {
evsel->priv = list->rps[evsel->core.idx / metrics_count];
}
list->nr_entries = 0;
err:
iio_root_ports_list_free(list);
free(iostat_cmd);
return ret;
}
int iostat_prepare(struct evlist *evlist, struct perf_stat_config *config)
{
if (evlist->core.nr_entries > 0) {
pr_warning("The -e and -M options are not supported."
"All chosen events/metrics will be dropped\n");
evlist__delete(evlist);
evlist = evlist__new();
if (!evlist)
return -ENOMEM;
}
config->metric_only = true;
config->aggr_mode = AGGR_GLOBAL;
return iostat_event_group(evlist, root_ports);
}
int iostat_parse(const struct option *opt, const char *str,
int unset __maybe_unused)
{
int ret;
struct perf_stat_config *config = (struct perf_stat_config *)opt->data;
ret = iio_root_ports_scan(&root_ports);
if (!ret) {
config->iostat_run = true;
if (!str)
iostat_mode = IOSTAT_RUN;
else if (!strcmp(str, "list"))
iostat_mode = IOSTAT_LIST;
else {
iostat_mode = IOSTAT_RUN;
ret = iio_root_ports_list_filter(&root_ports, str);
}
}
return ret;
}
void iostat_list(struct evlist *evlist, struct perf_stat_config *config)
{
struct evsel *evsel;
struct iio_root_port *rp = NULL;
evlist__for_each_entry(evlist, evsel) {
if (rp != evsel->priv) {
rp = evsel->priv;
iio_root_port_show(config->output, rp);
}
}
}
void iostat_release(struct evlist *evlist)
{
struct evsel *evsel;
struct iio_root_port *rp = NULL;
evlist__for_each_entry(evlist, evsel) {
if (rp != evsel->priv) {
rp = evsel->priv;
zfree(&evsel->priv);
}
}
}
void iostat_prefix(struct evlist *evlist,
struct perf_stat_config *config,
char *prefix, struct timespec *ts)
{
struct iio_root_port *rp = evlist->selected->priv;
if (rp) {
/*
* TODO: This is the incorrect format in JSON mode.
* See prepare_timestamp()
*/
if (ts)
sprintf(prefix, "%6lu.%09lu%s%04x:%02x%s",
ts->tv_sec, ts->tv_nsec,
config->csv_sep, rp->domain, rp->bus,
config->csv_sep);
else
sprintf(prefix, "%04x:%02x%s", rp->domain, rp->bus,
config->csv_sep);
}
}
void iostat_print_header_prefix(struct perf_stat_config *config)
{
if (config->csv_output)
fputs("port,", config->output);
else if (config->interval)
fprintf(config->output, "# time port ");
else
fprintf(config->output, " port ");
}
void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel,
struct perf_stat_output_ctx *out)
{
double iostat_value = 0;
u64 prev_count_val = 0;
const char *iostat_metric = iostat_metric_by_idx(evsel->core.idx);
u8 die = ((struct iio_root_port *)evsel->priv)->die;
struct perf_counts_values *count = perf_counts(evsel->counts, die, 0);
if (count && count->run && count->ena) {
if (evsel->prev_raw_counts && !out->force_header) {
struct perf_counts_values *prev_count =
perf_counts(evsel->prev_raw_counts, die, 0);
prev_count_val = prev_count->val;
prev_count->val = count->val;
}
iostat_value = (count->val - prev_count_val) /
((double) count->run / count->ena);
}
out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, "%8.0f", iostat_metric,
iostat_value / (256 * 1024));
}
void iostat_print_counters(struct evlist *evlist,
struct perf_stat_config *config, struct timespec *ts,
char *prefix, iostat_print_counter_t print_cnt_cb, void *arg)
{
void *perf_device = NULL;
struct evsel *counter = evlist__first(evlist);
evlist__set_selected(evlist, counter);
iostat_prefix(evlist, config, prefix, ts);
fprintf(config->output, "%s", prefix);
evlist__for_each_entry(evlist, counter) {
perf_device = evlist->selected->priv;
if (perf_device && perf_device != counter->priv) {
evlist__set_selected(evlist, counter);
iostat_prefix(evlist, config, prefix, ts);
fprintf(config->output, "\n%s", prefix);
}
print_cnt_cb(config, counter, arg);
}
fputc('\n', config->output);
}
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/string.h>
#include <limits.h>
#include <stdlib.h>
#include <internal/lib.h> // page_size
#include "../../../util/machine.h"
#include "../../../util/map.h"
#include "../../../util/symbol.h"
#include <linux/ctype.h>
#include <symbol/kallsyms.h>
#if defined(__x86_64__)
struct extra_kernel_map_info {
int cnt;
int max_cnt;
struct extra_kernel_map *maps;
bool get_entry_trampolines;
u64 entry_trampoline;
};
static int add_extra_kernel_map(struct extra_kernel_map_info *mi, u64 start,
u64 end, u64 pgoff, const char *name)
{
if (mi->cnt >= mi->max_cnt) {
void *buf;
size_t sz;
mi->max_cnt = mi->max_cnt ? mi->max_cnt * 2 : 32;
sz = sizeof(struct extra_kernel_map) * mi->max_cnt;
buf = realloc(mi->maps, sz);
if (!buf)
return -1;
mi->maps = buf;
}
mi->maps[mi->cnt].start = start;
mi->maps[mi->cnt].end = end;
mi->maps[mi->cnt].pgoff = pgoff;
strlcpy(mi->maps[mi->cnt].name, name, KMAP_NAME_LEN);
mi->cnt += 1;
return 0;
}
static int find_extra_kernel_maps(void *arg, const char *name, char type,
u64 start)
{
struct extra_kernel_map_info *mi = arg;
if (!mi->entry_trampoline && kallsyms2elf_binding(type) == STB_GLOBAL &&
!strcmp(name, "_entry_trampoline")) {
mi->entry_trampoline = start;
return 0;
}
if (is_entry_trampoline(name)) {
u64 end = start + page_size;
return add_extra_kernel_map(mi, start, end, 0, name);
}
return 0;
}
int machine__create_extra_kernel_maps(struct machine *machine,
struct dso *kernel)
{
struct extra_kernel_map_info mi = { .cnt = 0, };
char filename[PATH_MAX];
int ret;
int i;
machine__get_kallsyms_filename(machine, filename, PATH_MAX);
if (symbol__restricted_filename(filename, "/proc/kallsyms"))
return 0;
ret = kallsyms__parse(filename, &mi, find_extra_kernel_maps);
if (ret)
goto out_free;
if (!mi.entry_trampoline)
goto out_free;
for (i = 0; i < mi.cnt; i++) {
struct extra_kernel_map *xm = &mi.maps[i];
xm->pgoff = mi.entry_trampoline;
ret = machine__create_extra_kernel_map(machine, kernel, xm);
if (ret)
goto out_free;
}
machine->trampolines_mapped = mi.cnt;
out_free:
free(mi.maps);
return ret;
}
#endif
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0
#include "linux/string.h"
#include "util/map_symbol.h"
#include "util/mem-events.h"
#include "mem-events.h"
#define MEM_LOADS_AUX 0x8203
#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_intel[PERF_MEM_EVENTS__MAX] = {
E("ldlat-loads", "%s/mem-loads,ldlat=%u/P", "mem-loads", true, 0),
E("ldlat-stores", "%s/mem-stores/P", "mem-stores", false, 0),
E(NULL, NULL, NULL, false, 0),
};
struct perf_mem_event perf_mem_events_intel_aux[PERF_MEM_EVENTS__MAX] = {
E("ldlat-loads", "{%s/mem-loads-aux/,%s/mem-loads,ldlat=%u/}:P", "mem-loads", true, MEM_LOADS_AUX),
E("ldlat-stores", "%s/mem-stores/P", "mem-stores", false, 0),
E(NULL, NULL, NULL, false, 0),
};
struct perf_mem_event perf_mem_events_amd[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s//", NULL, false, 0),
};
struct perf_mem_event perf_mem_events_amd_ldlat[PERF_MEM_EVENTS__MAX] = {
E(NULL, NULL, NULL, false, 0),
E(NULL, NULL, NULL, false, 0),
E("mem-ldst", "%s/ldlat=%u/", NULL, true, 0),
};
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _X86_MEM_EVENTS_H
#define _X86_MEM_EVENTS_H
extern struct perf_mem_event perf_mem_events_intel[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_intel_aux[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd[PERF_MEM_EVENTS__MAX];
extern struct perf_mem_event perf_mem_events_amd_ldlat[PERF_MEM_EVENTS__MAX];
#endif /* _X86_MEM_EVENTS_H */
+310
View File
@@ -0,0 +1,310 @@
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/perf_event.h>
#include <api/fs/fs.h>
#include <api/io_dir.h>
#include <internal/cpumap.h>
#include <errno.h>
#include "../../../util/intel-pt.h"
#include "../../../util/intel-bts.h"
#include "../../../util/pmu.h"
#include "../../../util/fncache.h"
#include "../../../util/pmus.h"
#include "mem-events.h"
#include "util/debug.h"
#include "util/env.h"
#include "util/header.h"
static bool x86__is_intel_graniterapids(void)
{
static bool checked_if_graniterapids;
static bool is_graniterapids;
if (!checked_if_graniterapids) {
const char *graniterapids_cpuid = "GenuineIntel-6-A[DE]";
char *cpuid = get_cpuid_str((struct perf_cpu){0});
is_graniterapids = cpuid && strcmp_cpuid_str(graniterapids_cpuid, cpuid) == 0;
free(cpuid);
checked_if_graniterapids = true;
}
return is_graniterapids;
}
static struct perf_cpu_map *read_sysfs_cpu_map(const char *sysfs_path)
{
struct perf_cpu_map *cpus;
char *buf = NULL;
size_t buf_len;
if (sysfs__read_str(sysfs_path, &buf, &buf_len) < 0)
return NULL;
cpus = perf_cpu_map__new(buf);
free(buf);
return cpus;
}
static int snc_nodes_per_l3_cache(void)
{
static bool checked_snc;
static int snc_nodes;
if (!checked_snc) {
struct perf_cpu_map *node_cpus =
read_sysfs_cpu_map("devices/system/node/node0/cpulist");
struct perf_cpu_map *cache_cpus =
read_sysfs_cpu_map("devices/system/cpu/cpu0/cache/index3/shared_cpu_list");
snc_nodes = perf_cpu_map__nr(cache_cpus) / perf_cpu_map__nr(node_cpus);
perf_cpu_map__put(cache_cpus);
perf_cpu_map__put(node_cpus);
checked_snc = true;
}
return snc_nodes;
}
static int num_chas(void)
{
static bool checked_chas;
static int num_chas;
if (!checked_chas) {
int fd = perf_pmu__event_source_devices_fd();
struct io_dir dir;
struct io_dirent64 *dent;
if (fd < 0)
return -1;
io_dir__init(&dir, fd);
while ((dent = io_dir__readdir(&dir)) != NULL) {
/* Note, dent->d_type will be DT_LNK and so isn't a useful filter. */
if (strstarts(dent->d_name, "uncore_cha_"))
num_chas++;
}
close(fd);
checked_chas = true;
}
return num_chas;
}
#define MAX_SNCS 6
static int uncore_cha_snc(struct perf_pmu *pmu)
{
// CHA SNC numbers are ordered correspond to the CHAs number.
unsigned int cha_num;
int num_cha, chas_per_node, cha_snc;
int snc_nodes = snc_nodes_per_l3_cache();
if (snc_nodes <= 1)
return 0;
num_cha = num_chas();
if (num_cha <= 0) {
pr_warning("Unexpected: no CHAs found\n");
return 0;
}
/* Compute SNC for PMU. */
if (sscanf(pmu->name, "uncore_cha_%u", &cha_num) != 1) {
pr_warning("Unexpected: unable to compute CHA number '%s'\n", pmu->name);
return 0;
}
chas_per_node = num_cha / snc_nodes;
cha_snc = cha_num / chas_per_node;
/* Range check cha_snc. for unexpected out of bounds. */
return cha_snc >= MAX_SNCS ? 0 : cha_snc;
}
static int uncore_imc_snc(struct perf_pmu *pmu)
{
// Compute the IMC SNC using lookup tables.
unsigned int imc_num;
int snc_nodes = snc_nodes_per_l3_cache();
const u8 snc2_map[] = {1, 1, 0, 0, 1, 1, 0, 0};
const u8 snc3_map[] = {1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 2, 2};
const u8 *snc_map;
size_t snc_map_len;
switch (snc_nodes) {
case 2:
snc_map = snc2_map;
snc_map_len = ARRAY_SIZE(snc2_map);
break;
case 3:
snc_map = snc3_map;
snc_map_len = ARRAY_SIZE(snc3_map);
break;
default:
/* Error or no lookup support for SNC with >3 nodes. */
return 0;
}
/* Compute SNC for PMU. */
if (sscanf(pmu->name, "uncore_imc_%u", &imc_num) != 1) {
pr_warning("Unexpected: unable to compute IMC number '%s'\n", pmu->name);
return 0;
}
if (imc_num >= snc_map_len) {
pr_warning("Unexpected IMC %d for SNC%d mapping\n", imc_num, snc_nodes);
return 0;
}
return snc_map[imc_num];
}
static int uncore_cha_imc_compute_cpu_adjust(int pmu_snc)
{
static bool checked_cpu_adjust[MAX_SNCS];
static int cpu_adjust[MAX_SNCS];
struct perf_cpu_map *node_cpus;
char node_path[] = "devices/system/node/node0/cpulist";
/* Was adjust already computed? */
if (checked_cpu_adjust[pmu_snc])
return cpu_adjust[pmu_snc];
/* SNC0 doesn't need an adjust. */
if (pmu_snc == 0) {
cpu_adjust[0] = 0;
checked_cpu_adjust[0] = true;
return 0;
}
/*
* Use NUMA topology to compute first CPU of the NUMA node, we want to
* adjust CPU 0 to be this and similarly for other CPUs if there is >1
* socket.
*/
assert(pmu_snc >= 0 && pmu_snc <= 9);
node_path[24] += pmu_snc; // Shift node0 to be node<pmu_snc>.
node_cpus = read_sysfs_cpu_map(node_path);
cpu_adjust[pmu_snc] = perf_cpu_map__cpu(node_cpus, 0).cpu;
if (cpu_adjust[pmu_snc] < 0) {
pr_debug("Failed to read valid CPU list from <sysfs>/%s\n", node_path);
cpu_adjust[pmu_snc] = 0;
} else {
checked_cpu_adjust[pmu_snc] = true;
}
perf_cpu_map__put(node_cpus);
return cpu_adjust[pmu_snc];
}
static void gnr_uncore_cha_imc_adjust_cpumask_for_snc(struct perf_pmu *pmu, bool cha)
{
// With sub-NUMA clustering (SNC) there is a NUMA node per SNC in the
// topology. For example, a two socket graniterapids machine may be set
// up with 3-way SNC meaning there are 6 NUMA nodes that should be
// displayed with --per-node. The cpumask of the CHA and IMC PMUs
// reflects per-socket information meaning, for example, uncore_cha_60
// on a two socket graniterapids machine with 120 cores per socket will
// have a cpumask of "0,120". This cpumask needs adjusting to "40,160"
// to reflect that uncore_cha_60 is used for the 2nd SNC of each
// socket. Without the adjustment events on uncore_cha_60 will appear in
// node 0 and node 3 (in our example 2 socket 3-way set up), but with
// the adjustment they will appear in node 1 and node 4. The number of
// CHAs is typically larger than the number of cores. The CHA numbers
// are assumed to split evenly and inorder wrt core numbers. There are
// fewer memory IMC PMUs than cores and mapping is handled using lookup
// tables.
static struct perf_cpu_map *cha_adjusted[MAX_SNCS];
static struct perf_cpu_map *imc_adjusted[MAX_SNCS];
struct perf_cpu_map **adjusted = cha ? cha_adjusted : imc_adjusted;
unsigned int idx;
int pmu_snc, cpu_adjust;
struct perf_cpu cpu;
bool alloc;
// Cpus from the kernel holds first CPU of each socket. e.g. 0,120.
if (perf_cpu_map__cpu(pmu->cpus, 0).cpu != 0) {
pr_debug("Ignoring cpumask adjust for %s as unexpected first CPU\n", pmu->name);
return;
}
pmu_snc = cha ? uncore_cha_snc(pmu) : uncore_imc_snc(pmu);
if (pmu_snc == 0) {
// No adjustment necessary for the first SNC.
return;
}
alloc = adjusted[pmu_snc] == NULL;
if (alloc) {
// Hold onto the perf_cpu_map globally to avoid recomputation.
cpu_adjust = uncore_cha_imc_compute_cpu_adjust(pmu_snc);
adjusted[pmu_snc] = perf_cpu_map__empty_new(perf_cpu_map__nr(pmu->cpus));
if (!adjusted[pmu_snc])
return;
}
perf_cpu_map__for_each_cpu(cpu, idx, pmu->cpus) {
// Compute the new cpu map values or if not allocating, assert
// that they match expectations. asserts will be removed to
// avoid overhead in NDEBUG builds.
if (alloc) {
RC_CHK_ACCESS(adjusted[pmu_snc])->map[idx].cpu = cpu.cpu + cpu_adjust;
} else if (idx == 0) {
cpu_adjust = perf_cpu_map__cpu(adjusted[pmu_snc], idx).cpu - cpu.cpu;
assert(uncore_cha_imc_compute_cpu_adjust(pmu_snc) == cpu_adjust);
} else {
assert(perf_cpu_map__cpu(adjusted[pmu_snc], idx).cpu ==
cpu.cpu + cpu_adjust);
}
}
perf_cpu_map__put(pmu->cpus);
pmu->cpus = perf_cpu_map__get(adjusted[pmu_snc]);
}
void perf_pmu__arch_init(struct perf_pmu *pmu)
{
struct perf_pmu_caps *ldlat_cap;
if (!strcmp(pmu->name, INTEL_PT_PMU_NAME)) {
pmu->auxtrace = true;
pmu->selectable = true;
pmu->perf_event_attr_init_default = intel_pt_pmu_default_config;
}
if (!strcmp(pmu->name, INTEL_BTS_PMU_NAME)) {
pmu->auxtrace = true;
pmu->selectable = true;
}
if (x86__is_amd_cpu()) {
if (strcmp(pmu->name, "ibs_op"))
return;
pmu->mem_events = perf_mem_events_amd;
if (!perf_pmu__caps_parse(pmu))
return;
ldlat_cap = perf_pmu__get_cap(pmu, "ldlat");
if (!ldlat_cap || strcmp(ldlat_cap->value, "1"))
return;
perf_mem_events__loads_ldlat = 0;
pmu->mem_events = perf_mem_events_amd_ldlat;
} else {
if (pmu->is_core) {
if (perf_pmu__have_event(pmu, "mem-loads-aux"))
pmu->mem_events = perf_mem_events_intel_aux;
else
pmu->mem_events = perf_mem_events_intel;
} else if (x86__is_intel_graniterapids()) {
if (strstarts(pmu->name, "uncore_cha_"))
gnr_uncore_cha_imc_adjust_cpumask_for_snc(pmu, /*cha=*/true);
else if (strstarts(pmu->name, "uncore_imc_"))
gnr_uncore_cha_imc_adjust_cpumask_for_snc(pmu, /*cha=*/false);
}
}
}
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include "util/evlist.h"
#include "util/pmu.h"
#include "util/pmus.h"
#include "util/topdown.h"
#include "topdown.h"
#include "evsel.h"
// cmask=0, inv=0, pc=0, edge=0, umask=4, event=0
#define TOPDOWN_SLOTS 0x0400
/* Check whether there is a PMU which supports the perf metrics. */
bool topdown_sys_has_perf_metrics(void)
{
static bool has_perf_metrics;
static bool cached;
struct perf_pmu *pmu;
if (cached)
return has_perf_metrics;
/*
* The perf metrics feature is a core PMU feature.
* The PERF_TYPE_RAW type is the type of a core PMU.
* The slots event is only available when the core PMU
* supports the perf metrics feature.
*/
pmu = perf_pmus__find_by_type(PERF_TYPE_RAW);
if (pmu && perf_pmu__have_event(pmu, "slots"))
has_perf_metrics = true;
cached = true;
return has_perf_metrics;
}
bool arch_is_topdown_slots(const struct evsel *evsel)
{
return evsel->core.attr.type == PERF_TYPE_RAW &&
evsel->core.attr.config == TOPDOWN_SLOTS &&
evsel->core.attr.config1 == 0;
}
bool arch_is_topdown_metrics(const struct evsel *evsel)
{
// cmask=0, inv=0, pc=0, edge=0, umask=0x80-0x87, event=0
return evsel->core.attr.type == PERF_TYPE_RAW &&
(evsel->core.attr.config & 0xFFFFF8FF) == 0x8000 &&
evsel->core.attr.config1 == 0;
}
/*
* Check whether a topdown group supports sample-read.
*
* Only Topdown metric supports sample-read. The slots
* event must be the leader of the topdown group.
*/
bool arch_topdown_sample_read(struct evsel *leader)
{
struct evsel *evsel;
if (!evsel__sys_has_perf_metrics(leader))
return false;
if (!arch_is_topdown_slots(leader))
return false;
/*
* If slots event as leader event but no topdown metric events
* in group, slots event should still sample as leader.
*/
evlist__for_each_entry(leader->evlist, evsel) {
if (evsel->core.leader != leader->core.leader)
continue;
if (evsel != leader && arch_is_topdown_metrics(evsel))
return true;
}
return false;
}
/*
* Make a copy of the topdown metric event metric_event with the given index but
* change its configuration to be a topdown slots event. Copying from
* metric_event ensures modifiers are the same.
*/
int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event)
{
struct evsel *evsel = evsel__new_idx(&metric_event->core.attr, idx);
if (!evsel)
return -ENOMEM;
evsel->core.attr.config = TOPDOWN_SLOTS;
evsel->core.cpus = perf_cpu_map__get(metric_event->core.cpus);
evsel->core.pmu_cpus = perf_cpu_map__get(metric_event->core.pmu_cpus);
evsel->core.is_pmu_core = true;
evsel->pmu = metric_event->pmu;
evsel->name = strdup("slots");
evsel->precise_max = metric_event->precise_max;
evsel->sample_read = metric_event->sample_read;
evsel->weak_group = metric_event->weak_group;
evsel->bpf_counter = metric_event->bpf_counter;
evsel->retire_lat = metric_event->retire_lat;
evsel__set_leader(evsel, evsel__leader(metric_event));
list_add_tail(&evsel->core.node, list);
return 0;
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _TOPDOWN_H
#define _TOPDOWN_H 1
#include <stdbool.h>
struct evsel;
struct list_head;
bool topdown_sys_has_perf_metrics(void);
bool arch_is_topdown_slots(const struct evsel *evsel);
bool arch_is_topdown_metrics(const struct evsel *evsel);
int topdown_insert_slots_event(struct list_head *list, int idx, struct evsel *metric_event);
#endif
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "../../../util/debug.h"
#include "../../../util/tsc.h"
#include "cpuid.h"
u64 rdtsc(void)
{
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((u64)high) << 32;
}
/*
* Derive the TSC frequency in Hz from the /proc/cpuinfo, for example:
* ...
* model name : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz
* ...
* will return 3000000000.
*/
static u64 cpuinfo_tsc_freq(void)
{
u64 result = 0;
FILE *cpuinfo;
char *line = NULL;
size_t len = 0;
cpuinfo = fopen("/proc/cpuinfo", "r");
if (!cpuinfo) {
pr_err("Failed to read /proc/cpuinfo for TSC frequency\n");
return 0;
}
while (getline(&line, &len, cpuinfo) > 0) {
if (!strncmp(line, "model name", 10)) {
char *pos = strstr(line + 11, " @ ");
double float_result;
if (pos && sscanf(pos, " @ %lfGHz", &float_result) == 1) {
float_result *= 1000000000;
result = (u64)float_result;
goto out;
}
}
}
out:
if (result == 0)
pr_err("Failed to find TSC frequency in /proc/cpuinfo\n");
free(line);
fclose(cpuinfo);
return result;
}
u64 arch_get_tsc_freq(void)
{
unsigned int a, b, c, d, lvl;
static bool cached;
static double tsc;
char vendor[16];
if (cached)
return tsc;
cached = true;
get_cpuid_0(vendor, &lvl);
if (!strstr(vendor, "Intel"))
return 0;
/*
* Don't support Time Stamp Counter and
* Nominal Core Crystal Clock Information Leaf.
*/
if (lvl < 0x15) {
tsc = cpuinfo_tsc_freq();
return tsc;
}
cpuid(0x15, 0, &a, &b, &c, &d);
/* TSC frequency is not enumerated */
if (!a || !b || !c) {
tsc = cpuinfo_tsc_freq();
return tsc;
}
tsc = (u64)c * (u64)b / (u64)a;
return tsc;
}