/**********************************************************************
 * Copyright (c) 2005, 2009 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.c,v 1.4 2009/08/07 00:52:02 jwest Exp $
 *
 * Contributors:
 * IBM - Initial API and implementation
 * Spundun Bhatt (spundun@gmail.com), with the support and encouragement of the University of Southern California Information Sciences Institute Distributed Scalable Systems Division.
 * 
 **********************************************************************/

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "hash.h"
#include "RADataTransfer.h"
#include "RAComm.h"
#include "JvmpiWriter.h"
#include "print.h"
#include "options.h"
#include "utility.h"

#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
static ra_critsec_t _lock; /* 232010 */
#endif

/* The Hashtables for each of the types */
static Hashtable _classHashtable;
static Hashtable _methodHashtable;
static Hashtable _objectHashtable;
static Hashtable _threadHashtable;

/* The Hashtable entries */
static HashBucket _classEntries[CLASS_HASH_TABLE_SIZE];
static HashBucket _methodEntries[METHOD_HASH_TABLE_SIZE];
static HashBucket _objectEntries[OBJECT_HASH_TABLE_SIZE];
static HashBucket _threadEntries[THREAD_HASH_TABLE_SIZE];

/* The classhashEntries for each of the primatives */
static HashEntry *_booleanClass;
static HashEntry *_byteClass;
static HashEntry *_charClass;
static HashEntry *_shortClass;
static HashEntry *_intClass;
static HashEntry *_longClass;
static HashEntry *_floatClass;
static HashEntry *_doubleClass;


static unsigned long _staticIdCount = 1;
static unsigned long _fieldIdCount = 0;
extern GenerationInfo_t _heapDumpInfo;

#define DUMP_READ(buf, current, size) memcpy(buf, *current, size); *current+= size

#define SIZEOF_U1 1
#define SIZEOF_U2 2
#define SIZEOF_U4 4

/* The following variables are constantly used when class/method detail option is set.  Therefore their values are cached. */
static jclass javaClass, methodClass, modifierClass;
static jmethodID forNameID, getDeclaredMethodID, getModifiersID, isPublicID, isProtectedID, isPrivateID;
static jmethodID isStaticID, isNativeID, isSynchronizedID, isAbstractID, getExceptionTypesID, getNameID;
static jmethodID getClassLoaderID, getClassID, getSuperclassID, getInterfacesID;

static int isCachedVarsSet = 0;
unsigned const int MAX_EXCEPTION_LENGTH = 250, MAX_PARAM = 40, MAX_PARAM_NAME = 200;
char *exceptionNames, *parameters, *tmpStorage, **param;

int _requestClassObj = 0; /* used to flag when a object_alloc is being performed on a class object */ 


jobjectArray findParamType(JVMPI_Event *event, char *methodSignature, int *exceptionOccurred);
void swapDelimiters (char *name);



/** HASH_FUNCTION  *************************************************************
  * Find the bucket this id will belong in.
  */
static _inline unsigned long hash_function(Hashtable *table, jint id)
{
 return (unsigned long)id % table->size;
}

static _inline unsigned long object_hash_function(void *id)
{
  return ((unsigned long)((ObjectHashKey*) id)->id) % OBJECT_HASH_TABLE_SIZE;
}
static _inline unsigned long class_hash_function(void *id)
{
  return ((unsigned long)((ClassHashKey*) id)->id) % CLASS_HASH_TABLE_SIZE;
}
static _inline unsigned long method_hash_function(void *id)
{
  return ((unsigned long)((MethodHashKey*) id)->id) % METHOD_HASH_TABLE_SIZE;
}
static _inline unsigned long thread_hash_function(void *id)
{
  return ((unsigned long)((ThreadHashKey *) id)->id) % THREAD_HASH_TABLE_SIZE;
}

int objectKeyCompare(void *id1, void *id2) {
	ObjectHashKey *k1; 
	ObjectHashKey *k2; 
	k1 = (ObjectHashKey *) id1; 
	k2 = (ObjectHashKey *) id2; 
	return !(k1->id == k2->id); /* return 0 iff keys equal */ 
}

int classKeyCompare(void *id1, void *id2) {
	return objectKeyCompare(id1,id2); /* class keys are just object keys */ 
}

int methodKeyCompare(void *id1, void *id2) {
	MethodHashKey *k1; 
	MethodHashKey *k2; 
	k1 = (MethodHashKey *) id1; 
	k2 = (MethodHashKey *) id2; 
	return !(k1->id == k2->id); /* return 0 iff keys equal */ 
}

int threadKeyCompare(void *id1, void *id2) {
	ThreadHashKey *k1; 
	ThreadHashKey *k2; 
	k1 = (ThreadHashKey *) id1; 
	k2 = (ThreadHashKey *) id2; 
	return !(k1->id == k2->id); /* return 0 iff keys equal */ 
}

void threadKeyAssign(void *id1, void *id2) {
	((ThreadHashKey *)id1)->id = ((ThreadHashKey *)id2)->id; 
}

void objectKeyAssign(void *id1, void *id2) {
	((ObjectHashKey *)id1)->id = ((ObjectHashKey *)id2)->id; 
}

void classKeyAssign(void *id1, void *id2) {
	((ClassHashKey *)id1)->id = ((ClassHashKey *)id2)->id; 
}

void methodKeyAssign(void *id1, void *id2) {
	((MethodHashKey *)id1)->id = ((MethodHashKey *)id2)->id; 
}

/** CREATE_SYMBOL  *************************************************************
  *
  */
static HashEntry * jvmpiAgent_CreateSymbol(void *id)
{
 /* Create a new hash entry and then insert it into the hash table */
 HashEntry * hashEntry = (HashEntry *)jvmpiAgent_Calloc(sizeof(HashEntry));
 hashEntry->id=id;
 hashEntry->printed=0;
 hashEntry->deleted= 0;
 hashEntry->toBeFreed=0;
 hashEntry->next=NULL;
 return hashEntry;
}


/** FREE_SYMBOL  ***************************************************************
  *
  */
void freeSymbol(HashEntry *hashEntry, enum EntryType entryType)
{
 /* Free up all the fields */
 switch(entryType)
 {
  int i;
  case Class_t:
	
	  if(CLASS_ENTRY(hashEntry)->className) free(CLASS_ENTRY(hashEntry)->className);
	  if(CLASS_ENTRY(hashEntry)->sourceName) free(CLASS_ENTRY(hashEntry)->sourceName);
	  if(CLASS_ENTRY(hashEntry)->methods) free(CLASS_ENTRY(hashEntry)->methods);
	  /* Free PR */
	  if (CLASS_ENTRY(hashEntry)->statics)
	  {
		/*clean up static array */
		for (i=0; i<CLASS_ENTRY(hashEntry)->numStaticFields; i++)
		{
         if (CLASS_ENTRY(hashEntry)->statics[i].field_name)
         {
            free(CLASS_ENTRY(hashEntry)->statics[i].field_name);
            CLASS_ENTRY(hashEntry)->statics[i].field_name = NULL;
         }
         if (CLASS_ENTRY(hashEntry)->statics[i].field_signature)
         {
            free(CLASS_ENTRY(hashEntry)->statics[i].field_signature);
            CLASS_ENTRY(hashEntry)->statics[i].field_signature = NULL;
         }
		}
		free(CLASS_ENTRY(hashEntry)->statics);
		CLASS_ENTRY(hashEntry)->statics = NULL;
	 }

	 if (CLASS_ENTRY(hashEntry)->instances)
	 {
		 /* clean up instance array */
		for (i=0; i<CLASS_ENTRY(hashEntry)->numInstanceFields; i++)
		{
         if (CLASS_ENTRY(hashEntry)->instances[i].field_name)
         {
            free(CLASS_ENTRY(hashEntry)->instances[i].field_name);
           CLASS_ENTRY(hashEntry)->instances[i].field_name = NULL;
         }
         if (CLASS_ENTRY(hashEntry)->instances[i].field_signature)
         {
            free(CLASS_ENTRY(hashEntry)->instances[i].field_signature);
            CLASS_ENTRY(hashEntry)->instances[i].field_signature = NULL;
         }
		}
		free(CLASS_ENTRY(hashEntry)->instances);
		CLASS_ENTRY(hashEntry)->instances = NULL;
	}
	/*  free PR */
	 break;
  case Object_t:
	  /* 46046 RJD - object may be a class object, in which case we need to null
	     the classObject entry in the corresponding classHashEntry. */
	  if (OBJECT_ENTRY(hashEntry)->classObject == 1) {
		  if (OBJECT_ENTRY(hashEntry)->classHashEntry2 != NULL) {
			  CLASS_ENTRY(OBJECT_ENTRY(hashEntry)->classHashEntry2)->classObject = NULL;
		  }
	  }
	  break;
  case Method_t:
  case Thread_t:
  case Heap_t:
	break;
  default:
	return;
 }
 free(hashEntry->id); 
 free(hashEntry->entry);
 hashEntry->entry=NULL;
 free(hashEntry);
 hashEntry=NULL;
}


/** GET_BUCKET  ****************************************************************
  * Find the bucket in the table that this id belongs in.  Increment the use
  * count of this bucket by 1.
  */
HashBucket *getBucket(Hashtable *table, void* id, int* idx ) {
	HashBucket* bucket;
	int oldCnt;
	*idx=table->hash(id);
	bucket= &table->entries[*idx];
	oldCnt = bucket->count;

	while(!ra_atomicCAS((int*)&bucket->count, (int*)&oldCnt, (int)(oldCnt+1)));
	return bucket;
}

static _inline HashBucket *getObjectBucket(jobjectID id) {
  ObjectHashKey k;
  int idx; 
  k.id = id; 
  idx=_objectHashtable.hash((void *)&k); 
  return &_objectHashtable.entries[idx];
}
static _inline HashBucket *getMethodBucket(jmethodID id) {
  MethodHashKey k; 
  int idx; 
  k.id = id;
  idx=_methodHashtable.hash((void*)&k); 
  return &_methodHashtable.entries[idx];
}
static _inline HashBucket *getClassBucket(jobjectID id) {
  ClassHashKey k; 
  int idx; 
  k.id = id; 
  idx=_classHashtable.hash((void*)&k); 
  return &_classHashtable.entries[idx];
}
static _inline HashBucket *getThreadBucket(JNIEnv* id) {
  ThreadHashKey k; 
  int idx; 
  k.id = id; 
  idx=_threadHashtable.hash((void*)&k); 
  return &_threadHashtable.entries[idx];
}

HashBucket* getBucketAtIndex(Hashtable *table, int idx) {
	int oldCnt;
	HashBucket *bucket;
	bucket= &table->entries[idx];
	oldCnt = bucket->count;
	while(!ra_atomicCAS((int*)&bucket->count, (int*)&oldCnt, (int)(oldCnt+1)));
	return bucket;
}


/** RELEASE_BUCKET  ************************************************************
  * If the current count of this bucket is 1, walk the deleteQ chain and remove
  * the buckets.  Decrement the use count of this bucket by 1.
  */
void releaseBucket(Hashtable *table, HashBucket *bucket, int idx, BOOL freeMemory) {
	volatile HashEntry *scanQ = NULL;
	int newCnt, oldCnt = bucket->count;
	do {
		newCnt = oldCnt - 1;
		if ( newCnt == 0 ) {
			/* cut-off deleteQ:
			 * instantiate the deleteQ and make sure that activeQ anchor
			 * is not a part of the instantiated deleteQ.  Then deleteQ
			 * can be safely cut-off
			 */
			HashEntry *old_del_q = (HashEntry *)bucket->deleteQ;
			while ( old_del_q != NULL )  {
				/* make sure anchor pointer is not part of the logically
				 * deleted chain and it will point to valid element
				 */
				for ( scanQ = (HashEntry*)bucket->activeQ; ((scanQ != NULL) && scanQ->toBeFreed); scanQ = (HashEntry*)bucket->activeQ) {
					/* if csmac failed, somebody else added a new element
					 * Either element is valid or it modified deleteQ anchor
					 */
#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
					ra_mutexEnter(&_lock);
					if (bucket->activeQ == scanQ) {
						bucket->activeQ = scanQ->next;
					} else {
						scanQ = bucket->activeQ; 
						ra_mutexExit(&_lock);
						break; 
					}
					ra_mutexExit(&_lock);
#else
					if ( !ra_atomicCAS( (int *)&bucket->activeQ, (int *)&scanQ, (int)scanQ->next) ) {
						break;
					}
#endif
				}

				/* todo: jy: may be we should save away the scanQ then
				 * assign to endq after for loop , then for loop
				 * can check for scanNext==endq.  initially endq can
				 * be NULL  :: performance issue
				 */

				/* traverse the activeQ from the element next to the scanQ
				 * until the end of the chain is reached, and cut-off the
				 * logically deleted elements from the active chain
				 */
				if (scanQ != NULL)	{
					volatile HashEntry *scanNext;
					for (scanNext = scanQ->next; scanNext != NULL; scanNext = scanQ->next) {
						if (scanNext->toBeFreed) {
							scanQ->next = scanNext->next;
						}
						else {
							scanQ = scanNext;
						}
					}
				}

				/* cut-off the delete chain */
#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
				ra_mutexEnter(&_lock);
				if (bucket->deleteQ == old_del_q) {
                    bucket->deleteQ = 0; 
					ra_mutexExit(&_lock);
					break;
				} else {
					old_del_q = bucket->deleteQ; 
				}
				
				ra_mutexExit(&_lock);
#else
				if ( ra_atomicCAS((int*)&bucket->deleteQ, (int *)&old_del_q, 0) ) {
					break;
				}
#endif
			} /* end of cut-off delete chain */

			/* if deleteQ is cut-off successfully,
			 *  scanQ = safe element to start traversing
			 *    any elements previous to this element does not belong to cut-off delete chain
			 *    all elements on the cut-off chain is guaranteed not accessed by anybody
			 *  old_del_q = anchor of cut-off delete chain
			 */

			/* free the hash elements on the cut-off chain */
			while(old_del_q != NULL) {
				scanQ = old_del_q;
				old_del_q = scanQ->free;
				if(freeMemory) {
					freeSymbol((HashEntry*)scanQ, table->type);
				}
			} /* end of processing */
		} /* end of if ( newCnt == 0 ) */

	}while( !ra_atomicCAS((int*)&bucket->count, (int*)&oldCnt, (int)newCnt));
}

HashEntry *createPrimativeHashEntry(jint type) {
	ClassEntry *entry;
    Filter *tracing;
	ObjectHashKey *k; 
	HashEntry *hashEntry; 

	k = (ObjectHashKey *)jvmpiAgent_Calloc(sizeof(ObjectHashKey)); 
	k->id = (jobjectID)type; 

	/* Create a new hash entry and then insert it into the hash table */
	hashEntry = jvmpiAgent_CreateSymbol((void*)k);

	/* Allocate and copy the event data into the symbol table structure */
	hashEntry->entry  = (ClassEntry*)jvmpiAgent_Calloc(sizeof(ClassEntry));
	entry = CLASS_ENTRY(hashEntry);

	/* The class_id is zero  */
	entry->classId=0;

	/* We don't trace primative classes */
	entry->traceFlag = 0;

	/* This is an array class */
	entry->arrayClass = 0;

#ifdef __OS400__ /* 239987 starts */
#pragma convert(819)
#endif
	switch(type) { /* 235601 - change strdup to STRDUP */
        case JVMPI_BOOLEAN:	    STRDUP(entry->className, "boolean"); tracing=jvmpiAgent_getFilter("boolean", "");   break;
		case JVMPI_BYTE:		STRDUP(entry->className, "byte");    tracing=jvmpiAgent_getFilter("byte", "");      break;
		case JVMPI_CHAR:		STRDUP(entry->className, "char");    tracing=jvmpiAgent_getFilter("char", "");      break;
		case JVMPI_SHORT:		STRDUP(entry->className, "short");   tracing=jvmpiAgent_getFilter("short", "");     break;
		case JVMPI_INT:			STRDUP(entry->className, "int");     tracing=jvmpiAgent_getFilter("int", "");       break;
		case JVMPI_LONG:		STRDUP(entry->className, "long");    tracing=jvmpiAgent_getFilter("long", "");      break;
		case JVMPI_FLOAT:		STRDUP(entry->className, "float");   tracing=jvmpiAgent_getFilter("float", "");     break;
		case JVMPI_DOUBLE:	    STRDUP(entry->className, "double");  tracing=jvmpiAgent_getFilter("double", "");    break;
	}
#ifdef __OS400__
#pragma convert(0)
#endif /* 239987 ends */
	entry->static_id=_staticIdCount++;

	STRDUP(entry->sourceName, ""); /* 235601 */
	entry->numInterfaces=0;
	entry->numMethods=0;
	entry->numStaticFields=0;
	entry->superClassEntry=NULL;

	entry->classObject=NULL;
    entry->arrayClass=1;

	/* Set the class detail fields */
	entry->classLoaderName= "";
	entry->superClassName = "";
	entry->nameOfInterfaces = "";

    /* Indicate whether we are tracing this type */
    entry -> traceFlag = jvmpiAgent_getClassFilterMode(tracing);

	TIMESTAMP_ZERO(&entry->timestamp);

	/*##MW There is a race condition between the increment and the copy that must be fixed */
	jvmpiAgent_incrementSegmentedValue(&_jvmpiAgent_collation, 0);
	jvmpiAgent_copySegmentedValue(&entry->collation, &_jvmpiAgent_collation);
	return hashEntry;
}


HashEntry *jvmpiAgent_getPrimativeClassEntry(jint arrayType) {
	switch(arrayType) {
		case JVMPI_BOOLEAN: return _booleanClass;
		case JVMPI_BYTE:	return _byteClass;
		case JVMPI_CHAR:	return _charClass;
		case JVMPI_SHORT:	return _shortClass;
		case JVMPI_INT:		return _intClass;
		case JVMPI_LONG:	return _longClass;
		case JVMPI_FLOAT:	return _floatClass;
		case JVMPI_DOUBLE: return _doubleClass;
	}
	return NULL;
}

/** INITIALIZE_SYMBOL_TABLE  ***************************************************
  * Setup the hashtables.  Since the buckets are statically declared, they will
  * already be null-ed.
  */
void jvmpiAgent_InitializeSymbolTable()
{
#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
 ra_mutexCreate(&_lock); /* 232010 */
#endif

 _classHashtable.size=CLASS_HASH_TABLE_SIZE;
 _classHashtable.entries = _classEntries;
 _classHashtable.entryCount=0;
 _classHashtable.type=Class_t;
 _classHashtable.hash = class_hash_function; 
 _classHashtable.compare = classKeyCompare; 
 _classHashtable.assign = classKeyAssign; 
 _methodHashtable.size=METHOD_HASH_TABLE_SIZE;
 _methodHashtable.entries = _methodEntries;
 _methodHashtable.entryCount=0;
 _methodHashtable.type=Method_t;
 _methodHashtable.hash = method_hash_function; 
 _methodHashtable.compare = methodKeyCompare; 
 _methodHashtable.assign = methodKeyAssign; 
 _objectHashtable.size=OBJECT_HASH_TABLE_SIZE;
 _objectHashtable.entries = _objectEntries;
 _objectHashtable.entryCount=0;
 _objectHashtable.type=Object_t;
 _objectHashtable.hash = object_hash_function; 
 _objectHashtable.compare = objectKeyCompare; 
 _objectHashtable.assign = objectKeyAssign; 
 _threadHashtable.size=THREAD_HASH_TABLE_SIZE;
 _threadHashtable.entries = _threadEntries;
 _threadHashtable.entryCount=0;
 _threadHashtable.type=Thread_t;
 _threadHashtable.hash = thread_hash_function; 
 _threadHashtable.compare = threadKeyCompare; 
 _threadHashtable.assign = threadKeyAssign; 

 createPrimitiveTypes(); 
}


void createPrimitiveTypes() {

 /* Create the primative types as classes */
 _booleanClass=createPrimativeHashEntry(JVMPI_BOOLEAN);
 _byteClass=createPrimativeHashEntry(JVMPI_BYTE);
 _charClass=createPrimativeHashEntry(JVMPI_CHAR);
 _shortClass=createPrimativeHashEntry(JVMPI_SHORT);
 _intClass=createPrimativeHashEntry(JVMPI_INT);
 _longClass=createPrimativeHashEntry(JVMPI_LONG);
 _floatClass=createPrimativeHashEntry(JVMPI_FLOAT);
 _doubleClass=createPrimativeHashEntry(JVMPI_DOUBLE);

}


/** SET_TRACING_FLAGS_ON_PRIMITIVES  ***************************************************
  * After the filter process, set the tracing flags on the primitive class entries
  *
  * Prerequisite: Must be invoked after the primitive arrays are created
  */
void jvmpiAgent_SetTracingFlagsOnPrimitives() {
#ifdef __OS400__
#pragma convert(819) 
#endif

	Filter *tracing = 0;
	tracing = jvmpiAgent_getFilter("boolean", "");  CLASS_ENTRY(_booleanClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("byte", "");     CLASS_ENTRY(_byteClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("char", "");     CLASS_ENTRY(_charClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("short", "");    CLASS_ENTRY(_shortClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("int", "");      CLASS_ENTRY(_intClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("long", "");     CLASS_ENTRY(_longClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("float", "");    CLASS_ENTRY(_floatClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
	tracing = jvmpiAgent_getFilter("double", "");   CLASS_ENTRY(_doubleClass)->traceFlag=jvmpiAgent_getClassFilterMode(tracing);
#ifdef __OS400__
#pragma convert(0) 
#endif

}


/** INSERT_SYMBOL  ************************************************************
  *
  */
static HashEntry * insertSymbol(Hashtable *table,
								HashEntry * hashEntry) {
	int index;
	HashBucket *bucket=getBucket(table, hashEntry->id, &index);

	/* Add the cell to the start of the chain */
	HashEntry *oldEntry = (HashEntry *)bucket->activeQ;
	hashEntry->next = oldEntry;
#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
	ra_mutexEnter(&_lock);
	bucket->activeQ = hashEntry; /* 232010 */
	ra_mutexExit(&_lock);
#else
	while (!ra_atomicCAS( (int *)&bucket->activeQ, (int *)&oldEntry, (int)hashEntry)) {
		hashEntry->next = oldEntry;
	}
#endif

	table->entryCount++;
#ifdef _DEBUG
	table->createCount++;
#endif
	releaseBucket(table, bucket, index, TRUE);
	return hashEntry;
}


/** REMOVE_SYMBOL  **************************************************************
  * Do the actual work of setting hash pointers when a deletion is taking place.
  * @param   hashEntry - the entry in the table to delete.
  */
static void removeSymbol(Hashtable *table, HashEntry *hashEntry, BOOL freeMemory) {
	int index;
	HashBucket *bucket=getBucket(table, hashEntry->id, &index);
	int oldStatus = 0;
	
	if(ra_atomicCAS((int *)&(hashEntry->toBeFreed), (int *)&oldStatus, 1)) {
		/* Add the cell to the delete chain */
		HashEntry* oldEntry = (HashEntry*)bucket->deleteQ;

		hashEntry->free = oldEntry;
#if defined(__OS400__) || defined(__powerpc64__) || defined(_WIN64) /*ts. bug 120479*/
		ra_mutexEnter(&_lock);
		bucket->deleteQ = hashEntry; /* 232010 */
		ra_mutexExit(&_lock);
#else
		while(!ra_atomicCAS((int*)&bucket->deleteQ, (int*)&oldEntry, (int)hashEntry))  {
			hashEntry->free = oldEntry;
		}
#endif
	}
#ifdef _DEBUG
	table->deleteCount++;
#endif
	table->entryCount--;
	releaseBucket(table, bucket, index, freeMemory);
}


static int resetPrintedFlag(HashEntry *hashEntry, void * parm)
{
 hashEntry->printed = 0;
 return 0;
}


/** RESET_CLASS_TRACE_FLAG  ****************************************************
  *
  */
static int resetClassTraceFlag(HashEntry *hashEntry, void * parm)  {
	int i;
	Filter *filterInfo;
	ClassEntry *classEntry=CLASS_ENTRY(hashEntry);

	filterInfo=jvmpiAgent_getFilter(classEntry->className, "");
    classEntry -> traceFlag = jvmpiAgent_getClassFilterMode(filterInfo);


	/* Iterate through the methods and set the filter as well. */
	for(i=0; i<classEntry->numMethods; i++) {
		MethodEntry *methodEntry=METHOD_ENTRY(((HashEntry**)(classEntry->methods))[i]);

		filterInfo=jvmpiAgent_getFilter(classEntry->className, methodEntry->methodData.method_name);
		methodEntry->traceFlag=jvmpiAgent_checkMethodFilters(methodEntry->methodData.method_name, filterInfo);
		methodEntry->trigger=jvmpiAgent_checkTrigger(classEntry->className, methodEntry->methodData.method_name);
	}
	return 0;
}


/** RESET_OBJECT_TRACE_FLAG  ***************************************************
  *
  */
static int resetObjectTraceFlag(HashEntry *hashEntry, void * parm)  {
	ObjectEntry *objectEntry=OBJECT_ENTRY(hashEntry);

	if(objectEntry->classHashEntry) {
		objectEntry->traceFlag=CLASS_ENTRY(objectEntry->classHashEntry)->traceFlag;
	}
	return 0;
}


/** CREATE_METHOD_SYMBOL  ******************************************************
  * Allocates an entry in the symbol table for the method and sets the entry data
  * to point to the owning class.
  */
static HashEntry * jvmpiAgent_CreateMethodSymbol(JVMPI_Method *method, HashEntry *classHashEntry)  {
	HashEntry *methodHashEntry;
	MethodEntry *methodEntry;
	MethodHashKey *k; 
	k = (MethodHashKey *)jvmpiAgent_Calloc(sizeof(MethodHashKey)); 
	k->id=method->method_id; 
	methodHashEntry = jvmpiAgent_CreateSymbol((void *)k);

	/* Allocate and copy the event data into the symbol table structure */
	methodHashEntry->entry = (MethodEntry*)(jvmpiAgent_Calloc(sizeof(MethodEntry)));
	methodEntry = METHOD_ENTRY(methodHashEntry);
	methodEntry->classHashEntry = classHashEntry;  /* Set a pointer back to the containing class */
	methodEntry->methodCount = 0;
	methodEntry->static_id = ++_staticIdCount;

	/* Is this method a trigger */
	methodEntry->trigger=jvmpiAgent_checkTrigger(CLASS_ENTRY(classHashEntry)->className, method->method_name);

	/*##MW There is a race condition between the increment and the copy that must be fixed */
	jvmpiAgent_incrementSegmentedValue(&_jvmpiAgent_collation, 0);
	jvmpiAgent_copySegmentedValue(&methodEntry->collation, &_jvmpiAgent_collation);

	{
		JVMPI_Method * methodData    = &methodEntry->methodData;
		STRDUP(methodData->method_name, method->method_name); /* 235601 */
		STRDUP(methodData->method_signature, method->method_signature); /* 235601 */
		methodData->start_lineno     = method->start_lineno;
		methodData->end_lineno       = method->end_lineno;
		methodData->method_id        = method->method_id;
	}

	/* Is this method being traced? */
	methodEntry->traceFlag=jvmpiAgent_checkMethodFilters(method->method_name, jvmpiAgent_getFilter(CLASS_ENTRY(classHashEntry)->className, method->method_name));
	insertSymbol(&_methodHashtable, methodHashEntry);
	return methodHashEntry;
}





/** CLEAR_ALL_REFERENCES  ******************************************************
  * Walk all the hashtables and set the printed bit to 0.
*/
void jvmpiAgent_clearAllPrintFlags()  {
	jvmpiAgent_ForAll(Class_t, resetPrintedFlag, 0);
	jvmpiAgent_ForAll(Object_t, resetPrintedFlag, 0);
	jvmpiAgent_ForAll(Method_t, resetPrintedFlag, 0);
	jvmpiAgent_ForAll(Thread_t, resetPrintedFlag, 0);
}


/** RESET_TRACE_FLAGS  *********************************************************
  *  Walks through the hashtables and sets the traceFlag on each of the elements.
  */
void jvmpiAgent_resetTraceFlags() {
	/* RKD:  First we walk the class hashtable as this will set the traceflags for
	         the classes and the methods within the classes based upon the filter
			 criteria.  Next we walk the object hashtable and set the object trace
			 flag based upon the class traceflag.
	*/
	jvmpiAgent_ForAll(Class_t, resetClassTraceFlag, 0);
	jvmpiAgent_ForAll(Object_t, resetObjectTraceFlag, 0);
}

void jvmpiAgent_ForAll(enum EntryType entryType, HashIteratorFunction fn, void *parm) {
	unsigned int i;
	Hashtable *table;
	switch(entryType)  {
	case Class_t:  table=&_classHashtable; break;
	case Method_t: table=&_methodHashtable; break;
	case Object_t: table=&_objectHashtable; break;
	case Thread_t: table=&_threadHashtable; break;
	default: return;
	}

	/* Iterate through the table */
	for (i = 0; i < table->size; i++) {
		volatile HashEntry *p, *next;
		HashBucket *bucket;

		/* grab the current bucket */
		bucket=getBucketAtIndex(table, i);

		/* Walk through the collision chain */
		for (p = bucket->activeQ; p != NULL; p = next) {
			next=p->next;
			if (fn((HashEntry*)p,parm))  {
				return; /* Quit if the user function returns true */
			}
		}
		/* release the bucket */
		releaseBucket(table, bucket, i, TRUE);
	}
}

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

	HashEntry *hashEntry = 0;
	
	hashEntry = jvmpiAgent_FindObjectSymbol(object_id);

	/* if the hash entry for the object exists, make sure it is printed */
	if (hashEntry)
	{
		if (!hashEntry->printed)
		{
			jvmpiAgent_printObjAllocElement(hashEntry, env_id, 0);
		}

	}
	/* otherwise, lookup the object in the VM, and then ensure that it is printed */
	else {	
		_jvmpiAgent_jvmpiInterface->DisableGC();	
		REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, object_id);
		_jvmpiAgent_jvmpiInterface->EnableGC();
		hashEntry = jvmpiAgent_FindObjectSymbol(object_id);
		if (hashEntry) {	
			if (!hashEntry->printed)
			{
				jvmpiAgent_printObjAllocElement(hashEntry, env_id, 0);
			}
		}

	}


	return hashEntry;

}


/** FIND_OBJECT_SYMBOL_WITHOUT_LOCKING  **************************************
  * Find a symbol in Object hashtable and create an entry if it does not exist.
  * YOU MUST ALREADY HOLD THE LOCK ON THE HASHTABLE.
  */
HashEntry*
jvmpiAgent_FindObjectSymbolWithAllocate(jobjectID id,
										JNIEnv *env_id)
{
 HashEntry *hashEntry = jvmpiAgent_FindObjectSymbol(id);
 if (hashEntry)
 {
  if (!hashEntry->printed)
  {
   jvmpiAgent_printObjAllocElement(hashEntry, env_id, 0);
  }
 }
 return hashEntry;
}


/** FIND_SYMBOL  **************************************************************
  * Find an entry in a hashtable 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     >0  - the address of a HashEntry struct containing the result
  *                    of the lookup.
  *               0  - id is not in the table.
  */
static _inline HashEntry * jvmpiAgent_FindSymbol(void* id, Hashtable* table, HashBucket* bucket)  {
	volatile HashEntry *p;
#ifdef _DEBUG
	unsigned long pathLength = 1;
#endif

	/* Try to locate the entry in the hash bucket chain */
#ifdef _DEBUG
	table->findSymbolCount++;
#endif
	for(p = bucket->activeQ; p != NULL; p = p->next)  {
		if (table->compare(p->id,id)==0 && !p->deleted && !p->toBeFreed) {
#ifdef _DEBUG
			table->findSymbolFound++;
			if (pathLength == 1) {
				table->hashHitCount++;
			}
			else {
				table->hashMissCount++;
				table->hashMissLength += pathLength;
			}
#endif
			return (HashEntry*)p;
		}
#ifdef _DEBUG
		pathLength++;
#endif
	}
 /* Entry not found */
#ifdef _DEBUG
	table->findSymbolNotFound++;
#endif
	return 0;
}

HashEntry * jvmpiAgent_FindObjectSymbolFast(jobjectID id) {
  if (id == 0) {
    /* Only a thread can have a 0 id value so there is no point
       looking up 0 id values in the other hash tables
    */
    return NULL;
  }
  else {
	ObjectHashKey k; 
    HashBucket* bucket=getObjectBucket(id);
	k.id = id; 
    return jvmpiAgent_FindSymbol((void*)&k, &_objectHashtable, bucket);
  }
}
HashEntry * jvmpiAgent_FindObjectSymbol(jobjectID id) {
  if (id == 0) {
    /* Only a thread can have a 0 id value so there is no point
       looking up 0 id values in the other hash tables
    */
    return NULL;
  }
  else {
    ObjectHashKey k; 
    HashEntry* entry;
	HashBucket* bucket; 
    int index;
	k.id=id; 
    bucket=getBucket(&_objectHashtable, (void*)&k, &index);
    entry = jvmpiAgent_FindSymbol((void*)&k, &_objectHashtable, bucket);
    releaseBucket(&_objectHashtable, bucket, index, TRUE);
    return entry;
  }
}
HashEntry * jvmpiAgent_FindThreadSymbol(JNIEnv *id) {
  ThreadHashKey k; 
  HashBucket* bucket=getThreadBucket(id);
  k.id = id; 
  return jvmpiAgent_FindSymbol((void*)&k, &_threadHashtable, bucket);
}
HashEntry * jvmpiAgent_FindClassSymbol(jobjectID id) {
  if (id == 0) {
    /* Only a thread can have a 0 id value so there is no point
       looking up 0 id values in the other hash tables
    */
    return NULL;
  }
  else {
    ClassHashKey k; 
    HashBucket* bucket=getClassBucket(id);
	k.id=id; 
    return jvmpiAgent_FindSymbol((void*)&k, &_classHashtable, bucket);
  }
}
HashEntry * jvmpiAgent_FindMethodSymbol(jmethodID id) {
  if (id == 0) {
    /* Only a thread can have a 0 id value so there is no point
       looking up 0 id values in the other hash tables
    */
    return NULL;
  }
  else {
	MethodHashKey k; 
    HashBucket* bucket=getMethodBucket(id);
	k.id=id; 
    return jvmpiAgent_FindSymbol((void *)&k, &_methodHashtable, bucket);
  }
}

/* Bug Number 73480 */
#ifdef __OS400__
void insertClassOptSymbol(HashEntry *hashEntry) 
{
	insertSymbol(&_classHashtable, hashEntry);
}
#endif

/** MOVE_SYMBOL  ****************************************************************
  * Change the id of an element in the symbol table.
  * @param  hashEntry - the current entry in the table.
  * @param      id    - the old hashEntry->id 
  * @param      newId - the new symbol Id.
  */
void jvmpiAgent_MoveSymbol(HashEntry *hashEntry, enum EntryType entryType, void* id, 
						   void* newId) {
	unsigned long oldIndex;
	unsigned long newIndex;
	Hashtable *table;

	switch(entryType) {
	case Class_t:  table=&_classHashtable; break;
	case Method_t: table=&_methodHashtable; break;
	case Object_t: table=&_objectHashtable; break;
	case Thread_t: table=&_threadHashtable; break;
	default: return;
	}

	oldIndex = table->hash(id);
	newIndex = table->hash(newId);
#ifdef _DEBUG
	table->moveCount++;
#endif
	if (oldIndex != newIndex) {
		removeSymbol(table, hashEntry, FALSE);
		table->assign(id,newId);
                hashEntry->toBeFreed=0; /* 8735 */
                hashEntry->next=NULL;   /* 8735 */
		insertSymbol(table, hashEntry);
	}
	else  {
		table->assign(id,newId);
	}
}





/** 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 entryType)
{
 if(!hashEntry)
 {
   return;
 }
 switch(entryType)
 {
  case Class_t:
	  removeSymbol(&_classHashtable, hashEntry, TRUE);
	  break;
  case Method_t:
	  removeSymbol(&_methodHashtable, hashEntry, TRUE);
	  break;
  case Object_t:
	  removeSymbol(&_objectHashtable, hashEntry, TRUE);
	  /* Remove the duplicate copy of the object in the heap hashtable */
	  {
		/* RKD:  We need to save the id for later
		HashEntry *heapEntry=jvmpiAgent_FindSymbol(hashEntry->id, Heap_t);
		jvmpiAgent_DeleteSymbol(heapEntry, Heap_t);
		*/

	  }
	  break;
  case Thread_t:
	  removeSymbol(&_threadHashtable, hashEntry, TRUE);
	  break;
  default:
	  return;
 }
}

/** CREAT_THREAD_SYMBOL  *******************************************************
  *
  */
HashEntry * jvmpiAgent_CreateThreadSymbol(JNIEnv *id)
{
 ThreadHashKey *k; 
 HashEntry *entry;
 k = (ThreadHashKey *)jvmpiAgent_Calloc(sizeof(ThreadHashKey)); 
 k->id = id; 
 entry=jvmpiAgent_CreateSymbol((void *)k);
 insertSymbol(&_threadHashtable,entry);
 return entry;

}

/*************************************************************************************/
/* PR Heap Dump walk through */

/* CR:  Is this really needed..If there isn't a function that already does this there should be */
HashEntry * dumpGetObject(jobjectID object_id)
{
	HashEntry *objectHashEntry = 0;

	/* RKD:  We always look in the object table for our class object first. */
	objectHashEntry = jvmpiAgent_FindObjectSymbol(object_id);
	if (!objectHashEntry && object_id != 0) {
		REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, object_id); /* 232010 */
		objectHashEntry = jvmpiAgent_FindObjectSymbol(object_id);
	}
	return objectHashEntry;
}


unsigned char prof_dump_read_u1(unsigned char **current)
{
    unsigned char u1;
    DUMP_READ(&u1, current, 1);
    return u1;
}

unsigned short prof_dump_read_u2(unsigned char **current)
{
    unsigned short u2;
    DUMP_READ(&u2, current, 2);
    return ntohs(u2);
}

unsigned long prof_dump_read_u4(unsigned char **current)
{
    jint u4;
    DUMP_READ(&u4, current, 4);
    return ntohl(u4);
}

jobject prof_dump_read_ref(unsigned char **current) 
{
	jobject ref; 
	DUMP_READ(&ref,current,sizeof(jobject)); 
	return ref; 
}


jobjectID prof_dump_read_id(unsigned char **current)
{
    jobjectID p;
	DUMP_READ(&p, current, sizeof(jobjectID));
    return p;
}

void *prof_dump_read_ptr(unsigned char **current) 
{
	void *p; 
#ifdef __OS400__
/* On AS/400 pointers are 16-byte aligned within the buffer. 
   Forward to the next 16 byte boundary if we currently aren't on one */ 
	unsigned int i = (unsigned int) *current;
	i &= 0x0000000f;  /* grab lowest 4 bits of i */
	if (i > 0) {  
		*current = *current + (16 - i);  /* align the pointer */ 
	}
#endif
	DUMP_READ(&p, current, sizeof(void *));
    return p;
}

void outputObjReference(ThreadLocalStorage *tps,
                        HashEntry *objectHashEntry,
                        HashEntry *referencedObjectHashEntry,
						HashEntry *currentFieldClass,
                        int fieldIndex,
                        BOOL isArray) {

	HashEntry *referencedClassHashEntry, *classHashEntry;


	referencedClassHashEntry = OBJECT_ENTRY(referencedObjectHashEntry)->classHashEntry;
	classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry;

	/* If we don't know the class at either end of the reference we will abandon outputing the
	   reference.  This may result in missing information.  If need be, we can output it by
	   default by just placing the checks for filters inside this test.
	*/
	if(!referencedClassHashEntry || !classHashEntry) {
		return;
	}

	/* ## REMOVE */
	_jvmpiAgent_Options.objRefMode = Filter4Owner;


	/* If the object reference mode if "RespectFilter" we only print the reference
	   is we are tracing both ends of the refernce
	*/
	if (_jvmpiAgent_Options.objRefMode == RespectFilter)
	{
		if (!(CLASS_ENTRY(referencedClassHashEntry)->traceFlag && CLASS_ENTRY(classHashEntry)->traceFlag))
		{
			return;
		}
	}
	/* If the object reference mode if "Filter4Owner", we print the reference if we
	   are tracing either end.  This provides a "one deep" reference graph for all
	   traced classes.
	*/
	else if (_jvmpiAgent_Options.objRefMode == Filter4Owner)
	{
	  if (!(CLASS_ENTRY(classHashEntry)->traceFlag || CLASS_ENTRY(referencedClassHashEntry)->traceFlag))
	  {
		return;
	  }
	}

	/* If either end is not already printed do so */
	if(!referencedObjectHashEntry->printed)
	{
		jvmpiAgent_printObjAllocElement(referencedObjectHashEntry, tps->env, 0);
	}
	if(!objectHashEntry->printed)
	{
	   jvmpiAgent_printObjAllocElement(objectHashEntry, tps->env, 0);

	}

	/* If either end is a class object ensure the defining class is printed */
	if(OBJECT_ENTRY(referencedObjectHashEntry)->classObject)
	{
		jvmpiAgent_outputClassDeclaration(OBJECT_ENTRY(referencedObjectHashEntry)->classHashEntry2, tps);
	}
	if(OBJECT_ENTRY(objectHashEntry)->classObject)
	{
		jvmpiAgent_outputClassDeclaration(OBJECT_ENTRY(objectHashEntry)->classHashEntry2, tps);

	}

	if(isArray)
	{
	   jvmpiAgent_printObjectArrayReferenceElement(tps, OBJECT_ENTRY(objectHashEntry)->static_id, fieldIndex,  OBJECT_ENTRY(referencedObjectHashEntry)->static_id, OBJECT_ENTRY(referencedObjectHashEntry)->heapDumpIndex);
	}
	else
	{
	   jvmpiAgent_printObjectInstanceReferenceElement(tps, OBJECT_ENTRY(objectHashEntry)->static_id, fieldIndex,  OBJECT_ENTRY(referencedObjectHashEntry)->static_id,  OBJECT_ENTRY(referencedObjectHashEntry)->heapDumpIndex, CLASS_ENTRY(currentFieldClass)->instances[fieldIndex].fieldId);
	}


}

void outputPrimativeArray(ThreadLocalStorage *tps,
                          HashEntry *objectHashEntry) {


    /* If the array is not already printed do so */
	if(!objectHashEntry->printed)
	{
		jvmpiAgent_printObjAllocElement(objectHashEntry, tps->env, 0);
	}
}



void outputStaticObjReference(ThreadLocalStorage *tps,
                        HashEntry *objectHashEntry,
                        HashEntry *referencedObjectHashEntry,
						HashEntry *staticReferenceClass,
                        int fieldIndex,
                        BOOL isArray) {
	HashEntry *referencedClassHashEntry;

	referencedClassHashEntry = OBJECT_ENTRY(referencedObjectHashEntry)->classHashEntry;

	/* ## REMOVE */
	_jvmpiAgent_Options.objRefMode = Filter4Owner;


	/* If the object reference mode if "RespectFilter" we only print the reference
	   is we are tracing both ends of the refernce
	*/
	if (_jvmpiAgent_Options.objRefMode == RespectFilter)
	{
		if (!(CLASS_ENTRY(referencedClassHashEntry)->traceFlag && CLASS_ENTRY(staticReferenceClass)->traceFlag))
		{
			return;
		}
	}
	/* If the object reference mode if "Filter4Owner", we print the reference if we
	   are tracing either end.  This provides a "one deep" reference graph for all
	   traced classes.
	*/
	else if (_jvmpiAgent_Options.objRefMode == Filter4Owner)
	{
	  if (!(CLASS_ENTRY(staticReferenceClass)->traceFlag || CLASS_ENTRY(referencedClassHashEntry)->traceFlag))
	  {
		return;
	  }
	}

	/* If either end is not already printed do so */
	if(!referencedObjectHashEntry->printed)
	{
		jvmpiAgent_printObjAllocElement(referencedObjectHashEntry, tps->env,0);
	}
	if(OBJECT_ENTRY(referencedObjectHashEntry)->classObject)
	{
		jvmpiAgent_outputClassDeclaration(OBJECT_ENTRY(referencedObjectHashEntry)->classHashEntry2, tps);
	}

	if(!staticReferenceClass->printed) {
		jvmpiAgent_outputClassDeclaration(staticReferenceClass, tps);
	}

	jvmpiAgent_printObjectInstanceReferenceElement(tps, OBJECT_ENTRY(objectHashEntry)->static_id, fieldIndex, OBJECT_ENTRY(referencedObjectHashEntry)->static_id,  OBJECT_ENTRY(referencedObjectHashEntry)->heapDumpIndex,  CLASS_ENTRY(staticReferenceClass)->statics[fieldIndex].fieldId);

}


static int scrubObject(HashEntry *hashEntry, void * parm)  {
	ObjectEntry *objectEntry=OBJECT_ENTRY(hashEntry);
	if(!objectEntry->foundInHeap) {

		/* RJD We need to count the number of times an object has not been found in the heap. 
		   This problem arises from the fact that when doing a heap dump, after the VM has
		   created the heap dump and reported it to the profiler, other VM threads
		   may continue running. That is, we can potentially be scrubbing the heap concurrently
		   while objects are being created. This results in a situation where we may find a 
		   particular object in the hash table, but not in the heap (because it was created 
		   after the heap dump had been constructed by the VM). Removing the object just
		   because it wasn't found in the heap may be premature. During a second heap dump, 
		   however, the object should be present in the heap. Hence, it should be safe to
		   free objects that have not been found in the heap twice. bugzilla_57475 */ 

		objectEntry->notFoundInHeap++; 

		if (objectEntry->notFoundInHeap > 1) {

			/* We will only print object free for objects which we have already declared
			   within the trace and if we have been asked to print object free's
			*/
			if (hashEntry->printed && _jvmpiAgent_Options.gc!=GcNone) {
				jvmpiAgent_printObjFreeElement((ThreadPrivateStorage*)(parm), hashEntry);
			}

			/* Remove the object from the object hashtable */
			jvmpiAgent_DeleteSymbol(hashEntry, Object_t);
		}
	}
	else {
		objectEntry->foundInHeap=0;  
	}
	return 0;
}


/* This function is used dump Level 0 to store all the object and type in the Heap Hash table for comparition
*/
void jvmpiAgent_markHeap(JVMPI_Event *event) {
	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
	unsigned char* cursor = (unsigned char*)event->u.heap_dump.begin;
	unsigned char type;
	jobjectID oid;

    /* It appears some JVM's may give us a NULL start address.  Starting walking memory
       at NULL wouldn't be good
    */
    if(!cursor) {
        return;
    }

	while (cursor < (unsigned char*)event->u.heap_dump.end) {
		HashEntry *objectHashEntry = 0;
		
		type = prof_dump_read_u1(&cursor); 
		oid = prof_dump_read_id(&cursor); 

		/* If this is a normal mark of the heap we behave one way, if this is a scrub of the heap due to a
		   runGC request we do it another way.
		*/
		if(!tps->scrubbingHeap) {
			objectHashEntry = dumpGetObject(oid);
			if(objectHashEntry) /* The defect number is 175135. */ {
				OBJECT_ENTRY(objectHashEntry)->heapDumpIndex=_heapDumpInfo.index;
			}
		}
		else {
			HashEntry *objectHashEntry=jvmpiAgent_FindObjectSymbol(oid);
			if(objectHashEntry) {
				OBJECT_ENTRY(objectHashEntry)->foundInHeap = 1;
			}
		}

	} /* end while */


    /* If we are scrubbing we should generate all the necessary free events before returning to the JVM */
    if(tps->scrubbingHeap) {
        /* Iterate through the object table and find objects which we didn't get free's for */
        jvmpiAgent_ForAll(Object_t, scrubObject, tps);
    }

}

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

	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(event->env_id);
	unsigned char *current; 
	JNIEnv *ownerThread = 0; /* owner thread */ 

	current=(unsigned char*)event->u.monitor_dump.begin;

	if (current == 0) {
		/* bad data from the JVM */ 
		return 0;
	}
	
	while (current < (unsigned char*)event->u.monitor_dump.end) {
		unsigned char tag = prof_dump_read_u1(&current);
		switch (tag)
		{
		case JVMPI_MONITOR_JAVA:
		/*
			jobjectID 	 object ID
			JNIEnv * 	 owner thread
			u4 			 entry count
			u4 			 number of threads waiting to enter
			[JNIEnv *]* 	threads waiting to enter
			u4 			 number of threads waiting to be notified
			[JNIEnv *]* 	threads waiting to be notified 
		*/ 
			{
			jobjectID obj_id;
			unsigned int numThreads; 
			JNIEnv *thread_env; 
			
			obj_id = prof_dump_read_id(&current); /* object ID */ 
			thread_env =  (JNIEnv *)prof_dump_read_ptr(&current); /* owner thread */ 

			if (obj_id == monitorObj) { 
				ownerThread = thread_env; 
				/* we found the owner thread; no need to walk the rest of the dump */ /* <- this is no longer true */
				/*goto END_MONITORS_ANALYSE;*/
			}

			if (thread_env==tps->env) {
				HashEntry *objectHashEntry;
				/* lookup monitor object, ensuring that it exists in the trace */
				objectHashEntry = jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(event->env_id,obj_id);
				if (objectHashEntry) {
					jvmpiAgent_printMonitorStillOwnedElement(objectHashEntry,event);
				}
			}

			/* we don't care what's in the rest of this record */ 
			current += SIZEOF_U4; /* entry count */ 
			numThreads = prof_dump_read_u4(&current); /* number of threads waiting to enter */ 
			current += numThreads * sizeof(void *); /* threads waiting to enter */ 
			numThreads = prof_dump_read_u4(&current); /* number of threads waiting to be notified */ 
			current += numThreads * sizeof(void *); /* threads waiting to be notified */ 
			
			break;
			}
		case JVMPI_MONITOR_RAW:
		/*	 char * 	 raw monitor name
			 JVMPI_RawMonitor 	raw monitor ID
			 JNIEnv * 	owner thread
			 u4 	entry count
			 u4 	number of threads waiting to enter
			 [JNIEnv *]* 	threads waiting to enter
			 u4 	number of threads waiting to be notified
			 [JNIEnv *]* 	threads waiting to be notified
		 */ 
			{
			unsigned int numThreads; 

			prof_dump_read_ptr(&current); /* raw monitor name */ 
			prof_dump_read_id(&current); /* raw monitor ID */ 
			prof_dump_read_ptr(&current); /* owner thread */ 
			current += SIZEOF_U4;  /* entry count */ 
			numThreads = prof_dump_read_u4(&current); /* number of threads waiting to enter */
			current += numThreads * sizeof(void *); /* threads waiting to enter */ 
			numThreads = prof_dump_read_u4(&current); /* number of threads waiting to be notified */ 
			current += numThreads * sizeof(void *); /* threads waiting to be notified */ 

			break;
			}

		default:
			break;
		}
	}
/*END_MONITORS_ANALYSE:*/

	if (ownerThread != 0) {
		/* We found the owner thread. Now translate the JNIEnv * to the id printed 
		   the trace. */ 
		HashEntry *entry = jvmpiAgent_FindThreadSymbol(ownerThread);
		if (entry != 0) {
			if (jvmpiAgent_isPrintStaticId()) {
				return THREAD_ENTRY(entry)->staticThreadId; 
			}
			else {
				return (unsigned long)ownerThread; 
			}
		}
	}
	return 0; 
}



/* This function is used to store all the new object and there references into the heap hash table
*/
/* RKD:  I am disabling the optimizer for this function as it causes the heapdump to blow
         up on windows.  This appears to only occur with my compiler, remove when checking
		 in V5 code
*/
#ifdef _WIN32
#pragma optimize ( "", off )
#endif
void jvmpiAgent_analyseHeap(JVMPI_Event *event, char* heapDefName)
{
	unsigned char *current;
	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
	jvmpiAgent_printHDStartElement(tps, heapDefName);
	current=(unsigned char*)event->u.heap_dump.begin;
	
	/* RKD:  Cannot process heap if we get bad info.  We need to tell the user this is bad. */
	if(current == 0 || (unsigned char*)event->u.heap_dump.end == 0) {
		sendErrorMessage(RA_SEVERE,"0","Heap dump failed because bad data passed from JVM.");
		return;	
	}

    while (current < (unsigned char*)event->u.heap_dump.end)
	{
		HashEntry *tmpHash; /* For GC_ROOT */
		unsigned long id;  /* For GC_ROOT */

		int size = 0;
	    unsigned char tag;
		int in;
		int out;

		tag = prof_dump_read_u1(&current);
		size += sizeof(unsigned char);

START_SWITCH:
		switch (tag)
		{
			case JVMPI_GC_ROOT_UNKNOWN :
				{/* jobjectID            object */
					jobjectID obj_id = prof_dump_read_id(&current);/* object */

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;

						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}

						jvmpiAgent_printGcRootElement(event, id, "UNKNOWN");

					}
					break;
				 }
			case JVMPI_GC_ROOT_JNI_GLOBAL:
				{/*jobjectID        object
                   jobject          JNI global reference */
					jobjectID obj_id = prof_dump_read_id(&current); /* object  */
					prof_dump_read_ref(&current);/* JNI global reference */

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;
					
						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}
						jvmpiAgent_printGcRootElement(event, id, "JNI_GLOBAL");
					}
					break;
				}
			case JVMPI_GC_ROOT_JNI_LOCAL:
				{/*jobjectID       object
                   JNIEnv *        thread
                   u4              frame # in stack trace (-1 for empty) */
				    jobjectID obj_id = prof_dump_read_id(&current);
					JNIEnv *env = (JNIEnv *)prof_dump_read_ptr(&current);
					int depth = prof_dump_read_u4(&current);

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;
						
						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}
						jvmpiAgent_printGcRootElement(event, id, "JNI_LOCAL");
					}
					break;
				}
			case JVMPI_GC_ROOT_JAVA_FRAME:
				{/*jobjectID          object
                   JNIEnv *           thread
                   u4                 frame # in stack trace (-1 for empty) */
				    jobjectID obj_id = prof_dump_read_id(&current);
					JNIEnv *env = (JNIEnv *)prof_dump_read_ptr(&current);
					int depth = prof_dump_read_u4(&current);

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;
						
						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}
						jvmpiAgent_printGcRootElement(event, id, "JAVA_FRAME");
					}
					break;
				}
			case JVMPI_GC_ROOT_NATIVE_STACK :
				{/*jobjectID             object
                   JNIEnv *              thread */
					jobjectID obj_id;
					JNIEnv *JNIEnvPtr;
					obj_id = prof_dump_read_id(&current);/* object */
                    JNIEnvPtr = (JNIEnv *) prof_dump_read_ptr(&current);/* thread*/

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;
						
						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}
						jvmpiAgent_printGcRootElement(event, id, "NATIVE_STACK");
					}
					break;
				}
			case JVMPI_GC_ROOT_STICKY_CLASS :
				{/*jobjectID              class object */
                    jobjectID class_id = prof_dump_read_id(&current);/* class object */

					tmpHash = jvmpiAgent_FindClassSymbol(class_id);
					if(tmpHash != NULL)
					{
						id = CLASS_ENTRY(tmpHash)->static_id;
						if (!tmpHash->printed) {
							jvmpiAgent_outputClassDeclaration(tmpHash,tps); 
						}
						jvmpiAgent_printGcRootElement(event, id, "STICKY_CLASS");
					}
					break;
				}
			case JVMPI_GC_ROOT_THREAD_BLOCK:
				{/*jobjectID               thread object
                   JNIEnv *                thread */
				    jobjectID h_ID = prof_dump_read_id(&current);/* thread object */
					JNIEnv *env_ID = (JNIEnv *)prof_dump_read_ptr(&current);/* thread */
					/* Thread blocks are commented out in the corresponding print.c code 
					   Hence, commenting out here as well. 
					tmpHash = jvmpiAgent_FindThreadSymbol(env_ID);
					if(tmpHash != NULL)
					{
						id = THREAD_ENTRY(tmpHash)->staticThreadId;

						jvmpiAgent_printGcRootElement(event, id, "THREAD_BLOCK");
					} */ 
					break;
				}
			case JVMPI_GC_ROOT_MONITOR_USED:
				{/* jobjectID                object */
					jobjectID obj_Id = prof_dump_read_id(&current);/* object */

					tmpHash = jvmpiAgent_FindObjectSymbol(obj_Id);
					if(tmpHash != NULL)
					{
						id = OBJECT_ENTRY(tmpHash)->static_id;
						
						if (!tmpHash->printed) {
							jvmpiAgent_printObjAllocElement(tmpHash,tps->env,0); 
						}
						jvmpiAgent_printGcRootElement(event, id, "MONITOR_USED");
					}
					break;
				}
			case JVMPI_GC_CLASS_DUMP  :
				{/*jobjectID          class
                   jobjectID          super
                   jobjectID          class loader
				   jobjectID          signers
                   jobjectID          protection domain
                   jobjectID          class name (a String object, may be NULL)
                   void *             reserved
                   u4                 instance size (in bytes)
                   [jobjectID]*       interfaces
                   u2                 size of constant pool
                   [u2,               constant pool index,
                    ty,               type,
                    vl]*              value
                   [vl]*              static field values */
					/*jint ty; */
					int i;
				    jobjectID class_name;
					void *resv2;
					unsigned int size;
				    jobjectID class_id;
					jobjectID loader_id;
					jobjectID signers_id;
					jobjectID domain_id;
					unsigned short size_of_constant_pool;
					HashEntry *classHashEntry, *objectHashEntry;
					class_id = prof_dump_read_id(&current); /* class */

					/* Resolve the class object */
					_requestClassObj = 1;  /* specify that we're requesting a class object */
				    objectHashEntry = dumpGetObject(class_id);
					_requestClassObj = 0; 

					/* If we found the class object and we don't have the class it represents yet we better resolve it */
					if(objectHashEntry) {
						classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry2;
						
						if(!classHashEntry && class_id != 0) {
							REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, class_id);
							OBJECT_ENTRY(objectHashEntry)->classHashEntry2 = jvmpiAgent_FindClassSymbol(class_id);
							OBJECT_ENTRY(objectHashEntry)->classObject = 1; 
						}

					}

					/* It does occasionally happen that sun's JDK will assign a class id of zero
					 * to a normal object.  This will cause objectHashEntry to be NULL.  
					 This block of code handles this case. Note that we cannot recover from this
					 error since walking the rest of the heap dump correctly requires that
					 we know the details of the class. */

					if (!objectHashEntry) {
						sendErrorMessage(RA_SEVERE,"0","Heap dump failed because an unknown class could not be resolved.");
						return; 
					}
				
					classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry2;


					/* Store ref to superclass to use on obj instance dumps.
					 * You cannot just store the superclass id and defer the class
					 * load because later move events can invalidate the association
					 * between this class id and it's class object.  */
					{
						jobjectID super_id;
						HashEntry *superclassHashEntry;

						super_id = prof_dump_read_id(&current);
						superclassHashEntry = jvmpiAgent_FindClassSymbol(super_id);
						/* RJD: make sure that we're not requesting a class load event
						   for a null id */
						if (!superclassHashEntry && super_id != 0) {
							REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, super_id);  /*232010 */
							superclassHashEntry = jvmpiAgent_FindClassSymbol(super_id);
						}
						CLASS_ENTRY(classHashEntry)->superClassEntry = superclassHashEntry;
					}

					loader_id = prof_dump_read_id(&current); /*class loader */


					signers_id = prof_dump_read_id(&current); /*signers */
					domain_id = prof_dump_read_id(&current); /*protection domain */
					class_name = prof_dump_read_id(&current); /*class name (a String object, may be NULL) */

				    resv2 = prof_dump_read_ptr(&current);  /* reserved */
				    size = prof_dump_read_u4(&current); /*instance size (in bytes) */

					for (i=0; i<CLASS_ENTRY(classHashEntry)->numInterfaces; i++)
					{
						jobjectID iref = prof_dump_read_id(&current); /*walk through the the interface  */
					}

					size_of_constant_pool = prof_dump_read_u2(&current); /*size of constant pool  */

					for (i = 0; i < size_of_constant_pool; i++)
					{
						unsigned short index;
						unsigned char ty;
						index = prof_dump_read_u2(&current);/* constant pool index */
						ty = prof_dump_read_u1(&current);/* type */
					
						switch(ty)
						{
							case JVMPI_CLASS:
							case JVMPI_NORMAL_OBJECT:  
								prof_dump_read_id(&current);
								break;
							case JVMPI_BOOLEAN:
							case JVMPI_BYTE:
								prof_dump_read_u1(&current);
								break;
							case JVMPI_CHAR:
							case JVMPI_SHORT:
								prof_dump_read_u2(&current);
								break;
							case JVMPI_INT:
							case JVMPI_FLOAT:
								prof_dump_read_u4(&current);
								break;
							case JVMPI_LONG:
							case JVMPI_DOUBLE:
								prof_dump_read_u4(&current);
								prof_dump_read_u4(&current);
								break;
							default:
								printf("Wrong type\n");
								fflush(stdout); 
						} 
						
						 
					}
#ifdef __OS400__
#pragma convert(819)
#endif 
					for(i = 0; i<CLASS_ENTRY(classHashEntry)->numStaticFields; i++) /* walk through static field values */
					{
						jobjectID instanceFieldValue;
						char prim = CLASS_ENTRY(classHashEntry)->statics[i].field_signature[0];
						if(event->u.heap_dump.dump_level==JVMPI_DUMP_LEVEL_2)
						{
							switch(prim)
							{
								case 'Z':
								case 'B':
									prof_dump_read_u1(&current);
									break;
								case 'C':
								case 'S':
									prof_dump_read_u2(&current);
									break;
								case 'I':
								case 'F':
									prof_dump_read_u4(&current);
									break;
								case 'J':
								case 'D':
									prof_dump_read_u4(&current);
									prof_dump_read_u4(&current);
									break;
								case 'L':
								case '[':
									instanceFieldValue=prof_dump_read_id(&current);
							}
						}
						else
						{
							if(prim=='L' || prim=='[')
							{
#ifdef __OS400__
#pragma convert(0)
#endif 
								instanceFieldValue=prof_dump_read_id(&current);

								/* RKD: Static fields */
								if (instanceFieldValue)
								{
									HashEntry *objectHashEntry4Ref;


									objectHashEntry4Ref = dumpGetObject(instanceFieldValue);
									if(!objectHashEntry4Ref)
									{
										continue; /* The defect number is 174350.*/
									}

									/* Is this the first heap dump that we saw this object in? */
/* Changed for bug 46787
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex <  _heapDumpInfo.index) {
*/
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex == 0) {
										OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex =  _heapDumpInfo.index;
									}

									/* Forward this reference to be printed if either end of the
									   reference is new for this analysis
									*/
/* Added for bug 46787 */
										outputStaticObjReference(tps, CLASS_ENTRY(classHashEntry)->classObject, objectHashEntry4Ref, classHashEntry, i, FALSE);
/* Removed for bug 46787
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex==_heapDumpInfo.index)
									{
										outputStaticObjReference(tps, CLASS_ENTRY(classHashEntry)->classObject, objectHashEntry4Ref, classHashEntry, i, FALSE);
									}
*/
								}
							}
						}
					}/* for */
					break;
				}

			case JVMPI_GC_INSTANCE_DUMP:
				{/*jobjectID            object
                   jobjectID            class
                   u4                   number of bytes that follow
                   [vl]*                instance field values (class, followed by super, super's super ...)*/

					HashEntry *debugClassHashEntry=0, *classHashEntry = 0;
					HashEntry *objectHashEntry = 0;
					unsigned int i;
					jobjectID obj_id = prof_dump_read_id(&current);
					jobjectID class_id = prof_dump_read_id(&current);
					int superClass = 0;
					unsigned long valbytes = prof_dump_read_u4(&current);
					HashEntry *heapHashEntry = 0;

					/* Determine where the end of this instance is in case we need to
					   panic we can recover
					*/
					unsigned char* endAddr=current+valbytes;

					/* RKD:  Moved the find for the objecthashEntry up here as this should
					         resolve the classHashEntry simutaneously.
					*/

					/* Force the class to be resolved */
					_requestClassObj = 1; /* specify that we're requesting a class object */ 
					objectHashEntry=dumpGetObject(class_id);
					_requestClassObj = 0; 

					/* If we found the class object and we don't have the class it represents yet we better resolve it */
					if(objectHashEntry) {
						classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry2;
						if(!classHashEntry && class_id != 0) {
							REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, class_id); /* 232010 */
							OBJECT_ENTRY(objectHashEntry)->classHashEntry2 = jvmpiAgent_FindClassSymbol (class_id);
							OBJECT_ENTRY(objectHashEntry)->classObject = 1; 
						}
					}

					/* Find the object in the object allocation table */
					objectHashEntry = dumpGetObject(obj_id);

					/* If we can't find this object abort finding it's references */
					if(!objectHashEntry) {
						current=endAddr;
						break;
					}

					/* RKD:  Idealy we would use the reference already established but
					         this causes problems as the array types we established
							 are not as detailed as the contents in the heap.  For
					         example if a class_id is zero when requested the class
					         will not be in the heap
					*/
					classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry;

					if(!classHashEntry) {
						current=endAddr;
						break;
					}

		
/* Changed for bug 46787
					if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex <  _heapDumpInfo.index) {
*/
					if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex == 0) {
						OBJECT_ENTRY(objectHashEntry)->heapDumpIndex =  _heapDumpInfo.index;
					}

					while (classHashEntry)
					{
						for (i=0; i< (unsigned int)CLASS_ENTRY(classHashEntry)->numInstanceFields; i++)
						{
							jobjectID instanceFieldValue;
							char prim = CLASS_ENTRY(classHashEntry)->instances[i].field_signature[0];
							/* Grab this instance  */
#ifdef __OS400__
#pragma convert(819)
#endif 
							if(event->u.heap_dump.dump_level==JVMPI_DUMP_LEVEL_2)
							{
								switch(prim) {
								case 'Z':
								case 'B':
									prof_dump_read_u1(&current);
									break;
								case 'C':
								case 'S':
									prof_dump_read_u2(&current);
									break;
								case 'I':
								case 'F':
									prof_dump_read_u4(&current);
									break;
								case 'J':
								case 'D':
									prof_dump_read_u4(&current);
									prof_dump_read_u4(&current);
									break;
								case 'L':
								case '[':
									instanceFieldValue=prof_dump_read_id(&current);
								}
							}
							else
							{
								if(prim=='L' || prim=='[')
								{
									instanceFieldValue=prof_dump_read_id(&current);
								}
							}

							if (prim == 'L' || prim == '[')
							{
#ifdef __OS400__
#pragma convert(0)
#endif 
							    if (instanceFieldValue)
								{

									HashEntry *objectHashEntry4Ref;

									objectHashEntry4Ref = dumpGetObject(instanceFieldValue);
									if(!objectHashEntry4Ref)
									{
										continue; /* The defect number is 174350.*/
									}

/* Changed for bug 46787
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex <  _heapDumpInfo.index) {
*/
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex == 0) {
										OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex =  _heapDumpInfo.index;
									}

									/* Forward this reference to be printed if either end of the
									   reference is new for this analysis
									*/
/* Added for bug 46787 */
									outputObjReference(tps, objectHashEntry, objectHashEntry4Ref, classHashEntry, i, FALSE);
/* Removed for bug 46787
									if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex==_heapDumpInfo.index || OBJECT_ENTRY(objectHashEntry)->heapDumpIndex==_heapDumpInfo.index)
									{
										outputObjReference(tps, objectHashEntry, objectHashEntry4Ref, classHashEntry, i, FALSE);
									}
*/
								}
							}

						} /* for */
						classHashEntry = CLASS_ENTRY(classHashEntry)->superClassEntry;
					} /* while */

					current=endAddr;
					break;
				}

			case JVMPI_GC_OBJ_ARRAY_DUMP :
				{/*jobjectID             array object
                   u4                    number of elements
                   jobjectID             element class ID (may be NULL in the Java 2 SDK)
                   [jobjectID]*          elements */
					unsigned int i;
					jobjectID objRef = 0;
					HashEntry *objectHashEntry = 0;

					HashEntry *classHashEntry=NULL;
					HashEntry *heapHashEntry=NULL;

				    jobjectID objArray_id = prof_dump_read_id(&current);
					unsigned long num_elements = prof_dump_read_u4(&current);
					jobjectID class_id = prof_dump_read_id(&current);


					/* RKD:  Moved the locate object up here and then use the objects type first
					         we will ignore the class_id in the dump.  By rights, locating the
							 object will also locate the class object as well.
					*/

					/* Locate the array object */
					objectHashEntry = dumpGetObject(objArray_id);



					/* If we can't find the array object just skip this object array */
					if(!objectHashEntry) {
						current+=num_elements * sizeof(jobjectID);
						break;
					}

					classHashEntry=OBJECT_ENTRY(objectHashEntry)->classHashEntry;

					/* If we have no type information just skip this object array */
					if(!classHashEntry) {
						current+=num_elements * sizeof(jobjectID);
						break;
					}

/* Changed for bug 46787
					if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex <  _heapDumpInfo.index) {
*/
					if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex == 0) {
						OBJECT_ENTRY(objectHashEntry)->heapDumpIndex =  _heapDumpInfo.index;
					}

					/* Load all the array element pointers into the heap table */
					for (i = 0; i < num_elements; i++)
					{

						/* Get the array element */
						objRef = prof_dump_read_id(&current);

						/* RKD:  We could save all the references to determine if they change
						   between heap analysis but this is not the present goal.
						heapEntry->objRef_id[i] = 0;
						*/


						/* Inspect the array element (ie. ensure non-NULL)*/

						if(objRef)
						{
							HashEntry *objectHashEntry4Ref = 0;
/* Removed for bug 46787
							HashEntry *referencedObjectHeapEntry = 0;
*/

							/* Lookup/Load the endpoint of this reference into the object Hashtable*/
							objectHashEntry4Ref = dumpGetObject(objRef);

							/* If we don't have the endpoint abort this reference */
							if(!objectHashEntry4Ref)
							{
								continue;
							}

/* Removed for bug 46787
							if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex <  _heapDumpInfo.index) {
*/
							if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex == 0) {
								OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex =  _heapDumpInfo.index;
							}

							/* Forward this reference to be printed if either end of the
							   reference is new for this analysis
							*/

/* Added for bug 46787 */
							outputObjReference(tps, objectHashEntry, objectHashEntry4Ref, classHashEntry, i, TRUE);
/* Removed for bug 46787
							if(OBJECT_ENTRY(objectHashEntry4Ref)->heapDumpIndex==_heapDumpInfo.index || OBJECT_ENTRY(referencedObjectHeapEntry)->heapDumpIndex==_heapDumpInfo.index)
							{
								outputObjReference(tps, objectHashEntry, objectHashEntry4Ref, classHashEntry, i, TRUE);
							}
*/
						}
					}/* for */
					break;
				}
			case JVMPI_GC_PRIM_ARRAY_DUMP:
				{/*jobjectID            array object
                   u4                   number of elements
                   ty                   element type
                   [vl]*                elements */
				    unsigned int num_elements;
				    unsigned char ty;
                    HashEntry *objectHashEntry=NULL;
                    HashEntry *classHashEntry=NULL;
				    jobjectID obj_id = prof_dump_read_id(&current);

					in = sizeof(void *);
					out = sizeof(void *) + sizeof(unsigned long);
					size += out;
					num_elements = prof_dump_read_u4(&current); /* number of elements */
					size += sizeof(unsigned long);
					ty = prof_dump_read_u1(&current);/* element type */
					size += sizeof(unsigned char);

                    objectHashEntry = dumpGetObject(obj_id);

                    if(objectHashEntry) {
/* Removed for bug 46787
						if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex <  _heapDumpInfo.index) {
*/
                        if(OBJECT_ENTRY(objectHashEntry)->heapDumpIndex == 0) {
						    OBJECT_ENTRY(objectHashEntry)->heapDumpIndex =  _heapDumpInfo.index;
					    }

					    outputPrimativeArray(tps, objectHashEntry);
                    }


					if(event->u.heap_dump.dump_level == JVMPI_DUMP_LEVEL_2) /* elements */
					{
					  switch (ty)
					  {
						case JVMPI_BYTE:
						case JVMPI_BOOLEAN:
							in = num_elements * 1;
							out = in;
							size += out;
							current+=in;
							break;
						case JVMPI_SHORT:
						case JVMPI_CHAR:
							in = num_elements * 2;
							out = in;
							size += out;
							current+=in;
							break;
						case JVMPI_FLOAT:
						case JVMPI_INT:
							in = num_elements * 4;
							out = in;
							size += out;
							current+=in;
							break;
						case JVMPI_DOUBLE:
						case JVMPI_LONG:
							in = num_elements * 8;
							out = in;
							size += out;
							current+=in;
							break;
					  }
					}
					break;
				}
			default:
				{
					break;
				}
			}
	}

}
#ifdef _WIN32
#pragma optimize ( "", on )
#endif
/* PR */
/*************************************************************************************/


/* Allocates an entry in the symbol table for the object
   ##RKD:  Must hold the write lock before invoking
*/
HashEntry * jvmpiAgent_CreateObjectSymbol(JVMPI_Event *event,
                                          BOOL allocatedOnTracedStackframe,
                                          BOOL allocationEventSeen)
{
 HashEntry *classHashEntry;
 HashEntry *objectHashEntry;
 ObjectEntry *objectEntry;
 ObjectHashKey *k; 

 /* ##RKD:  We know these when we are called, should hand as params.  Also
            if the class_id is zero this is an array object and we dont set
			the classHashEntry pointer as we don't know the class type.
 */
 if(event->u.obj_alloc.class_id) {
	 classHashEntry=jvmpiAgent_FindClassSymbol(event->u.obj_alloc.class_id);
 }
 else {
     classHashEntry=jvmpiAgent_getPrimativeClassEntry(event->u.obj_alloc.is_array);
 }

 k = (ObjectHashKey *)jvmpiAgent_Calloc(sizeof(ObjectHashKey)); 
 k->id = event->u.obj_alloc.obj_id; 
 objectHashEntry = jvmpiAgent_CreateSymbol((void*)k);

 /* Allocate and copy the event data into the symbol table structure */
 objectHashEntry->entry = (ObjectEntry*)jvmpiAgent_Calloc(sizeof(ObjectEntry));
 objectEntry = OBJECT_ENTRY(objectHashEntry);

 /* RKD: Indicate if this object is being traced or not based upon it's class object.  We don't
    trace arrays.
 */
 if(classHashEntry) {
	objectEntry->traceFlag=CLASS_ENTRY(classHashEntry)->traceFlag;
 }

 /* Initially this class is not a class object, the classLoad event may change these later */
 objectEntry->classObject=0;
 objectEntry->classHashEntry2=NULL;

 objectEntry->heapDumpIndex=0;

 objectEntry->classHashEntry = classHashEntry;      /* Set a pointer back to the containing class */
 objectEntry->is_array = (event->u.obj_alloc.is_array==JVMPI_NORMAL_OBJECT?0:1) ;        /* JVMPI_NORMAL_OBJECT, ... */
 objectEntry->size =     event->u.obj_alloc.size;            /* size in number of bytes */
 objectEntry->static_id = ++_staticIdCount;

 /*##MW There is a race condition between the increment and the copy that must be fixed */
 jvmpiAgent_incrementSegmentedValue(&_jvmpiAgent_collation, 0);
 jvmpiAgent_copySegmentedValue(&objectEntry->collation, &_jvmpiAgent_collation);

 insertSymbol(&_objectHashtable, objectHashEntry);

 return objectHashEntry;
}


/* Allocates an entry in the symbol table for the class */
HashEntry * jvmpiAgent_CreateClassSymbol(JVMPI_Event *event, ThreadLocalStorage *tps, Filter *filterInfo) {
	int i, exceptionOccurred=0;
	HashEntry *hashEntry;
	ClassEntry *classEntry;
	HashEntry *classObjectEntry;
	ClassHashKey *k; 

    jstring currClassName;
	jobject currentClass;
	jclass javaClazz;


	/* RKD:  Look for our class object.  This should always be there except with java.lang.Class */
	classObjectEntry=jvmpiAgent_FindObjectSymbol(event->u.class_load.class_id);

	/* AM: This block of code will ensure that a class symbol is not created more than once for the same class */
	if (hashEntry = jvmpiAgent_FindClassSymbol (event->u.class_load.class_id))
	{
		/* In case the stored class symbol did not have a resolved classObjectEntry, then try to
		 * resolve now */
		
		if (!CLASS_ENTRY(hashEntry)->classObject)
		{
			if (classObjectEntry)
			{ CLASS_ENTRY(hashEntry)->classObject = classObjectEntry; }
			else
			{
				/* Try and request our class object */
				REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, event->u.class_load.class_id); /* 232010 */
				CLASS_ENTRY(hashEntry)->classObject = jvmpiAgent_FindObjectSymbol(event->u.class_load.class_id);
			}
		}

		return hashEntry;
	}

	k = (ClassHashKey *)jvmpiAgent_Calloc(sizeof(ClassHashKey)); 
	k->id=event->u.class_load.class_id; 
	hashEntry = jvmpiAgent_CreateSymbol((void *)k);

	/* Allocate and copy the event data into the symbol table structure */
	hashEntry->entry  = (ClassEntry*)jvmpiAgent_Calloc(sizeof(ClassEntry));
	classEntry = CLASS_ENTRY(hashEntry);


	/* Lookup the filter information for this class */
    classEntry->traceFlag = jvmpiAgent_getClassFilterMode(filterInfo);

	/* This is not a primitive array class */
	classEntry->arrayClass = 0;

	/* 235601 begins */
	classEntry->className = "";
	classEntry->sourceName = "";
    classEntry->classLoaderName = "";
    classEntry->nameOfInterfaces = "";
	if(event->u.class_load.class_name) {
		STRDUP(classEntry->className, event->u.class_load.class_name);
	}
	if(event->u.class_load.source_name) {
		STRDUP(classEntry->sourceName, event->u.class_load.source_name);
	}
   else {
      /* create blank source name */
	   STRDUP(classEntry->sourceName, "");
   }
	/* 235601 ends */

	classEntry->numInterfaces = event->u.class_load.num_interfaces;
	classEntry->numMethods    = event->u.class_load.num_methods;
	classEntry->numStaticFields   = event->u.class_load.num_static_fields;

	if (classEntry->numStaticFields > 0) {
		/* Create and fill the static field array */
		classEntry->statics = (PI_Field *) jvmpiAgent_Calloc(sizeof(PI_Field) * classEntry->numStaticFields);
		for (i=0; i<classEntry->numStaticFields; i++) {
            STRDUP(classEntry->statics[i].field_name , event->u.class_load.statics[i].field_name);
			STRDUP(classEntry->statics[i].field_signature, event->u.class_load.statics[i].field_signature);
			classEntry->statics[i].fieldId = ++_fieldIdCount;
		}
	}

	classEntry->numInstanceFields = event->u.class_load.num_instance_fields;
	if (classEntry->numInstanceFields > 0)  {
		/* Create and fill the instance field array */
		classEntry->instances = (PI_Field *) jvmpiAgent_Calloc(sizeof(PI_Field) * classEntry->numInstanceFields);
		for (i=0; i<classEntry->numInstanceFields; i++) {
			STRDUP(classEntry->instances[i].field_name, event->u.class_load.instances[i].field_name);
			STRDUP(classEntry->instances[i].field_signature, event->u.class_load.instances[i].field_signature);
			classEntry->instances[i].fieldId = ++_fieldIdCount;
		}
	}

	classEntry->classId = event->u.class_load.class_id;

	/*##MW There is a race condition between the increment and the copy that must be fixed */
	jvmpiAgent_incrementSegmentedValue(&_jvmpiAgent_collation, 0);
	jvmpiAgent_copySegmentedValue(&classEntry->collation, &_jvmpiAgent_collation);


	/* Get the class object of the current class only if the proper option is set */
	if (_jvmpiAgent_Options.classLoadDetails || _jvmpiAgent_Options.methodDetails)
	{
		char *clazzName = classEntry->className;
		
		/* Disable events on the current thread */
		tps->disableEventsForThisThread=1;

		/* Change the delimiter if IBMs JDK is being used */
		if (_setPathDelimiter == '/')
		{ swapDelimiters(clazzName); }
		
		/* Clear any previous exceptions that may have occurred previously */
		CLEAR_EXCEPTIONS(event->env_id);

		currClassName = CREATE_JSTRING(event->env_id, classEntry->className);
		currentClass = 0;


		/* Set the required variables that will need to be cached */
		if (!isCachedVarsSet)
		{
			isCachedVarsSet = 1;

			/* Variables common to both class and method detail */			
			if (javaClazz = FIND_CLASS(event->env_id, "java/lang/Class")) {
				javaClass = (jclass)(ENV(event->env_id)->NewGlobalRef(ENVPARM(event->env_id) javaClazz));
				forNameID = GET_SMETHODID(event->env_id, javaClass, "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
				getNameID = GET_METHODID(event->env_id, javaClass, "getName", "()Ljava/lang/String;");
			}

			/* Variables specific to method detail */
			if (_jvmpiAgent_Options.methodDetails && javaClass)
			{

				if (javaClazz = (jclass)FIND_CLASS(event->env_id, "java/lang/reflect/Method"))
				{
					methodClass = (jclass)(ENV(event->env_id)->NewGlobalRef(ENVPARM(event->env_id) javaClazz));
					getModifiersID = GET_METHODID(event->env_id, methodClass, "getModifiers", "()I");
					getExceptionTypesID = GET_METHODID(event->env_id, methodClass, "getExceptionTypes", "()[Ljava/lang/Class;");
				}
				
				getDeclaredMethodID = GET_METHODID(event->env_id, javaClass, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");				
				exceptionNames = (char *) malloc (MAX_EXCEPTION_LENGTH);
				parameters = (char *) malloc (MAX_PARAM_NAME);
				tmpStorage = (char *) malloc (MAX_PARAM_NAME);
				param = (char **) malloc (sizeof (char *) * MAX_PARAM);
				
				if (javaClazz = (jclass)FIND_CLASS(event->env_id, "java/lang/reflect/Modifier"))
				{
					modifierClass = (jclass)(ENV(event->env_id)->NewGlobalRef(ENVPARM(event->env_id) javaClazz));
					isPublicID = GET_SMETHODID(event->env_id, modifierClass, "isPublic", "(I)Z");
					isProtectedID = GET_SMETHODID(event->env_id, modifierClass, "isProtected", "(I)Z");
					isPrivateID = GET_SMETHODID(event->env_id, modifierClass, "isPrivate", "(I)Z");
					isAbstractID = GET_SMETHODID(event->env_id, modifierClass, "isAbstract", "(I)Z");
					isNativeID = GET_SMETHODID(event->env_id, modifierClass, "isNative", "(I)Z");
					isSynchronizedID = GET_SMETHODID(event->env_id, modifierClass, "isSynchronized", "(I)Z");
					isStaticID = GET_SMETHODID(event->env_id, modifierClass, "isStatic", "(I)Z");					
				}
			}

			/* Variables specific to the class detail */
			if (_jvmpiAgent_Options.classLoadDetails && javaClass)
			{
				getClassLoaderID = GET_METHODID(event->env_id, javaClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
				getClassID = GET_METHODID(event->env_id, javaClass, "getClass", "()Ljava/lang/Class;");
				getSuperclassID = GET_METHODID(event->env_id, javaClass, "getSuperclass", "()Ljava/lang/Class;");
				getInterfacesID = GET_METHODID(event->env_id, javaClass, "getInterfaces", "()[Ljava/lang/Class;");
			}


			/* Set isCachedVarsSet to 2 if any one of the required variables were not successfully initialized */
			if (!javaClass || !forNameID || !getNameID || (_jvmpiAgent_Options.methodDetails && (!methodClass || !getDeclaredMethodID || !getModifiersID || !modifierClass ||
				!isPublicID || !isProtectedID || !isPrivateID || !isAbstractID || !getExceptionTypesID)) || (_jvmpiAgent_Options.classLoadDetails && (!getClassLoaderID || !getClassID || !getSuperclassID || !getInterfacesID)))
			{ isCachedVarsSet = 2; }
		}

		
		/* Make sure that javaClass and foNameMethodID were retrieved properly */
		/* NOTE: This section of the code needs to be fixed.  Because if the default class loader is changed, then some classes will not be found */
		if (isCachedVarsSet) {
			currentClass = CALL_OBJ_SMETHOD1(event->env_id, javaClass, forNameID, currClassName);		
		}

		/* Enable events on the current thread */
		tps->disableEventsForThisThread=0;

	}

	/* For each method, create a new hash entry and add it to the method array. */
	if(event->u.class_load.num_methods>0) {
		JVMPI_Method *methods;
		HashEntry **methodEntry;
		int i;
		classEntry->methods = (JVMPI_Method*)jvmpiAgent_Calloc(event->u.class_load.num_methods * sizeof(HashEntry *));
		methods = event->u.class_load.methods;
		methodEntry = (HashEntry **)classEntry->methods;
		for (i=0; i < event->u.class_load.num_methods; i++) {
			methodEntry[i] = jvmpiAgent_CreateMethodSymbol(methods, hashEntry);


			/* A.M: Find out the detailed properties of the method if the proper option is set.
			 * The properties: visibility, isNative, isAbstract, isStatic, isSynchronized, and the
			 * possible exceptions that can be thrown by the method. */
			if (_jvmpiAgent_Options.methodDetails)
			{
				jobject jcurrentMethod = 0;
				jstring jmethodName = 0;
				jobjectArray jparamType = 0;

				jint jmethodModifier = 0;

				jobject jmethodExceptions, currentException;
				jobjectArray methodExceptions;
				jsize arrayLength, counter;
				jstring jexceptionName;
				char *exceptionName;

				/* Disable events on the current thread */
				tps->disableEventsForThisThread=1;


				/* Initialize the desired fields of the method detail */
				METHOD_ENTRY(methodEntry[i])->isNative = 0;
				METHOD_ENTRY(methodEntry[i])->isAbstract = 0;
				METHOD_ENTRY(methodEntry[i])->isStatic = 0;
				METHOD_ENTRY(methodEntry[i])->isSynchronized = 0;
				METHOD_ENTRY(methodEntry[i])->isPrivate = 0;
				METHOD_ENTRY(methodEntry[i])->isProtected = 0;
				METHOD_ENTRY(methodEntry[i])->isPublic = 0;
				METHOD_ENTRY(methodEntry[i])->exceptions = NULL;

				
				/* Skip the method detail if in case the current class could not be found, the required variables were not cached or the class name starts with '<' */
				if (!currentClass || !isCachedVarsSet || isCachedVarsSet == 2 || METHOD_ENTRY(methodEntry[i])->methodData.method_name[0] == '<')
				{ goto SKIP_METHOD_DETAIL; }


				/* Allocate Space */
				exceptionNames[0] = '\0';

				/* Get the java representation of the method name for the current method */
				jmethodName = CREATE_JSTRING(event->env_id, METHOD_ENTRY(methodEntry[i])->methodData.method_name);

				/* Find the parameter type of the current method */
				jparamType = findParamType (event, METHOD_ENTRY(methodEntry[i])->methodData.method_signature, &exceptionOccurred);

				/* An Exception has occured while attempting to get the param type of the current method */
				if (jparamType == (int)NULL && exceptionOccurred) {
					CLEAR_EXCEPTIONS(event->env_id);
					goto SKIP_METHOD_DETAIL;
				}

				if (jmethodName)
				{ jcurrentMethod = CALL_OBJ_METHOD2(event->env_id, currentClass, getDeclaredMethodID, jmethodName, jparamType); }
				
				if (CHECK_EXCEPTION(event->env_id)) {
					CLEAR_EXCEPTIONS(event->env_id);
					goto SKIP_METHOD_DETAIL;
				}


				/* Get the method modifier */
				if (jcurrentMethod)
					{ jmethodModifier = ENV(event->env_id)->CallIntMethod(ENVPARM(event->env_id) jcurrentMethod, getModifiersID); }

			
				/* Note: The following seven JNI calls can be avoided by analyzing the modifier using the 'log' function
				 * with base two */

				/* Find the visibility of the method */				
				if (jmethodModifier)
				{
					if(ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isPublicID, jmethodModifier))
						{ METHOD_ENTRY(methodEntry[i])->isPublic=1;}
					else if(ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isPrivateID, jmethodModifier))
						{ METHOD_ENTRY(methodEntry[i])->isPrivate=1; }
					else if (ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isProtectedID, jmethodModifier))
						{ METHOD_ENTRY(methodEntry[i])->isProtected=1; }
				}


				/* Attempt to find if the method is native, abstract, static and/or synchronized */
				METHOD_ENTRY(methodEntry[i])->isNative = (ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isNativeID, jmethodModifier) ? 1 : 0);
				METHOD_ENTRY(methodEntry[i])->isAbstract = (ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isAbstractID, jmethodModifier) ? 1 : 0);
				METHOD_ENTRY(methodEntry[i])->isStatic = (ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isStaticID, jmethodModifier) ? 1 : 0);
				METHOD_ENTRY(methodEntry[i])->isSynchronized = (ENV(event->env_id)->CallStaticBooleanMethod(ENVPARM(event->env_id) modifierClass, isSynchronizedID, jmethodModifier) ? 1 : 0);


				/* Find the possible exceptions that the method can throw */
				if (jcurrentMethod)
				{
					jmethodExceptions = CALL_OBJ_METHOD0(event->env_id, jcurrentMethod, getExceptionTypesID);
					if (jmethodExceptions)
					{
						methodExceptions = (jobjectArray) jmethodExceptions;
						arrayLength = GET_ARRAY_SIZE(event->env_id, methodExceptions);

						/* Step through the exceptions */
						for (counter = 0; counter < arrayLength; counter++)
						{
							currentException = GET_ARRAY_ELEM(event->env_id, methodExceptions, counter);
							
							jexceptionName = CALL_OBJ_METHOD0(event->env_id, currentException, getNameID);
							exceptionName = (char*)CONVERT_TO_UTF(event->env_id, jexceptionName);
							if (strlen(exceptionNames) + strlen(exceptionName) >= MAX_EXCEPTION_LENGTH)
								break;
							if (strlen(exceptionNames) != 0)
								strcat (exceptionNames, ",");
							strcat (exceptionNames, exceptionName);
							RELEASE_UTF_CHARS(event->env_id, jexceptionName, exceptionName);
						}
						STRDUP(METHOD_ENTRY(methodEntry[i])->exceptions, exceptionNames);
					}
				}
			}
			
SKIP_METHOD_DETAIL:
			/* Enable events on the current thread */
			tps->disableEventsForThisThread=0;
			methods++; /* Skip to the next method */
		}
	}
	else {
		classEntry->methods=NULL;
	}
	classEntry->static_id = ++_staticIdCount;
	jvmpiAgent_getCurrentTime(&classEntry->timestamp);

	insertSymbol(&_classHashtable, hashEntry);

	if(!classObjectEntry) {
		/* Try and request our class object */
		REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, event->u.class_load.class_id); /* 232010 */
		classObjectEntry=jvmpiAgent_FindObjectSymbol(event->u.class_load.class_id);
	}

	/* If there is a class obejct for this class we need to make sure it knows it is a class object and
	   set the bidirectional reference between the two.
	*/
	if(classObjectEntry) {
		classEntry->classObject=classObjectEntry;
		OBJECT_ENTRY(classObjectEntry)->classObject=1;
		OBJECT_ENTRY(classObjectEntry)->classHashEntry2=hashEntry;
	}
	else {
		classEntry->classObject=0;
	}


	/* A.M: If the class load detail option is set, then find the class loader, the
	 * super class, and the interfaces implemented by this class entry.
	 * Note: This should be the last block of code (Because 'return' is used in case that
	 * an exception occurs) */
	if (_jvmpiAgent_Options.classLoadDetails)
	{
		char *className, *classLoaderName, *currInterface, *interfaceNames;
		jobject jclassLoader = (int)NULL, jSuperClass, jsuperClassName, jinterfaceName, obj = (int)NULL;
		jobjectArray interfacesArray;
		jsize count, index;
		unsigned const int MAX_CHARS = 250;
		int currLength = 0, counter = 0;


		/* Disable events on the current thread */
		tps->disableEventsForThisThread=1;

		/* Initialize the appropriate fields */
		classEntry->classLoaderName = "";
		classEntry->superClassName = "";
		classEntry->nameOfInterfaces = "";

		
		/* Skip class detail if the required variables could not be initialized or the currentClass was set properly */
		if (!isCachedVarsSet || isCachedVarsSet == 2 || !currentClass)
			{ goto SKIP_CLASS_DETAIL; }

		/* Allocate 250 characters for all the interface names */
		interfaceNames = (char *) calloc(MAX_CHARS, 1);
		className = classEntry->className;

		/* Get the class loader of this class entry (Equivalent to this.getClassLoader().getClass().getName()) */
		jclassLoader = CALL_OBJ_METHOD0(event->env_id, currentClass, getClassLoaderID);

		/* Get the name of the class loader */
		if (jclassLoader && !CHECK_EXCEPTION(event->env_id))
		{
			obj = CALL_OBJ_METHOD0(event->env_id, jclassLoader, getClassID);
				
			if (obj)
			{
				jobject tempObj;
				tempObj = CALL_OBJ_METHOD0(event->env_id, obj, getNameID);
				classLoaderName = (char*)CONVERT_TO_UTF(event->env_id, tempObj);
				classEntry->classLoaderName = classLoaderName;
			}

		}
		/* The previous JNI call may throw a SecurityException */
		else if (CHECK_EXCEPTION(event->env_id))
			{ CLEAR_EXCEPTIONS(event->env_id); }
		


		/* Get the super class */
		jSuperClass = CALL_OBJ_METHOD0(event->env_id, currentClass, getSuperclassID);
		
		if (jSuperClass)
		{
			if (jsuperClassName = CALL_OBJ_METHOD0(event->env_id, jSuperClass, getNameID))
				{ classEntry->superClassName = (char*)CONVERT_TO_UTF(event->env_id, jsuperClassName); }
		}


		/* Get the implemented interfaces */
		interfacesArray = (jobjectArray)CALL_OBJ_METHOD0(event->env_id, currentClass, getInterfacesID);

		if (interfacesArray)
		{
			count = GET_ARRAY_SIZE(event->env_id, interfacesArray);
			for (index = 0; index < count; index++)
			{
				/* Retreive the name of each element */
				if (obj = GET_ARRAY_ELEM(event->env_id, interfacesArray, index))
				{
					jinterfaceName = CALL_OBJ_METHOD0(event->env_id, obj, getNameID);
			
					/* Convert to UTF-8, and print the string.  Flush the buffer afterwards */
					currInterface = (char*)CONVERT_TO_UTF(event->env_id, jinterfaceName);

					if ((currLength + strlen(currInterface) + 2) > MAX_CHARS)
						{ break; }
					else
					{
						if (strlen(interfaceNames) != 0)
							strcat (interfaceNames, ",");
						strcat (interfaceNames, currInterface);
					}
					RELEASE_UTF_CHARS(event->env_id, jinterfaceName, currInterface);
				}
				classEntry->nameOfInterfaces = interfaceNames;
			}
		}
	}

SKIP_CLASS_DETAIL:
	/* Enable events on the current thread */
	tps->disableEventsForThisThread=0;

	return hashEntry;
}

/**
 * The following helper function is used to determine the parameter types of a method based on
 * its specified signature.
 *
 * Prerequisite: 'methodSignature' is a valid string (i.e. It contains a null terminator).
 *				 'methodSignature' is in the expected form of a method signature (i.e. (param)return).
 *
 * @param methodSignature - The signature of the method
 * @param event			  - The event leading to the invokation of this function
 * @param exceptionOccured- Nonzero if an exception did occured while attempting to find the parameter type
 * @return jobject - A Class[] representing the parameter types of the method
 */

jobjectArray findParamType(JVMPI_Event *event, char *methodSignature, int *exceptionOccurred)
{
	char successiveChar;
	unsigned int index, paramIndex, tmpStorageIndex, counter;

	jobjectArray jlangClassArray;
	jstring currClassName;
	jobject current_Class;


	/* Extract the prameter from the signature */
	for (index = 0; methodSignature[index] != ')' && index < strlen(methodSignature); index++);
	
	strncpy (parameters, methodSignature + 1, index);
	parameters[index - 1] = '\0';


	/* Store the individual parameters in the string array 'param' */
	if (!parameters || strlen(parameters) == 0)
	{
		return (int)NULL;
	}

	
	index = 0, paramIndex = 0, tmpStorageIndex = 0, counter = 0;


	while (parameters[index] != '\0')
	{
		param[paramIndex] = (char *) malloc (MAX_PARAM_NAME);
		switch (parameters[index])
		{
			/* Boolean */
			case 'Z':
				param[paramIndex] = ">java/lang/Boolean";
				paramIndex++;
				break;
			/* Byte */
			case 'B':
				param[paramIndex] = ">java/lang/Byte";
				paramIndex++;
				break;
			/* Char */
			case 'C':
				param[paramIndex] = ">java/lang/Character";
				paramIndex++;
				break;
			/* Short */
			case 'S':
				param[paramIndex] = ">java/lang/Short";
				paramIndex++;
				break;
			/* Int */
			case 'I':
				param[paramIndex] = ">java/lang/Integer";
				paramIndex++;
				break;
			/* Long */
			case 'J':
				param[paramIndex] = ">java/lang/Long";
				paramIndex++;
				break;
			/* Float */
			case 'F':
				param[paramIndex] = ">java/lang/Float";
				paramIndex++;
				break;
			/* Double */
			case 'D':
				param[paramIndex] = ">java/lang/Double";
				paramIndex++;
				break;
			/* Complex Object */
			case 'L':
				counter = 0;
				for (tmpStorageIndex = ++index; parameters[tmpStorageIndex] != ';'; tmpStorageIndex++)
				{
					/* Change the delimiter */
					if (parameters[tmpStorageIndex] == '/')
						tmpStorage[counter] = '.';
					else
						tmpStorage[counter] = parameters[tmpStorageIndex];
					counter++;
					index++;
				}
				tmpStorage[counter] = '\0';
				strcpy (param[paramIndex], tmpStorage);
				paramIndex++;
				break;
			/* Arrays */
			case '[':

				/* Multi-dimensional array */
				counter = 0;
				while (parameters[index] == '[')
				{
					tmpStorage[counter] = parameters[index];
					index++; counter++;
				}


				successiveChar = parameters[index];

				/* Primitive Array */
				if (successiveChar == 'Z' || successiveChar == 'B' || successiveChar == 'C' || successiveChar == 'S' ||
					successiveChar == 'I' || successiveChar == 'J' || successiveChar == 'F' || successiveChar == 'D')
				{
					tmpStorage[counter] = successiveChar; tmpStorage[counter + 1] = '\0';
				}
				/* Complex Array */
				else
				{
					for (tmpStorageIndex = index; parameters[tmpStorageIndex] != ';'; tmpStorageIndex++)
					{
						/* Change the delimiter */
						if (parameters[tmpStorageIndex] == '/')
							tmpStorage[counter] = '.';
						else
							tmpStorage[counter] = parameters[tmpStorageIndex];

						counter++;
						index++;
					}
					tmpStorage[counter] = parameters[tmpStorageIndex];
					tmpStorage[counter + 1] = '\0';
				}

				strcpy (param[paramIndex], tmpStorage);
				paramIndex++;
		}
		index++;

	}

	/* Create the java class array and its elements */
	jlangClassArray = NEW_ARRAY(event->env_id, paramIndex, javaClass, (int)NULL);
	
	/* Begin Creating the classes and adding them to the jlangClassArray */
	for (index = 0; index < paramIndex; index++)
	{
		current_Class = 0;

		/* If the parameter name begins with '>', then it is a primitive type */
		if	(param[index][0] == '>')
		{
			jclass currClass;
			jfieldID fieldID;

			if (currClass = FIND_CLASS(event->env_id, param[index] + 1))
				if (fieldID = GET_SFIELDID(event->env_id, currClass, "TYPE", "Ljava/lang/Class;"))
					current_Class = GET_SFIELD(event->env_id, currClass, fieldID);
			
		}

		/* The parameter is a complex type */
		else
		{
			/* Get the class representation of the current parameter */
			currClassName = CREATE_JSTRING(event->env_id, param[index]);
			current_Class = CALL_OBJ_SMETHOD1(event->env_id, javaClass, forNameID, currClassName);
		}

		/* Add the found class representation to the class array */
		if (current_Class)
		{	ADD_TO_ARRAY(event->env_id, jlangClassArray, index, current_Class); }
		else
		{	/* Something has gone wrong.  Abort */
			*exceptionOccurred = 1; jlangClassArray = 0; break;
		}
	}


	return jlangClassArray;

}


/* Used to swap the delimiter of 'name' from '.' to '\' or vice-versa */
void swapDelimiters (char *name)
{
	int i;
	for (i = 0; i < (int)strlen (name); i++)
	{
		if (name[i] == '.')
			name[i] = '/';
		else if (name[i] == '/')
			name[i] = '.';
	}
}


#ifdef _DEBUG

unsigned int jvmpiAgent_getHashTableEntryCount(enum EntryType entryType)
{
 switch(entryType)
 {
  case Class_t:  return _classHashtable.entryCount; break;
  case Method_t: return _methodHashtable.entryCount; break;
  case Object_t: return _objectHashtable.entryCount; break;
  case Thread_t: return _threadHashtable.entryCount; break;
  default: return 0;
 }

}

void jvmpiAgent_DumpHashTableStatistics()
{

 int j;
 FILE * f;
 f = fopen("heapStatistics.txt", "w");
 for(j=0; j<4; j++)
 {
  unsigned int i;
  Hashtable *table;
  int emptyChains = 0;
  int totalEntries = 0;
  int longestChain = 0;
  int averageChain = 0;
  switch(j)
  {
   case 0:table=&_classHashtable; fprintf(f,"\nCLASS "); break;
   case 1:table=&_methodHashtable; fprintf(f,"\nMETHOD "); break;
   case 2:table=&_objectHashtable; fprintf(f,"\nOBJECT "); break;
   case 3:table=&_threadHashtable; fprintf(f,"\nTHREAD "); break;
  }
  for (i = 0; i < table->size; i++)
  {
   if (table->entries[i].activeQ == NULL)
   {
    emptyChains++;
   }
   else
   {
    int depth = 0;
    volatile HashEntry *p;
    for (p = table->entries[i].activeQ; p != NULL; p = p->next)
	{
     depth++;
     totalEntries++;
    }
    if (depth > longestChain)
	{
     longestChain = depth;
	}
   }
  }
  fprintf(f,"Hash Table Statistics\n\n");
  fprintf(f,"Hash Table Size = %d\n", table->size);
  fprintf(f,"Empty chains = %d\n", emptyChains);
  fprintf(f,"Total entries = %d\n", totalEntries);
  fprintf(f,"Average chain depth = %f\n", totalEntries/(double)table->size);
  fprintf(f,"Longest chain depth = %d\n", longestChain);
  fprintf(f,"Total number of Symbol Lookups = %d\n", table->findSymbolCount);
  fprintf(f,"Number of successful symbol lookups = %d\n", table->findSymbolFound);
  fprintf(f,"Number of unsuccessful symbol lookups = %d\n", table->findSymbolNotFound);
  fprintf(f,"Number of hash hits = %d (%f%%)\n", table->hashHitCount, table->hashHitCount/(double)table->findSymbolCount * 100);
  fprintf(f,"Number of hash misses = %d (%f%%)\n", table->hashMissCount, table->hashMissCount/(double)table->findSymbolCount * 100);
  fprintf(f,"Average miss length = %f\n", (table)->hashMissCount ? table->hashMissLength/(double)table->hashMissCount: (double)0);
  fprintf(f,"Total Created Symbols = %d\n", table->createCount);
  fprintf(f,"Total Deleted Symbols = %d\n", table->deleteCount);
  fprintf(f,"Total Moved Symbols = %d\n", table->moveCount);
  fprintf(f,"\n\n");
 }
 fclose(f);
}
#endif

