/* Copyright (C) 2021-2024 Free Software Foundation, Inc. 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 #include #include #include #include #include #include #include #include #include "gp-defs.h" #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 ("data handle not created\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 = __collector_strchr (params, ';'); if (params) params++; } if (params == NULL) /* HWC profiling not specified */ return COL_ERROR_HWCINIT; char *s = __collector_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 ("%s\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 ("%s\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 ("%s\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 ("%s\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 ("%s\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 */ CALL_UTIL (getcontext) (&expr_dummy_uc); SETFUNCTIONCONTEXT (&expr_dummy_uc, NULL); /* initialize dummy context for "out-of-range" frames */ CALL_UTIL (getcontext) (&expr_out_of_range_uc); SETFUNCTIONCONTEXT (&expr_out_of_range_uc, &__collector_hwcs_out_of_range); /* initialize dummy context for "frozen" frames */ CALL_UTIL (getcontext) (&expr_frozen_uc); SETFUNCTIONCONTEXT (&expr_frozen_uc, &__collector_hwcs_frozen); /* initialize dummy context for non-program-related frames */ CALL_UTIL (getcontext) (&expr_nopc_uc); SETFUNCTIONCONTEXT (&expr_nopc_uc, &__collector_not_program_related); /* initialize dummy context for lost-counts-related frames */ CALL_UTIL (getcontext) (&expr_lostcounts_uc); 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 ("old handler could not be determined\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 */ static struct sigaction c_act_0 = {.sa_flags = SA_RESTART | SA_SIGINFO}; struct sigaction c_act = c_act_0; 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 handler could not be installed\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 ("0x%p\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. */ /* 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) { static MHwcntr_packet hwc_packet_0 = {.comm.type = HW_PCKT, .comm.tsize = sizeof (Hwcntr_packet)}; MHwcntr_packet pckt = hwc_packet_0; 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; 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 ("old handler for signal %d could not be determined\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 ("%s: errno=%d\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 ("%s: errno=%d\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 ("\n", module_interface.description); /* Record Hwcntr_packet description */ collector_interface->writeLog ("\n", HW_PCKT); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, comm.lwp_id), fld_sizeof (Hwcntr_packet, comm.lwp_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, comm.thr_id), fld_sizeof (Hwcntr_packet, comm.thr_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, comm.cpu_id), fld_sizeof (Hwcntr_packet, comm.cpu_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, comm.tstamp), fld_sizeof (Hwcntr_packet, comm.tstamp) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, comm.frinfo), fld_sizeof (Hwcntr_packet, comm.frinfo) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, tag), fld_sizeof (Hwcntr_packet, tag) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (Hwcntr_packet, interval), fld_sizeof (Hwcntr_packet, interval) == 4 ? "INT32" : "INT64"); collector_interface->writeLog ("\n"); if (hwcdef_has_memspace) { /* Record MHwcntr_packet description */ collector_interface->writeLog ("\n", MHWC_PCKT); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, comm.lwp_id), fld_sizeof (MHwcntr_packet, comm.lwp_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, comm.thr_id), fld_sizeof (MHwcntr_packet, comm.thr_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, comm.cpu_id), fld_sizeof (MHwcntr_packet, comm.cpu_id) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, comm.tstamp), fld_sizeof (MHwcntr_packet, comm.tstamp) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, comm.frinfo), fld_sizeof (MHwcntr_packet, comm.frinfo) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, tag), fld_sizeof (MHwcntr_packet, tag) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, interval), fld_sizeof (MHwcntr_packet, interval) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, ea_vaddr), fld_sizeof (MHwcntr_packet, ea_vaddr) == 4 ? "UINT32" : "UINT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, ea_paddr), fld_sizeof (MHwcntr_packet, ea_paddr) == 4 ? "UINT32" : "UINT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, pc_vaddr), fld_sizeof (MHwcntr_packet, pc_vaddr) == 4 ? "UINT32" : "UINT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, pc_paddr), fld_sizeof (MHwcntr_packet, pc_paddr) == 4 ? "UINT32" : "UINT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, ea_pagesz), fld_sizeof (MHwcntr_packet, ea_pagesz) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, pc_pagesz), fld_sizeof (MHwcntr_packet, pc_pagesz) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, ea_lgrp), fld_sizeof (MHwcntr_packet, ea_lgrp) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, pc_lgrp), fld_sizeof (MHwcntr_packet, pc_lgrp) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, lgrp_lwp), fld_sizeof (MHwcntr_packet, lgrp_lwp) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, lgrp_ps), fld_sizeof (MHwcntr_packet, lgrp_ps) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, latency), fld_sizeof (MHwcntr_packet, latency) == 4 ? "INT32" : "INT64"); collector_interface->writeLog (" \n", (int) offsetof (MHwcntr_packet, data_source), fld_sizeof (MHwcntr_packet, data_source) == 4 ? "INT32" : "INT64"); collector_interface->writeLog ("\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 ("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"); }