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

#ifndef _EEXPERIMENT_H
#define _EEXPERIMENT_H

// The experiment class is responsible for managing all the data
//  for an individual experiment

#include "Metric.h"
#include "Histable.h"
#include "Stats_data.h"
#include "DefaultMap.h"
#include "HeapMap.h"

class Data_window;
class DbeFile;
class CallStack;
class JMethod;
class Sample;
class SegMem;
class LoadObject;
class SourceFile;
class UserLabel;
class PRBTree;
class Emsg;
class Emsgqueue;
struct JThread;
struct GCEvent;
class FileData;
class Module;
class Experiment;
template <class ITEM> class Vector;

#define JTHREAD_DEFAULT     ((JThread*)0)
#define JTHREAD_NONE        ((JThread*)-1)

// When we perform the pipelined optimization on resolve_frame_info() and add_stack()
// this is the number of iterations one phase works on before passing on the work to
// the next phase

#define CSTCTX_CHUNK_SZ 10000
#define PIPELINE_QUEUE_SZ_HI 8
#define PIPELINE_QUEUE_SZ_LOW 2

// the add_stack_ctx structure contains the intermediate state (context) after
// CSTCTX_CHUNK_SZ number of iterations to pass on the work to another thread to
// operate on the next stage
typedef struct
{
  Vector<DbeInstr*> *natpcs;
  Vector<Histable*> *jpcs;
  long idx;
  FramePacket *frp;
  hrtime_t tstamp;
  uint32_t thrid;
  bool last_ctx;
} cstk_ctx;

// To minimize threadpool overhead, the granularity of a job submitted is made larger:
// containing a chunk of iterations (of size CSTCTX_CHUNK_SZ)
typedef struct
{
  cstk_ctx* cstCtxAr[CSTCTX_CHUNK_SZ];
  int last_idx;
  long idx_begin;
  long idx_end;
  DataDescriptor *dDscr;
  Experiment *exp;
  void *cstk;
} cstk_ctx_chunk;

class Experiment : public Histable, public DbeMessages
{
public:

  enum Exp_status
  {
    SUCCESS,
    INCOMPLETE,
    FAILURE
  };

  Experiment ();
  virtual ~Experiment ();

  virtual Histable_type
  get_type ()
  {
    return EXPERIMENT;
  };
  virtual Vector<Histable*> *get_comparable_objs ();

  int groupId;
  Experiment *founder_exp;              // parent of this experiment
  Vector<Experiment*> *children_exps;   // children of this experiment

  // Configuration Information
  char *hostname;       // Hosthame (e.g. mymachine)
  hrtime_t start_sec;       // Starting timeval secs.
  char *username;       // name of person performing the test
  char *architecture;   // Architecture name ("sun4")
  Platform_t platform;  // Sparc,Sparcv9,Intel
  WSize_t wsize;        // word size: may be w32 or w64
  int clock;            // CPU clock frequency, Mhz
  int varclock;         // Set if CPU clock frequency can change: turbo-mode
  int maxclock;         // max. CPU clock frequency on MP machine
  int minclock;         // min. CPU clock frequency on MP machine
  int ncpus;            // count of CPUs where expt was recorded
  int hw_cpuver;        // CPU version from libcpc
  char *machinemodel;   // machine model of machine on which experiment was recorded
  char *os_version;     // Operating system name
  int page_size;        // Page size (bytes)
  int npages;           // Number of page size
  int exp_maj_version;  // major version number of current experiment
  int exp_min_version;  // minor version number of current experiment
  int hex_field_width;  // number of digits in hex form of address
			// for current experiment, i.e. 8 for 32bit addresses
  int broken;           // If SP_JCMD_RUN line not seen
  int obsolete;         // If pointer file experiment detected
  bool hwc_default;     // True if HW counters were enabled by default
  int hwc_bogus;        // Count of bogus HWC packets
  int hwc_lost_int;     // Count of packets reflecting lost interrupt
  int hwc_scanned;      // If the HWC packets have been scanned
  int invalid_packet;   // Count of invalid packets
  bool exec_started;    // True if exec was called, and exec error not yet seen
  bool dataspaceavail;  // True if dataspace data is in the experiment
  bool leaklistavail;   // True if leaklist data is in the experiment
  bool heapdataavail;   // True if heap data is in the experiment
  bool racelistavail;   // true if there are race events in the experiment
  bool iodataavail;     // true if there are io events in the experiment
  bool deadlocklistavail; // true if there are deadlock events in the experiment
  bool timelineavail;   // true if there are valid timestamps in the experiment
  bool ifreqavail;      // True if instruction-frequency data is in the experiment
  bool ompavail;        // true if there is OpenMP data in the experiment
  bool has_java;
  char *uarglist;       // argv[] array, as a string
  char *utargname;      // basename of argv[0] extracted from uarglist
  char *ucwd;           // working directory
  char *cversion;       // collector version string
  char *dversion;       // driver version string (er_kernel)
  char *jversion;       // Java version string (java profiling)

  // Open the named experiment record and process log file
  Exp_status open (char *directory_name);

  // Update experiment (read and process new data)
  Exp_status update ();

  // Returns collector parameters for the current sample selection
  Collection_params *
  get_params ()
  {
    return &coll_params;
  }

  Exp_status
  get_status ()
  {
    return status;
  }

  // Returns the number of samples. For use by FilterNumeric
  int
  nsamples ()
  {
    return samples->size ();
  }

  // Release any releasable memory.
  void purge ();

  void resetShowHideStack ();
  int save_notes (char*, bool);
  int delete_notes (bool);
  Experiment *getBaseFounder (); // returns topmost founder or this if no descendents

  hrtime_t
  getStartTime ()
  {
    return exp_start_time;
  }
  hrtime_t getRelativeStartTime (); // delta between start and founder's start

  hrtime_t
  getWallStartSec ()
  {
    return start_sec;
  }

  hrtime_t
  getLastEvent ()
  {
    if (last_event != ZERO_TIME)
      return last_event;
    return exp_start_time;
  }

  hrtime_t
  getGCDuration ()
  {
    return gc_duration;
  }

  int
  getPID ()
  {
    return pid;
  }

  int
  getUserExpId ()
  {
    return userExpId;
  }

  int
  getExpIdx ()
  {
    return expIdx;
  }

  void
  setExpIdx (int idx)
  {
    expIdx = idx;
  }

  void
  setUserExpId (int idx)
  {
    userExpId = idx;
  }

  void
  setTinyThreshold (int limit)
  {
    tiny_threshold = limit;
  }

  bool
  isDiscardedTinyExperiment ()
  {
    return discardTiny;
  }

  Exp_status open_epilogue ();
  void read_experiment_data (bool read_ahead);
  static int copy_file_to_archive (const char *name, const char *aname, int hide_msg);
  static int copy_file_to_common_archive (const char *name, const char *aname,
	       int hide_msg, const char *common_archive, int relative_path = 0);
  static int copy_file (char *name, char *aname, int hide_msg,
			char *common_archive = NULL, int relative_path = 0);

  // get_raw_events()
  // action: get unfiltered packets, loading them if required
  // parameters: data_id (see ProfData_type)
  DataDescriptor *get_raw_events (int data_id);
  Vector<DataDescriptor*> *getDataDescriptors ();

  // Some DATA_* types are derived from others, e.g. DATA_HEAPSZ is derived from DATA_HEAP
  // The following hooks support derived DataViews
  int base_data_id (int data_id); // returns base data_id type  (ProfData_type DATA_*)
  DataView *create_derived_data_view (int data_id, DataView *dview);

  Vector<BaseMetric*>*
  get_metric_list ()
  {
    return metrics;
  }

  char *
  get_expt_name ()
  {
    return expt_name;   // Return the pathname to the experiment
  };

  Vector<char*> *get_descendants_names ();
  char *get_fndr_arch_name ();
  char *get_arch_name ();
  char *getNameInArchive (const char *fname, bool archiveFile = false);
  char *checkFileInArchive (const char *fname, bool archiveFile = false);
  DbeFile *findFileInArchive (const char *className, const char *runTimePath);
  DbeFile *findFileInArchive (const char *fname);
  bool create_dir (char *dname);

  Vaddr
  ret_stack_base ()
  {
    return stack_base;
  };

  // Map a virtual address to a PC pair
  DbeInstr *map_Vaddr_to_PC (Vaddr addr, hrtime_t ts);
  DbeInstr *map_jmid_to_PC (Vaddr mid, int lineno, hrtime_t ts);
  Sample *map_event_to_Sample (hrtime_t ts);
  GCEvent *map_event_to_GCEvent (hrtime_t ts);

  DataView *
  getOpenMPdata ()
  {
    return openMPdata;
  }

  time_t
  get_mtime ()
  {
    return mtime;
  }

  Emsg *fetch_comments (void);  // fetch the queue of comment messages
  Emsg *fetch_runlogq (void);   // fetch the queue of run log messages
  Emsg *fetch_errors (void);    // fetch the queue of error messages
  Emsg *fetch_warnings (void);  // fetch the queue of warning messages
  Emsg *fetch_notes (void);     // fetch the queue of notes messages
  Emsg *fetch_ifreq (void);     // fetch the queue of ifreq messages
  Emsg *fetch_pprocq (void);    // fetch the queue of post-processing messages

  // message queues
  Emsgqueue *commentq;  // comments for the experiment header
  Emsgqueue *runlogq;   // used temporarily; after log file processing,
  // messages are appended to the commentq
  Emsgqueue *errorq;    // error messages
  Emsgqueue *warnq;     // warning messages
  Emsgqueue *notesq;    // user-written notes messages
  Emsgqueue *pprocq;    // postprocessing messages
  Emsgqueue *ifreqq;    // Instruction frequency data, from count experiment
  Map<const char*, LoadObject*> *loadObjMap;
  Vector<LoadObject*> *loadObjs;
  void append (LoadObject *lo);
  LoadObject *createLoadObject (const char *path, uint64_t chksum = 0);
  LoadObject *createLoadObject (const char *path, const char *runTimePath);
  SourceFile *get_source (const char *path);
  void set_clock (int clk);

  CallStack *
  callTree ()
  {
    return cstack;
  }

  CallStack *
  callTreeShowHide ()
  {
    return cstackShowHide;
  }

  uint32_t mapTagValue (Prop_type, uint64_t value);
  Histable *getTagObj (Prop_type, uint32_t idx);
  Vector<Histable*> *getTagObjs (Prop_type);

  JThread *map_pckt_to_Jthread (uint32_t tid, hrtime_t tstamp);
  JThread *get_jthread (uint32_t tid);

  Vector<JThread*> *
  get_jthreads ()
  {
    return jthreads;
  }

  Vector<GCEvent*> *
  get_gcevents ()
  {
    return gcevents;
  }

  bool need_swap_endian;
  Collection_params coll_params; // Collection params

  // Ranges for threads, lwps, cpu
  uint64_t min_thread;
  uint64_t max_thread;
  uint64_t thread_cnt;
  uint64_t min_lwp;
  uint64_t max_lwp;
  uint64_t lwp_cnt;
  uint64_t min_cpu;
  uint64_t max_cpu;
  uint64_t cpu_cnt;
  uint64_t dsevents;        // count of dataspace events
  uint64_t dsnoxhwcevents;  /* count of ds events that could be be validated
			     * because of no branch target info */

  PacketDescriptor *newPacketDescriptor (int kind, DataDescriptor *dDscr);
  PacketDescriptor *getPacketDescriptor (int kind);

  // debugging aids -- dump_stacks, dump_map
  void dump_stacks (FILE *);
  void dump_map (FILE *);

  // These methods are used in nightly performance regression testing
  void DBG_memuse (Sample *);
  void DBG_memuse (const char *sname);
  void init_cache ();

  DefaultMap<int64_t, FileData*> *
  getFDataMap ()
  {
    return fDataMap;
  }
  CallStack *cstack;

protected:

  Exp_status status;        // Error status
  Vector<SegMem*> *seg_items; // Master list of seg_items
  CallStack *cstackShowHide;
  PRBTree *maps;            // All maps in (Vaddr,time)

  hrtime_t gc_duration;     // wall-clock hrtime of total GC intervals
  hrtime_t exp_start_time;  // wall-clock hrtime at exp start
  hrtime_t last_event;      // wall-clock hrtime of last known sample or log.xml entry
  hrtime_t non_paused_time; // sum of periods where data collection is active (not paused)
  hrtime_t resume_ts;       // tracks log.xml start/resume times
  void update_last_event (hrtime_t ts /*wall time (not 0-based)*/);

  char *expt_name;      // name of experiment
  char *arch_name;      // <experiment>/archive
  char *fndr_arch_name; // <founder_experiment>/archive
  //TBR? hrtime_t sample_time;  // total of sample durations
  int yyparse ();       // Allow yyparse actions to access
  Vaddr stack_base;     // Stack base

  // Write experiment header to comment queue
  void write_header ();
  void write_coll_params ();

  Exp_status find_expdir (char *directory_name);

  // Invoke the parser to process a file.
  void read_data_file (const char*, const char*);
  int read_log_file ();
  void read_labels_file ();
  void read_notes_file ();
  void read_archives ();
  int read_java_classes_file ();
  void read_map_file ();
  int read_overview_file ();
  int read_dyntext_file ();
  void read_omp_file ();
  void read_omp_preg ();
  void read_omp_task ();
  void read_ifreq_file ();
  void read_frameinfo_file ();

  // Functions to process the log and loadobjects file entries
  // They are deliberately made virtual to overload them
  // in er_export.
  virtual int process_arglist_cmd (char *, char *);
  virtual int process_desc_start_cmd (char *, hrtime_t, char *, char *, int, char *);
  virtual int process_desc_started_cmd (char *, hrtime_t, char *, char *, int, char *);
  virtual int process_fn_load_cmd (Module *mod, char *fname, Vaddr vaddr, int fsize, hrtime_t ts);
  virtual int process_fn_unload_cmd (char *, Vaddr, hrtime_t);
  virtual int process_hwcounter_cmd (char *, int, char *, char *, int, int, int, char *);
  virtual int process_hwsimctr_cmd (char *, int, char *, char *, char*, int, int, int, int, int);
  virtual int process_jcm_load_cmd (char*, Vaddr, Vaddr, int, hrtime_t);
  virtual int process_jcm_unload_cmd (char*, Vaddr, hrtime_t);
  virtual int process_Linux_kernel_cmd (hrtime_t);
  virtual int process_jthr_end_cmd (char *, uint64_t, Vaddr, Vaddr, hrtime_t);
  virtual int process_jthr_start_cmd (char *, char *, char *, char *, uint64_t, Vaddr, Vaddr, hrtime_t);
  virtual int process_gc_end_cmd (hrtime_t);
  virtual int process_gc_start_cmd (hrtime_t);
  virtual int process_sample_cmd (char *, hrtime_t, int id, char *lbl);
  virtual int process_sample_sig_cmd (char *, int);
  virtual int process_seg_map_cmd (char *, hrtime_t, Vaddr, int, int, int64_t, int64_t, int64_t, char *);
  virtual int process_seg_unmap_cmd (char *, hrtime_t, Vaddr);

  // creation time for experiment
  time_t mtime;
  hrtime_t exp_rel_start_time;      // start of exp. relative to founder
  bool exp_rel_start_time_set;
  Vector<UserLabel*> *userLabels;   // List of er_labels
  int userExpId;                    // user value for EXPID
  int expIdx;                       // DbeSession exp identifier
  PRBTree *jmaps;                   // JAVA_CLASSES: (id,time)->Histable
  Experiment* baseFounder;  // outermost experiment (null until lazily computed)

  // Represents a file in experiment
  class ExperimentFile;

  // XML handler to parse various experiment files
  class ExperimentHandler;
  class ExperimentLabelsHandler;

  uint64_t readPacket (Data_window *dwin, Data_window::Span *span);
  void readPacket (Data_window *dwin, char *ptr, PacketDescriptor *pDscr,
		   DataDescriptor *dDscr, int arg, uint64_t pktsz);

  // read data
  DataDescriptor *get_profile_events ();
  DataDescriptor *get_sync_events ();
  DataDescriptor *get_hwc_events ();
  DataDescriptor *get_heap_events ();
  DataDescriptor *get_heapsz_events ();
  DataDescriptor *get_iotrace_events ();
  DataDescriptor *get_race_events ();
  DataDescriptor *get_deadlock_events ();
  DataDescriptor *get_sample_events ();
  DataDescriptor *get_gc_events ();
  DataDescriptor *getDataDescriptor (int data_id);
  DataDescriptor *newDataDescriptor (int data_id, int flags = 0,
				     DataDescriptor *master_dDscr = NULL);

  // Frame info data structures and methods
  struct UIDnode;
  struct RawFramePacket;

  Vector<RawFramePacket*>*frmpckts; // frame info data
  static int frUidCmp (const void*, const void*);
  RawFramePacket *find_frame_packet (uint64_t uid);

  static const int CHUNKSZ = 16384;
  long nnodes;
  long nchunks;
  UIDnode **chunks;
  UIDnode **uidHTable;
  Vector<UIDnode*> *uidnodes;
  bool resolveFrameInfo;
  bool discardTiny;
  int tiny_threshold; /* optimize away tiny experiments which ran
		       * for less than specified time (ms): default 0 */

  static int uidNodeCmp (const void *a, const void *b);
  UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint32_t *array, uint64_t link_uid);
  UIDnode *add_uid (Data_window *dwin, uint64_t uid, int size, uint64_t *array, uint64_t link_uid);
  UIDnode *new_uid_node (uint64_t uid, uint64_t val);
  UIDnode *get_uid_node (uint64_t uid, uint64_t val);
  UIDnode *get_uid_node (uint64_t uid);
  UIDnode *find_uid_node (uint64_t uid);

  ExperimentFile *logFile;

  // Data descriptors
  Vector<DataDescriptor*> *dataDscrs;
  Vector<PacketDescriptor*> *pcktDscrs;
  long blksz; // binary data file block size

  // Processed data packets
  DataView *openMPdata; // OMP fork events

  // Map events to OpenMP parallel regions and tasks
  Map2D<uint32_t, hrtime_t, uint64_t> *mapPRid;
  Map2D<uint32_t, hrtime_t, void*> *mapPReg;
  Map2D<uint32_t, hrtime_t, void*> *mapTask;

  // Archive content
  Map<const char*, DbeFile *> *archiveMap;
  Map<const char*, SourceFile*>*sourcesMap;

  void init ();
  void fini ();
  void post_process ();
  void constructJavaStack (FramePacket *, UIDnode *, Map<uint64_t, uint64_t> *);
  void resolve_frame_info (DataDescriptor*);
  void cleanup_cstk_ctx_chunk ();
  void register_metric (Metric::Type type);
  void register_metric (Hwcentry *ctr, const char* aux, const char* username);

  Sample *sample_last_used;
  GCEvent *gcevent_last_used;
  char *first_sample_label;
  Module *get_jclass (const char *className, const char *fileName);
  LoadObject *get_j_lo (const char *className, const char *fileName);

  Vector<BaseMetric*> *metrics;
  Vector<JThread*> *jthreads;       // master list of Java threads
  Vector<JThread*> *jthreads_idx;   // index in the master list
  Vector<GCEvent*> *gcevents;
  Vector<UnmapChunk*> *heapUnmapEvents;
  Vector<Sample*> *samples;         // Array of Sample pointers

  DefaultMap<int64_t, FileData*> *fDataMap; // list of FileData objects using the virtual File descriptor as the key
  DefaultMap<int, int64_t> *vFdMap; // list of virtual file descrptors using the file descriptor as the key

  Vector<Vector<Histable*>*> *tagObjs; // tag objects
  bool sparse_threads;

  SegMem **smemHTable; // hash table for SegMem's
  DbeInstr **instHTable; // hash table for DbeInstr
  Map<unsigned long long, JMethod*> *jmidHTable; // hash table for jmid

  // identity of target process
  int pid;
  int ppid;
  int pgrp;
  int sid;

  // Map file processing related data
  struct MapRecord
  {

    enum
    {
      LOAD, UNLOAD
    } kind;
    Histable *obj;
    Vaddr base;
    Size size;
    hrtime_t ts;
    uint64_t foff;
  };

  void mrec_insert (MapRecord *mrec);
  SegMem *update_ts_in_maps (Vaddr addr, hrtime_t ts);
  int read_warn_file ();
  LoadObject *get_dynfunc_lo (const char *loName);
  Function *create_dynfunc (Module *mod, char *fname, int64_t vaddr, int64_t fsize);
  char *get_archived_name (const char *fname, bool archiveFile = false);

  Vector<MapRecord*> *mrecs;

private:
  void add_evt_time_to_profile_events (DataDescriptor *dDscr);
  DataView *create_heapsz_data_view (DataView *heap_dview);
  void compute_heapsz_data_view (DataView *heapsz_dview);
};

struct JThread
{
  JThread *next;
  char *name;
  char *group_name;
  char *parent_name;
  uint32_t tid;     // system thread id
  Vaddr jthr;       // recorded Java thread id
  Vaddr jenv;       // recorded JNIEnv id
  uint32_t jthr_id; // internal JThread object id
  hrtime_t start;
  hrtime_t end;

  JThread ()
  {
    name = NULL;
    group_name = NULL;
    parent_name = NULL;
  }

  ~JThread ()
  {
    free (name);
    free (group_name);
    free (parent_name);
  }
  bool is_system ();
};

struct GCEvent
{

  GCEvent ()
  {
    id = -1;
  }

  ~GCEvent () { }

  hrtime_t start;
  hrtime_t end;
  int id;
};

class ExperimentLoadCancelException
{
public:

  ExperimentLoadCancelException () { };

  ~ExperimentLoadCancelException () { };
};


#endif  /* _EEXPERIMENT_H */
