aboutsummaryrefslogtreecommitdiff
path: root/gprofng/libcollector/hwprofile.c
diff options
context:
space:
mode:
authorVladimir Mezentsev <vladimir.mezentsev@oracle.com>2022-03-11 08:58:31 +0000
committerNick Clifton <nickc@redhat.com>2022-03-11 08:58:31 +0000
commitbb368aad297fe3ad40cf397e6fc85aa471429a28 (patch)
tree0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/libcollector/hwprofile.c
parenta655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff)
downloadgdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip
gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz
gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2
gprofng: a new GNU profiler
top-level * Makefile.def: Add gprofng module. * configure.ac: Add --enable-gprofng option. * src-release.sh: Add gprofng. * Makefile.in: Regenerate. * configure: Regenerate. * gprofng: New directory. binutils * MAINTAINERS: Add gprofng maintainer. * README-how-to-make-a-release: Add gprofng. include. * collectorAPI.h: New file. * libcollector.h: New file. * libfcollector.h: New file.
Diffstat (limited to 'gprofng/libcollector/hwprofile.c')
-rw-r--r--gprofng/libcollector/hwprofile.c905
1 files changed, 905 insertions, 0 deletions
diff --git a/gprofng/libcollector/hwprofile.c b/gprofng/libcollector/hwprofile.c
new file mode 100644
index 0000000..6fdaf1b
--- /dev/null
+++ b/gprofng/libcollector/hwprofile.c
@@ -0,0 +1,905 @@
+/* Copyright (C) 2021 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 <alloca.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/syscall.h>
+#include <signal.h>
+#include <ucontext.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"
+
+/* TprintfT(<level>,...) definitions. Adjust per module as needed */
+#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
+#define DBG_LT1 1 // for configuration details, warnings
+#define DBG_LT2 2
+#define DBG_LT3 3
+#define DBG_LT4 4
+#define DBG_LT5 5
+
+#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 */
+ getcontext (&expr_dummy_uc);
+ SETFUNCTIONCONTEXT (&expr_dummy_uc, NULL);
+
+ /* initialize dummy context for "out-of-range" frames */
+ getcontext (&expr_out_of_range_uc);
+ SETFUNCTIONCONTEXT (&expr_out_of_range_uc, &__collector_hwcs_out_of_range);
+
+ /* initialize dummy context for "frozen" frames */
+ getcontext (&expr_frozen_uc);
+ SETFUNCTIONCONTEXT (&expr_frozen_uc, &__collector_hwcs_frozen);
+
+ /* initialize dummy context for non-program-related frames */
+ getcontext (&expr_nopc_uc);
+ SETFUNCTIONCONTEXT (&expr_nopc_uc, &__collector_not_program_related);
+
+ /* initialize dummy context for lost-counts-related frames */
+ 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 ("<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 */
+ Hwcntr_packet *pp = NULL;
+ 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",
+ &pp->comm.lwp_id, sizeof (pp->comm.lwp_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->comm.thr_id, sizeof (pp->comm.thr_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->comm.cpu_id, sizeof (pp->comm.cpu_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->comm.tstamp, sizeof (pp->comm.tstamp) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->comm.frinfo, sizeof (pp->comm.frinfo) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"HWCTAG\" uname=\"" STXT ("Hardware counter index") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->tag, sizeof (pp->tag) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"HWCINT\" uname=\"" STXT ("Hardware counter interval") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &pp->interval, sizeof (pp->interval) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog ("</profpckt>\n");
+ if (hwcdef_has_memspace)
+ {
+ /* Record MHwcntr_packet description */
+ MHwcntr_packet *xpp = NULL;
+ 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",
+ &xpp->comm.lwp_id, sizeof (xpp->comm.lwp_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"THRID\" uname=\"" STXT ("Thread number") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->comm.thr_id, sizeof (xpp->comm.thr_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"CPUID\" uname=\"" STXT ("CPU id") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->comm.cpu_id, sizeof (xpp->comm.cpu_id) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"TSTAMP\" uname=\"" STXT ("High resolution timestamp") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->comm.tstamp, sizeof (xpp->comm.tstamp) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->comm.frinfo, sizeof (xpp->comm.frinfo) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"HWCTAG\" uname=\"" STXT ("Hardware counter index") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->tag, sizeof (xpp->tag) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"HWCINT\" uname=\"" STXT ("Hardware counter interval") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->interval, sizeof (xpp->interval) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"VADDR\" uname=\"" STXT ("Virtual address (data)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->ea_vaddr, sizeof (xpp->ea_vaddr) == 4 ? "UINT32" : "UINT64");
+ collector_interface->writeLog (" <field name=\"PADDR\" uname=\"" STXT ("Physical address (data)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->ea_paddr, sizeof (xpp->ea_paddr) == 4 ? "UINT32" : "UINT64");
+ collector_interface->writeLog (" <field name=\"VIRTPC\" uname=\"" STXT ("Virtual address (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->pc_vaddr, sizeof (xpp->pc_vaddr) == 4 ? "UINT32" : "UINT64");
+ collector_interface->writeLog (" <field name=\"PHYSPC\" uname=\"" STXT ("Physical address (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->pc_paddr, sizeof (xpp->pc_paddr) == 4 ? "UINT32" : "UINT64");
+ collector_interface->writeLog (" <field name=\"EA_PAGESIZE\" uname=\"" STXT ("Page size (data)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->ea_pagesz, sizeof (xpp->ea_pagesz) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"PC_PAGESIZE\" uname=\"" STXT ("Page size (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->pc_pagesz, sizeof (xpp->pc_pagesz) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"EA_LGRP\" uname=\"" STXT ("Page locality group (data)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->ea_lgrp, sizeof (xpp->ea_lgrp) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"PC_LGRP\" uname=\"" STXT ("Page locality group (instruction)") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->pc_lgrp, sizeof (xpp->pc_lgrp) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"LWP_LGRP_HOME\" uname=\"" STXT ("LWP home lgroup id") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->lgrp_lwp, sizeof (xpp->lgrp_lwp) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"PS_LGRP_HOME\" uname=\"" STXT ("Process home lgroup id") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->lgrp_ps, sizeof (xpp->lgrp_ps) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"MEM_LAT\" uname=\"" STXT ("Memory Latency Cycles") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->latency, sizeof (xpp->latency) == 4 ? "INT32" : "INT64");
+ collector_interface->writeLog (" <field name=\"MEM_SRC\" uname=\"" STXT ("Memory Data Source") "\" offset=\"%d\" type=\"%s\"/>\n",
+ &xpp->data_source, sizeof (xpp->data_source) == 4 ? "INT32" : "INT64");
+ 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");
+}