/* * %CopyrightBegin% * * Copyright Ericsson AB 2004-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved online at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * %CopyrightEnd% */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sys.h" #include "error.h" #include "global.h" #include "bif.h" #include "big.h" #include "erl_binary.h" #include "hipe_perfctr.h" #include "libperfctr.h" static struct vperfctr *vperfctr; static unsigned int have_rdtsc; static double tsc_to_ms; static unsigned int tsc_on; /* control calls must set tsc_on if have_rdtsc is true */ static unsigned int nractrs; static unsigned int users; #define USER_BIFS (1<<0) #define USER_HRVTIME (1<<1) static int hipe_perfctr_open(unsigned int user) { struct perfctr_info info; if (!vperfctr) { vperfctr = vperfctr_open(); if (!vperfctr) return -1; if (vperfctr_info(vperfctr, &info) >= 0) { tsc_to_ms = (double)(info.tsc_to_cpu_mult ? : 1) / (double)info.cpu_khz; have_rdtsc = (info.cpu_features & PERFCTR_FEATURE_RDTSC) ? 1 : 0; } tsc_on = 0; nractrs = 0; } users |= user; return 0; } static void hipe_perfctr_reset(void) { struct vperfctr_control control; memset(&control, 0, sizeof control); if (have_rdtsc) control.cpu_control.tsc_on = 1; nractrs = 0; if (vperfctr_control(vperfctr, &control) >= 0) tsc_on = 1; } static void hipe_perfctr_close(unsigned int user) { if (!vperfctr) return; users &= ~user; switch (users) { case 0: vperfctr_unlink(vperfctr); vperfctr_close(vperfctr); vperfctr = NULL; tsc_on = 0; nractrs = 0; break; case USER_HRVTIME: hipe_perfctr_reset(); } } /* * Interface for HiPE's hrvtime code. */ int hipe_perfctr_hrvtime_open(void) { if (hipe_perfctr_open(USER_HRVTIME) < 0) return -1; if (have_rdtsc) { if (!tsc_on) hipe_perfctr_reset(); /* note: updates tsc_on */ if (tsc_on) return 0; } hipe_perfctr_hrvtime_close(); return -1; } void hipe_perfctr_hrvtime_close(void) { hipe_perfctr_close(USER_HRVTIME); } double hipe_perfctr_hrvtime_get(void) { return (double)vperfctr_read_tsc(vperfctr) * tsc_to_ms; } /* * BIF interface for user-programmable performance counters. */ BIF_RETTYPE hipe_bifs_vperfctr_open_0(BIF_ALIST_0) { if (hipe_perfctr_open(USER_BIFS) < 0) BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ BIF_RET(am_true); } BIF_RETTYPE hipe_bifs_vperfctr_close_0(BIF_ALIST_0) { hipe_perfctr_close(USER_BIFS); BIF_RET(NIL); } static Eterm ull_to_integer(unsigned long long x, Process *p) { unsigned long long tmpx; unsigned int ds, i; size_t sz; Eterm *hp; ErtsDigit *xp; if (x <= (unsigned long long)MAX_SMALL) return make_small(x); /* Calculate number of digits. */ ds = 0; tmpx = x; do { ++ds; tmpx = (tmpx >> (D_EXP / 2)) >> (D_EXP / 2); } while (tmpx != 0); sz = BIG_NEED_SIZE(ds); /* number of words including arity */ hp = HAlloc(p, sz); *hp = make_pos_bignum_header(sz-1); xp = (ErtsDigit*)(hp+1); i = 0; do { xp[i++] = (ErtsDigit)x; x = (x >> (D_EXP / 2)) >> (D_EXP / 2); } while (i < ds); while (i & (BIG_DIGITS_PER_WORD-1)) xp[i++] = 0; return make_big(hp); } BIF_RETTYPE hipe_bifs_vperfctr_info_0(BIF_ALIST_0) { struct perfctr_info info; if (!vperfctr || vperfctr_info(vperfctr, &info) < 0) BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ BIF_RET(new_binary(BIF_P, (void*)&info, sizeof info)); } BIF_RETTYPE hipe_bifs_vperfctr_read_tsc_0(BIF_ALIST_0) { unsigned long long val; if (!vperfctr || !tsc_on) BIF_RET(am_false); /* arity 0 BIFs can't fail :-( */ val = vperfctr_read_tsc(vperfctr); BIF_RET(ull_to_integer(val, BIF_P)); } BIF_RETTYPE hipe_bifs_vperfctr_read_pmc_1(BIF_ALIST_1) { Uint pmc; unsigned long long val; if (!vperfctr || is_not_small(BIF_ARG_1) || (pmc = unsigned_val(BIF_ARG_1), pmc >= nractrs)) BIF_RET(am_false); /* for consistency with the arity 0 BIFs */ val = vperfctr_read_pmc(vperfctr, pmc); BIF_RET(ull_to_integer(val, BIF_P)); } BIF_RETTYPE hipe_bifs_vperfctr_control_1(BIF_ALIST_1) { void *bytes; struct vperfctr_control control; Uint bitoffs; Uint bitsize; if (!vperfctr) BIF_ERROR(BIF_P, BADARG); if (is_not_binary(BIF_ARG_1)) BIF_ERROR(BIF_P, BADARG); if (binary_size(BIF_ARG_1) != sizeof control) BIF_ERROR(BIF_P, BADARG); ERTS_GET_BINARY_BYTES(BIF_ARG_1, bytes, bitoffs, bitsize); ASSERT(bitoffs == 0); ASSERT(bitsize == 0); memcpy(&control, bytes, sizeof control); if (have_rdtsc) control.cpu_control.tsc_on = 1; if (vperfctr_control(vperfctr, &control) < 0) { hipe_perfctr_reset(); BIF_ERROR(BIF_P, BADARG); } tsc_on = control.cpu_control.tsc_on; nractrs = control.cpu_control.nractrs; BIF_RET(NIL); }