/**********************************************************************
 * Copyright (c) 2005, 2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: hash.h,v 1.2 2007/04/06 00:41:57 jkubasta Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

#ifndef __HASH_H__
#define __HASH_H__ 1

#ifdef __linux__
  #define _inline inline
#endif

#include <jvmpi.h>
#include "performance.h"
#include "segmentedValue.h"
#include "stack.h"
#include "RABindings.h"
#include "filters.h"


#ifdef __cplusplus
 extern "C" {
#endif

/******************************
 * Symbol table types and methods
 ******************************/
#define CLASS_HASH_TABLE_SIZE    4999	/* The number of 1st level entries in the CLASS hashtable */
#define METHOD_HASH_TABLE_SIZE   10007	/* The number of 1st level entries in the METHOD hashtable */
#define OBJECT_HASH_TABLE_SIZE   20011	/* The number of 1st level entries in the OBJECT Thashtable */
#define THREAD_HASH_TABLE_SIZE   101	/* The number of 1st level entries in the THREAD hashtable */
#define HEAP_HASH_TABLE_SIZE   20011

#define ThreadLocalStorage ThreadPrivateStorage

/* Macros to access the data variants of the symbol table */
#define CLASS_ENTRY(hashEntry) ((ClassEntry *)hashEntry->entry)
#define METHOD_ENTRY(hashEntry) ((MethodEntry *)hashEntry->entry)
#define OBJECT_ENTRY(hashEntry) ((ObjectEntry *)hashEntry->entry)
#define THREAD_ENTRY(hashEntry) ((ThreadLocalStorage *)hashEntry->entry)
#define HEAP_ENTRY(hashEntry) ((HeapEntry *)hashEntry->entry)

/* The various types that will be stored in the various hashtables */
enum EntryType {Class_t, Method_t, Object_t, Thread_t, Heap_t};


/* these are the various types of keys that can be used in the hash tables */ 
typedef struct objectHashKey {
	jobjectID id; 
} ObjectHashKey; 

typedef ObjectHashKey ClassHashKey; 

typedef struct methodHashKey {
	jmethodID id; 
} MethodHashKey; 

typedef struct threadHashKey {
	JNIEnv *id; 
} ThreadHashKey; 


/* an entry in the hash table */ 
typedef struct hashEntry
{
 void* id;					/* The hash key (ObjectHashKey*, MethodHashKey*, ThreadHashKey* */
 unsigned int printed:1;    /* Indicates that this symbol has been referenced */
 unsigned int deleted:1;    /* Indicates that this symbol is deleted */
 volatile unsigned int toBeFreed;	/* Indicates that this symbol is pending deletion */
 void *entry;               /* A pointer to the actual entry data */
 volatile struct hashEntry *next;	/* Hash table collision chain pointer */
 struct hashEntry *free;
} HashEntry;

/* RKD:  Added HashBucket to implement lockless hashtable */
typedef struct hashBucket
{
  volatile int  count;			  /* Use count for the chain */
  volatile HashEntry *activeQ;    /* First HashEntry in the active chain */
  volatile HashEntry *deleteQ;    /* First HashEntry in the delete chain */
}HashBucket;

/* The original depth of the local stack that mirrors the JVM stack */
#define ORIGINAL_STACK_SIZE  40	

/* Increment the local stack by this amount to avoid stack overflow */
#define STACK_INCREMENT_SIZE 20	

/* The size of the ThreadLocalStorage buffer for IO */
#define ORIGINAL_MESSAGE_BUFFER_SIZE 1024

/* For pre-aggregation: A stack frame to accumulate time-data along unique call chains */
typedef struct _StackFrame
{
	HashEntry *methodHashEntry;

	timestamp_t baseTime;
	timestamp_t baseCPUTime;
	timestamp_t minTime;
	timestamp_t maxTime;

	timestamp_t filteredMethodsBaseTime;
	timestamp_t filteredMethodsBaseCPUTime;

	struct _StackFrame * calledList;
	struct _StackFrame * next;
	struct _StackFrame * caller;
	unsigned int numCalls;
} StackFrame;

/* A single entry on the stack */
typedef struct
{
 SegmentedValue_t ticket;
 HashEntry *objectHashEntry;
 Uint64 cpuTime;
 timestamp_t timestamp;
 HashEntry *methodHashEntry;
 unsigned int currentBoundaryDepth;
 unsigned int printed:1;		/* Whether this entry was printed */
 unsigned int trigger:1;		/* Whether this entry was a trigger */
 unsigned int entryEventSeen:1;	/* Whether this stack frame is the begining of the trace */
 unsigned int realFrame:1;		/* pre-agg: a flag, true when this is a "real" frame, false when it represents a filtered-out function */
 struct ThreadPrivateStorage *tps;
 Uint64  cumulative_overhead;          /* the total tracing overhead of this invocation */
 Uint64  cumulative_cpu_overhead;	   /* the total tracing overhead of this invocation (cpu time) */ 
 StackFrame* stackFrame;		/* pre-agg: the frame in the unique call chain database for this active frame */
 timestamp_t baseTime;			/* pre-agg: time so far in this active frame */
 timestamp_t cumulativeTime;	/* pre-agg: time so far in this active frame plus its children */
 timestamp_t lastEntryTime;		/* pre-agg: the last time this frame was entered, e.g. when the last call to a child returned. */
 timestamp_t baseCPUTime;		/* pre-agg: CPU time so far in this active frame */
 timestamp_t lastEntryCPUTime;	/* pre-agg: the last CPU time this frame was entered, e.g. when the last call to a child returned. */
} StackEntry;

typedef int (*HashIteratorFunction)(HashEntry * hashEntry, void *parm);

typedef struct hashtable
{
#ifdef _DEBUG							/* Various hashtable metrics for debugging */
 unsigned long findSymbolCount;
 unsigned long findSymbolFound;
 unsigned long findSymbolNotFound;
 unsigned long hashHitCount;
 unsigned long hashMissCount;
 unsigned long hashMissLength;
 unsigned long createCount;
 unsigned long deleteCount;
 unsigned long moveCount;
#endif
 unsigned long size;
 unsigned long entryCount;
 enum EntryType type;
 HashBucket *entries;
 int (*compare)(void *, void *);		/* compares two hash keys in a hash entry; return 0 iff keys are equal */ 
 unsigned long (*hash)(void *);			/* hash a key  */ 
 void (*assign)(void *,void*);           /* assign the value of the second key to the first */ 
} Hashtable;


typedef  struct ThreadPrivateStorage
{
 unsigned int threadStartEventSeen : 1;		/* Whether this thread recieved a THREAD_START or was synthetically created  - 170752 - long not supported on 390 */
 unsigned int threadStackKnown : 1;			/* Whether the stack has been materialized previously */
 unsigned int scrubbingHeap:1;			
 unsigned int disableEventsForThisThread:1; /* Disables events for this thread */
 unsigned int mode:1;						/* Determines the behaviour of the event handlers. (i.e. A handler may behave differently depending on the mode of this thread) */
 jobjectID threadId;						/* The thread ID */
 unsigned long staticThreadId;				/* The thread static ID */
 unsigned long currentStackSize;			/* The size of the stack memory segment */
 unsigned long tos;							/* The current depth of the stack */
 unsigned int boundaryDepth;				/* The current boundary depth for this thread */
 SegmentedValue_t ticket;					/* The ticket counter for this thread */
 char *buffer;								/* The storage buffer pointer */
#ifdef __OS400__
 char *buffer2;							    /* buffer for etoa scratch space on as400 */ 
#endif
 char *threadName;							/* The name of the thread */
 char *groupName;							/* The name of the thread group */
 char *parentName;							/* The nmae of the parent thread */
 JNIEnv *env;
 StackEntry *stackEntry;
 SegmentedValue_t collation;
 jobjectID monitorObj;						/* used when performing monitor dumps */ 
 unsigned long ownerThread;					/* used when performing monitor dumps */ 
 void *agent_extension_slot;				/* a void* usable by an agent extension */
 StackFrame* calledList;					/* pre-aggregation: the list of top-level frames called from this thread */
 timestamp_t last_cpu_timestamp;			/* pre-aggregation: the CPU time of the most recent event on this thread */
} ThreadPrivateStorage;


typedef struct {
    char *field_name;                   /* name of field */
    char *field_signature;	            /* signature of field */
	unsigned long fieldId;
} PI_Field;


typedef struct classEntry
{
 unsigned int traceFlag:1;	    /* Indicates whether this entry has passed the filter tests */
 unsigned int arrayClass:1;     /* Indicates whether this class represents a primitive array */
 jobjectID classId;				/* The class ID */
 unsigned long static_id;		/* an id that does not change wrt class unloads */
 jint numInterfaces;			/* Number of interfaces this class implements */
 jint numMethods;				/* Number of methods this class contains */
 jint numStaticFields;			/* The number of static fields in this class */
 jint numInstanceFields;		/* The number of fields that each instance has */
 HashEntry *classObject;		/* Pointer to the object which is the class object for this class */
 HashEntry *superClassEntry;	/* Ptr to superclass class hash tbl entry, if any */
 char * className;				/* The class name */
 char * sourceName;				/* The source file name */
 char *classLoaderName;			/* The name of the class loader */
 char *superClassName;			/* The name of the super class */
 char *nameOfInterfaces;		/* The name of interfaces implemented (separated by a comma). */
 PI_Field *statics;				/* Array of statics */
 PI_Field *instances;			/* Array of instances */
 JVMPI_Method *methods;			/* Array of methods */
 timestamp_t timestamp;         /* capture the time of the class_load event */
 SegmentedValue_t collation;    /* capture the collation value of the class_load event */
} ClassEntry;


typedef struct methodEntry
{
 unsigned int traceFlag:1;		/* Indicates whether this method is being traced (TraceModeStartFilter) */
 unsigned int trigger:1;		/* Indicates whether this method is a trigger (TraceModeStartTrigger) */
 unsigned int detailsComplete:1;/* Indicates if the following fields are filled */
 unsigned int isNative:1;
 unsigned int isAbstract:1;
 unsigned int isStatic:1;
 unsigned int isSynchronized:1;
 unsigned int isPublic:1;
 unsigned int isPrivate:1;
 unsigned int isProtected:1;
 unsigned long static_id;		/* The un-changing id for this method */
 unsigned long methodCount;		/* A counter for coverage purposes */
 HashEntry * classHashEntry;	/* A pointer back to the hashtable entry for the containing class */
 char *exceptions; 
 JVMPI_Method methodData;		/* A copy of the JVMPI method data structure */
 SegmentedValue_t collation;    /* capture the collation value of the event */
} MethodEntry;


typedef struct objectEntry
{
 unsigned int traceFlag:1;	    /* Indicates whether this object is being traced */
 unsigned int foundInHeap:1;	/* Indicates whether this object was found in the heap while scrubbing */
 unsigned int notFoundInHeap:4; /* Indicates number of times object was not found in heap */
 unsigned int classObject:1;	/* Is this object a class object? */
 unsigned int allocationSeen:1; /* Was the allocation event seen for this object */
 unsigned int heapDumpIndex:8;	/* The heap dump where this object was first seen at (256 posibilities)*/
 unsigned int is_array:1;		/* JVMPI_NORMAL_OBJECT, ... (up to 16 posibilities) */
 unsigned int unusedBits:15;    /* UNUSED BITS */
 HashEntry *classHashEntry;		/* The class definition for this object */
 HashEntry *classHashEntry2;	/* The class definition for which this is a class object (only if classObject==1) */
 jsize size;					/* size in number of bytes */ /* fix 89008: change from jint */
 unsigned long static_id;		/* an id that does not change wrt garbage collection */
 SegmentedValue_t collation;    /* capture the collation value of the OBJ_ALLOC event */
} ObjectEntry;


/** INITIALIZE_SYMBOL_TABLE  ***************************************************
  * Setup the hashtables.  Since the buckets are statically declared, they will
  * already be null-ed.
  */
void jvmpiAgent_InitializeSymbolTable();

void jvmpiAgent_SetTracingFlagsOnPrimitives();
 

/** FIND_SYMBOL  **************************************************************
  * Find an entry in the symbol table based upon its Id.
  * @param       id  - the id provided by the JVMPI_Event
  * @param entryType - the type to look up be it Class/Method/Object/Thread
  * @returns         - the address of a HashEntry struct containing the result
  *                    of the lookup
  */
HashEntry * jvmpiAgent_FindObjectSymbolFast(jobjectID id);
HashEntry * jvmpiAgent_FindObjectSymbol(jobjectID id);
HashEntry * jvmpiAgent_FindThreadSymbol(JNIEnv * id);
HashEntry * jvmpiAgent_FindClassSymbol(jobjectID id);
HashEntry * jvmpiAgent_FindMethodSymbol(jmethodID id);

/* Bug Number 73480 */
#ifdef __OS400__
void insertClassOptSymbol(HashEntry *hashEntry) ;
#endif

/** FindObjectSymbolWithAllocateAndPrint
 *
 * Look up object in the VM if it doesn't exist in the hash table and make sure it is printed 
 * to the trace. 
 *
 * args - 
 *	env_id - thread which to associate with the object allocation (if it has yet to be printed)
 *  object_id - the object we are looking for 
 * 
 * returns - 
 *	the hash entry corresponding to the given object 
 */ 
HashEntry*
jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(JNIEnv *env_id, jobjectID object_id);

/** FIND_OBJECT_SYMBOL_WITH_ALLOCATE  **************************************
  * Find a symbol in Object hashtable and create an entry if it does not exist.
  */
HashEntry * jvmpiAgent_FindObjectSymbolWithAllocate(jobjectID id,
													JNIEnv *env_id);

/** DELETE_SYMBOL  **************************************************************
  * Remove an entry in the symbol table and free the memory associated with the
  * entry.
  * @param   hashEntry - the entry in the table to delete.
  */
void jvmpiAgent_DeleteSymbol(HashEntry *hashEntry,
							 enum EntryType type);

/** MOVE_SYMBOL  ****************************************************************
  * Change the id of an element in the symbol table.
  * @param  hashEntry - the current entry in the table.
  * @param      id    - the current symbol id 
  * @param      newId - the new symbol Id.
  */
void jvmpiAgent_MoveSymbol(HashEntry *hashEntry,
						   enum EntryType type,
						   void *id, void* newId);

/** CREATE_THREAD_SYMBOL  *****************************************************
  *
  */
HashEntry * jvmpiAgent_CreateThreadSymbol(JNIEnv* id);

/** CREATE_CLASS_SYMBOL  ******************************************************
  *
  */
HashEntry * jvmpiAgent_CreateClassSymbol(JVMPI_Event *event, ThreadLocalStorage *tps, Filter *filterInfo);

/** CREATE_OBJECT_SYMBOL  *****************************************************
  *
  */
HashEntry * jvmpiAgent_CreateObjectSymbol(JVMPI_Event *event,
                                          BOOL allocatedOnTracedStackframe,
                                          BOOL allocationEventSeen);

void jvmpiAgent_clearAllPrintFlags();

void jvmpiAgent_resetTraceFlags();

void jvmpiAgent_analyseHeap(JVMPI_Event *event, char* heapDefName);

/* unsigned long jvmpiAgent_analyseMonitorDump(JVMPI_Event *event, jobjectID monitorObj)

  Determines the owner thread of the monitor represented
  by monitorObj by analysing the monitor dump.

  args -
	event - the monitor dump event as reported by JVMPI
	monitorObj - the monitor for which we wish to determine the owner thread 

  returns
	the id of the thread that owns the given monitor 
*/

unsigned long jvmpiAgent_analyseMonitorDump(JVMPI_Event *event, jobjectID monitorObj);

void jvmpiAgent_markHeap(JVMPI_Event *event);

HashEntry *jvmpiAgent_getPrimativeClassEntry(jint arrayType);


void jvmpiAgent_ForAll(enum EntryType type,  HashIteratorFunction fn, void *parm);

/** CREATE_STACK  *************************************************************
  *
  */
HashEntry * jvmpiAgent_CreateStack(JNIEnv * env_id);

/** DESTROY_STACK  ************************************************************
  *
  */
void jvmpiAgent_DestroyStack(JNIEnv *env_id);

/** PUSH  *********************************************************************
  */
StackEntry * jvmpiAgent_Push(ThreadPrivateStorage *tps,
							 JNIEnv *env_id,
							 HashEntry *methodHashEntry,
							 HashEntry *objectHashEntry,
			     timestamp_t timestamp, 
				 timestamp_t cpu_timestamp);

/** POP  **********************************************************************
  *
  */
void jvmpiAgent_Pop(ThreadPrivateStorage *tps);

/** PEEK  *********************************************************************
  *
  */
StackEntry * jvmpiAgent_Peek(ThreadPrivateStorage *tps,
							 int offset);


/* Pre-Aggregation: Create a stack frame */
void jvmpiAgent_CreateStackFrame(ThreadPrivateStorage * tps, 
								 HashEntry *methodHashEntry, 
								 StackEntry* caller);

/* Pre-Aggregation: Adjust a filtered-out method's stack frame to the parent */
void jvmpiAgent_AdjustStackFrame(ThreadPrivateStorage * tps);

/* Pre-Aggregation: Roll up a stack entry's data into its frame and its caller */
void jvmpiAgent_agPop(StackEntry* entry);

unsigned long jvmpiAgent_TOS(ThreadPrivateStorage *tps);

unsigned long jvmpiAgent_StackDepth(ThreadPrivateStorage *tps);

/** createPrimitiveTypes() 
  * 
  * Used to generate the primitive types in the hashtable. 
  */ 
void createPrimitiveTypes(); 

#ifdef _DEBUG
 unsigned int jvmpiAgent_getHashTableEntryCount(enum EntryType entryType);
 void jvmpiAgent_DumpHashTableStatistics();
#endif

#ifdef __cplusplus
 }
#endif

#endif
