2021-11-15 14:28:23 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Basic test for sigtrap support .
*
* Copyright ( C ) 2021 , Google LLC .
*/
2021-11-16 15:51:48 +03:00
# include <errno.h>
2021-11-15 14:28:23 +03:00
# include <stdint.h>
# include <stdlib.h>
# include <linux/hw_breakpoint.h>
2021-11-16 15:51:48 +03:00
# include <linux/string.h>
2021-11-15 14:28:23 +03:00
# include <pthread.h>
# include <signal.h>
# include <sys/ioctl.h>
# include <sys/syscall.h>
# include <unistd.h>
# include "cloexec.h"
# include "debug.h"
# include "event.h"
# include "tests.h"
# include "../perf-sys.h"
# define NUM_THREADS 5
static struct {
int tids_want_signal ; /* Which threads still want a signal. */
int signal_count ; /* Sanity check number of signals received. */
volatile int iterate_on ; /* Variable to set breakpoint on. */
siginfo_t first_siginfo ; /* First observed siginfo_t. */
} ctx ;
# define TEST_SIG_DATA (~(unsigned long)(&ctx.iterate_on))
static struct perf_event_attr make_event_attr ( void )
{
struct perf_event_attr attr = {
. type = PERF_TYPE_BREAKPOINT ,
. size = sizeof ( attr ) ,
. sample_period = 1 ,
. disabled = 1 ,
. bp_addr = ( unsigned long ) & ctx . iterate_on ,
. bp_type = HW_BREAKPOINT_RW ,
. bp_len = HW_BREAKPOINT_LEN_1 ,
. inherit = 1 , /* Children inherit events ... */
. inherit_thread = 1 , /* ... but only cloned with CLONE_THREAD. */
. remove_on_exec = 1 , /* Required by sigtrap. */
. sigtrap = 1 , /* Request synchronous SIGTRAP on event. */
. sig_data = TEST_SIG_DATA ,
. exclude_kernel = 1 , /* To allow */
. exclude_hv = 1 , /* running as !root */
} ;
return attr ;
}
2022-09-09 02:01:50 +03:00
# ifdef HAVE_BPF_SKEL
# include <bpf/btf.h>
static bool attr_has_sigtrap ( void )
{
bool ret = false ;
struct btf * btf ;
const struct btf_type * t ;
const struct btf_member * m ;
const char * name ;
int i , id ;
btf = btf__load_vmlinux_btf ( ) ;
if ( btf = = NULL ) {
/* should be an old kernel */
return false ;
}
id = btf__find_by_name_kind ( btf , " perf_event_attr " , BTF_KIND_STRUCT ) ;
if ( id < 0 )
goto out ;
t = btf__type_by_id ( btf , id ) ;
for ( i = 0 , m = btf_members ( t ) ; i < btf_vlen ( t ) ; i + + , m + + ) {
name = btf__name_by_offset ( btf , m - > name_off ) ;
if ( ! strcmp ( name , " sigtrap " ) ) {
ret = true ;
break ;
}
}
out :
btf__free ( btf ) ;
return ret ;
}
# else /* !HAVE_BPF_SKEL */
static bool attr_has_sigtrap ( void )
{
struct perf_event_attr attr = {
. type = PERF_TYPE_SOFTWARE ,
. config = PERF_COUNT_SW_DUMMY ,
. size = sizeof ( attr ) ,
. remove_on_exec = 1 , /* Required by sigtrap. */
. sigtrap = 1 , /* Request synchronous SIGTRAP on event. */
} ;
int fd ;
bool ret = false ;
fd = sys_perf_event_open ( & attr , 0 , - 1 , - 1 , perf_event_open_cloexec_flag ( ) ) ;
if ( fd > = 0 ) {
ret = true ;
close ( fd ) ;
}
return ret ;
}
# endif /* HAVE_BPF_SKEL */
2021-11-15 14:28:23 +03:00
static void
sigtrap_handler ( int signum __maybe_unused , siginfo_t * info , void * ucontext __maybe_unused )
{
if ( ! __atomic_fetch_add ( & ctx . signal_count , 1 , __ATOMIC_RELAXED ) )
ctx . first_siginfo = * info ;
__atomic_fetch_sub ( & ctx . tids_want_signal , syscall ( SYS_gettid ) , __ATOMIC_RELAXED ) ;
}
static void * test_thread ( void * arg )
{
pthread_barrier_t * barrier = ( pthread_barrier_t * ) arg ;
pid_t tid = syscall ( SYS_gettid ) ;
int i ;
pthread_barrier_wait ( barrier ) ;
__atomic_fetch_add ( & ctx . tids_want_signal , tid , __ATOMIC_RELAXED ) ;
for ( i = 0 ; i < ctx . iterate_on - 1 ; i + + )
__atomic_fetch_add ( & ctx . tids_want_signal , tid , __ATOMIC_RELAXED ) ;
return NULL ;
}
static int run_test_threads ( pthread_t * threads , pthread_barrier_t * barrier )
{
int i ;
pthread_barrier_wait ( barrier ) ;
for ( i = 0 ; i < NUM_THREADS ; i + + )
TEST_ASSERT_EQUAL ( " pthread_join() failed " , pthread_join ( threads [ i ] , NULL ) , 0 ) ;
return TEST_OK ;
}
static int run_stress_test ( int fd , pthread_t * threads , pthread_barrier_t * barrier )
{
int ret ;
ctx . iterate_on = 3000 ;
TEST_ASSERT_EQUAL ( " misfired signal? " , ctx . signal_count , 0 ) ;
TEST_ASSERT_EQUAL ( " enable failed " , ioctl ( fd , PERF_EVENT_IOC_ENABLE , 0 ) , 0 ) ;
ret = run_test_threads ( threads , barrier ) ;
TEST_ASSERT_EQUAL ( " disable failed " , ioctl ( fd , PERF_EVENT_IOC_DISABLE , 0 ) , 0 ) ;
TEST_ASSERT_EQUAL ( " unexpected sigtraps " , ctx . signal_count , NUM_THREADS * ctx . iterate_on ) ;
TEST_ASSERT_EQUAL ( " missing signals or incorrectly delivered " , ctx . tids_want_signal , 0 ) ;
TEST_ASSERT_VAL ( " unexpected si_addr " , ctx . first_siginfo . si_addr = = & ctx . iterate_on ) ;
#if 0 /* FIXME: enable when libc's signal.h has si_perf_{type,data} */
TEST_ASSERT_EQUAL ( " unexpected si_perf_type " , ctx . first_siginfo . si_perf_type ,
PERF_TYPE_BREAKPOINT ) ;
TEST_ASSERT_EQUAL ( " unexpected si_perf_data " , ctx . first_siginfo . si_perf_data ,
TEST_SIG_DATA ) ;
# endif
return ret ;
}
static int test__sigtrap ( struct test_suite * test __maybe_unused , int subtest __maybe_unused )
{
struct perf_event_attr attr = make_event_attr ( ) ;
struct sigaction action = { } ;
struct sigaction oldact ;
pthread_t threads [ NUM_THREADS ] ;
pthread_barrier_t barrier ;
2021-11-16 15:51:48 +03:00
char sbuf [ STRERR_BUFSIZE ] ;
2021-11-15 14:28:23 +03:00
int i , fd , ret = TEST_FAIL ;
2022-02-18 12:33:33 +03:00
if ( ! BP_SIGNAL_IS_SUPPORTED ) {
2021-12-16 18:14:54 +03:00
pr_debug ( " Test not supported on this architecture " ) ;
return TEST_SKIP ;
}
2021-11-15 14:28:23 +03:00
pthread_barrier_init ( & barrier , NULL , NUM_THREADS + 1 ) ;
action . sa_flags = SA_SIGINFO | SA_NODEFER ;
action . sa_sigaction = sigtrap_handler ;
sigemptyset ( & action . sa_mask ) ;
if ( sigaction ( SIGTRAP , & action , & oldact ) ) {
2021-11-16 15:51:48 +03:00
pr_debug ( " FAILED sigaction(): %s \n " , str_error_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2021-11-15 14:28:23 +03:00
goto out ;
}
fd = sys_perf_event_open ( & attr , 0 , - 1 , - 1 , perf_event_open_cloexec_flag ( ) ) ;
if ( fd < 0 ) {
2022-09-09 02:01:50 +03:00
if ( attr_has_sigtrap ( ) ) {
pr_debug ( " FAILED sys_perf_event_open(): %s \n " ,
str_error_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
} else {
pr_debug ( " perf_event_attr doesn't have sigtrap \n " ) ;
ret = TEST_SKIP ;
}
2021-11-15 14:28:23 +03:00
goto out_restore_sigaction ;
}
for ( i = 0 ; i < NUM_THREADS ; i + + ) {
if ( pthread_create ( & threads [ i ] , NULL , test_thread , & barrier ) ) {
2021-11-16 15:51:48 +03:00
pr_debug ( " FAILED pthread_create(): %s \n " , str_error_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2021-11-15 14:28:23 +03:00
goto out_close_perf_event ;
}
}
ret = run_stress_test ( fd , threads , & barrier ) ;
out_close_perf_event :
close ( fd ) ;
out_restore_sigaction :
sigaction ( SIGTRAP , & oldact , NULL ) ;
out :
pthread_barrier_destroy ( & barrier ) ;
return ret ;
}
DEFINE_SUITE ( " Sigtrap " , sigtrap ) ;