/* 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 */

#ifndef __HWCFUNCS_H
#define __HWCFUNCS_H

#ifdef LIBCOLLECTOR_SRC /* running in libcollector */
#define hwcfuncs_int_logerr         __collector_hwcfuncs_int_logerr
#define hwcfuncs_parse_ctr          __collector_hwcfuncs_parse_ctr
#define hwcfuncs_parse_attrs        __collector_hwcfuncs_parse_attrs
#define hwcfuncs_bind_descriptor    __collector_hwcfuncs_bind_descriptor
#define hwcfuncs_bind_hwcentry      __collector_hwcfuncs_bind_hwcentry
#define hwcfuncs_assign_regnos      __collector_hwcfuncs_assign_regnos
#define regno_is_valid              __collector_regno_is_valid
#define hwcfuncs_get_ctrs           __collector_hwcfuncs_get_ctrs
#define hwcfuncs_errmsg_get         __collector_hwcfuncs_errmsg_get
#endif  /* --- LIBCOLLECTOR_SRC --- */

#include <signal.h>     /* siginfo_t */
#include <limits.h>     /* UINT64_t */
#include <sys/types.h>
#include <stdint.h>

#include "hwcentry.h"   /* for Hwcentry type */
#include "gp-time.h"

typedef unsigned int uint_t;

#ifdef	__cplusplus
extern "C" {
#endif

/*---------------------------------------------------------------------------*/
/* compile options */

#define HWC_DEBUG   0 /* 0/1 to enable extra HWC debug */

/*---------------------------------------------------------------------------*/
/* typedefs */
/* generic hw event */
  typedef struct _hwc_event_t
  { /* generalized counter event */
    hrtime_t ce_hrt;            /* gethrtime() */
    uint64_t ce_pic[MAX_PICS];  /* counter samples or start values */
  } hwc_event_t;

  /* supplementary data that accompanies some hw events */
  typedef struct
  { /* supplementary data fields */
    uint64_t smpl_pc;           /* pc related to event */
    uint64_t smpl_data_source;  /* chip-specific data source encoding */
    uint64_t smpl_latency;      /* latency related to event */
    uint64_t smpl_mem_addr;     /* memory address related to event */
  } hwc_sample_t;
#define HWCFUNCS_INVALID_U64 0xFEEDBEEFDEADBEEFllu /* identifies fields as unused */

typedef struct {                                /* supplementary data fields */
    hwc_sample_t sample[MAX_PICS];  /* counter samples or start values */
} hwc_event_samples_t;

#define HWCFUNCS_SAMPLE_RESET(sample) \
	do { \
	    (sample)->smpl_pc          =HWCFUNCS_INVALID_U64; \
	    (sample)->smpl_data_source =HWCFUNCS_INVALID_U64; \
	    (sample)->smpl_latency     =HWCFUNCS_INVALID_U64; \
	    (sample)->smpl_mem_addr    =HWCFUNCS_INVALID_U64; \
	} while(0)

#define HWCFUNCS_SAMPLE_IS_RESET(sample) \
	( \
	    (sample)->smpl_pc         ==HWCFUNCS_INVALID_U64 && \
	    (sample)->smpl_data_source==HWCFUNCS_INVALID_U64 && \
	    (sample)->smpl_latency    ==HWCFUNCS_INVALID_U64 && \
	    (sample)->smpl_mem_addr   ==HWCFUNCS_INVALID_U64 \
	)

/*---------------------------------------------------------------------------*/
/* macros */

#define HW_INTERVAL_MAX         UINT64_MAX
#define HW_INTERVAL_PRESET(x)   (HW_INTERVAL_MAX - ((uint64_t)(x) - 1))
#define HW_INTERVAL_TYPE(x)     ((uint64_t) (x)

/* parsing */
#define HWCFUNCS_MAX_ATTRS              20
#define HWCFUNCS_PARSE_ATTR             '~'
#define HWCFUNCS_PARSE_EQUAL            '='
#define HWCFUNCS_PARSE_BACKTRACK        '+'
#define HWCFUNCS_PARSE_BACKTRACK_OFF    '-'
#define HWCFUNCS_PARSE_REGNUM           '/'
#define HWCFUNCS_PARSE_VALUE            ','

/* error codes */
#define HWCFUNCS_ERROR_GENERIC          (-1)
#define HWCFUNCS_ERROR_NOT_SUPPORTED    (-2)
#define HWCFUNCS_ERROR_ALREADY_CALLED   (-3)
#define HWCFUNCS_ERROR_HWCINIT          (-4)
#define HWCFUNCS_ERROR_HWCARGS          (-5)
#define HWCFUNCS_ERROR_MEMORY           (-6)
#define HWCFUNCS_ERROR_UNAVAIL          (-7)
#define HWCFUNCS_ERROR_ERRNO_ZERO       (-8)
#define HWCFUNCS_ERROR_UNEXPECTED       (-99)

/*---------------------------------------------------------------------------*/
/* prototypes */

typedef void (*hwcfuncs_abort_fn_t) (int errnum, const char *msg);

extern void hwcfuncs_int_logerr(const char *format,...);
/* Log an error to the internal error buffer. See hwcfuncs_errmsg_get().
     Note: Not MT-safe; don't even enable logging in an MT environment.
       Recommend using this call only during init.
     Note: when a libcpc call fails, it may automatically call
       cpcN_capture_errmsg() to log the error message in the same internal buffer.
       Recommend using this call only for non-cpc failures.
 */

#define HWCFUNCS_SUPPORT_OVERFLOW_PROFILING 0x01llu
#define HWCFUNCS_SUPPORT_PEBS_SAMPLING      0x02llu
#define HWCFUNCS_SUPPORT_OVERFLOW_CTR_ID    0x04llu // OS identifies which counter overflowed
  /* get info about session
     Input:
       <cpuver>: if not NULL, returns value of CPC cpu version
       <cciname>: if not NULL, returns name of CPU
       <npics>: if not NULL, returns maximum # of HWCs
       <docref>: if not NULL, returns documentation reference
       <support>: if not NULL, returns bitmask (see above) of hwc support
     Return: none
   */

  typedef void* (*hwcfuncs_tsd_get_fn_t) (void);
  typedef void (hwcf_hwc_cb_t) (uint_t cpcregno, const char *name);
  typedef void (hwcf_attr_cb_t) (const char *attr);

  extern void
  hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly,
		      char **pattrs, char **pregstr, regno_t *pregno);
/* Parse a counter definition string (value must already be stripped off).
     Input:
       <counter_def>: input whose format is
	 [+|-]<countername>[~attrs...][/<regno>]
       pointers to return values:  Any can be NULL.
     Return:
       <plus>: 1 if [+] is found, -1 if [-] is found, 0 otherwise
       <pnameonly>: strdup(<countername>)
       <pattrs>: strdup([~attrs...]) if specified, NULL otherwise.
       <pregstr>: strdup(/<regno>) if specified, NULL otherwise.
       <pregno>: <regno> if readable, REGNO_ANY if not specd, or -2 otherwise.
   */

  typedef struct
  {
    char *ca_name;
    uint64_t ca_val;
  } hwcfuncs_attr_t; /* matches cpc_attr_t */

  void * hwcfuncs_parse_attrs (const char *countername,
			       hwcfuncs_attr_t attrs[], unsigned max_attrs,
			       uint_t *pnum_attrs, char **errstring);
  /* Extract the attribute fields from <countername>.
       Input:
	 <countername>: string whose format is
	    [+]<ctrname>[~attributes...][/<regno>][,...]
	 <attrs>: array of attributes to be returned
	 <max_attrs>: number of elements in <attrs>
	 <pnum_attrs>: if not NULL, will return how many attrs were found.
	 <errstring>: pointer to a buffer for storing error info, or NULL.
       Return: upon success, a pointer to an allocated copy of <countername>, or
	   NULL if there's a failure.  (A copy is made in order to provide storage
	   for the ca_name fields in the <attrs> array.)

	   The pointer should be freed when <attrs> is no longer in use.
	   <attrs> will be filled in data from countername.
	 <pnum_attrs> will have the number of elements in <attrs>.  May be
	   non-zero even if return value indicates an error.
	 <errstring> NULL if no error, otherwise, a malloc'd GTXT string.
   */

  extern int hwcfuncs_bind_descriptor (const char *defstring);
  /* Bind counters to resources.
     Input:
       <defstring>: string whose format is
	  :%s:%s:0x%x:%d:%d,0x%x[:%s...repeat for each ctr]
	 where the fields are:
	  :<userName>:<internalCtr>:<register>:<timeoutVal>:<tag>:<memop>
     Return: 0 if successful
	HWCFUNCS_ERROR_HWCINIT if resources unavailable
	HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly
   */

  extern int hwcfuncs_bind_hwcentry (const Hwcentry *entries[],
				     unsigned numctrs);
  /* Bind counters to resources.
     Input:
       <entries>: array of counters
       <numctrs>: number of items in <entries>
     Return: 0 if successful
	HWCFUNCS_ERROR_HWCINIT if resources unavailable
	HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly
   */

  extern int hwcfuncs_assign_regnos (Hwcentry *entries[], unsigned numctrs);
  /* Assign entries[]->reg_num values as needed by platform
       Note: modifies <entries> by supplying a regno to each counter
     Input:
       <entries>: array of counters
       <numctrs>: number of items in <entries>
     Output:
       <entries>: array of counters is modified
     Return: 0 if successful
	HWCFUNCS_ERROR_HWCINIT if resources unavailable
	HWCFUNCS_ERROR_HWCARGS if counters were not specified correctly
   */

  extern int regno_is_valid (const Hwcentry *pctr, regno_t regno);
  /* return 1 if <regno> is in Hwcentry's list
     Input:
       <pctr>: counter definition, reg_list[] should be initialized
       <regno>: register to check
     Return: 1 if <regno> is in Hwcentry's list, 0 otherwise
   */

  extern Hwcentry **hwcfuncs_get_ctrs (unsigned *defcnt);
  /* Get descriptions of the currently bound counters.
     Input:
       <defcnt>: if not NULL, returns number of counter definitions.
     Return:
       table of counter definition pointers
   */

  extern char *hwcfuncs_errmsg_get (char * buf, size_t bufsize,
				    int enable_capture);
  /* Gets a recent HWC error message.
       To clear previous error messages and insure error message is enabled,
       call hwcfuncs_errmsg_get(NULL,0,1).
       Once enabled, one error is stored in an internal buffer.  A call to this
       function will clear the buffer and allow a new message to be captured.
       Note: Not MT-safe - don't enable this feature in an MT environment.
     Input:
       <buf>: pointer to buffer or NULL.
       <bufsize>: size of <buf>
       <enable_capture>: 0 - disable buffering, 1 - enable buffering.
     Return: error string or an empty string.
   */

#ifdef	__cplusplus
}
#endif

#endif /* ! __HWCFUNCS_H */
