2024-01-04 19:52:08 +08:00
/* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2022-03-11 16:58:31 +08:00
Contributed by Oracle .
This file is part of GNU Binutils .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 , or ( at your option )
any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , 51 Franklin Street - Fifth Floor , Boston ,
MA 02110 - 1301 , USA . */
/* Hardware counter profiling */
# include "config.h"
# include <alloca.h>
# include <dlfcn.h>
# include <stdlib.h>
# include <stdio.h>
2023-10-24 10:55:01 +08:00
# include <stddef.h>
2022-03-11 16:58:31 +08:00
# include <unistd.h>
# include <errno.h>
# include <sys/syscall.h>
# include <signal.h>
# include "gp-defs.h"
# define _STRING_H 1 /* XXX MEZ: temporary workaround */
# include "hwcdrv.h"
# include "collector_module.h"
# include "gp-experiment.h"
# include "libcol_util.h"
# include "hwprofile.h"
# include "ABS.h"
# include "tsd.h"
# define SD_OFF 0 /* before start or after close she shut down process */
# define SD_PENDING 1 /* before running real_detach_experiment() */
# define SD_COMPLETE 2 /* after running real_detach_experiment() */
static int init_interface ( CollectorInterface * ) ;
static int open_experiment ( const char * ) ;
static int start_data_collection ( void ) ;
static int stop_data_collection ( void ) ;
static int close_experiment ( void ) ;
static int detach_experiment ( void ) ;
static int real_detach_experiment ( void ) ;
static ModuleInterface module_interface = {
SP_HWCNTR_FILE , /* description */
init_interface , /* initInterface */
open_experiment , /* openExperiment */
start_data_collection , /* startDataCollection */
stop_data_collection , /* stopDataCollection */
close_experiment , /* closeExperiment */
detach_experiment /* detachExperiment (fork child) */
} ;
static CollectorInterface * collector_interface = NULL ;
/*---------------------------------------------------------------------------*/
/* compile options and workarounds */
/* Solaris: We set ITIMER_REALPROF to ensure that counters get started on
* LWPs that existed before the collector initialization .
*
* In addition , if the appropriate # define ' s are set , we check for :
* lost - hw - overflow - - the HW counters rollover , but the overflow
* interrupt is not generated ( counters keep running )
* lost - sigemt - - the interrupt is received by the kernel ,
* which stops the counters , but the kernel fails
* to deliver the signal .
*/
/*---------------------------------------------------------------------------*/
/* typedefs */
typedef enum {
HWCMODE_OFF , /* before start or after close */
HWCMODE_SUSPEND , /* stop_data_collection called */
HWCMODE_ACTIVE , /* counters are defined and after start_data_collection() */
HWCMODE_ABORT /* fatal error occured. Log a message, stop recording */
} hwc_mode_t ;
/*---------------------------------------------------------------------------*/
/* prototypes */
static void init_ucontexts ( void ) ;
static int hwc_initialize_handlers ( void ) ;
static void collector_record_counter ( ucontext_t * ,
int timecvt ,
ABST_type , hrtime_t ,
unsigned , uint64_t ) ;
static void collector_hwc_ABORT ( int errnum , const char * msg ) ;
static void hwclogwrite0 ( ) ;
static void hwclogwrite ( Hwcentry * ) ;
static void set_hwc_mode ( hwc_mode_t ) ;
static void collector_sigemt_handler ( int sig , siginfo_t * si , void * puc ) ;
/*---------------------------------------------------------------------------*/
/* static variables */
/* --- user counter selections and options */
static int hwcdef_has_memspace ; /* true to indicate use of extened packets */
static unsigned hwcdef_cnt ; /* number of *active* hardware counters */
static unsigned hwcdef_num_sampling_ctrdefs ; /* ctrs that use sampling */
static unsigned hwcdef_num_overflow_ctrdefs ; /* ctrs that use overflow */
static Hwcentry * * hwcdef ; /* HWC definitions */
static int cpcN_cpuver = CPUVER_UNDEFINED ;
static int hwcdrv_inited ; /* Don't call hwcdrv_init() in fork_child */
static hwcdrv_api_t * hwc_driver = NULL ;
static unsigned hwprofile_tsd_key = COLLECTOR_TSD_INVALID_KEY ;
static int hwprofile_tsd_sz = 0 ;
static volatile hwc_mode_t hwc_mode = HWCMODE_OFF ;
static volatile unsigned int nthreads_in_sighandler = 0 ;
static volatile unsigned int sd_state = SD_OFF ;
/* --- experiment logging state */
static CollectorModule expr_hndl = COLLECTOR_MODULE_ERR ;
static ucontext_t expr_dummy_uc ; // used for hacked "collector" frames
static ucontext_t expr_out_of_range_uc ; // used for "out-of-range" frames
static ucontext_t expr_frozen_uc ; // used for "frozen" frames
static ucontext_t expr_nopc_uc ; // used for not-program-related frames
static ucontext_t expr_lostcounts_uc ; // used for lost_counts frames
/* --- signal handler state */
static struct sigaction old_sigemt_handler ; //overwritten in fork-child
/*---------------------------------------------------------------------------*/
/* macros */
# define COUNTERS_ENABLED() (hwcdef_cnt)
# define gethrtime collector_interface->getHiResTime
# ifdef DEBUG
# define Tprintf(...) if (collector_interface) collector_interface->writeDebugInfo( 0, __VA_ARGS__ )
# define TprintfT(...) if (collector_interface) collector_interface->writeDebugInfo( 1, __VA_ARGS__ )
# else
# define Tprintf(...)
# define TprintfT(...)
# endif
/*---------------------------------------------------------------------------*/
/* Initialization routines */
static hwcdrv_api_t *
get_hwc_driver ( )
{
if ( hwc_driver = = NULL )
hwc_driver = __collector_get_hwcdrv ( ) ;
return hwc_driver ;
}
static void init_module ( ) __attribute__ ( ( constructor ) ) ;
static void
init_module ( )
{
__collector_dlsym_guard = 1 ;
RegModuleFunc reg_module = ( RegModuleFunc ) dlsym ( RTLD_DEFAULT , " __collector_register_module " ) ;
__collector_dlsym_guard = 0 ;
if ( reg_module = = NULL )
{
TprintfT ( 0 , " hwprofile: init_module FAILED - reg_module = NULL \n " ) ;
return ;
}
expr_hndl = reg_module ( & module_interface ) ;
if ( expr_hndl = = COLLECTOR_MODULE_ERR )
{
TprintfT ( 0 , " hwprofile: ERROR: handle not created. \n " ) ;
if ( collector_interface )
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >data handle not created</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCINIT ) ;
}
}
static int
init_interface ( CollectorInterface * _collector_interface )
{
collector_interface = _collector_interface ;
return COL_ERROR_NONE ;
}
static void *
hwprofile_get_tsd ( )
{
return collector_interface - > getKey ( hwprofile_tsd_key ) ;
}
static int
open_experiment ( const char * exp )
{
if ( collector_interface = = NULL )
{
TprintfT ( 0 , " hwprofile: ERROR: collector_interface is null. \n " ) ;
return COL_ERROR_HWCINIT ;
}
const char * params = collector_interface - > getParams ( ) ;
while ( params )
{
if ( __collector_strStartWith ( params , " h:* " ) = = 0 )
{
/* HWC counters set by default */
collector_interface - > writeLog ( " <%s %s= \" 1 \" /> \n " ,
SP_TAG_SETTING , SP_JCMD_HWC_DEFAULT ) ;
params + = 3 ;
break ;
}
else if ( __collector_strStartWith ( params , " h: " ) = = 0 )
{
params + = 2 ;
break ;
}
params = CALL_UTIL ( strchr ) ( params , ' ; ' ) ;
if ( params )
params + + ;
}
if ( params = = NULL ) /* HWC profiling not specified */
return COL_ERROR_HWCINIT ;
char * s = CALL_UTIL ( strchr ) ( params , ( int ) ' ; ' ) ;
int sz = s ? s - params : CALL_UTIL ( strlen ) ( params ) ;
char * defstring = ( char * ) alloca ( sz + 1 ) ;
CALL_UTIL ( strlcpy ) ( defstring , params , sz + 1 ) ;
TprintfT ( 0 , " hwprofile: open_experiment %s -- %s \n " , exp , defstring ) ;
int err = COL_ERROR_NONE ;
/* init counter library */
if ( ! hwcdrv_inited )
{ /* do not call hwcdrv_init() from fork-child */
hwcdrv_inited = 1 ;
get_hwc_driver ( ) ;
if ( hwc_driver - > hwcdrv_init ( collector_hwc_ABORT , & hwprofile_tsd_sz ) = = 0 )
{
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCINIT , defstring ) ;
TprintfT ( 0 , " hwprofile: ERROR: hwcfuncs_init() failed \n " ) ;
return COL_ERROR_HWCINIT ;
}
if ( hwc_driver - > hwcdrv_enable_mt ( hwprofile_get_tsd ) )
{
// It is OK to call hwcdrv_enable_mt() before tsd key is created
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCINIT , defstring ) ;
TprintfT ( 0 , " hwprofile: ERROR: hwcdrv_enable_mt() failed \n " ) ;
return COL_ERROR_HWCINIT ;
}
hwc_driver - > hwcdrv_get_info ( & cpcN_cpuver , NULL , NULL , NULL , NULL ) ;
if ( cpcN_cpuver < 0 )
{
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCINIT , defstring ) ;
TprintfT ( 0 , " hwprofile: ERROR: hwcdrv_get_info() failed \n " ) ;
return COL_ERROR_HWCINIT ;
}
}
if ( hwprofile_tsd_sz )
{
hwprofile_tsd_key = collector_interface - > createKey ( hwprofile_tsd_sz , NULL , NULL ) ;
if ( hwprofile_tsd_key = = COLLECTOR_TSD_INVALID_KEY )
{
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCINIT , defstring ) ;
TprintfT ( 0 , " hwprofile: ERROR: TSD createKey failed \n " ) ;
return COL_ERROR_HWCINIT ;
}
}
hwcdef_cnt = 0 ;
hwcdef_has_memspace = 0 ;
/* create counters based on hwcdef[] */
err = __collector_hwcfuncs_bind_descriptor ( defstring ) ;
if ( err )
{
err = err = = HWCFUNCS_ERROR_HWCINIT ? COL_ERROR_HWCINIT : COL_ERROR_HWCARGS ;
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s</event> \n " ,
SP_JCMD_CERROR , err , defstring ) ;
TprintfT ( 0 , " hwprofile: ERROR: open_experiment() failed, RC=%d \n " , err ) ;
return err ;
}
/* generate an array of counter structures for each requested counter */
hwcdef = __collector_hwcfuncs_get_ctrs ( & hwcdef_cnt ) ;
hwcdef_num_sampling_ctrdefs = hwcdef_num_overflow_ctrdefs = 0 ;
int idx ;
for ( idx = 0 ; idx < hwcdef_cnt ; idx + + )
{
if ( HWCENTRY_USES_SAMPLING ( hwcdef [ idx ] ) )
{
hwcdef_num_sampling_ctrdefs + + ;
}
else
{
hwcdef_num_overflow_ctrdefs + + ;
}
}
init_ucontexts ( ) ;
/* initialize the SIGEMT handler, and the periodic HWC checker */
err = hwc_initialize_handlers ( ) ;
if ( err ! = COL_ERROR_NONE )
{
hwcdef_cnt = 0 ;
TprintfT ( 0 , " hwprofile: ERROR: open_experiment() failed, RC=%d \n " , err ) ;
/* log written by hwc_initialize_handlers() */
return err ;
}
for ( idx = 0 ; idx < hwcdef_cnt ; idx + + )
if ( ABST_BACKTRACK_ENABLED ( hwcdef [ idx ] - > memop ) )
hwcdef_has_memspace = 1 ;
/* record the hwc definitions in the log, based on the counter array */
hwclogwrite0 ( ) ;
for ( idx = 0 ; idx < hwcdef_cnt ; idx + + )
hwclogwrite ( hwcdef [ idx ] ) ;
return COL_ERROR_NONE ;
}
int
__collector_ext_hwc_lwp_init ( )
{
return get_hwc_driver ( ) - > hwcdrv_lwp_init ( ) ;
}
void
__collector_ext_hwc_lwp_fini ( )
{
get_hwc_driver ( ) - > hwcdrv_lwp_fini ( ) ;
}
int
__collector_ext_hwc_lwp_suspend ( )
{
return get_hwc_driver ( ) - > hwcdrv_lwp_suspend ( ) ;
}
int
__collector_ext_hwc_lwp_resume ( )
{
return get_hwc_driver ( ) - > hwcdrv_lwp_resume ( ) ;
}
/* Dummy routine, used to provide a context for non-program related profiles */
void
__collector_not_program_related ( ) { }
/* Dummy routine, used to provide a context for lost counts (perf_events) */
void
__collector_hwc_samples_lost ( ) { }
/* Dummy routine, used to provide a context */
void
__collector_hwcs_frozen ( ) { }
/* Dummy routine, used to provide a context */
void
__collector_hwcs_out_of_range ( ) { }
/* initialize some structures */
static void
init_ucontexts ( void )
{
/* initialize dummy context for "collector" frames */
2022-09-14 16:11:45 +08:00
CALL_UTIL ( getcontext ) ( & expr_dummy_uc ) ;
2022-03-11 16:58:31 +08:00
SETFUNCTIONCONTEXT ( & expr_dummy_uc , NULL ) ;
/* initialize dummy context for "out-of-range" frames */
2022-09-14 16:11:45 +08:00
CALL_UTIL ( getcontext ) ( & expr_out_of_range_uc ) ;
2022-03-11 16:58:31 +08:00
SETFUNCTIONCONTEXT ( & expr_out_of_range_uc , & __collector_hwcs_out_of_range ) ;
/* initialize dummy context for "frozen" frames */
2022-09-14 16:11:45 +08:00
CALL_UTIL ( getcontext ) ( & expr_frozen_uc ) ;
2022-03-11 16:58:31 +08:00
SETFUNCTIONCONTEXT ( & expr_frozen_uc , & __collector_hwcs_frozen ) ;
/* initialize dummy context for non-program-related frames */
2022-09-14 16:11:45 +08:00
CALL_UTIL ( getcontext ) ( & expr_nopc_uc ) ;
2022-03-11 16:58:31 +08:00
SETFUNCTIONCONTEXT ( & expr_nopc_uc , & __collector_not_program_related ) ;
/* initialize dummy context for lost-counts-related frames */
2022-09-14 16:11:45 +08:00
CALL_UTIL ( getcontext ) ( & expr_lostcounts_uc ) ;
2022-03-11 16:58:31 +08:00
SETFUNCTIONCONTEXT ( & expr_lostcounts_uc , & __collector_hwc_samples_lost ) ;
}
/* initialize the signal handler */
static int
hwc_initialize_handlers ( void )
{
/* install the signal handler for SIGEMT */
struct sigaction oact ;
if ( __collector_sigaction ( HWCFUNCS_SIGNAL , NULL , & oact ) ! = 0 )
{
TprintfT ( 0 , " hwc_initialize_handlers(): ERROR: hwc_initialize_handlers(): __collector_sigaction() failed to get oact \n " ) ;
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >old handler could not be determined</event> \n " , SP_JCMD_CERROR , COL_ERROR_HWCINIT ) ;
return COL_ERROR_HWCINIT ;
}
if ( oact . sa_sigaction = = collector_sigemt_handler )
{
/* signal handler is already in place; we are probably in a fork-child */
TprintfT ( DBG_LT1 , " hwc_initialize_handlers(): hwc_initialize_handlers() collector_sigemt_handler already installed \n " ) ;
}
else
{
/* set our signal handler */
struct sigaction c_act ;
CALL_UTIL ( memset ) ( & c_act , 0 , sizeof c_act ) ;
sigemptyset ( & c_act . sa_mask ) ;
sigaddset ( & c_act . sa_mask , SIGPROF ) ; /* block SIGPROF delivery in handler */
/* XXXX should probably also block sample_sig & pause_sig */
c_act . sa_sigaction = collector_sigemt_handler ; /* note: used to set sa_handler instead */
c_act . sa_flags = SA_RESTART | SA_SIGINFO ;
if ( __collector_sigaction ( HWCFUNCS_SIGNAL , & c_act , & old_sigemt_handler ) ! = 0 )
{
TprintfT ( 0 , " hwc_initialize_handlers(): ERROR: hwc_initialize_handlers(): __collector_sigaction() failed to set cact \n " ) ;
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >event handler could not be installed</event> \n " , SP_JCMD_CERROR , COL_ERROR_HWCINIT ) ;
return COL_ERROR_HWCINIT ;
}
}
return COL_ERROR_NONE ;
}
static int
close_experiment ( void )
{
/* note: stop_data_collection() should have already been called by
* collector_close_experiment ( )
*/
if ( ! COUNTERS_ENABLED ( ) )
return COL_ERROR_NONE ;
detach_experiment ( ) ;
/* cpc or libperfctr may still generate sigemts for a while */
/* verify that SIGEMT handler is still installed */
/* (still required with sigaction interposition and management,
since interposition is not done for attach experiments )
*/
struct sigaction curr ;
if ( __collector_sigaction ( HWCFUNCS_SIGNAL , NULL , & curr ) = = - 1 )
{
TprintfT ( 0 , " hwprofile close_experiment: ERROR: hwc sigaction check failed: errno=%d \n " , errno ) ;
}
else if ( curr . sa_sigaction ! = collector_sigemt_handler )
{
TprintfT ( DBG_LT1 , " hwprofile close_experiment: WARNING: collector sigemt handler replaced by 0x%p! \n " , curr . sa_handler ) ;
( void ) collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >0x%p</event> \n " ,
SP_JCMD_CWARN , COL_WARN_SIGEMT , curr . sa_handler ) ;
}
else
TprintfT ( DBG_LT1 , " hwprofile close_experiment: collector sigemt handler integrity verified! \n " ) ;
TprintfT ( 0 , " hwprofile: close_experiment \n " ) ;
return 0 ;
}
static int
detach_experiment ( void )
{
/* fork child. Clean up state but don't write to experiment */
/* note: stop_data_collection() has already been called by the fork_prologue */
// detach_experiment() can be called asynchronously
// from anywhere, even from within a sigemt handler
// via DBX detach.
// Important: stop_data_collection() _must_ be called
// before detach_experiment() is called.
if ( ! COUNTERS_ENABLED ( ) )
return COL_ERROR_NONE ;
TprintfT ( 0 , " hwprofile: detach_experiment() \n " ) ;
if ( SD_OFF ! = __collector_cas_32 ( & sd_state , SD_OFF , SD_PENDING ) )
return 0 ;
// one and only one call should ever make it here here.
if ( hwc_mode = = HWCMODE_ACTIVE )
{
TprintfT ( 0 , " hwprofile: ERROR: stop_data_collection() should have been called before detach_experiment() \n " ) ;
stop_data_collection ( ) ;
}
// Assumption: The only calls to sigemt_handler
// we should see at this point
// will be those that were already in-flight before
// stop_new_sigemts() was called.
if ( nthreads_in_sighandler > 0 )
{
// sigemt handlers should see
// SD_PENDING and should call real_detach_experiment()
// when the last handler is finished.
TprintfT ( DBG_LT1 , " hwprofile: detach in the middle of signal handler. \n " ) ;
return 0 ;
}
// If we get here, there should be no remaining
// sigemt handlers. However, we don't really know
// if there were ever any in flight, so call
// real_detach_experiment() here:
return real_detach_experiment ( ) ; // multiple calls to this OK
}
static int
real_detach_experiment ( void )
{
/*multiple calls to this routine are OK.*/
if ( SD_PENDING ! = __collector_cas_32 ( & sd_state , SD_PENDING , SD_COMPLETE ) )
return 0 ;
// only the first caller to this routine should get here.
hwcdef_cnt = 0 ; /* since now deinstalled */
hwcdef = NULL ;
set_hwc_mode ( HWCMODE_OFF ) ;
if ( SD_COMPLETE ! = __collector_cas_32 ( & sd_state , SD_COMPLETE , SD_OFF ) )
{
TprintfT ( 0 , " hwprofile: ERROR: unexpected sd_state in real_detach_experiment() \n " ) ;
sd_state = SD_OFF ;
}
hwprofile_tsd_key = COLLECTOR_TSD_INVALID_KEY ;
TprintfT ( DBG_LT0 , " hwprofile: real_detach_experiment() detached from experiment. \n " ) ;
return 0 ;
}
/*---------------------------------------------------------------------------*/
/* Record counter values. */
/* <value> should already be adjusted to be "zero-based" (counting up from 0).*/
static void
collector_record_counter_internal ( ucontext_t * ucp , int timecvt ,
ABST_type ABS_memop , hrtime_t time ,
unsigned tag , uint64_t value , uint64_t pc ,
uint64_t va , uint64_t latency ,
uint64_t data_source )
{
MHwcntr_packet pckt ;
CALL_UTIL ( memset ) ( & pckt , 0 , sizeof ( MHwcntr_packet ) ) ;
pckt . comm . tstamp = time ;
pckt . tag = tag ;
if ( timecvt > 1 )
{
if ( HWCVAL_HAS_ERR ( value ) )
{
value = HWCVAL_CLR_ERR ( value ) ;
value * = timecvt ;
value = HWCVAL_SET_ERR ( value ) ;
}
else
value * = timecvt ;
}
pckt . interval = value ;
pckt . comm . type = HW_PCKT ;
pckt . comm . tsize = sizeof ( Hwcntr_packet ) ;
TprintfT ( DBG_LT4 , " hwprofile: %llu sample %lld tag %u recorded \n " ,
( unsigned long long ) time , ( long long ) value , tag ) ;
if ( ABS_memop = = ABST_NOPC )
ucp = & expr_nopc_uc ;
pckt . comm . frinfo = collector_interface - > getFrameInfo ( expr_hndl , pckt . comm . tstamp , FRINFO_FROM_UC , ucp ) ;
collector_interface - > writeDataRecord ( expr_hndl , ( Common_packet * ) & pckt ) ;
}
static void
collector_record_counter ( ucontext_t * ucp , int timecvt , ABST_type ABS_memop ,
hrtime_t time , unsigned tag , uint64_t value )
{
collector_record_counter_internal ( ucp , timecvt , ABS_memop , time , tag , value ,
HWCFUNCS_INVALID_U64 , HWCFUNCS_INVALID_U64 ,
HWCFUNCS_INVALID_U64 , HWCFUNCS_INVALID_U64 ) ;
}
/*---------------------------------------------------------------------------*/
/* Signal handlers */
/* SIGEMT -- relayed from libcpc, when the counter overflows */
/* Generates the appropriate event or events, and resets the counters */
static void
collector_sigemt_handler ( int sig , siginfo_t * si , void * puc )
{
int rc ;
hwc_event_t sample , lost_samples ;
if ( sig ! = HWCFUNCS_SIGNAL )
{
TprintfT ( 0 , " hwprofile: ERROR: %s: unexpected signal %d \n " , " collector_sigemt_handler " , sig ) ;
return ;
}
if ( ! COUNTERS_ENABLED ( ) )
{ /* apparently deinstalled */
TprintfT ( 0 , " hwprofile: WARNING: SIGEMT detected after close_experiment() \n " ) ;
/* kills future sigemts since hwcdrv_sighlr_restart() not called */
return ;
}
/* Typically, we expect HWC overflow signals to come from the kernel: si_code > 0.
* On Linux , however , dbx might be " forwarding " a signal using tkill ( ) / tgkill ( ) .
* For more information on what si_code values can be expected on Linux , check :
* cmn_components / Collector_Interface / hwcdrv_pcl . c hwcdrv_overflow ( )
* cmn_components / Collector_Interface / hwcdrv_perfctr . c hdrv_perfctr_overflow ( )
*/
if ( puc = = NULL | | si = = NULL | | ( si - > si_code < = 0 & & si - > si_code ! = SI_TKILL ) )
{
TprintfT ( DBG_LT3 , " hwprofile: collector_sigemt_handler SIG%02d \n " , sig ) ;
if ( old_sigemt_handler . sa_handler = = SIG_DFL )
__collector_SIGDFL_handler ( HWCFUNCS_SIGNAL ) ;
else if ( old_sigemt_handler . sa_handler ! = SIG_IGN & &
old_sigemt_handler . sa_sigaction ! = & collector_sigemt_handler )
{
/* Redirect the signal to the previous signal handler */
( old_sigemt_handler . sa_sigaction ) ( sig , si , puc ) ;
TprintfT ( DBG_LT1 , " hwprofile: collector_sigemt_handler SIG%02d redirected to original handler \n " , sig ) ;
}
return ;
}
rc = get_hwc_driver ( ) - > hwcdrv_overflow ( si , & sample , & lost_samples ) ;
if ( rc )
{
/* hwcdrv_sighlr_restart() should not be called */
TprintfT ( 0 , " hwprofile: ERROR: collector_sigemt_handler: hwcdrv_overflow() failed \n " ) ;
return ;
}
if ( hwc_mode = = HWCMODE_ACTIVE )
{
/* record the event only if counters are active */
/* The following has been copied from dispatcher.c */
# if ARCH(SPARC)
/* 23340823 signal handler third argument should point to a ucontext_t */
/* Convert sigcontext to ucontext_t on sparc-Linux */
ucontext_t uctxmem ;
struct sigcontext * sctx = ( struct sigcontext * ) puc ;
ucontext_t * uctx = & uctxmem ;
uctx - > uc_link = NULL ;
# if WSIZE(32)
uctx - > uc_mcontext . gregs [ REG_PC ] = sctx - > si_regs . pc ;
__collector_memcpy ( & uctx - > uc_mcontext . gregs [ 3 ] ,
sctx - > si_regs . u_regs ,
sizeof ( sctx - > si_regs . u_regs ) ) ;
# else
uctx - > uc_mcontext . mc_gregs [ MC_PC ] = sctx - > sigc_regs . tpc ;
__collector_memcpy ( & uctx - > uc_mcontext . mc_gregs [ 3 ] ,
sctx - > sigc_regs . u_regs ,
sizeof ( sctx - > sigc_regs . u_regs ) ) ;
# endif /* WSIZE() */
# else
ucontext_t * uctx = ( ucontext_t * ) puc ;
# endif /* ARCH() */
for ( int ii = 0 ; ii < hwcdef_cnt ; ii + + )
if ( lost_samples . ce_pic [ ii ] )
collector_record_counter ( & expr_lostcounts_uc , hwcdef [ ii ] - > timecvt ,
hwcdef [ ii ] - > memop , lost_samples . ce_hrt ,
hwcdef [ ii ] - > sort_order , lost_samples . ce_pic [ ii ] ) ;
for ( int ii = 0 ; ii < hwcdef_cnt ; ii + + )
if ( sample . ce_pic [ ii ] )
collector_record_counter ( uctx , hwcdef [ ii ] - > timecvt ,
hwcdef [ ii ] - > memop , sample . ce_hrt ,
hwcdef [ ii ] - > sort_order , sample . ce_pic [ ii ] ) ;
}
rc = get_hwc_driver ( ) - > hwcdrv_sighlr_restart ( NULL ) ;
}
/* SIGPROF -- not installed as handler, but
* __collector_ext_hwc_check : called by ( SIGPROF ) dispatcher .
* Periodical check of integrity of HWC count / signal mechanism ,
* as required for various chip / system bugs / workarounds .
*/
void
__collector_ext_hwc_check ( siginfo_t * info , ucontext_t * vcontext ) { }
/*---------------------------------------------------------------------------*/
int
collector_sigemt_sigaction ( const struct sigaction * nact ,
struct sigaction * oact )
{
struct sigaction oact_check ;
/* Error codes and messages that refer to HWC are tricky.
* E . g . , HWC profiling might not even be on ; we might
* encounter an error here simply because the user is
* trying to set a handler for a signal that happens to
* be HWCFUNCS_SIGNAL , which we aren ' t even using .
*/
if ( __collector_sigaction ( HWCFUNCS_SIGNAL , NULL , & oact_check ) ! = 0 )
{
TprintfT ( 0 , " hwprofile: ERROR: collector_sigemt_sigaction(): request to set handler for signal %d, but check on existing handler failed \n " , HWCFUNCS_SIGNAL ) ;
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >old handler for signal %d could not be determined</event> \n " , SP_JCMD_CERROR , COL_ERROR_HWCINIT , HWCFUNCS_SIGNAL ) ;
return COL_ERROR_HWCINIT ;
}
if ( oact_check . sa_sigaction = = collector_sigemt_handler )
{
/* dispatcher is in place, so nact/oact apply to old_sigemt_handler */
if ( oact ! = NULL )
{
oact - > sa_handler = old_sigemt_handler . sa_handler ;
oact - > sa_mask = old_sigemt_handler . sa_mask ;
oact - > sa_flags = old_sigemt_handler . sa_flags ;
}
if ( nact ! = NULL )
{
old_sigemt_handler . sa_handler = nact - > sa_handler ;
old_sigemt_handler . sa_mask = nact - > sa_mask ;
old_sigemt_handler . sa_flags = nact - > sa_flags ;
}
return COL_ERROR_NONE ;
}
else /* no dispatcher in place, so just act like normal sigaction() */
return __collector_sigaction ( HWCFUNCS_SIGNAL , nact , oact ) ;
}
static void
collector_hwc_ABORT ( int errnum , const char * msg )
{
TprintfT ( 0 , " hwprofile: collector_hwc_ABORT: [%d] %s \n " , errnum , msg ) ;
if ( hwc_mode = = HWCMODE_ABORT ) /* HWC collection already aborted! */
return ;
set_hwc_mode ( HWCMODE_ABORT ) ; /* set global flag to disable handlers and indicate abort */
/* Write the error message to the experiment */
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s: errno=%d</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCFAIL , msg , errnum ) ;
# ifdef REAL_DEBUG
abort ( ) ;
# else
TprintfT ( 0 , " hwprofile: Continuing without HWC collection... \n " ) ;
# endif
}
static int
start_data_collection ( void )
{
hwc_mode_t old_mode = hwc_mode ;
if ( ! COUNTERS_ENABLED ( ) )
return COL_ERROR_NONE ;
TprintfT ( 0 , " hwprofile: start_data_collection (hwc_mode=%d) \n " , old_mode ) ;
switch ( old_mode )
{
case HWCMODE_OFF :
if ( get_hwc_driver ( ) - > hwcdrv_start ( ) )
{
TprintfT ( 0 , " hwprofile: ERROR: start_data_collection() failed in hwcdrv_start() \n " ) ;
collector_interface - > writeLog ( " <event kind= \" %s \" id= \" %d \" >%s: errno=%d</event> \n " ,
SP_JCMD_CERROR , COL_ERROR_HWCFAIL ,
" start_data_collection() " , errno ) ;
return COL_ERROR_HWCINIT ;
}
set_hwc_mode ( HWCMODE_ACTIVE ) ; /* start handling events on signals */
break ;
case HWCMODE_SUSPEND :
if ( get_hwc_driver ( ) - > hwcdrv_lwp_resume ( ) )
{
TprintfT ( 0 , " hwprofile: ERROR: start_data_collection() failed in hwcdrv_lwp_resume() \n " ) ;
/* ignore errors from lwp_resume() */
}
set_hwc_mode ( HWCMODE_ACTIVE ) ; /* start handling events on signals */
break ;
default :
TprintfT ( 0 , " hwprofile: ERROR: start_data_collection() invalid mode \n " ) ;
return COL_ERROR_HWCINIT ;
}
return COL_ERROR_NONE ;
}
static int
stop_data_collection ( void )
{
hwc_mode_t old_mode = hwc_mode ;
if ( ! COUNTERS_ENABLED ( ) )
return COL_ERROR_NONE ;
TprintfT ( 0 , " hwprofile: stop_data_collection (hwc_mode=%d) \n " , old_mode ) ;
switch ( old_mode )
{
case HWCMODE_SUSPEND :
return COL_ERROR_NONE ;
case HWCMODE_ACTIVE :
set_hwc_mode ( HWCMODE_SUSPEND ) ; /* stop handling signals */
break ;
default :
/* Don't change the mode, but attempt to suspend anyway... */
break ;
}
if ( get_hwc_driver ( ) - > hwcdrv_lwp_suspend ( ) )
/* ignore errors from lwp_suspend() */
TprintfT ( 0 , " hwprofile: ERROR: stop_data_collection() failed in hwcdrv_lwp_suspend() \n " ) ;
/*
* hwcdrv_lwp_suspend ( ) cannot guarantee that all SIGEMTs will stop
* but hwc_mode will prevent logging and counters will overflow once
* then stay frozen .
*/
/* There may still be pending SIGEMTs so don't reset the SIG_DFL handler.
*/
/* see comment in dispatcher.c */
/* ret = __collector_sigaction( SIGEMT, &old_sigemt_handler, NULL ); */
return COL_ERROR_NONE ;
}
/*---------------------------------------------------------------------------*/
/* utilities */
static void
set_hwc_mode ( hwc_mode_t md )
{
TprintfT ( DBG_LT1 , " hwprofile: set_hwc_mode(%d) \n " , md ) ;
hwc_mode = md ;
}
int
__collector_ext_hwc_active ( )
{
return ( hwc_mode = = HWCMODE_ACTIVE ) ;
}
static void
hwclogwrite0 ( )
{
collector_interface - > writeLog ( " <profdata fname= \" %s \" /> \n " ,
module_interface . description ) ;
/* Record Hwcntr_packet description */
collector_interface - > writeLog ( " <profpckt kind= \" %d \" uname= \" " STXT ( " Hardware counter profiling data " ) " \" > \n " , HW_PCKT ) ;
collector_interface - > writeLog ( " <field name= \" LWPID \" uname= \" " STXT ( " Lightweight process id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , comm . lwp_id ) ,
fld_sizeof ( Hwcntr_packet , comm . lwp_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" THRID \" uname= \" " STXT ( " Thread number " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , comm . thr_id ) ,
fld_sizeof ( Hwcntr_packet , comm . thr_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" CPUID \" uname= \" " STXT ( " CPU id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , comm . cpu_id ) ,
fld_sizeof ( Hwcntr_packet , comm . cpu_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" TSTAMP \" uname= \" " STXT ( " High resolution timestamp " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , comm . tstamp ) ,
fld_sizeof ( Hwcntr_packet , comm . tstamp ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" FRINFO \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , comm . frinfo ) ,
fld_sizeof ( Hwcntr_packet , comm . frinfo ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" HWCTAG \" uname= \" " STXT ( " Hardware counter index " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , tag ) ,
fld_sizeof ( Hwcntr_packet , tag ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" HWCINT \" uname= \" " STXT ( " Hardware counter interval " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( Hwcntr_packet , interval ) ,
fld_sizeof ( Hwcntr_packet , interval ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " </profpckt> \n " ) ;
if ( hwcdef_has_memspace )
{
/* Record MHwcntr_packet description */
collector_interface - > writeLog ( " <profpckt kind= \" %d \" uname= \" " STXT ( " Hardware counter profiling data " ) " \" > \n " , MHWC_PCKT ) ;
collector_interface - > writeLog ( " <field name= \" LWPID \" uname= \" " STXT ( " Lightweight process id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , comm . lwp_id ) ,
fld_sizeof ( MHwcntr_packet , comm . lwp_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" THRID \" uname= \" " STXT ( " Thread number " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , comm . thr_id ) ,
fld_sizeof ( MHwcntr_packet , comm . thr_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" CPUID \" uname= \" " STXT ( " CPU id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , comm . cpu_id ) ,
fld_sizeof ( MHwcntr_packet , comm . cpu_id ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" TSTAMP \" uname= \" " STXT ( " High resolution timestamp " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , comm . tstamp ) ,
fld_sizeof ( MHwcntr_packet , comm . tstamp ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" FRINFO \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , comm . frinfo ) ,
fld_sizeof ( MHwcntr_packet , comm . frinfo ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" HWCTAG \" uname= \" " STXT ( " Hardware counter index " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , tag ) ,
fld_sizeof ( MHwcntr_packet , tag ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" HWCINT \" uname= \" " STXT ( " Hardware counter interval " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , interval ) ,
fld_sizeof ( MHwcntr_packet , interval ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" VADDR \" uname= \" " STXT ( " Virtual address (data) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , ea_vaddr ) ,
fld_sizeof ( MHwcntr_packet , ea_vaddr ) = = 4 ? " UINT32 " : " UINT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" PADDR \" uname= \" " STXT ( " Physical address (data) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , ea_paddr ) ,
fld_sizeof ( MHwcntr_packet , ea_paddr ) = = 4 ? " UINT32 " : " UINT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" VIRTPC \" uname= \" " STXT ( " Virtual address (instruction) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , pc_vaddr ) ,
fld_sizeof ( MHwcntr_packet , pc_vaddr ) = = 4 ? " UINT32 " : " UINT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" PHYSPC \" uname= \" " STXT ( " Physical address (instruction) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , pc_paddr ) ,
fld_sizeof ( MHwcntr_packet , pc_paddr ) = = 4 ? " UINT32 " : " UINT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" EA_PAGESIZE \" uname= \" " STXT ( " Page size (data) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , ea_pagesz ) ,
fld_sizeof ( MHwcntr_packet , ea_pagesz ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" PC_PAGESIZE \" uname= \" " STXT ( " Page size (instruction) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , pc_pagesz ) ,
fld_sizeof ( MHwcntr_packet , pc_pagesz ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" EA_LGRP \" uname= \" " STXT ( " Page locality group (data) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , ea_lgrp ) ,
fld_sizeof ( MHwcntr_packet , ea_lgrp ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" PC_LGRP \" uname= \" " STXT ( " Page locality group (instruction) " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , pc_lgrp ) ,
fld_sizeof ( MHwcntr_packet , pc_lgrp ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" LWP_LGRP_HOME \" uname= \" " STXT ( " LWP home lgroup id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , lgrp_lwp ) ,
fld_sizeof ( MHwcntr_packet , lgrp_lwp ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" PS_LGRP_HOME \" uname= \" " STXT ( " Process home lgroup id " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , lgrp_ps ) ,
fld_sizeof ( MHwcntr_packet , lgrp_ps ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" MEM_LAT \" uname= \" " STXT ( " Memory Latency Cycles " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , latency ) ,
fld_sizeof ( MHwcntr_packet , latency ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " <field name= \" MEM_SRC \" uname= \" " STXT ( " Memory Data Source " ) " \" offset= \" %d \" type= \" %s \" /> \n " ,
2023-10-24 10:55:01 +08:00
( int ) offsetof ( MHwcntr_packet , data_source ) ,
fld_sizeof ( MHwcntr_packet , data_source ) = = 4 ? " INT32 " : " INT64 " ) ;
2022-03-11 16:58:31 +08:00
collector_interface - > writeLog ( " </profpckt> \n " ) ;
}
}
static void
hwclogwrite ( Hwcentry * ctr )
{
TprintfT ( DBG_LT1 , " hwprofile: writeLog(%s %u %s %d %u %d) \n " ,
SP_JCMD_HW_COUNTER , cpcN_cpuver , ctr - > name ? ctr - > name : " NULL " ,
ctr - > val , ctr - > sort_order , ctr - > memop ) ;
collector_interface - > writeLog ( " <profile name= \" %s \" " , SP_JCMD_HW_COUNTER ) ;
collector_interface - > writeLog ( " cpuver= \" %u \" " , cpcN_cpuver ) ;
collector_interface - > writeLog ( " hwcname= \" %s \" " , ctr - > name ) ;
collector_interface - > writeLog ( " int_name= \" %s \" " , ctr - > int_name ) ;
collector_interface - > writeLog ( " interval= \" %d \" " , ctr - > val ) ;
collector_interface - > writeLog ( " tag= \" %u \" " , ctr - > sort_order ) ;
collector_interface - > writeLog ( " memop= \" %d \" " , ctr - > memop ) ;
collector_interface - > writeLog ( " /> \n " ) ;
}