/*
 * Copyright (c) 2002-2006 Hewlett-Packard Development Company, L.P.
 * Contributed by Stephane Eranian <eranian@hpl.hp.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * This file is part of libpfm, a performance monitoring support library for
 * applications on Linux.
 */
#ifndef __PFMLIB_PRIV_H__
#define __PFMLIB_PRIV_H__
#include <perfmon/pfmlib.h>
#include <string.h>

#define PFM_PLM_ALL (PFM_PLM0|PFM_PLM1|PFM_PLM2|PFM_PLM3|PFM_PLMH)

#define PFMLIB_ATTR_DELIM	':'	/* event attribute delimiter */
#define PFMLIB_PMU_DELIM	"::"	/* pmu to event delimiter */
#define PFMLIB_EVENT_DELIM	','	/* event to event delimiter */

#define PFM_ATTR_I(y, d) { .name = (y), .type = PFM_ATTR_MOD_INTEGER, .desc = (d) }
#define PFM_ATTR_B(y, d) { .name = (y), .type = PFM_ATTR_MOD_BOOL, .desc = (d) }
#define PFM_ATTR_SKIP	 { .name = "" } /* entry not populated (skipped) */
#define PFM_ATTR_NULL	{ .name = NULL }

#define PFMLIB_EVT_MAX_NAME_LEN	256

/*
 * event identifier encoding:
 * bit 00-20 : event table specific index (2097152 possibilities)
 * bit 21-30 : PMU identifier (1024 possibilities)
 * bit 31    : reserved (to distinguish from a negative error code)
 */
#define PFMLIB_PMU_SHIFT	21
#define PFMLIB_PMU_MASK		0x3ff /* must fit PFM_PMU_MAX */
#define PFMLIB_PMU_PIDX_MASK	((1<< PFMLIB_PMU_SHIFT)-1)

typedef struct {
	const char	*name;	/* name */
	const char	*desc;	/* description */
	pfm_attr_t	type;	/* used to validate value (if any) */
} pfmlib_attr_desc_t;

/*
 * attribute description passed to model-specific layer
 */
typedef struct {
	int	id;			/* attribute index */
	union {
		uint64_t	ival;	/* integer value (incl. bool) */
		char		*sval;	/* string */
	};
} pfmlib_attr_t;

/*
 * must be big enough to hold all possible priv level attributes
 */
#define PFMLIB_MAX_ATTRS	64 /* max attributes per event desc */
#define PFMLIB_MAX_ENCODING	4  /* max encoding length */

/*
 * we add one entry to hold any raw umask users may specify
 * the last entry in pattrs[] hold that raw umask info
 */
#define PFMLIB_MAX_PATTRS	(PFMLIB_MAX_ATTRS+1)

struct pfmlib_pmu;
typedef struct {
	struct pfmlib_pmu	*pmu;				/* pmu */
	int			dfl_plm;			/* default priv level mask */
	int			event;				/* pidx */
	int			npattrs;			/* number of attrs in pattrs[] */
	int			nattrs;				/* number of attrs in attrs[] */
	pfm_os_t		osid;				/* OS API requested */
	int			count;				/* number of entries in codes[] */
	pfmlib_attr_t		attrs[PFMLIB_MAX_ATTRS];	/* list of requested attributes */

	pfm_event_attr_info_t	*pattrs;			/* list of possible attributes */
	char			fstr[PFMLIB_EVT_MAX_NAME_LEN];	/* fully qualified event string */
	uint64_t		codes[PFMLIB_MAX_ENCODING];	/* event encoding */
	void			*os_data;
} pfmlib_event_desc_t;
#define modx(atdesc, a, z)	(atdesc[(a)].z)
#define attr(e, k)		((e)->pattrs + (e)->attrs[k].id)

typedef struct pfmlib_pmu {
	const char 	*desc;			/* PMU description */
	const char 	*name;			/* pmu short name */
	const char	*perf_name;		/* perf_event pmu name (optional) */
	pfm_pmu_t	pmu;			/* PMU model */
	int		pme_count;		/* number of events */
	int		max_encoding;		/* max number of uint64_t to encode an event */
	int		flags;			/* 16 LSB: common, 16 MSB: arch spec*/
	int		pmu_rev;		/* PMU model specific revision */
	int		num_cntrs;		/* number of generic counters */
	int		num_fixed_cntrs;	/* number of fixed counters */
	int		supported_plm;		/* supported priv levels */

	pfm_pmu_type_t	type;			/* PMU type */
	const void	*pe;			/* pointer to event table */

	const pfmlib_attr_desc_t *atdesc;	/* pointer to attrs table */

	const int	cpu_family;		/* cpu family number for detection */
	const int	*cpu_models;		/* cpu model numbers for detection (zero terminated) */

	int 		 (*pmu_detect)(void *this);
	int 		 (*pmu_init)(void *this);	/* optional */
	void		 (*pmu_terminate)(void *this); /* optional */
	int		 (*get_event_first)(void *this);
	int		 (*get_event_next)(void *this, int pidx);
	int		 (*get_event_info)(void *this, int pidx, pfm_event_info_t *info);
	unsigned int	 (*get_event_nattrs)(void *this, int pidx);
	int		 (*event_is_valid)(void *this, int pidx);
	int		 (*can_auto_encode)(void *this, int pidx, int uidx);

	int		 (*get_event_attr_info)(void *this, int pidx, int umask_idx, pfm_event_attr_info_t *info);
	int		 (*get_event_encoding[PFM_OS_MAX])(void *this, pfmlib_event_desc_t *e);

	void		 (*validate_pattrs[PFM_OS_MAX])(void *this, pfmlib_event_desc_t *e);
	int		 (*os_detect[PFM_OS_MAX])(void *this);
	int		 (*validate_table)(void *this, FILE *fp);
	/*
	 * optional callbacks
	 */
	int 		 (*get_num_events)(void *this);
	void		 (*display_reg)(void *this, pfmlib_event_desc_t *e, void *val);
	int 		 (*match_event)(void *this, pfmlib_event_desc_t *d, const char *e, const char *s);
} pfmlib_pmu_t;

typedef struct {
	const char			*name;
	const pfmlib_attr_desc_t	*atdesc;
	pfm_os_t			id;
	int				flags;
	int				(*detect)(void *this);
	int				(*get_os_attr_info)(void *this, pfmlib_event_desc_t *e);
	int				(*get_os_nattrs)(void *this, pfmlib_event_desc_t *e);
	int				(*encode)(void *this, const char *str, int dfl_plm, void *args);
} pfmlib_os_t;

#define PFMLIB_OS_FL_ACTIVATED	0x1	/* OS layer detected */

/*
 * pfmlib_pmu_t common flags (LSB 16 bits)
 */
#define PFMLIB_PMU_FL_INIT	0x1	/* PMU initialized correctly */
#define PFMLIB_PMU_FL_ACTIVE	0x2	/* PMU is initialized + detected on host */
#define PFMLIB_PMU_FL_RAW_UMASK	0x4	/* PMU supports PFM_ATTR_RAW_UMASKS */
#define PFMLIB_PMU_FL_ARCH_DFL	0x8	/* PMU is arch default */
#define PFMLIB_PMU_FL_NO_SMPL	0x10	/* PMU does not support sampling */

typedef struct {
	int	initdone;
	int	verbose;
	int	debug;
	int	inactive;
	char	*forced_pmu;
	char	*blacklist_pmus;
	FILE 	*fp;	/* verbose and debug file descriptor, default stderr or PFMLIB_DEBUG_STDOUT */
} pfmlib_config_t;	

#define PFMLIB_INITIALIZED()	(pfm_cfg.initdone)

extern pfmlib_config_t pfm_cfg;

extern void __pfm_vbprintf(const char *fmt,...);
extern void __pfm_dbprintf(const char *fmt,...);
extern void pfmlib_strconcat(char *str, size_t max, const char *fmt, ...);
extern int pfmlib_getl(char **buffer, size_t *len, FILE *fp);
extern void pfmlib_compact_pattrs(pfmlib_event_desc_t *e, int i);
#define evt_strcat(str, fmt, a...) pfmlib_strconcat(str, PFMLIB_EVT_MAX_NAME_LEN, fmt, a)

extern int pfmlib_parse_event(const char *event, pfmlib_event_desc_t *d);
extern int pfmlib_build_fstr(pfmlib_event_desc_t *e, char **fstr);
extern void pfmlib_sort_attr(pfmlib_event_desc_t *e);
extern pfmlib_pmu_t * pfmlib_get_pmu_by_type(pfm_pmu_type_t t);
extern void pfmlib_release_event(pfmlib_event_desc_t *e);

extern size_t pfmlib_check_struct(void *st, size_t usz, size_t refsz, size_t sz);

#ifdef CONFIG_PFMLIB_DEBUG
#define DPRINT(fmt, a...) \
	do { \
		__pfm_dbprintf("%s (%s.%d): " fmt, __FILE__, __func__, __LINE__, ## a); \
	} while (0)
#else
#define DPRINT(fmt, a...)
#endif

extern pfmlib_pmu_t montecito_support;
extern pfmlib_pmu_t itanium2_support;
extern pfmlib_pmu_t itanium_support;
extern pfmlib_pmu_t generic_ia64_support;
extern pfmlib_pmu_t amd64_k7_support;
extern pfmlib_pmu_t amd64_k8_revb_support;
extern pfmlib_pmu_t amd64_k8_revc_support;
extern pfmlib_pmu_t amd64_k8_revd_support;
extern pfmlib_pmu_t amd64_k8_reve_support;
extern pfmlib_pmu_t amd64_k8_revf_support;
extern pfmlib_pmu_t amd64_k8_revg_support;
extern pfmlib_pmu_t amd64_fam10h_barcelona_support;
extern pfmlib_pmu_t amd64_fam10h_shanghai_support;
extern pfmlib_pmu_t amd64_fam10h_istanbul_support;
extern pfmlib_pmu_t amd64_fam11h_turion_support;
extern pfmlib_pmu_t amd64_fam12h_llano_support;
extern pfmlib_pmu_t amd64_fam14h_bobcat_support;
extern pfmlib_pmu_t amd64_fam15h_interlagos_support;
extern pfmlib_pmu_t amd64_fam15h_nb_support;
extern pfmlib_pmu_t intel_p6_support;
extern pfmlib_pmu_t intel_ppro_support;
extern pfmlib_pmu_t intel_pii_support;
extern pfmlib_pmu_t intel_pm_support;
extern pfmlib_pmu_t sicortex_support;
extern pfmlib_pmu_t netburst_support;
extern pfmlib_pmu_t netburst_p_support;
extern pfmlib_pmu_t intel_coreduo_support;
extern pfmlib_pmu_t intel_core_support;
extern pfmlib_pmu_t intel_x86_arch_support;
extern pfmlib_pmu_t intel_atom_support;
extern pfmlib_pmu_t intel_nhm_support;
extern pfmlib_pmu_t intel_nhm_ex_support;
extern pfmlib_pmu_t intel_nhm_unc_support;
extern pfmlib_pmu_t intel_snb_support;
extern pfmlib_pmu_t intel_snb_unc_cbo0_support;
extern pfmlib_pmu_t intel_snb_unc_cbo1_support;
extern pfmlib_pmu_t intel_snb_unc_cbo2_support;
extern pfmlib_pmu_t intel_snb_unc_cbo3_support;
extern pfmlib_pmu_t intel_snb_ep_support;
extern pfmlib_pmu_t intel_ivb_support;
extern pfmlib_pmu_t intel_ivb_unc_cbo0_support;
extern pfmlib_pmu_t intel_ivb_unc_cbo1_support;
extern pfmlib_pmu_t intel_ivb_unc_cbo2_support;
extern pfmlib_pmu_t intel_ivb_unc_cbo3_support;
extern pfmlib_pmu_t intel_ivb_ep_support;
extern pfmlib_pmu_t intel_hsw_support;
extern pfmlib_pmu_t intel_hsw_ep_support;
extern pfmlib_pmu_t intel_bdw_support;
extern pfmlib_pmu_t intel_rapl_support;
extern pfmlib_pmu_t intel_snbep_unc_cb0_support;
extern pfmlib_pmu_t intel_snbep_unc_cb1_support;
extern pfmlib_pmu_t intel_snbep_unc_cb2_support;
extern pfmlib_pmu_t intel_snbep_unc_cb3_support;
extern pfmlib_pmu_t intel_snbep_unc_cb4_support;
extern pfmlib_pmu_t intel_snbep_unc_cb5_support;
extern pfmlib_pmu_t intel_snbep_unc_cb6_support;
extern pfmlib_pmu_t intel_snbep_unc_cb7_support;
extern pfmlib_pmu_t intel_snbep_unc_ha_support;
extern pfmlib_pmu_t intel_snbep_unc_imc0_support;
extern pfmlib_pmu_t intel_snbep_unc_imc1_support;
extern pfmlib_pmu_t intel_snbep_unc_imc2_support;
extern pfmlib_pmu_t intel_snbep_unc_imc3_support;
extern pfmlib_pmu_t intel_snbep_unc_pcu_support;
extern pfmlib_pmu_t intel_snbep_unc_qpi0_support;
extern pfmlib_pmu_t intel_snbep_unc_qpi1_support;
extern pfmlib_pmu_t intel_snbep_unc_ubo_support;
extern pfmlib_pmu_t intel_snbep_unc_r2pcie_support;
extern pfmlib_pmu_t intel_snbep_unc_r3qpi0_support;
extern pfmlib_pmu_t intel_snbep_unc_r3qpi1_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb0_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb1_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb2_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb3_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb4_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb5_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb6_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb7_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb8_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb9_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb10_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb11_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb12_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb13_support;
extern pfmlib_pmu_t intel_ivbep_unc_cb14_support;
extern pfmlib_pmu_t intel_ivbep_unc_ha0_support;
extern pfmlib_pmu_t intel_ivbep_unc_ha1_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc0_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc1_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc2_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc3_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc4_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc5_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc6_support;
extern pfmlib_pmu_t intel_ivbep_unc_imc7_support;
extern pfmlib_pmu_t intel_ivbep_unc_pcu_support;
extern pfmlib_pmu_t intel_ivbep_unc_qpi0_support;
extern pfmlib_pmu_t intel_ivbep_unc_qpi1_support;
extern pfmlib_pmu_t intel_ivbep_unc_qpi2_support;
extern pfmlib_pmu_t intel_ivbep_unc_ubo_support;
extern pfmlib_pmu_t intel_ivbep_unc_r2pcie_support;
extern pfmlib_pmu_t intel_ivbep_unc_r3qpi0_support;
extern pfmlib_pmu_t intel_ivbep_unc_r3qpi1_support;
extern pfmlib_pmu_t intel_ivbep_unc_r3qpi2_support;
extern pfmlib_pmu_t intel_ivbep_unc_irp_support;
extern pfmlib_pmu_t intel_knc_support;
extern pfmlib_pmu_t intel_slm_support;
extern pfmlib_pmu_t power4_support;
extern pfmlib_pmu_t ppc970_support;
extern pfmlib_pmu_t ppc970mp_support;
extern pfmlib_pmu_t power5_support;
extern pfmlib_pmu_t power5p_support;
extern pfmlib_pmu_t power6_support;
extern pfmlib_pmu_t power7_support;
extern pfmlib_pmu_t power8_support;
extern pfmlib_pmu_t torrent_support;
extern pfmlib_pmu_t sparc_support;
extern pfmlib_pmu_t sparc_ultra12_support;
extern pfmlib_pmu_t sparc_ultra3_support;
extern pfmlib_pmu_t sparc_ultra3i_support;
extern pfmlib_pmu_t sparc_ultra3plus_support;
extern pfmlib_pmu_t sparc_ultra4plus_support;
extern pfmlib_pmu_t sparc_niagara1_support;
extern pfmlib_pmu_t sparc_niagara2_support;
extern pfmlib_pmu_t cell_support;
extern pfmlib_pmu_t perf_event_support;
extern pfmlib_pmu_t perf_event_raw_support;
extern pfmlib_pmu_t intel_wsm_sp_support;
extern pfmlib_pmu_t intel_wsm_dp_support;
extern pfmlib_pmu_t intel_wsm_unc_support;
extern pfmlib_pmu_t arm_cortex_a7_support;
extern pfmlib_pmu_t arm_cortex_a8_support;
extern pfmlib_pmu_t arm_cortex_a9_support;
extern pfmlib_pmu_t arm_cortex_a15_support;
extern pfmlib_pmu_t arm_1176_support;
extern pfmlib_pmu_t arm_qcom_krait_support;
extern pfmlib_pmu_t arm_cortex_a57_support;
extern pfmlib_pmu_t arm_cortex_a53_support;
extern pfmlib_pmu_t arm_xgene_support;
extern pfmlib_pmu_t mips_74k_support;
extern pfmlib_pmu_t s390x_cpum_cf_support;
extern pfmlib_pmu_t s390x_cpum_sf_support;

extern pfmlib_os_t *pfmlib_os;
extern pfmlib_os_t pfmlib_os_perf;
extern pfmlib_os_t pfmlib_os_perf_ext;

extern char *pfmlib_forced_pmu;

#define this_pe(t)		(((pfmlib_pmu_t *)t)->pe)
#define this_atdesc(t)		(((pfmlib_pmu_t *)t)->atdesc)

#define LIBPFM_ARRAY_SIZE(a) (sizeof(a) / sizeof(typeof(*(a))))
/*
 * population count (number of bits set)
 */
static inline int
pfmlib_popcnt(unsigned long v)
{
	int sum = 0;

	for(; v ; v >>=1) {
		if (v & 0x1) sum++;
	}
	return sum;
}

/*
 * find next bit set
 */
static inline size_t
pfmlib_fnb(unsigned long value, size_t nbits, int p)
{
	unsigned long m;
	size_t i;

	for(i=p; i < nbits; i++) {
		m = 1 << i;
		if (value & m)
			return i;
	}
	return i;
}

/*
 * PMU + internal idx -> external opaque idx
 */
static inline int
pfmlib_pidx2idx(pfmlib_pmu_t *pmu, int pidx)
{
	int idx;

	idx = pmu->pmu << PFMLIB_PMU_SHIFT;
	idx |= pidx;

	return  idx;
}

#define pfmlib_for_each_bit(x, m) \
	for((x) = pfmlib_fnb((m), (sizeof(m)<<3), 0); (x) < (sizeof(m)<<3); (x) = pfmlib_fnb((m), (sizeof(m)<<3), (x)+1))

#ifdef __linux__

#define PFMLIB_VALID_PERF_PATTRS(f)  \
	.validate_pattrs[PFM_OS_PERF_EVENT] = f, \
	.validate_pattrs[PFM_OS_PERF_EVENT_EXT]	= f

#define PFMLIB_ENCODE_PERF(f)  \
	.get_event_encoding[PFM_OS_PERF_EVENT] = f, \
	.get_event_encoding[PFM_OS_PERF_EVENT_EXT] = f

#define PFMLIB_OS_DETECT(f)  \
	.os_detect[PFM_OS_PERF_EVENT] = f, \
	.os_detect[PFM_OS_PERF_EVENT_EXT] = f
#else
#define PFMLIB_VALID_PERF_PATTRS(f) \
	.validate_pattrs[PFM_OS_PERF_EVENT] = NULL, \
	.validate_pattrs[PFM_OS_PERF_EVENT_EXT]	= NULL

#define PFMLIB_ENCODE_PERF(f)  \
	.get_event_encoding[PFM_OS_PERF_EVENT] = NULL, \
	.get_event_encoding[PFM_OS_PERF_EVENT_EXT] = NULL

#define PFMLIB_OS_DETECT(f)  \
	.os_detect[PFM_OS_PERF_EVENT] = NULL, \
	.os_detect[PFM_OS_PERF_EVENT_EXT] = NULL
#endif

static inline int
is_empty_attr(const pfmlib_attr_desc_t *a)
{
	return !a || !a->name || strlen(a->name) == 0 ? 1 : 0;
}

#endif /* __PFMLIB_PRIV_H__ */
