/**********************************************************************
 * 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: JvmpiWriter.c,v 1.8 2009/08/26 14:59:53 jwest Exp $
 *
 * Contributors:
 * IBM - Initial API and implementation
 **********************************************************************/

#include <ctype.h>
#include <stddef.h>
#include <time.h>
#include <sys/types.h>
#include <jvmpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <jni.h>
#ifndef __OS400__
#include <sys/timeb.h>
#endif
#include <sys/types.h>
#include <time.h>
#include "eventmask.h"
#include "hash.h"
#include "JvmpiWriter.h"
#include "piAgentExtension.h"
#include "options.h"
#include "filters.h"
#include "RABindings.h"
#include "JVMPIException.h"
#ifndef __OS400__
#endif
#include "RASocket.h"
#include "print.h"
#include "performance.h"
#include "strings.h"
#include "utility.h"

#include "StatelessHeapSnapshotManager_C.h"

#if defined __cplusplus && defined _HPUX
 #define ENV(e) e
 #define JOBJECT jclass
#else
 #define ENV(e) (*e)
 #define JOBJECT jobject
#endif

#ifdef _SHOOTDEBUG
 #include "agentDebug.h"
#endif

/*
 * A note about agent extensions:
 *
 * Allan Pratt of the Rational software division of IBM added a feature
 * to this agent on March 31, 2004: a simple extensibility system
 * that lets people write agent extensions. Instructions for building an agent
 * extension can be found in piAgentExtension.h.
 *
 * The system is quite limited: it doesn't solve any of the hard problems
 * related to sending JVMPI events to multiple agents. It is barely adequate
 * for an agent extension that wants JVM_INIT_DONE and CLASS_LOAD_HOOK,
 * which is all we need it for right now.
 *
 * Every code segment related to this extension system has the
 * words "agent extension" in a comment nearby.
 */

/**
  * MACROS
  *
  */
/* following two undefs were added to avoid conflict from new java.h defs ... 135623 */
#undef ATTACH_THREAD
#undef DETACH_THREAD

#ifdef _WIN32
 #define MODULE_REFERENCE HMODULE
 #define DLL_REFERENCE HINSTANCE
 #define DLL_NAME "jvm"
 #define RESOLVE_MODULE(name) GetModuleHandle(name)
 #define LOAD_LIBRARY(name) LoadLibrary(name)
 #define RESOLVE_ENTRY_POINT(mod, entry) GetProcAddress(mod, entry)
 #define ATTACH_THREAD(env) (*_jvmpiAgent_jvm)->AttachCurrentThread(_jvmpiAgent_jvm,  (void**)&env, NULL)
 #define DETACH_THREAD() (*_jvmpiAgent_jvm)->DetachCurrentThread(_jvmpiAgent_jvm)
 #ifndef CDECL
   #define CDECL __cdecl
 #endif
#elif MVS
 #include <dll.h>
 #include <dlfcn.h>
 #define MODULE_REFERENCE dllhandle *
 #define DLL_REFERENCE dllhandle *
 #define DLL_NAME "libjava.so"
 #define RESOLVE_MODULE(name) dlopen(name, RTLD_LAZY)
 #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY)
 #define RESOLVE_ENTRY_POINT(mod, entry) dlsym(mod, entry)
// old: #define RESOLVE_MODULE(name) dllload(name)
// old: #define LOAD_LIBRARY(name) dllload(name)
// old: #define RESOLVE_ENTRY_POINT(mod, entry) dllqueryfn(mod, entry)
 #define ATTACH_THREAD(env) (*_jvmpiAgent_jvm)->AttachCurrentThread(_jvmpiAgent_jvm,  (void**)&env, NULL)
 #define DETACH_THREAD() (*_jvmpiAgent_jvm)->DetachCurrentThread(_jvmpiAgent_jvm)
 #ifndef CDECL
  #define CDECL
 #endif
#elif _HPUX
 #include <dlfcn.h>
 #define MODULE_REFERENCE void *
 #define DLL_REFERENCE void *
 #define DLL_NAME "libjvm.sl"
 #define RESOLVE_MODULE(name) dlopen(name, RTLD_LAZY)
 #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY)
 #define RESOLVE_ENTRY_POINT(mod, entry) dlsym(mod, entry)
 #define ATTACH_THREAD(env) (_jvmpiAgent_jvm)->AttachCurrentThread((void**)&env, NULL)
 #define DETACH_THREAD() (_jvmpiAgent_jvm)->DetachCurrentThread()
 #ifndef CDECL
  #define CDECL
 #endif
#elif __OS400__
 #define MODULE_REFERENCE void *
 #define DLL_REFERENCE void *
 #define DLL_NAME "libjvm"
 #define RESOLVE_MODULE(name) loadServicePgm(name)
 #define LOAD_LIBRARY(name) loadServicePgm(name)
 #define RESOLVE_ENTRY_POINT(mod, entry) findServicePgmEntry(mod, entry)
 #define ATTACH_THREAD(env) (*_jvmpiAgent_jvm)->AttachCurrentThread(_jvmpiAgent_jvm,  (void**)&env, NULL)
 #define DETACH_THREAD() (*_jvmpiAgent_jvm)->DetachCurrentThread(_jvmpiAgent_jvm)
 #ifndef CDECL
  #define CDECL
 #endif
#else
 #include <dlfcn.h>
 #define MODULE_REFERENCE void *
 #define DLL_REFERENCE void *
 #define DLL_NAME "libjvm.so"
 #define RESOLVE_MODULE(name) dlopen(name, RTLD_LAZY)
 #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY)
 #define RESOLVE_ENTRY_POINT(mod, entry) dlsym(mod, entry)
 #define ATTACH_THREAD(env) (*_jvmpiAgent_jvm)->AttachCurrentThread(_jvmpiAgent_jvm,  (void**)&env, NULL)
 #define DETACH_THREAD() (*_jvmpiAgent_jvm)->DetachCurrentThread(_jvmpiAgent_jvm)
 #ifndef CDECL
  #define CDECL
 #endif
#endif

#define IBM_VENDOR_STRING "IBM"
#define IBM_VENDOR_STRING_LENGTH 3
#define SUN_VENDOR_STRING "Sun"
#define SUN_VENDOR_STRING_LENGTH 3
/* we only use the first 3 characters in HP's vendor string "Hewlett-Packard Co." */ 
#define HP_VENDOR_STRING "Hew"
#define HP_VENDOR_STRING_LENGTH 3 

#define org_eclipse_hyades_collection_profiler_Profiler_PROFILER_EXECUTION_ONLY 1L
#define org_eclipse_hyades_collection_profiler_Profiler_PROFILER_HEAP_ONLY 2L
#define org_eclipse_hyades_collection_profiler_Profiler_PROFILER_EXECUTION_AND_HEAP 3L
#define org_eclipse_hyades_collection_profiler_Profiler_PROFILER_OPTIMIZED_HEAP_ONLY 4L
#define org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_SUCCESS 0L
#define org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_SUSPENDED_IO 1L
#define org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_FAIL 2L

/**
  * TYPEDEFS
  */

/* TraceON - turn on tracing
   TracePause - Pause tracing (keeping in mind that hash tables are not scrubbed)
   TraceOFF - Turn off tracing completely 
*/ 
enum TraceToggleType {TraceON,TracePause,TraceOFF};
typedef enum TraceToggleType TraceToggle;   

/* This is the signature declaration for all the event handlers from JVMPI. With a common
   signature we can load the functions into an array and random access the proper
   handler based upon the event type.
*/
typedef void (*ProcessEventFunction)(JVMPI_Event *event, ThreadPrivateStorage *tps, BOOL isRequested, timestamp_t timestamp, timestamp_t cpu_timestamp);

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
void *prevMthdTps=NULL;
int  meCount=0;  /* methodEntryEvents count */
int  mxCount=0;  /* methodExitEvents count*/

void dumpMethodEventCounts(void) {/* dump MethodEvent counts */
	if (meCount>0 || mxCount>0) {
		printf("Method entries/exits on tps %8x = %5d/%5d\n", (void *)prevMthdTps, meCount, mxCount); fflush(stdout); 	
	}	
	meCount=mxCount=0;
}	
#endif

/* For pre-aggregation */
/* The threadsRoot list is a list of all "live" threads.
   I believe you could reach all the same elements using the thread hash table.
   This is an optimization opportunity.
 */
struct ThreadListElement {
	ThreadPrivateStorage * data;
	struct ThreadListElement * next;
	struct ThreadListElement * prev;
};

struct ThreadListElement * threadsRoot = 0;

/*
 * Locks for pre-aggregation logic.
 *
 * threadListLock is used when you are reading or writing the list starting at threadsRoot.
 *
 * stackFrameStructureLock is used when you are changing (but not just reading) 
 * the "next" and "child" pointers of any StackFrame object. This might seem unnecessary
 * since each thread has its own rooted StackFrame structure, but the killer is dumping:
 * during a dump, one thread reads all StackFrame structures, and having other threads
 * running and updating their "next" pointers could crash the dump.
 *
 * Technically, this lock could also be acquired when you update the field values
 * in any StackFrame, since the dump could see inconsistent data if it visits a node
 * at the same time you're updating it. But I don't want to do that - it's too expensive
 * to acquire this on every method entry and exit event.
 *
 * There is a no-lock alternative to stackFrameStructureLock: in the dumping logic,
 * use JNI to suspend all other Java threads. I think this will suspend them in Java,
 * not in the middle of a JVMPI event handler, so once they're all suspended the
 * thread doing the dumping will know it's safe. If you implement this, you can remove
 * all references to stackFrameStructureLock.
 *
 * Here's another no-lock trick: use the garbage collector instead. [Credit to Victor Havin 
 * for this one.] If you want to do a dump, you use JNI to cause a GC. In the handler for 
 * the "GC Started" notification from JVMPI, that's where you actually do the dump. 
 * When you're in that handler, you know that all other Java threads are suspended in 
 * safe states - definitely not executing handlers for other JVMPI events.
 */

static ra_critsec_t * stackFrameStructureLock = 0;
static ra_critsec_t * threadListLock = 0;

/* For pre-aggregation */
void getStackFrameStructureLock() {
	if (stackFrameStructureLock == 0 ) {
		stackFrameStructureLock = (ra_critsec_t *)jvmpiAgent_Calloc(sizeof(ra_critsec_t));
		ra_mutexCreate(stackFrameStructureLock);
	}

	ra_mutexEnter(stackFrameStructureLock);
}

/* For pre-aggregation */
void releaseStackFrameStructureLock() {
	if( stackFrameStructureLock == 0 ) {
		fprintf(stdout, "Error!! stackFrameStructureLock is NULL " );
		fflush(stdout);
	}
	ra_mutexExit(stackFrameStructureLock);
}


/* For pre-aggregation */
void getThreadListLock() {
	if (threadListLock == 0 ) {
		threadListLock = (ra_critsec_t *)jvmpiAgent_Calloc(sizeof(ra_critsec_t));
		ra_mutexCreate(threadListLock);
	}

	ra_mutexEnter(threadListLock);
}

/* For pre-aggregation */
void releaseThreadListLock() {
	if( threadListLock == 0 ) {
		fprintf(stdout, "Error!! threadListLock is NULL " );
		fflush(stdout);
	}
	ra_mutexExit(threadListLock);
}

static BOOL                   _traceResourcesGone = FALSE; /* Trace Hash Tables were cleaned up * pre-agg: 134635 */

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

/**
  * File Scope Variable Definitions
  */
static Jobject2jobjectID      _jobject2jobjectID = 0;   /* Function pointer to JVM interface function that converts JNI object handles to
																	  JVMPI object identifiers */
#ifdef __OS400__
#pragma convert(819)
#endif
static char	                  _heapDefName[8]	= "default";
#ifdef __OS400__
#pragma convert(0)
#endif 


static BOOL                   _jvmShutDown = FALSE;              /* JVM is in shut down mode*/
static BOOL                   _jvmpiAgent_isJVMInitDone=FALSE;   /* JVM has finished initialization */
static BOOL					  _jvmpiAgent_isListenerUnblocked=FALSE;	 /* Has ra_startListener returned? */
static BOOL                   _jvmpiAgent_isMonitored=FALSE;     /* Are we currently monitored?  */
static BOOL                   _jvmpiAgent_isSuspended=FALSE;     /* Are we currently suspended? */
static BOOL                   _jvmpiAgent_burstTimeoutSet=FALSE; /* Is our timout value set? */

/* In order to limit tracing to a single thread these variables are used */
static JNIEnv               *_jvmpiAgent_limitingThread=NULL;
static BOOL                  _jvmpiAgent_singleThreaded=FALSE;

static unsigned int          _invocationCountRemaining=0;        /* Used in burst tracing to count invocations */
static timestamp_t           _burstTimeout;
static BOOL                  _triggerSqueezed=FALSE;               /* Used in trigger tracing to indicate we have started */

static BOOL                  _stackSamplerActive=FALSE;          /* Stack sampler shutoff switch */

static BOOL                  _xmlHeadersPrinted=FALSE;

/* Piyush Agarwal */
static BOOL					 _analyseOptHeap = FALSE ; /* To determine if the analyse
																 message came from the RAC */
static int                   _optHeapError = 0 ;      /*58049 Handle Error if OptHeap files
													          cannot be written */
static int					 _unknownClassSuffix = 1;
static char					 _classNameBuffer[64]; /* "unknown" + "int" */
													          
/* Giri: <Defect 64462> */
static BOOL					_optHeapSetupDone = FALSE ; /* For tracking status of optHeap setup */
/*Bug 82214*/
static int optHeapContextId = -1 ;
													          
/*Bug 59544 SetConfig changes for opt heap dump location*/
/* Bug 64476  */
#ifdef MVS
#pragma convlit(suspend)
#endif
static char                    _tempDirOptionName[21] = "LOCAL_AGENT_TEMP_DIR" ;  
#ifdef MVS
#pragma convlit(resume)
#endif

/* Array of function pointers to the JVMPI event handling functions */
static ProcessEventFunction _processEventFunction[JVMPI_MAX_EVENT_TYPE_VAL + 1]; /* The defect number is 174948. */

/* Array of function pointers for our agent extension; the signature is slightly different */
static AgentExtensionEventHandler agent_extension_handlers[JVMPI_MAX_EVENT_TYPE_VAL + 1];

/* Pointer to handler function for the agent extension to handle RAC commands. */
/* Remains null unless an extension extension utilizes it. */
static void (*agent_extension_command_handler)(ra_command_t *command);

/**
  * FUNCTION PROTOTYPES
  */
static int enableJvmpiEvent(jint event_type,
                            ProcessEventFunction eventHandler);

static void processObjAllocEvent(JVMPI_Event *event,
								 ThreadPrivateStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp, 
								 timestamp_t cpu_timestamp);

static void processGcStartEvent(JVMPI_Event *event,
								ThreadPrivateStorage *tps,
                                BOOL isRequested,
								timestamp_t timestamp, 
								 timestamp_t cpu_timestamp);

static void processGcFinishEvent(JVMPI_Event *event,
								 ThreadPrivateStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp, 
								 timestamp_t cpu_timestamp);

static void processCountingMethodEntryEvent(JVMPI_Event *event, 
											ThreadLocalStorage *tps, 
											BOOL isRequested, 
											timestamp_t timestamp,
											timestamp_t cpu_timestamp); 

static void toggleActiveJvmpiEvents(TraceToggle toggle);

static void cleanUpAllTraceResources();

static void loadStack(ThreadPrivateStorage *tps);

static void startTracing(BOOL standalone);

static void suspendTracing(TraceToggle toggle);

static void resumeTracing();

static void stopTracing();

static void jvmpiAgent_PrintStartingXMLFragments();

static void setNowTimes(ThreadLocalStorage * tps, timestamp_t * nowTime_P, timestamp_t * nowCpuTime_P);

static void recordAgMethodEntry(ThreadLocalStorage * tps, StackEntry * stackEntry, StackEntry * caller);

/* Piyush Agarwal  */
static void sendHeapDumpInformation(char *filename) ;
static void sendHeapDumpEndMessage(RA_AGENT_HANDLE handle,ra_uint_t contextId,char* filename) ;
static void processRACDataDumpRequest(BOOL finalheap,ra_uint_t contextId) ;
/* Added Call Back Message */
OptHeapCallBack  _jvmpiAgent_optHeapCallBack = { sendHeapDumpInformation };

/* functions for agent extensions to call to enable events and command listening */
static void agentExtensionSetEventHandler(jint event_type,
									AgentExtensionEventHandler handler);
static void agentExtensionSetCommandHandler(void (*handler)(ra_command_t *command));


static void sendOptHeapErrorMessage(RA_AGENT_HANDLE handle,ra_uint_t contextId) ; /* Piyush 58049 */

/* Giri: Defect 64462 For performing opt heap related setup */
static void jvmpiAgent_DoOptHeapSetup();

/**
  * GLOBALS
  */
jclass                        _jvmpiAgent_JVMPIException = 0;      /* JNI hook to JVMPIException class */
jmethodID                     _jvmpiAgent_setExceptionTracing = 0; /* JNI hook to JVMPIException.setExceptionTracing method */
JavaVM *                      _jvmpiAgent_jvm = 0;                 /* JVM reference (established in JVM_OnLoad) */
JVMPI_Interface *             _jvmpiAgent_jvmpiInterface = 0;      /* JVMPI Interface pointer (established in JVM_OnLoad */
Lock_t                        _jvmpiAgent_synchLock;
char *                        _jvmpiAgent_trace_id;
SegmentedValue_t              _jvmpiAgent_collation;
GenerationInfo_t              _heapDumpInfo;						/* GENERATION Index for Object reference*/
RA_AGENT_HANDLE               _jvmpiAgent_bindingStorage;
char                          _setPathDelimiter = 0;
jobjectID					  _resolvedClassId = 0;					/* Used to store the class id for the associated class of an object being dumped */
enum JavaVendor				  _javaVendor = VendorOther; 
enum JavaVersion			  _javaVersion = VersionOther; 


/** ANALYSE_HEAP  **************************************************************
  * This is the function that must be called to request a heap dump.
  *
  */
jint analyseHeap(jint type) {
    jint result;
	JVMPI_HeapDumpArg arg;


	/* RKD:  When we are stack sampling or we have been told to ignore heap information
	         we don't want to be processing heap dump requests
	 */
	if(_jvmpiAgent_Options.mode==TraceModeStackSampling  || !jvmpiAgent_isTracingHeap()) {
		return -1;
	}

	/* If we get a heap dump level 0 we are marking the heap and we
	   mark the generation to be zero.
	   RKD:  Should we scrub the table here?
	*/
	if(type==JVMPI_DUMP_LEVEL_0) {
		_heapDumpInfo.index = 0;
	}
	/* Bug Number 71068*/
	/*else {
		_heapDumpInfo.index++;

	}*/

	/* To make sure that analyseheap is being called from TraceOptHeap methods only */
	if ( _jvmpiAgent_Options.mode == TraceOptimizedHeap && _analyseOptHeap == FALSE ) {
		/* Bug Number 71043 required for AS400 */
		if ( type == JVMPI_DUMP_LEVEL_0 ) 
			jvmpiAgent_getCurrentTime(&_heapDumpInfo.lastGenerationTime);
		return (jint)0 ;
	}

	/* Bug Number 71068*/
	if(type != JVMPI_DUMP_LEVEL_0) {
		_heapDumpInfo.index++;
	}

	arg.heap_dump_level=type;

	/* Because this can be invoked via JNI we need to check to
	   ensure we have the interface.  Someone may invoke the
	   dump but JVMPI may not be enabled.
	*/
	
	if(_jvmpiAgent_jvmpiInterface) {
		result = REQUEST_EVENT(JVMPI_EVENT_HEAP_DUMP, &arg);
	}

	/* Save the time we finished the dump at */
	jvmpiAgent_getCurrentTime(&_heapDumpInfo.lastGenerationTime);

	return result;
}


jint runGC() {
		jint result;
		JVMPI_HeapDumpArg arg;
		ThreadPrivateStorage *tps=jvmpiAgent_getThreadLocalStorage(0);	
		tps->scrubbingHeap=1;
		
		/* Scrub the heap */
		arg.heap_dump_level=JVMPI_DUMP_LEVEL_0;
		if(_jvmpiAgent_jvmpiInterface) {
			result = REQUEST_EVENT(JVMPI_EVENT_HEAP_DUMP, &arg);
		}

		tps->scrubbingHeap=0;
		return result;
}


/* bugzilla 71388 start - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
extern "C" {
#endif


/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    initialize0
 * Signature: ()V
 */
JNIEXPORT jint JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_initialize0(JNIEnv *env,
                                                                                      JOBJECT obj) {

	/* We just need to check if we are running in application mode */
	if(_jvmpiAgent_jvm && _jvmpiAgent_Options.application) {
		return (jint)0;
	}
	else {
		return (jint)-1;
	}
}

																					  /*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    setMode0
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_setMode0
(JNIEnv *env, jobject obj, jint mode) {

	/* call to OPTIMIZED HEAP DUMP CODE for method setMode0() goes here */
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    startProfiling0
 * Signature: (ZZ)I
 */
JNIEXPORT jint JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_startProfiling0(JNIEnv *env,
                                                                                          JOBJECT obj,
                                                                                          jboolean currentThread,
																						  jint boundaryDepth) {

	if(_jvmpiAgent_jvm) {
		if(currentThread) {
			_jvmpiAgent_singleThreaded=TRUE;
			_jvmpiAgent_limitingThread=env;
		}
		else {
			_jvmpiAgent_singleThreaded=FALSE;
		}


		/* If we have a boundary depth we are tracing in boundary and contiguous mode.  Otherwise we
		   need to reset the stack info back to the orignal state in case this is a subsequent call
		   to start profiling.
		*/
		if(boundaryDepth>0) {
			_jvmpiAgent_Options.stackInfo=StackInfoBoundaryAndContiguous;
			_jvmpiAgent_Options.boundaryDepth=(unsigned short)boundaryDepth;
		}
		else {
			_jvmpiAgent_Options.stackInfo=StackInfoNormal;
			_jvmpiAgent_Options.boundaryDepth=0;
		}

		/* If someone is attached we start tracing now, otherwise we ignore this call */
		if(_jvmpiAgent_isMonitored) {
			if(_jvmpiAgent_isSuspended) {
				resumeTracing();

			}
			else {
				startTracing(TRUE);
			}

			/* Squeeze the trigger */
			_triggerSqueezed=TRUE;

			return (jint)0;

		}
	}
	return (jint)-1;
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    stopProfiling0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_stopProfiling0(JNIEnv *env,
                                                                                         JOBJECT obj) {

	if(_jvmpiAgent_jvm) {
		suspendTracing(TracePause);
		_jvmpiAgent_limitingThread=NULL;
	}
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    markHeap0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_markHeap0(JNIEnv *env,
                                                                                    JOBJECT obj) {
	if(_jvmpiAgent_jvm) {
		analyseHeap(JVMPI_DUMP_LEVEL_0);
	}
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    analyzeHeap0
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_analyzeHeap0(JNIEnv *env,
                                                                                       JOBJECT obj,
                                                                                       jstring name) {
	if(_jvmpiAgent_jvm) {
		analyseHeap(JVMPI_DUMP_LEVEL_1);
	}
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    optimizedHeapDump0
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_optimizedHeapDump0
(JNIEnv *env, jobject obj) {
	
	/* call to OPTIMIZED HEAP DUMP CODE for method optimizedHeapDump0() goes here */
	processRACDataDumpRequest(FALSE,0) ;
	return (jstring)0;

}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    stopOptimizedHeapInfoSnapshot0
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_stopOptimizedHeapInfoSnapshot0
(JNIEnv *env, jobject obj) {


	/* call to OPTIMIZED HEAP DUMP CODE for method stopOptimizedHeapInfoSnapshot0()
	   goes here */
		processRACDataDumpRequest(TRUE,0) ;
        return (jstring)0; 	

}


/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    disableGC0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_disableGC0
(JNIEnv *env, jobject obj) {

	if (_jvmpiAgent_jvmpiInterface) {
		_jvmpiAgent_jvmpiInterface->DisableGC();
	}
	

}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    enableGC0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_enableGC0
(JNIEnv *env, jobject obj) {

	if (_jvmpiAgent_jvmpiInterface) {
		_jvmpiAgent_jvmpiInterface->EnableGC();
	}

}


/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    runGC0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_runGC0(JNIEnv *env,
																				 JOBJECT obj) {
	if(_jvmpiAgent_jvm) {
		runGC();
	}
}
																				

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    release0
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_release0
(JNIEnv *env, jobject obj) {

	/* RJD: currently the following is the body of the cleanupAndExit method. Other
	  cleanup may be necessary -- need to discuss */

	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);

	/* If the application is logging to a file */
	if (_jvmpiAgent_outFile) {
		jvmpiAgent_printStandaloneTraceTagClose(tps);
		jvmpiAgent_cleanupStandaloneIO();
	} else  if (_jvmpiAgent_Options.application) {  /*94955*/
		jvmpiAgent_printStandaloneTraceTagClose(tps);		 
	} 	

	/* If this application is being controlled/moitored remotely */
	if (!_jvmpiAgent_Options.standalone) {

		/* Wait until all the data is sent over the wire if there is a data connection */
		if(!_jvmpiAgent_suspendIO)  {
			if(_jvmpiAgent_Options.targetHdl.dtarget==RA_SOCKET && _jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD>-1) {
				ra_closeSocket(_jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD);
			}
			else if (_jvmpiAgent_Options.targetHdl.dtarget == RA_SHAREDMEMORY) {
				ra_stopFlushingShm(&_jvmpiAgent_Options.targetHdl.dtargetHdl.shmHdl); /* 175248 */
			}

			_jvmpiAgent_suspendIO=1;
		}
		ra_stopListener(_jvmpiAgent_bindingStorage);
	}

	

}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    emitXMLFragment0
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_emitXMLFragment0
(JNIEnv *env, jclass clz, jbyteArray arr, jint offset, jint len) {
	char *carr; /* note that jbyte * is a signed char* on most platforms */ 
	char *outputBuffer; 
#ifdef __OS400__
	char *tmp, *tmp2; /* temporary storage used only for AS/400 */ 
#endif 
	jboolean iscopy;
	ThreadLocalStorage *tps; 
	iscopy = JNI_FALSE; 

	if (_jvmpiAgent_suspendIO) {
		return org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_SUSPENDED_IO;
	}
	
	carr = (char *) ENV(env)->GetByteArrayElements(ENVPARM(env) arr,&iscopy);

	if (carr == NULL) {
		/* NULL is returned by GetByteArrayElements only when an OutOfMemoryError occurs */ 
		return org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_FAIL; 
	}

	/* get the thread private storage */ 
	tps=jvmpiAgent_getThreadLocalStorage(env);
	
	if (tps == 0) {
		tps = jvmpiAgent_getThreadLocalStorage(0); 
	}

	/* Ensure that the output buffer is large enough. Note, the jvmpiAgent_print
	   method adds a '\n', and on zOS a '\0' to the buffer. We take this into account
	   when sizing the buffer.  */ 
	if (len > (ORIGINAL_MESSAGE_BUFFER_SIZE - 2)) {
		outputBuffer = (char *)ra_allocateMessageBlock((unsigned short)len+2);
#ifdef __OS400__
		/* we need to increase the size of the etoa scratch buffer */ 
		tmp = tps->buffer2; /* remember the normal buffer */ 
		tps->buffer2 = ra_allocateMessageBlock((unsigned short)len+2); 
#endif 
		if (!outputBuffer) {
			return org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_FAIL; 
		}
	}
	else {
		outputBuffer = tps->buffer; 
	}

#ifdef __OS400__
	/* on AS400 we assume the passed in string is in UTF-8. Convert it to EBCDIC before
	   putting it into the output buffer.  */ 

	/* this is ugly, but it's required: the as400_atoe routines require a null terminated string, 
	and that the 'from' buffer is not the same as the 'to' buffer */ 
	tmp2 = ra_malloc(sizeof(char)*(len+1)); 
	if (!tmp2) {
		return org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_FAIL; 
	}
	memcpy(tmp2,&carr[offset],len); 
	tmp2[len] = '\0';
	outputBuffer = as400_atoe_tobuffer(tmp2,outputBuffer); 
	ra_free(tmp2); 
#else
	/* copy the XML fragment into the tps->buffer for printing */ 
	memcpy(outputBuffer,&carr[offset],len); 
#endif

	jvmpiAgent_print(tps,outputBuffer,(unsigned short) len); 

	if (tps->buffer != outputBuffer) {
		ra_freeMessageBlock((unsigned char *)outputBuffer); 
#ifdef __OS400__
		ra_freeMessageBlock((unsigned char *)tps->buffer2); 
		tps->buffer2 = tmp; /* restore the normal buffer */ 
#endif 
	}

	ENV(env)->ReleaseByteArrayElements(ENVPARM(env) arr, (signed char *) carr, JNI_ABORT);

	return org_eclipse_hyades_collection_profiler_Profiler_EMIT_XML_SUCCESS; 

}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    getCurrentTime0()
 * Signature: ()D
 */
JNIEXPORT jdouble JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_getCurrentTime0
(JNIEnv *env, jclass clz) {
	timestamp_t timestamp;

	jvmpiAgent_getCurrentTime(&timestamp);
	return ticksToTime2(timestamp, TRUE);
}

/*
 * Class:     org_eclipse_hyades_collection_profiler_Profiler
 * Method:    getCurrentThreadCpuTime0()
 * Signature: ()D
 */
JNIEXPORT jdouble JNICALL Java_org_eclipse_hyades_collection_profiler_Profiler_getCurrentThreadCpuTime0
(JNIEnv *env, jclass clz) {
	timestamp_t cpu_timestamp; 

	cpu_timestamp = jvmpiAgent_getCurrentThreadCPUTime(); 
	return ((jdouble)
#ifdef WIN32
		(__int64)
#endif 		
		cpu_timestamp)/1000000000; /* nanoseconds but very fat grain, about 10 milliseconds!!! */
}


/* bugzilla 71388 end - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
}
#endif



#ifndef __OS400__
/* Resolve to the jobject2jobjectID interface */
static jobjectID resolveJobject2jobjectID(jthrowable e, JNIEnv *env)
{
 static resolved = 0;
 if (_jobject2jobjectID)
 {
  return _jobject2jobjectID(e);
 }
 else if ( resolved )
 {
  return 0;
 }
 else
 {
  resolved = 1;
  /* jobject2jobjectID         IBM JDK 1.2.2	IBM JDK1.3	Sun JDK 1.2.2 Classic	Sun JDK 1.3 Classic	 Sun JDK 1.3 Hotspot
     JVM.dll entry point	   not available	yes	        yes	                    yes	                 no
     JVMPI interface pointer   not available	yes	        no	                    no	                 yes - but no header
  */
  /* First attempt to resolve the entry point directly from the JVM dll */
  _jobject2jobjectID = (Jobject2jobjectID)RESOLVE_ENTRY_POINT(RESOLVE_MODULE(DLL_NAME), "jobject2jobjectID");

  if (!_jobject2jobjectID)
  {
   /* We couldn't find the entry point exported from the JVM dll so now we check the JVM version
      to see if it is safe to retreive the entry point from the JVMPI interface structure.  We assume
      that any JVM above version 2.1 should support this interface... */
#if defined __cplusplus && defined _HPUX
   jint version = ENV(env)->GetVersion();
#else
   jint version = (*env)->GetVersion(env);
#endif
   unsigned short *p = (unsigned short *)&version;
   unsigned short major = *p;
   unsigned short minor = *(p+1);
   if (major >= 2 && minor >= 1)
   {
#ifndef _HPUX
    _jobject2jobjectID = _jvmpiAgent_jvmpiInterface->jobject2jobjectID;
#endif
   }
  }
  return _jobject2jobjectID ? _jobject2jobjectID(e) : 0;
 }
}
#endif /* __OS400__ */


/** GET_THREAD_LOCAL_STORAGE  *************************************************
  * This function returns the address of the thread local storage for the
  * specified JNIEnv (aka thread).  A special thread local storage structure is
  * allocated for static references and is returned when the JNIEnv parameter is
  * 0 or if the JVM is in Garbage Collection or Shutdown mode. Garbage Collection
  * and Shutdown mode must be handled in this way because some versions of the
  * JVM use special threads to do this processing which cause the
  * GetThreadLocalStorage interface function to trap.
  */
ThreadPrivateStorage *jvmpiAgent_getThreadLocalStorage(JNIEnv *env_id) {
	HashEntry *hashEntry = jvmpiAgent_FindThreadSymbol(env_id);
	if (hashEntry) {
		if (!hashEntry->printed && env_id && THREAD_ENTRY(hashEntry)->threadStartEventSeen && _xmlHeadersPrinted) {
			jvmpiAgent_printThreadStartElement(env_id, hashEntry);
		}
		return (ThreadPrivateStorage *)hashEntry->entry;
	}
	else {
		hashEntry = jvmpiAgent_CreateStack(env_id);
		return (ThreadPrivateStorage *)hashEntry->entry;
	}
}


/** SEND_ERROR_MESSAGE  *******************************************************************
  */
void sendErrorMessage(RA_ERROR_LEVEL severity,
							 const char *errorId,
							 const char *errorString)
{
#ifdef MVS
	__atoe(errorId);
	__atoe(errorString);
#endif
	if(!_jvmpiAgent_Options.standalone)
	{
		ra_logErrorMessage(_jvmpiAgent_bindingStorage, severity, errorId, errorString);
	}
	else
	{
		/* 60642 standalone mode - write to stderr if warning or worse */
		if (severity >= RA_WARNING)
		{
#ifdef MVS                   
#pragma convlit(suspend)
#endif
			fprintf(stderr, "%s: %s\n", errorId, errorString);
#ifdef MVS                   
#pragma convlit(resume)
#endif
			fflush(stderr);
		}
	}
/* we need to undo the conversion on 390 in case this method is called with the same
   string more than once */ 
#ifdef MVS
	__etoa(errorId);
	__etoa(errorString);
#endif
}

/** CLEANUP_AND_EXIT  **********************************************************
  * Flushes the I/O buffers, closes the data channel or file and shuts down the
  * RAC connection.
  */
static void CDECL cleanupAndExit(int sig) {
	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("cleanupAndExit underway\n"); fflush(stdout);
#endif
	/* If the application is logging to a file */
	if (_jvmpiAgent_outFile) {
		jvmpiAgent_printStandaloneTraceTagClose(tps);
		jvmpiAgent_cleanupStandaloneIO();
	} else  if (_jvmpiAgent_Options.application) {  /*94955*/
		jvmpiAgent_printStandaloneTraceTagClose(tps);		 
	} 	

	/* If this application is being controlled/moitored remotely */
	if (!_jvmpiAgent_Options.standalone) {

		/* Wait until all the data is sent over the wire if there is a data connection */
		if(!_jvmpiAgent_suspendIO)  {
			if(_jvmpiAgent_Options.targetHdl.dtarget==RA_SOCKET && _jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD>-1) {
				ra_closeSocket(_jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD);
			}
			else if (_jvmpiAgent_Options.targetHdl.dtarget == RA_SHAREDMEMORY) {
				ra_stopFlushingShm(&_jvmpiAgent_Options.targetHdl.dtargetHdl.shmHdl); /* 175248 */
			}

			_jvmpiAgent_suspendIO=1;
		}
		ra_stopListener(_jvmpiAgent_bindingStorage);
	}
}


/** OUTPUT_CLASS_DECLARATION  **************************************************
  * Conditionally outputs a classLoad element if it has not already been output.
  */
void jvmpiAgent_outputClassDeclaration(HashEntry *classEntry,
                                       ThreadPrivateStorage *tps) {
	/* RKD:  The testing for printing and the marking of printed elements is
	         not good.  We need to mark the element as printed at the same
			 time as we check to see if it is printed.  This is the only
			 way to ensure we don't have a race condition and an element
			 gets printed.
	*/
	if (!_jvmpiAgent_suspendIO && classEntry != NULL && !classEntry->printed) {

		/* Ensure that the class object and the java.lang.Class have been printed unless we are
		   specifically not tracing heap information or we have been specified to ignore heap information */
		if(CLASS_ENTRY(classEntry)->classObject!=NULL && jvmpiAgent_isTracingHeap()) {
			if(!CLASS_ENTRY(classEntry)->classObject->printed) {
				jvmpiAgent_printObjAllocElement(CLASS_ENTRY(classEntry)->classObject, tps->env, 0);
			}
		}
		/* Have to check whether the class is printed already which is the case for java.lang.Class */
		
		if (!classEntry->printed && CLASS_ENTRY(classEntry)->classId) {   /* 218494 */
			jvmpiAgent_printClass(classEntry, tps);
   	    }
        /* If this is a primative class there is no class object */
        else if(CLASS_ENTRY(classEntry)->arrayClass) {
            jvmpiAgent_printClass(classEntry, tps);
        }

	}
}



/**  OUTPUT_METHOD_DECLARATION  *********************************************************
  *  Conditionally outputs a METHOD element (and any referenced CLASS_LOAD elements)
  *  if it has not already been output.
  *  Preconditions:  In order to avoid redundant testing this method does not test to
  *                  determine if IO is suspended and hense should not be called when
  *                  it is.
  *  methodEntry  - the method you wish to print the declaration for.
  *  buffer       - the buffer to flush the IO to.
  */
void jvmpiAgent_outputMethodDeclaration(HashEntry *methodHashEntry,
                                        ThreadPrivateStorage *tps) {

	/* RKD:  The testing for printing and the marking of printed elements is
	         not good.  We need to mark the element as printed at the same
			 time as we check to see if it is printed.  This is the only
			 way to ensure we don't have a race condition and an element
			 gets printed.
	*/
	if (!methodHashEntry->printed) {
		methodHashEntry->printed=1;
		jvmpiAgent_outputClassDeclaration(METHOD_ENTRY(methodHashEntry)->classHashEntry, tps);
		jvmpiAgent_printMethod(METHOD_ENTRY(methodHashEntry), tps);
	}
}


/*
 This function resolves to the JAVA method JVMPIException.setExceptionTracing if not already done, and then synchronizes the
 state of the traceExceptions option with the Java class.
 The function returns the current state of the traceExceptions option.
*/
static int isTracingExceptions(JNIEnv *env, jclass cls)
{
 if (!_jvmpiAgent_setExceptionTracing)
 {
#if defined __cplusplus && defined _HPUX
  _jvmpiAgent_setExceptionTracing = ENV(env)->GetStaticMethodID(cls, "setExceptionTracing", "(Z)V");
#else
  _jvmpiAgent_setExceptionTracing = (*env)->GetStaticMethodID(env, cls, "setExceptionTracing", "(Z)V");
#endif
  _jvmpiAgent_JVMPIException = cls;
 }
 if (!_jvmpiAgent_Options.traceExceptions && _jvmpiAgent_setExceptionTracing)
 {
#if defined __cplusplus && defined _HPUX
  ENV(env)->CallStaticVoidMethod(_jvmpiAgent_JVMPIException, _jvmpiAgent_setExceptionTracing, (jboolean)0 );
#else
  (*env)->CallStaticVoidMethod(env, _jvmpiAgent_JVMPIException, _jvmpiAgent_setExceptionTracing, (jboolean)0 );
#endif
 }
 return _jvmpiAgent_Options.traceExceptions;
}


#ifndef __OS400__
/** PROCESS_EXCEPTION_EVENT  **************************************************
  */
static void processExceptionEvent(JNIEnv *env, ThreadPrivateStorage *tps, jclass cls, jthrowable e, enum ThrowOrCatch type)
{

 /* RKD:  We shouldn't be testing IO here. */
 if (!_jvmpiAgent_suspendIO && isTracingExceptions(env, cls))
 {
  HashEntry * methodHashEntry = 0;
  methodHashEntry = jvmpiAgent_Peek(tps,2)->methodHashEntry;
  if (methodHashEntry && CLASS_ENTRY(METHOD_ENTRY(methodHashEntry)->classHashEntry)->traceFlag)
  {
   jobjectID objId = resolveJobject2jobjectID(e, env);
   jvmpiAgent_printExceptionElement(env, tps, type, methodHashEntry, objId, e);
  }
 }
}
#endif /* __OS400__ */

/* bugzilla 71388 start - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
extern "C" {
#endif

/*
 * Class:     JVMPIException
 * Method:    JVMPICatch
 * Signature: (Ljava/lang/Throwable;)V
 */
JNIEXPORT void JNICALL Java_com_ibm_etools_logging_tracing_agent_Callback_JVMPICatch0(JNIEnv *env, jclass cls, jthrowable e)
{
#ifndef __OS400__
 processExceptionEvent(env, jvmpiAgent_getThreadLocalStorage(env), cls, e, Catch);
#endif /* __OS400__ */
}


/*
 * Class:     JVMPIException
 * Method:    JVMPIThrow
 * Signature: (Ljava/lang/Throwable;)V
 */
JNIEXPORT void JNICALL Java_com_ibm_etools_logging_tracing_agent_Callback_JVMPIThrow0(JNIEnv *env, jclass cls, jthrowable e)
{
#ifndef __OS400__
 processExceptionEvent(env, jvmpiAgent_getThreadLocalStorage(env), cls, e, Throw);
#endif
}

/* bugzilla 71388 end - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
}
#endif

/** PROCESS_METHOD_EXIT_EVENT  ************************************************
  *
  */
static void processMethodExitEvent(JVMPI_Event *event,
								   ThreadPrivateStorage *tps,
								   BOOL isRequested,
								   timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	HashEntry *methodHashEntry;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)	
    if (tps!=prevMthdTps) {
    	dumpMethodEventCounts(); prevMthdTps=tps;
    }
    mxCount++;
#endif
    if(_jvmpiAgent_Options.mode == TraceModeNone) {
        return;
    }

	if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
	  /* ToDo: remove. I know that, in TraceGcOptimizedHeap mode, it will never come
	   * here because METHOD_EXIT event is disbled, but just to make sure that it
	   * is not accidently causing lack of LeakCandidates.
	   */
	  /* do nothing */
	  return;
	}

	/* RKD:  If we don't know the stack for this thread
           we will abort this event.
	*/
	if(!tps->threadStackKnown) {
		return;
	}


	/* RKD:  It could be possible that the stack will be empty.  This happens
          on various platforms with various JDK's due to bugs in the JVMPI
		  implementation.  In order to avoid the almightly stack underflow
		  that would result in us bringing down the JVM, I will check and
		  see if the stack is empty.  It this is the case I will back out
		  the profiler and allow the JVM to continue processing.
	*/
	if(!tps->tos) {
		tps->threadStackKnown=0;
		return;
	}

	/* Compare the current method with that on the local stack */
	methodHashEntry = tps->stackEntry[tps->tos].methodHashEntry;
	/* 174190 - added check for non-NULL methodHashEntry in case there isn't one stored on the stack */
	if(methodHashEntry && ((MethodHashKey *)methodHashEntry->id)->id != event->u.method.method_id) {
		/* Stack is not synched, look up the method */
		methodHashEntry = jvmpiAgent_FindMethodSymbol(event->u.method.method_id);
	}

	/* 174190 - added check for non-NULL methodHashEntry in case there isn't one stored on the stack */
	if (methodHashEntry) {
		/* If not pre-aggregating, print the method-exit event for those where we printed the entry. */
		if (_jvmpiAgent_Options.compressLevel==CompressNone) {
			if (jvmpiAgent_Peek(tps, 0)->printed)  {
				jvmpiAgent_printMethodExitElement(event, tps, methodHashEntry, timestamp,cpu_timestamp);
			}
		}
		/* If this was a trigger that started tracing, stop tracing (only if trigger tracing) */
		if(_jvmpiAgent_Options.startMode>TraceStartModeFilter && !_jvmpiAgent_Options.application && jvmpiAgent_Peek(tps, 0)->trigger) {
			suspendTracing(TracePause);
		}
	}

	/* if pre-aggregation, accumulate time into this StackEntry (unless bad lastEntryTime), and set lastEntryTime of caller. */
	if(_jvmpiAgent_Options.compressLevel==CompressAggregate)
	{
		StackEntry * top = jvmpiAgent_Peek(tps, 0);
		StackEntry * caller = jvmpiAgent_Peek(tps, 1);
		if( top != NULL && top->lastEntryTime!=0) { /* lastEntryTime is 0 for first stackEntry after loadstack * 139537 */
			TIMESTAMP_ADD(top->baseTime , timestamp - top->lastEntryTime);
			TIMESTAMP_ADD(top->baseCPUTime, cpu_timestamp - top->lastEntryCPUTime);
			DBG_CHK_AND_REPORT_TIME_OVERFLOW(top->baseTime, "Time overflowed?! (MethodExit: top->baseTime)\n"); /* 134577 */
		}
		if( caller != NULL ) {
			caller->lastEntryTime = timestamp;
			caller->lastEntryCPUTime = cpu_timestamp;
		}
		/* The logic in jvmpiAgent_Pop will roll data from the StackEntry to the proper StackFrame. */
	}
	jvmpiAgent_Pop(tps);
}

/* bugzilla 71388 start - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
extern "C" {
#endif

/*
 * Class:     com_ibm_etools_logging_tracing_agent_Callback
 * Method:    JVMPICoverage0
 * Signature: (S)V
 */
JNIEXPORT void JNICALL Java_com_ibm_etools_logging_tracing_agent_Callback_JVMPICoverage0(JNIEnv *env, jclass cls, jshort e)
{

 /* RKD:  We shouldn't be testing IO here */
 if (!_jvmpiAgent_suspendIO && _jvmpiAgent_jvm) /* Only do this if the dll has been initialized correctly */
 {
  HashEntry * methodHashEntry = 0;
  ThreadLocalStorage *tps;
  tps = jvmpiAgent_getThreadLocalStorage(env);

  methodHashEntry = jvmpiAgent_Peek(tps,2)->methodHashEntry;
  if (methodHashEntry && CLASS_ENTRY(METHOD_ENTRY(methodHashEntry)->classHashEntry)->traceFlag)
  {
   jvmpiAgent_printLineElement(env, tps, methodHashEntry, e);
  }
 }

}

/* bugzilla 71388 end - JNI methods need to be enclosed in a conditional 'extern "C"' declaration */ 
#if defined __cplusplus
}
#endif

static int decrementInvocationCount() {
	/* Decrement the burst count if necessary.
	   RKD: This should be atomic.
	*/
	/* decrementInvocationCount() is called at the END of processMethodEntry, and so 
	when the invocationCountRemaining is 1, this means that we've traced the number
	of methods asked for by the user */ 
	if(_invocationCountRemaining<=1) {
		suspendTracing(TracePause);
		return -1;
	}
	else {
		_invocationCountRemaining--;
		return 0;
	}
}


static int incrementInvocationCount() {
	_invocationCountRemaining--;
}

/** PROCESS_MISSING_STACK_ENTRIES  ********************************************
  *
  */
static void processMissingStackEntries(JVMPI_Event *event, ThreadLocalStorage *tps) {
	StackEntry * se = 0;
	int i = 1;
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)	
    dumpMethodEventCounts(); printf("processMissingStackEntries: tps = %8x\n", (void *)tps); fflush(stdout);
#endif
    if(_jvmpiAgent_Options.mode == TraceModeNone) {
        return;
    }
	if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
	  /* ToDo: remove. I know that, in TraceGcOptimizedHeap mode, it will never come
	   * here because it is called only from processMethodEntry() method, and
	   * METHOD_ENTRY events are disabled. But just to make sure that it
	   * is not accidently causing lack of LeakCandidates.
	   */
	  /* do nothing */
	  return;
	}

	/* Walk up the stack looking for the first non filtered out method.  This loop will leave
	   i being the stack offset of the first unfiltered method on the stack and se points to this
	   stackEntry.  If se is null then we walked off the beginning of the stack without finding
	   an unfiltered method.
	*/
	for (se = jvmpiAgent_Peek(tps, i); se && !se->printed; se = jvmpiAgent_Peek(tps, ++i));
	
	
	/* Now we walk through the filtered out stack entries producing METHOD_ENTRYs for each entry.*/
	for (--i; se && i > 0; --i) {
		se = jvmpiAgent_Peek(tps, i);
		
		/* If we are handling object collation we better ensure we know the object */
		if(_jvmpiAgent_Options.mode == TraceModeFull) {
			/* RKD:  If we know the object we should check our filter against it for inheritance reasons (only when filter tracing) */
			if (se->objectHashEntry && _jvmpiAgent_Options.startMode==TraceStartModeFilter && !se->objectHashEntry->printed) {
			    jvmpiAgent_printObjAllocElement(se->objectHashEntry, event->env_id, 0);
			}
		}

		/* Note: following if-else-if pattern repeats with variation in ProcessMethodEntry */
		/* Test pre-aggregation mode */
		if (_jvmpiAgent_Options.compressLevel==CompressNone) {
			/* Full Trace: print the method entry for this stack frame */
			jvmpiAgent_printMethodEntryEvent(event, tps, se, tps->tos-i);
		}
		else { /* 134635C - fix contiguous mode (besides detach/attach/pause) */
			
			/* pre-agg mode: print a methodDef if needed and create a stackFrame for this stackEntry. 
			 *
			 * We can safely create a stackFrame after the fact here. It is later than usual (normally
			 * done on method entry) but still before it is needed (in the agPop invoked at method exit).
			 * Note that the baseTime/baseCpuTime accumulations have already been done correctly for this 
			 * stackEntry by the normal MethodEntryEvent operations which accumulate time the same way 
			 * regardless of whether the method is filtered or not. It is the processing of MethodExitEvents 
			 * (specifically the jvmpiAgent_agPop) that differentiates between filtered and not.
			 */
			StackEntry * caller;
			caller = jvmpiAgent_Peek(tps, i+1); /* se is guaranteed to have a caller on the stack */
			/* Pre-agg: print method def if needed & create/re-use StackFrame for method * 134577 */ 
			/*        : set lastEntryTime (& lastEntryCpuTime as needed)                 * 134577 */
			recordAgMethodEntry(tps, se, caller);                                       /* 134577 */
		}
		/* Deal with burst mode if we are running in that mode (ignore BurstModeSeconds here) */
		if( _jvmpiAgent_Options.burstMode == BurstModeInvocations ||
			_jvmpiAgent_Options.burstMode == BurstModeSecondsAndInvocations ) { 
			decrementInvocationCount(); /* non-zero indicates count exhausted: tracing has suspended */ 
		}

	}
}

/**
 * loadMethodHashEntry() 
 * 
 * Load the method hash entry for a given method_id, ensuring that a CLASS_LOAD
 * is requested if the piAgent doesn't currently know about the method 
 */ 
static HashEntry *loadMethodHashEntry(jmethodID method_id) {
	HashEntry *methodHashEntry = NULL; 
	/* The JVM may give us a method_id of '0' */
	if(method_id != 0) {
		methodHashEntry = jvmpiAgent_FindMethodSymbol(method_id);

		/* If this is an unknown method we need to ask for the CLASS_LOAD event */
		if(!methodHashEntry) {
			jobjectID clazz;
			jint result;
			clazz=_jvmpiAgent_jvmpiInterface->GetMethodClass(method_id);
			result=!JVMPI_SUCCESS;
			
			if(clazz) {
				result=REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, clazz); /* 232010 */
				/* If we found the class we should have the method */
				if(result == JVMPI_SUCCESS) {
					methodHashEntry = jvmpiAgent_FindMethodSymbol(method_id);
				}
			}
		}
	}
	return methodHashEntry; 
}


/** PROCESS_COUNTING_METHOD_ENTRY_EVENT 
 * 
 * This version of processMethodEntryEvent is called when the METHOD_COUNTS_ONLY option is turned
 * on. We don't want the overhead of the regular processMethodEntry event in this case. 
 */ 

static void processCountingMethodEntryEvent(JVMPI_Event *event, ThreadLocalStorage *tps, BOOL isRequested, timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	HashEntry *methodHashEntry;

	methodHashEntry = loadMethodHashEntry(event->u.method.method_id);
	if (methodHashEntry != NULL) {
		METHOD_ENTRY(methodHashEntry)->methodCount++;
	}
}

/* For pre-aggregation */
/* Recursively free stack frames.
 * This function frees all children of the frame being passed in,
 * then frees that frame itself. It returns the "next" (sibling) pointer
 * from the frame being freed.
 *
 * OPTIMIZATION OPPORTUNITY HERE: use a chunky allocator for StackFrame
 * objects, with one chunk chain per thread. Since the frames for a thread
 * are always freed all at once, you could just free the chunks and not have
 * to do this recursive descent.
 */
void freeStackFrames(StackFrame* frame)
{
	if (frame == NULL) return;

	/* first, free children */
	freeStackFrames(frame->calledList);

	/* then, free the next sibling */
	freeStackFrames(frame->next);

	/* finally, free this frame */
	free(frame);
}

/* For pre-aggregation */
/*
 * Add a thread to the list of live threads that the
 * pre-aggregation logic is tracking.
 *
 * This may seem redundant - the list of live threads is also available
 * in the ThreadHashTable data structure. But the timing and semantics
 * adding and removing threads from that list and this one are different,
 * and I didn't want to go poking around to make sure the right things would
 * always happen in the right order, so I left this as a separate data
 * structure just like the original prototype implementors had.
 */
void addThreadToList( ThreadPrivateStorage * tpsLocal )
{
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
    dumpMethodEventCounts(); printf("addThreadToList (try): tpsLocal = %8x\n", (void *)tpsLocal); fflush(stdout);
#endif
    /* Get the thread list lock because we're managing that list here */
    getThreadListLock();
    {
		/* It seems we are recieving duplicate events for thread starts. First, make
		 * sure we don't already have the thread */
		struct ThreadListElement * it = threadsRoot;
		while (it) {
			if (it->data == tpsLocal ) {
				/* Thread already seen; exit with "it" != NULL */
				break;
			}
			it = it->next;
		}

		if (it == NULL ) {
			/* usual case: we don't already have an entry for this thread */
			struct ThreadListElement * newThreadElement = (struct ThreadListElement *)jvmpiAgent_Calloc( sizeof( struct ThreadListElement ) );
			newThreadElement->data = tpsLocal;
			newThreadElement->next = threadsRoot;
			newThreadElement->prev = 0;
			if( threadsRoot )
				threadsRoot->prev = newThreadElement;
			threadsRoot = newThreadElement;
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
			printf("addThreadToList (succeed)\n"); fflush(stdout);
#endif
		}	
    }
    releaseThreadListLock();
}

/* For pre-aggregation */
/*
 * Remove a thread from the list of live threads being maintained
 * by the pre-aggregation logic. See notes above for why this
 * exists in parallel with the ThreadHashTable stuff.
 *
 * Frees all StackFrames associated with the thread being removed.
 * Does not dump any data; this function assumes you did that already or you
 * don't care.
 */
void removeThreadFromList( ThreadPrivateStorage * target_tps )
{
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); printf("removeThreadFromList (try): target_tps = %8x\n", (void *)target_tps); fflush(stdout);
#endif
	/* get the thread list lock because we're managing that list here */
    getThreadListLock();
    {
		struct ThreadListElement * it = threadsRoot;
		while (it) {
			if( it->data == target_tps )
				break;
			it = it->next;
		}

		if (it) { /* If found */
			/* Free all the elements from calledList, recursively */
			freeStackFrames(it->data->calledList);
			it->data->calledList = NULL;

			/* Now actually remove the thread from the list */
			if( it->next ) 
				it->next->prev = it->prev;
			if( it->prev )
				it->prev->next = it->next;
			if( threadsRoot == it )
				threadsRoot = it->next;
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
			printf("removeThreadFromList (succeed)\n"); fflush(stdout);
#endif
		}

		free(it);
	}
	target_tps->calledList = NULL;  /* 134635: frames are all gone so thread's calledList must be nulled */
    releaseThreadListLock();
}

/* For pre-aggregation */
/*
 * Recursively print the aggregated data for a single thread, 
 * starting from a given StackFrame. Called from PrintAgThreadData.
 */

void printAgData(ThreadPrivateStorage * tps, StackFrame * call) {

	if (call == NULL)
		return;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)		
	dumpMethodEventCounts(); 
	printf("printAgData: agMethodEntry, (children), agMethodExit\n"); fflush(stdout);
	printf("           : tps, &stackframe, &methodHashEntry, MethodIdRef= %8x, %8x, %8x",
		    (void *)tps, (void *)call, (void*)(METHOD_ENTRY(call->methodHashEntry)) ); fflush(stdout);
	printf(", %5d\n", (jint)((METHOD_ENTRY(call->methodHashEntry))->static_id)); fflush(stdout);
#endif

	jvmpiAgent_printAgMethodEntryEvent(tps, call);    /* 135437 */	
	
	/* Note: The following loop prints the call chains of the direct children of the current "call", 
	 * depth first and then breadth. It terminates when the "next" ptr of one of the direct children 
	 * of the current call is null. Thus it terminates after printing the call chains of the last
	 * direct child of the current call frame. (The loop & recursion is compact but can confuse.)
	 */  

	/* Dump this call's children */
	{
		StackFrame * it = call->calledList; /* select the first direct child of the current "call" */
		while( it != NULL ) {               /* stop if there are no more direct children of the current caller */ 
			printAgData(tps, it);           /* print the call chains of this direct child of the current caller */
			it = it->next;                  /* select the next direct child of the current caller */ 
		}
	}
	
	jvmpiAgent_printAgMethodExitElement(tps, call);   /* 135457 */
}



/* For pre-aggregation */
/*
 * Send the call chain counts for the indicated thread.
 * This is called from two places: when a single thread dies,
 * and when you do a full-process snapshot.
 *
 * This function calls getStackFrameStructureLock, but
 * only if it's called from a place where you didn't already have it.
 * This is conveyed by a boolean argument, true if the caller
 * already has the lock. That's necessary because the
 * ra_critsec_t implementation isn't reentrant on Linux.
 *
 * The reason we need to acquire StackFrameStructureLock even though
 * we're only dumping a single thread is that another dump request
 * (a snapshot or JVM shutdown) could come in on another thread
 * while this dump is still going on. We don't want to have
 * multiple dumps happening in parallel: they would cause
 * duplicate data to be sent.
 *
 * This does mean that one thread's death will stall any other thread that 
 * makes a unique call (and thus also needs StackFrameStructureLock)
 * for the duration of the dump, even though they couldn't
 * possibly step on each other. This makes me wish for a different 
 * lock implementation, one that more completely expresses the possible collisions.
 */
void printThreadAgData ( ThreadLocalStorage * tps, int alreadyHasLock ) {
	StackFrame * it;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); 
	printf("printThreadAgData: tps, tps->calledList, _traceResourcesGone = %8x, %8x, %2d\n",
			(void *)tps, (void *)tps->calledList, (int)_traceResourcesGone); fflush(stdout);
#endif
	/* pre-agg: 134635 
	 * This test is necessary because it is possible for a print of agg data 
	 * to be attempted after the underlying trace resources (esp. method hash table)
	 * have been cleaned-up via cleanupAllTraceResources. This is known to occur at
	 * least sometimes when a processThreadEndEvent occurs after DETACH_FROM_AGENT
	 * has been processed. It is a harmless test  for the non-pre-agg case
	 */
	if (_traceResourcesGone) {  
		return;
	}	 

	if (!alreadyHasLock) {
		getStackFrameStructureLock();
	}
	it = tps->calledList;
	while( it != NULL ) {
		printAgData(tps, it);
		it = it->next;
	}
	if (!alreadyHasLock) {
		releaseStackFrameStructureLock();
	}
}

/* For pre-aggregation */
/*
 * Send the call chain counts for all threads. This is a "snapshot"
 * operation that acquires the StackFrameStructureLock
 * and walks the StackFrames of all threads.
 */
void printAllThreadAgData() {

	/* Get the thread list lock because we walk the whole thread list here */
  getThreadListLock();
  {
	struct ThreadListElement * it = threadsRoot;

	/*
	 * Acquire this lock so we block out any other thread
	 * that is trying to add new callers to frames.
	 * See notes elsewhere about a no-lock alternative 
	 * to stackFrameStructureLock.
	 */
	getStackFrameStructureLock();
	{
		while (it) {
			printThreadAgData( it->data, TRUE );
			it = it->next;
		}
	}
	releaseStackFrameStructureLock();
  }
  releaseThreadListLock();
}

/*
 *  Return current time and (if options so indicate) current cpu time. As a side-effect,
 *  tps->last_cpu_time is updated if cpu time is updated. This function is used in 
 *  processMethodEntryEvent and processMissingStackEntries because they share a pattern
 *  of usage where latest time/cpu_time are used for both stackEntry aggregation (if applicable)
 *  and method entry overhead calculations.
 */
void setNowTimes(ThreadLocalStorage * tps, timestamp_t * nowTime_P, timestamp_t * nowCpuTime_P) { /* 134577 */
	jvmpiAgent_getCurrentTime(nowTime_P);
	if (_jvmpiAgent_Options.cpuTime) {
		*nowCpuTime_P = jvmpiAgent_getCurrentThreadCPUTime(); 
		tps->last_cpu_timestamp = *nowCpuTime_P;
	}	
}

/* For pre-aggregation */                                            
/*
 * Print method def if needed and create/re-use StackFrame for method along this chain since it 
 * is not filtered out. Get latest time/cpuTime. Store them in stackEntry->lastEntryTime and 
 * lastEntryCPUTime to allow for potential suspendTracing (which references these values) from 
 * subsequent decrementInvocationCount calls and for later overhead calculations in the caller. 
 * They are purposely calculated as late in this function as possible to eliminate overhjeas from method stats.
 */
void recordAgMethodEntry(ThreadLocalStorage * tps, StackEntry * stackEntry, StackEntry * caller) { /*134577 */
 	timestamp_t nowTime; 
 	timestamp_t nowCpuTime = 0;

	stackEntry->printed = 1; /* Indicate that this entry is "printed"=non-filtered - 134635C */  
	jvmpiAgent_outputMethodDeclaration(stackEntry->methodHashEntry, tps);
	jvmpiAgent_CreateStackFrame(tps, stackEntry->methodHashEntry, caller);
	
    setNowTimes(tps, &nowTime, &nowCpuTime);	
	stackEntry->lastEntryTime = nowTime;  /* critical in case suspend occurs from burst mode before Method entry ends *134577 */
	stackEntry->lastEntryCPUTime = nowCpuTime;
}


/** PROCESS_METHOD_ENTRY_EVENT  ***********************************************
  *
  */
static void processMethodEntryEvent(JVMPI_Event *event, ThreadLocalStorage *tps, BOOL isRequested, timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	HashEntry *methodHashEntry, *objectHashEntry;
	StackEntry * stackEntry, *caller;
	int isPrinting;
	jmethodID method_id;
	jobjectID object_id;
	BOOL triggeringEvent=FALSE;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	if (tps!=prevMthdTps) {
		dumpMethodEventCounts(); prevMthdTps=tps;
	}
	meCount++;
#endif

    if(_jvmpiAgent_Options.mode == TraceModeNone) {
        return;
    }
	if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
	  /* ToDo: remove. I know that, in TraceGcOptimizedHeap mode, it will never come
	   * here because METHOD_ENTRY event is disbled, but just to make sure that it
	   * is not accidently causing lack of LeakCandidates.
	   */
	  /* do nothing */
	  return;
	}

	objectHashEntry=methodHashEntry=NULL;
	method_id = event->u.method.method_id;
	object_id=(event->event_type==JVMPI_EVENT_METHOD_ENTRY2) ? event->u.method_entry2.obj_id : 0;

	/* RKD:  If we don't know the stack for this thread
           we better materialize this stack now.
	*/
	if(!tps->threadStackKnown) {
		/* Disable GC */
		_jvmpiAgent_jvmpiInterface->DisableGC();
		loadStack(tps);

		/* Re-enable GC */
		_jvmpiAgent_jvmpiInterface->EnableGC();

		/* We are already on the stack from the load.  Pop us off */
		jvmpiAgent_Pop(tps);

		/* RKD:  Do to problems we have with the JIT being enabled it may be possible that the
                 thread stack is still not known.  If this case we abort this method
		*/
		if(!tps->threadStackKnown) {
			return;
		}

	}

	
	methodHashEntry = loadMethodHashEntry(method_id); 

	/* Determine if we are supposed to print this method.  Depending upon the trace start mode will determine this test */
	if(_jvmpiAgent_Options.startMode==TraceStartModeFilter) {
		isPrinting = (methodHashEntry && METHOD_ENTRY(methodHashEntry)->traceFlag && _jvmpiAgent_Options.stackInfo > StackInfoNone);
	}
	/* Trigger tracing */
	else {
		/* If the trigger hasn't been squeezed yet check to see if we are the trigger and scope to thread if neccessary */
		if(!_triggerSqueezed) {
			isPrinting = (methodHashEntry
				      && (METHOD_ENTRY(methodHashEntry)->trigger
				          == METHOD_ENTRY(methodHashEntry)->methodCount));

			if(isPrinting) {
				_triggerSqueezed=TRUE;
				triggeringEvent=TRUE;
				if(_jvmpiAgent_Options.startMode==TraceStartModeTriggerSingleThreaded) {
					_jvmpiAgent_limitingThread=event->env_id;
					_jvmpiAgent_singleThreaded=TRUE;	
				}
				if(_jvmpiAgent_isSuspended) {
					resumeTracing();
				}
			}
			isPrinting=isPrinting && _jvmpiAgent_Options.stackInfo > StackInfoNone;
		}
		else {
			isPrinting = (methodHashEntry && _jvmpiAgent_Options.stackInfo > StackInfoNone);
		}
	}

	/* If we are handling object collation we better ensure we know the object */
	if(_jvmpiAgent_Options.mode == TraceModeFull && object_id) {
		/* Look for the object */

	  /* 226233: we can safely use the "fast", non-ref counting
	     version here, since we'd only be here if this is an
	     ENTRY2 event, in which case the heap is locked */
		objectHashEntry = jvmpiAgent_FindObjectSymbolFast(object_id);

		/* If the object is not currently in the DB look it upo in the VM */
		if(!objectHashEntry || !objectHashEntry->id || 
			!((ObjectHashKey*)objectHashEntry->id)->id) {
			int result;
			/* RKD:  Disable the garbage collector while we get the object */
			_jvmpiAgent_jvmpiInterface->DisableGC();
			result=REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, event->u.method_entry2.obj_id); /* 232010 */
			_jvmpiAgent_jvmpiInterface->EnableGC();

			/* We should now find the object without incident */
			objectHashEntry = jvmpiAgent_FindObjectSymbolFast(object_id);
		}

		/* RKD:  If we know the object we should check our filter against it for inheritance reasons (only when filter tracing) */
		if ( objectHashEntry && _jvmpiAgent_Options.startMode==TraceStartModeFilter 
			&& OBJECT_ENTRY(objectHashEntry)->traceFlag && !objectHashEntry->printed ) {
			jvmpiAgent_printObjAllocElement(objectHashEntry, event->env_id, 0);
			isPrinting=1;
		}
	}

	/* pre-aggregation mode: accumulate time into the caller (unless caller was added to stack by loadstack) */
	if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {
		caller = jvmpiAgent_Peek(tps, 0);
		if (caller != NULL && caller->lastEntryTime!=0) { /* lastEntryTime is 0 for first caller after loadstack * 139537 */
			TIMESTAMP_ADD(caller->baseTime, timestamp - caller->lastEntryTime);
			TIMESTAMP_ADD(caller->baseCPUTime, cpu_timestamp - caller->lastEntryCPUTime);
			DBG_CHK_AND_REPORT_TIME_OVERFLOW(caller->baseTime, "Time overflowed?! (MethodEntry: caller->baseTime)\n"); /* 134577 */
			caller->lastEntryTime = -1;	/* -1 means this frame active right now */
			caller->lastEntryCPUTime = -1;
		}
	}
	
	/* Push the method on the stack. (Note: this initializes all pre-agg stackEntry fields to 0) */
	stackEntry = jvmpiAgent_Push(tps, event->env_id, methodHashEntry, objectHashEntry, timestamp,cpu_timestamp);

	/* If we are in a pure trigger mode we need to indicate whether this frame is the trigger so we stop
	   profiling when the frame is popped.
	*/
	stackEntry->trigger=triggeringEvent;


	if (methodHashEntry) {
		if (isPrinting) {
			if(_jvmpiAgent_Options.startMode==TraceStartModeFilter && (_jvmpiAgent_Options.stackInfo==StackInfoContiguous || _jvmpiAgent_Options.stackInfo==StackInfoBoundaryAndContiguous)) {
				processMissingStackEntries(event, tps);	
			}

			/* When in filtering mode, reset the boundary depth every time we hit a traced method. */
			if(_jvmpiAgent_Options.startMode==TraceStartModeFilter) {
				stackEntry->currentBoundaryDepth=_jvmpiAgent_Options.boundaryDepth+1;
			}

			/* If we are in application mode we need to set the boundary depth only when we did not see
			   the entry event on the next frame up the stack.
			*/
			if(_jvmpiAgent_Options.application  && _jvmpiAgent_Options.boundaryDepth) {
				StackEntry *invokerFrame=jvmpiAgent_Peek(tps,1);
				if(invokerFrame && !invokerFrame->entryEventSeen) {
					stackEntry->currentBoundaryDepth=_jvmpiAgent_Options.boundaryDepth;
				}
				
				/* Application mode is a different then all the other modes in the fact that we
				   don't keep tracing deeper if our boundaryDepth is
				*/
				if(!stackEntry->currentBoundaryDepth) {
					return;
				}
			}
			/* Note: following if-else-if pattern repeats below and with variation in ProcessMissingStackEntries */
			if (_jvmpiAgent_Options.compressLevel==CompressNone) { 
				/* Full trace: print the method entry for this event */
				jvmpiAgent_printMethodEntryEvent(event, tps, stackEntry, tps->tos);
			}
			else {
				/* Pre-agg: print method def if needed & create/re-use StackFrame for method * 134577 */ 
				/*        : set lastEntryTime (& lastEntryCpuTime as needed)                 * 134577 */
				recordAgMethodEntry(tps, stackEntry, caller);                               /* 134577 */
			}
			/* Deal with burst mode if we are running in that mode (ignore BurstModeSeconds here) */
			if( _jvmpiAgent_Options.burstMode == BurstModeInvocations ||
				_jvmpiAgent_Options.burstMode == BurstModeSecondsAndInvocations ) { 
				if(decrementInvocationCount()) { /* non-zero indicates count exhausted: tracing has suspended */ 
					return;                      /* don't count suspension time in entry overhead             */ 
				}
			}
		}
		else if(_jvmpiAgent_Options.stackInfo>=StackInfoBoundary && stackEntry->currentBoundaryDepth) {
			/* Note: following if-else-if pattern repeats above and with variation in ProcessMissingStackEntries */
			if (_jvmpiAgent_Options.compressLevel==CompressNone) {
				/* Full trace: print the method entry for this event */
				jvmpiAgent_printMethodEntryEvent(event, tps, stackEntry, tps->tos);
			}
			else {
				/* Pre-agg: print method def if needed & create/re-use StackFrame for method * 134577 */
				/*        : set lastEntryTime (& lastEntryCpuTime as needed)                 * 134577 */ 
				recordAgMethodEntry(tps, stackEntry, caller);                                /*134577 */
			}
			/* Deal with burst mode if we are running in that mode (ignore BurstModeSeconds here) */
			if( _jvmpiAgent_Options.burstMode == BurstModeInvocations ||
				_jvmpiAgent_Options.burstMode == BurstModeSecondsAndInvocations ) { 
				if(decrementInvocationCount()) { /* non-zero indicates count exhausted: tracing has suspended */ 
					return;                      /* don't count suspension time in entry overhead             */ 
				}
			}
		}
	}

	{
	  /* NMM: here is where we accumulate the tracing overhead due
	     to method enter processing */
		timestamp_t now;
		timestamp_t cpu_now = 0; 
		Uint64      enter_overhead;
		Uint64      cpu_enter_overhead = 0;  
		
		/* Get new times that include our overhead in them */
		if (_jvmpiAgent_Options.compressLevel==CompressNone) { 
			setNowTimes(tps, &now, &cpu_now);
		} else {		
			/* 134577 *
			 * Pre-agg: only set now & cpu_now here if they were not previously set by recordAgMethodEntry. 
			 *          The difference in calculating them here and in an earlier recordAgMethod is 
			 *          negligible. The intent is to conserve getCurrentTime & getCurrentThreadCPUtime
			 *          calls because they are expensive and would skew the overhead calculation.
			 */
			if (stackEntry->lastEntryTime == 0) { /* not set by either previous recordAgMethodEntry */ 
				setNowTimes(tps, &now, &cpu_now);
				stackEntry->lastEntryTime = now;
				stackEntry->lastEntryCPUTime = cpu_now;
			} else {                              /* set by one of the previous recordAgMethodEntry */
				now     = stackEntry->lastEntryTime;
				cpu_now = stackEntry->lastEntryCPUTime;	
			}	
		}
		
		enter_overhead = TIMESTAMP_SUB(now,timestamp);
		if (_jvmpiAgent_Options.cpuTime) {
			cpu_enter_overhead = TIMESTAMP_SUB(cpu_now,cpu_timestamp); 
		}	
		
		stackEntry->cumulative_overhead = enter_overhead;
		stackEntry->cumulative_cpu_overhead = cpu_enter_overhead; 
	}
}


/** SET_PATH_DELIMITER  ********************************************************
  * Checks class name's provided by JVMPI to determine if this JVM uses the '.'
  * package delimiter.  If it does, we will tell the filters checker to use
  * change all it's strings to the '.' delimiter.
  * RKD:  I hate the use of a global here.  I bet we can get around this.
  */
static void setPathDelimiter(char * str) {
#ifdef __OS400__
#pragma convert(819) 
#endif

	/* The path delimiter on IBM's JDK */
	char ibmch = '/';
	/* The path delimiter on SUN's JDK */
	char sunch = '.';
	/* The location of the delimiter */
	char *pdest;

	/* Look for the IBM delimiter in the given string */
	pdest = strchr(str, ibmch );

	/* Set the global delimiter */
	if(pdest)  {
		_setPathDelimiter = ibmch;
	}
	else {
		pdest = strchr(str, sunch );
		if(pdest) {
			_setPathDelimiter = sunch;
			jvmpiAgent_changeDelimiter();
		}
	}
#ifdef __OS400__
#pragma convert(0) 
#endif
}

#ifdef __OS400__
/** PROCESS_OBJECT_DUMP_EVENT  *************************************************
  * Handles the output of a OBJECT_DUMP element and attributes along with any
  * @param  event      - the event data provided by the JVM.
  * @param isRequested - whether this event is generated as a result of a specific
  *                      RequestEvent call.  If the event is requested it is assumed
  *                      that the writelock is held for the hashtables.
  */
static void processObjectDumpEvent(JVMPI_Event *event,
								  ThreadLocalStorage *tps,
                                  BOOL isRequested,
								  timestamp_t timestamp,
								 timestamp_t cpu_timestamp) {

	if((_jvmpiAgent_Options.mode == TraceOptimizedHeap) && (event->u.object_dump.data_len > 0) &&
		(event->u.object_dump.data[0] == JVMPI_GC_CLASS_DUMP)) {

		StatelessHeapSnapshotManager_objectDumpCallbackMethod(event);
		return;
	}

}
#endif

/** PROCESS_CLASS_LOAD_EVENT  *************************************************
  * Handles the output of a CLASS_LOAD element and attributes along with any
  * associated METHOD, STATIC_FIELD and INSTANCE_FIELD elements.
  * @param  event      - the event data provided by the JVM.
  * @param isRequested - whether this event is generated as a result of a specific
  *                      RequestEvent call.  If the event is requested it is assumed
  *                      that the writelock is held for the hashtables.
  */
static void processClassLoadEvent(JVMPI_Event *event,
								  ThreadLocalStorage *tps,
                                  BOOL isRequested,
								  timestamp_t timestamp,
								  timestamp_t cpu_timestamp) {
	char * buffer;
	HashEntry *classHashEntry;

    if(_jvmpiAgent_Options.mode == TraceModeNone) {
        return;
    }
	if(_jvmpiAgent_Options.mode == TraceOptimizedHeap) {
		/* ToDo: We do not track callInfo currently
		if (_jvmpiAgent_Options.classInfoCallback) {
		*/
		
			/* Giri <Defect 78980/78981>: Sometimes, the IBM Classic VM sends
			 * class load events with class_name set to NULL. Other fields are set correctly.
			 * Handle these cases properly.
			 *
			 * Anandi Aug 2005 Fix Giri's fix, bugzilla 106779. 
			 * Don't simply return when className is
			 * NULL. Not calling StatelessHeapSnapshotManager_classInfoCallback leaves stale
			 * values in the globals that the function sets and corrupts the parsing of
			 * the heap. Anoint the class with a name and call 
			 * StatelessHeapSnapshotManager_classInfoCallback. 
			 * Also handle the empty string case. 
			 *
			 * Not thread-safe and that is OK. This code is conditional
			 * on mode==TraceOptimizedHeap and that always runs in a single thread.
			 * (JVMPI CLASS_LOAD events are not enabled from JVMPI; they only occur
			 * when we ask for them, and we only ask from a single thread.)
			 */
			
			if (event->u.class_load.class_name == NULL || 
				(strlen(event->u.class_load.class_name) == 0)) {
				sprintf(_classNameBuffer, "unknown%d", _unknownClassSuffix++);
				StatelessHeapSnapshotManager_classInfoCallback(_classNameBuffer,
						   event->u.class_load.num_interfaces,
						   event->u.class_load.num_static_fields,
						   event->u.class_load.statics);
			} else {
						
				StatelessHeapSnapshotManager_classInfoCallback(event->u.class_load.class_name,
						   event->u.class_load.num_interfaces,
						   event->u.class_load.num_static_fields,
						   event->u.class_load.statics);
			}
		/* } */
		return;
	}

	buffer = tps->buffer;

	
	
	/* If we haven't determined the path delimiter of this JVM try to use this
	   class name to determine the delimiter.
	*/
	if(!_setPathDelimiter) {
		setPathDelimiter((char*)event->u.class_load.class_name);
	}

	

	/* Create the class symbol */
	classHashEntry = jvmpiAgent_CreateClassSymbol(event, tps, jvmpiAgent_getFilter((char *)event->u.class_load.class_name, ""));

	

	/* RKD:  We print this class definition if we are traing this type or if we are asked
	         to output classes irregardless of whether the are ever used.  Also, if this class
			 has already had its class object printed then we should also print the class definition
	*/
	if (_jvmpiAgent_Options.unreferencedSymbols && CLASS_ENTRY(classHashEntry)->traceFlag) {
		jvmpiAgent_outputClassDeclaration(classHashEntry, tps);
		jvmpiAgent_printMethods((HashEntry **)CLASS_ENTRY(classHashEntry)->methods, CLASS_ENTRY(classHashEntry)->numMethods, tps);
	}
	/* DNS:  218494 - added this check back in so class is not output indiscrimantly */
	else if(CLASS_ENTRY(classHashEntry)->classObject && CLASS_ENTRY(classHashEntry)->classObject->printed) {
		jvmpiAgent_outputClassDeclaration(classHashEntry, tps);

	}

	if(!isRequested) {
	  /* NMM: here is where we accumulate the tracing overhead due
	     to classload processing */
	  StackEntry* tos;
	  if((tos=jvmpiAgent_Peek(tps, 0))
	     && tos->entryEventSeen) {
	    timestamp_t now;
		timestamp_t cpu_now = 0;
	    Uint64    classload_overhead;
		Uint64    cpu_classload_overhead = 0; 

		if (_jvmpiAgent_Options.cpuTime) {
			cpu_now = jvmpiAgent_getCurrentThreadCPUTime(); 
			tps->last_cpu_timestamp = cpu_timestamp;	/* remember last known CPU time */
			cpu_classload_overhead = TIMESTAMP_SUB(cpu_now,cpu_timestamp); 
		}
	    jvmpiAgent_getCurrentTime(&now);
	    classload_overhead        = TIMESTAMP_SUB(now,timestamp);

	    tos->cumulative_overhead += classload_overhead;
		tos->cumulative_cpu_overhead += cpu_classload_overhead; 

	  }
	}

}

static void agRollUpStack(ThreadLocalStorage* tps,
						  timestamp_t timestamp,
						  timestamp_t cpu_timestamp)
{
	/* simulate an exit for the top frame - that is,
	 * perform the same boookkeeping that a method exit would have.
	 * All other frames have accurate baseTime already,
	 * and agPop will update their cumulative times.
	 */
	int depth = 0;
	StackEntry* stkentry = jvmpiAgent_Peek(tps, depth);
	if (stkentry != NULL) {
		TIMESTAMP_ADD(stkentry->baseTime, timestamp - stkentry->lastEntryTime);
		DBG_CHK_AND_REPORT_TIME_OVERFLOW(stkentry->baseTime, "Time overflowed?! (agRollUpStack: stkentry->baseTime)\n"); /* 134577 */		

		/* Add last known CPU time on the thread to the frame's 
		 * base CPU time, if it's greater. It might not be greater
		 * because it's just a guess - the last time we updated
		 * tps->last_cpu_timestamp for that thread. If the logic
		 * that updates that value isn't perfect, then we
		 * could be behind the times and we would add negative time.
		 * In that case just don't add anything. 
		 * 
		 * This system always under-reports the CPU time of the top
		 * frame on other threads, but we can't help that because we
		 * don't have a platform-independent "get the CPU time on 
		 * another thread" query.
		 */
		if (cpu_timestamp > stkentry->lastEntryCPUTime) {
			TIMESTAMP_ADD(stkentry->baseCPUTime, cpu_timestamp - stkentry->lastEntryCPUTime);
		}
	}

	/* now call agPop for each active frame, to roll up data */
	while ((stkentry = jvmpiAgent_Peek(tps, depth)) != NULL) {
		jvmpiAgent_agPop(stkentry);
		depth++;
	}
}

/** PROCESS_THREAD_END_EVENT  ***********************************************
  * Handles thread end events from JVMPI.
  */
static void processThreadEndEvent(JVMPI_Event *event,
								  ThreadLocalStorage *tps,
                                  BOOL isRequested,
								  timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); 
	printf("processThreadEnd: tps, tps->calledList, _traceResourcesGone = %8x, %8x, %2d\n",
			(void *)tps, (void *)tps->calledList, (int)_traceResourcesGone); fflush(stdout);
#endif
	/* In TraceTraceOptimizedHeap mode, we don't need any hashing */
	if (_jvmpiAgent_Options.mode == TraceOptimizedHeap || _jvmpiAgent_Options.mode == TraceModeNone) {
		/* do nothing */
		return;
	}

	if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {
		/* We are pre-aggregating: 
		 * Roll up this stack, then report its data.
		 *
		 * We send the data now so the thread-exit notification occurs
		 * in the proper sequence; if we saved up the data and sent
		 * it later, we'd be sending agMethodEntry events on a thread
		 * that had already been seen to exit.
		 * 
		 * Locking note: printThreadAgData will call getStackFrameStructureLock
		 * because we're passing FALSE, saying we don't already have it.
		 * See comments at printThreadAgData for why this is a shame.
		 */
		agRollUpStack(tps, timestamp, cpu_timestamp);
		printThreadAgData(tps, FALSE);
		removeThreadFromList(tps);
	}

	jvmpiAgent_printThreadEndElement(event->env_id, tps);
	jvmpiAgent_DestroyStack(event->env_id);
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("processThreadEnd: done!\n"); fflush(stdout);
#endif
}


/** PROCESS_THREAD_START_EVENT  ***********************************************
  * Handles thread start events from JVMPI.
  */
static void processThreadStartEvent(JVMPI_Event *event,
									ThreadLocalStorage *tps,
                                    BOOL isRequested,
									timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	ThreadLocalStorage *tpsLocal = 0;
	HashEntry *hashEntry;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); 
	printf("processThreadStart: tps, tps->calledList, _traceResourcesGone = %8x, %8x, %2d\n",
	(void *)tps, (void *)tps->calledList, (int)_traceResourcesGone); fflush(stdout);
#endif

	/* In TraceTraceOptimizedHeap mode, we don't need any hashing */
	if (_jvmpiAgent_Options.mode == TraceOptimizedHeap || _jvmpiAgent_Options.mode == TraceModeNone) {
		/* do nothing */
		return;
	}

	/* Find the thread to determine if it already exists.  This should never happen. */
	hashEntry = jvmpiAgent_FindThreadSymbol(event->env_id);

	/* If this is a new thread create a stack for it */
	if (!hashEntry) {
		hashEntry = jvmpiAgent_CreateStack(event->env_id);
	}




	tpsLocal = THREAD_ENTRY(hashEntry);

	/* 236501 begins */
	tpsLocal->threadName = "";
	tpsLocal->groupName = "";
	tpsLocal->parentName = "" ;

	if(event->u.thread_start.thread_name) {
		STRDUP(tps->threadName, event->u.thread_start.thread_name);
	}
	if(event->u.thread_start.group_name) {
		STRDUP(tps->groupName, event->u.thread_start.group_name);
	}
	if(event->u.thread_start.parent_name) {
		STRDUP(tps->parentName, event->u.thread_start.parent_name);
	}
	/* 236501 ends */

	tpsLocal->threadId = event->u.thread_start.thread_id;
	tpsLocal->threadStartEventSeen = 1;
	tpsLocal->threadStackKnown=0;
    tpsLocal->disableEventsForThisThread=0;

	if(_jvmpiAgent_Options.compressLevel==CompressAggregate) {
		/* we are pre-aggregating: add this thread to the list
		 * of threads that we will want to dump data for when requested,
		 * or when they die.
		 */
		addThreadToList(tpsLocal);
	}

	if (hashEntry) { /* bugzilla 72292 */ 
		if (!hashEntry->printed && event->env_id && THREAD_ENTRY(hashEntry)->threadStartEventSeen && _xmlHeadersPrinted) {
			jvmpiAgent_printThreadStartElement(event->env_id, hashEntry);
		}
	}
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("processThreadStart: done!\n"); fflush(stdout);
#endif
}

/* pre-aggregation */

/* rollUpAndPrintAllThreadAgData
 *
 * This function rolls up all threads' stacks and prints their data.
 * You use this when the JVM is shutting down, or as a final data dump
 * when the user asks to detach from the target process.
 *
 * Note: I'm not sure what happens if you do this and then keep
 * running the process and collecting more data. The live frame at the
 * top of each stack will probably accumulate more baseTime, and when
 * that frame exits the new baseTime will roll up into its callers AGAIN,
 * so the time it had run before the first snapshot will get double-counted.
 */
void rollUpAndPrintAllThreadAgData(timestamp_t timestamp)
{
	/* Roll up stacks and emit data for living threads.
	 * (Data for threads that have died has already been transmitted.)
	 *
	 * Notice that the cpu_timestamp we use for rolling up is the 
	 * last_cpu_timestamp recorded for each thread.
	 * That field in tps is updated for each event notification,
	 * so we're acting like each thread has consumed zero
	 * time since the last notification on that thread.
	 * This is the best we can do because we can't ask
	 * what the CPU time is on a thread other than the current thread.
	 *
	 * IMPORTANT LOCKING NOTE: the function dumpAllThreadStatistics
	 * calls getThreadListLock, and on some platforms the lock 
	 * implementation we get from the RAC shared libraries is the 
	 * kind that you can't reenter. So we have to get that lock, 
	 * roll up the live stack frames, then release it before calling
	 * dumpAllThreadStatistics ... otherwise we'll deadlock on ourselves.
	 * (The lock impl on Windows is reentrant, but not on Linux.)
	 */
	struct ThreadListElement * it;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); printf("rollUpAndPrintAllThreadAgData\n"); fflush(stdout);
#endif
	getThreadListLock();	/* see IMPORTANT LOCKING NOTE above */
	it = threadsRoot;
	while (it) {
		ThreadPrivateStorage * target_tps = it->data;
		if (target_tps != NULL) {
			agRollUpStack(target_tps, timestamp, target_tps->last_cpu_timestamp);
		}
		it = it->next;
	}
	releaseThreadListLock();
	printAllThreadAgData();
}

/** PROCESSES_JVM_SHUTDOWN_EVENT  *********************************************
  *
  */
static void processesJVMShutdownEvent(JVMPI_Event *event,
									  ThreadLocalStorage *tps,
                                      BOOL isRequested,
									  timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	_jvmShutDown = (enum _BOOL)1;

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	dumpMethodEventCounts(); printf("processesJVMShutdownEvent\n"); fflush(stdout);
#endif
	/* If pre-aggregating, dump aggregated data for all living threads */
	if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {
		rollUpAndPrintAllThreadAgData(timestamp);
		/* Note: we leak StackFrames here for all living threads.
		 * To fix this leak, call removeThreadFromList for all living threads.
		 * But, in general, the process is coming down anyway,
		 * so that would be a waste of time.
		 */
	}
	jvmpiAgent_printJvmShutdownElement(event);
#ifdef _DEBUG
	if (_jvmpiAgent_Options.debugHash) {
		jvmpiAgent_DumpHashTableStatistics();
	}
#endif

	cleanupAndExit(0);
	ra_releaseVMLock();
}

/** PROCESS_OBJ_MOVE_EVENT  ***************************************************
  * Handler for object move events recieved during GC from JVMPI.
  *
  * The class id for the class associated with the object being moved is subject to change.
  * In such cases, the hash entry for the associated class will also be moved.
  */
static void processObjMoveEvent(JVMPI_Event *event,
								ThreadLocalStorage *tps,
                                BOOL isRequested,
								timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	
	HashEntry *objectHashEntry;			/* Hash entry of the object being moved */
	HashEntry *classHashEntry;			/* Hash entry of the class associated with the object being moved */

	if (_jvmpiAgent_Options.mode == TraceModeNone) {
		return;
	}

	if(_jvmpiAgent_Options.mode ==  TraceOptimizedHeap ) { /*58049*/
	  return;
	}
	/* Find the object */
	objectHashEntry = jvmpiAgent_FindObjectSymbol(event->u.obj_move.obj_id);


	if (objectHashEntry) {
		void *id; 
		ObjectHashKey newId; 
		newId.id = event->u.obj_move.new_obj_id; 
		id = objectHashEntry->id; 

		/* Move the object hash entry and determine its associated class */
		jvmpiAgent_MoveSymbol(objectHashEntry, Object_t, id, (void*)&newId);
		
		

		/* RJD- The following should be unnecessary. According to JVMPI spec, class id's 
		   should also have their own object move events, and not be implicit as part of a regular
		   object move. 
		   
		
		classHashEntry = OBJECT_ENTRY(objectHashEntry)->classHashEntry;
		if (classHashEntry)
		{
			// Mode 1: Resolve the class id for the object being requested.  The class id for
			//   the object will be stored in the global variable '_resolvedClassId' 
			tps->mode = 1;
			REQUEST_EVENT2(JVMPI_EVENT_OBJECT_ALLOC, event->u.obj_move.new_obj_id);
			tps->mode = 0;
			
			
			// Move the class hash entry if necessary 
			if (_resolvedClassId !=	CLASS_ENTRY(classHashEntry)->classId)
				jvmpiAgent_MoveSymbol(classHashEntry, Class_t, (jint)_resolvedClassId);
		}
		*/ 

		/* We will only print object move for objects which we have already declared
		   within the trace and if we have been asked to print object moves
		*/

		if (objectHashEntry->printed && 
			(_jvmpiAgent_Options.gc==GcMoves || _jvmpiAgent_Options.gc==GcDeletesAndMoves) &&
			!_jvmpiAgent_suspendIO) {
			jvmpiAgent_printObjMoveElement(event, objectHashEntry);
		}
	}
	

	/* The object move may be that of a java.lang.Class instance, in which 
	   case the class table must be updated as well */
	classHashEntry = jvmpiAgent_FindClassSymbol(event->u.obj_move.obj_id);

	if (classHashEntry) {
		void *id; 
		ClassHashKey newId; 
		newId.id = event->u.obj_move.new_obj_id; 
		id = classHashEntry->id; 
		jvmpiAgent_MoveSymbol(classHashEntry, Class_t, id,(void*)&newId); 
	}



}

/** PROCESS_OBJ_FREE_EVENT  ***************************************************
  * Handler for object free events recieved during GC from JVMPI.
  */
static void processObjFreeEvent(JVMPI_Event *event,
								ThreadLocalStorage *tps,
                                BOOL isRequested,
								timestamp_t timestamp,
								timestamp_t cpu_timestamp) {
	HashEntry *objectHashEntry;

	if (_jvmpiAgent_Options.mode == TraceModeNone) {
		return;
	}

	if(_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) { /*58049*/
	  return;
	}
	/* Do we know of this object */
	objectHashEntry = jvmpiAgent_FindObjectSymbol(event->u.obj_free.obj_id);
	if (objectHashEntry) {
		/* We will only print object move for objects which we have already declared
		   within the trace and if we have been asked to print object free's
		*/
		if (objectHashEntry->printed && 
			(_jvmpiAgent_Options.gc==GcDeletes || _jvmpiAgent_Options.gc==GcDeletesAndMoves) 
			&& !_jvmpiAgent_suspendIO) { 
				
			jvmpiAgent_printObjFreeElement(jvmpiAgent_getThreadLocalStorage(event->env_id), objectHashEntry);
		}

		/* Remove the object from the object hashtable */
		jvmpiAgent_DeleteSymbol(objectHashEntry, Object_t);
	}
}

/** PROCESS_CLASS_UNLOAD_EVENT  ***********************************************
  * Handle class unload events.
  */
static void processClassUnloadEvent(JVMPI_Event *event,
									ThreadLocalStorage *tps,
                                    BOOL isRequested,
									timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	HashEntry *classHashEntry;
	if (_jvmpiAgent_Options.mode == TraceModeNone) {
		return;
	}

	if(_jvmpiAgent_Options.mode ==  TraceOptimizedHeap ) {
	  return;
	}

	classHashEntry = jvmpiAgent_FindClassSymbol(event->u.class_unload.class_id);

	/* We will only print class unloads for classes which we have already declared
	   within the trace
	*/
	if (classHashEntry) {
		classHashEntry->deleted = 1; /* Mark as deleted */

		if (!_jvmpiAgent_suspendIO) {
			jvmpiAgent_printClassUnloadElement(event, tps, classHashEntry);
		}
	}
}

/** PROCESS_GC_START_EVENT  ***************************************************
  * Handle GC start events.
  */
static void processGcStartEvent(JVMPI_Event *event,
								ThreadLocalStorage *tps,
                                BOOL isRequested,
								timestamp_t timestamp,
								timestamp_t cpu_timestamp) {
	if (_jvmpiAgent_Options.mode == TraceModeNone) {
		return;
	}

	if(_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
	  /* do nothing */
	  return;
	}
	if (!_jvmpiAgent_suspendIO) {
		jvmpiAgent_printGcStartElement(event);
	}
}

/** PROCESS_GC_FINISH_EVENT  **************************************************
  * Handle GC finish  events.
  */
static void processGcFinishEvent(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap || _jvmpiAgent_Options.mode == TraceModeNone) {
	  /* do nothing */
	  return;
	}
	if (!_jvmpiAgent_suspendIO) {
		jvmpiAgent_printGcFinishElement(event);
	}
}

/** PROCESS_OBJ_ALLOC_EVENT  **************************************************
  *
  */
static void processObjAllocEvent(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
								 BOOL isRequested,
								 timestamp_t timestamp,
								 timestamp_t cpu_timestamp)
{
 HashEntry *classHashEntry;
 HashEntry *objectHashEntry;
 BOOL allocatedOnTracedStackframe=FALSE;

 if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap || _jvmpiAgent_Options.mode == TraceModeNone) {
	/* do nothing */
	return;
 }

 /* In case the mode of 'tps' is set to 1, then this handler will only need to resolve the class
  * id of the object that has been dumped */
 if (tps->mode == 1)
 {
	 _resolvedClassId = event->u.obj_alloc.class_id;
	 return;
 }


 objectHashEntry = jvmpiAgent_FindObjectSymbol(event->u.obj_alloc.obj_id); 
 if (objectHashEntry) {
	 /* the object already exists, don't do anything */ 
	 return; 
 }

 /* RKD:  Determine if this object is being allocated on a traced stack frame
  */
  if(_jvmpiAgent_Options.stackInfo>StackInfoNone) {
    StackEntry *tos;
	if(tos=jvmpiAgent_Peek(tps, 0)) {
	   allocatedOnTracedStackframe=(BOOL)(tos->printed);
	}
  }


  /* RJD We normally shouldn't receive an OBJ_ALLOC with class_id == 0 and 
     is_array = JVMPI_NORMAL_OBJECT; however, the Sun JVM seems to send 
	 this for the object instance of java.lang.Class. 
	 We handle this by assigning the class_id to the object_id (since the 
	 object instance of java.lang.Class is clearly of type java.lang.Class).

	 Only do this when _requestClassObj is set (which should only be set 
	 from a heap dump). Further, only do this when IsRequest bit is set to true. 
	 This is a work around that may have some subtle concurrency issues crop up (because
	 of bugs with different JVMs), so this should be kept in mind if examining a 
	 future bug that seems to be a result of the following code. 
	 bugzilla_57475 */ 

  if (!event->u.obj_alloc.class_id && 
	  event->u.obj_alloc.is_array == JVMPI_NORMAL_OBJECT &&
	  _requestClassObj && isRequested) {
	  event->u.obj_alloc.class_id = event->u.obj_alloc.obj_id; 
  }



 /* ##RKD:  We could have an object allocate and not know about it's class.  This
            is a performance hit to look up the class whenever we get an object
	        with unknown type..but I don't see any way around it right now.  Also
			don't try and lookup primitive array classes as this fries the JVM.  As for
			classes with a class id of "0", this is a documented limitation.  It
			appears that the class id of all object arrays is always NULL.  If we have an
			array object manually associate it with our spoofed  array class objects. We
			only need to do this if the array object was explicitly asked for.
 */
 if(event->u.obj_alloc.class_id && (event->u.obj_alloc.is_array==JVMPI_NORMAL_OBJECT
 #if !(defined  _HPUX || defined _SOLARIS)
 || event->u.obj_alloc.is_array==JVMPI_CLASS) ) {
 #else
        )) {
 #endif

	
	classHashEntry = jvmpiAgent_FindClassSymbol(event->u.obj_alloc.class_id);
	if(!classHashEntry) {
		jint result;
		result=REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, event->u.obj_alloc.class_id); /* 232010 */
		/* Look up the class again*/
		if(result == JVMPI_SUCCESS) {
			classHashEntry = jvmpiAgent_FindClassSymbol(event->u.obj_alloc.class_id);
		}
	}

	/* Always add this object if we explicitly asked for it.  In filtered mode store it if
	   we are tracing this type.  In triggered mode we will store it if it is on the stack.
	*/
	if(classHashEntry) {
		if(_jvmpiAgent_Options.startMode==TraceStartModeFilter) {
			if(CLASS_ENTRY(classHashEntry)->traceFlag || isRequested) {
				objectHashEntry = jvmpiAgent_CreateObjectSymbol(event, (BOOL)(!isRequested), (BOOL)(!isRequested));
				OBJECT_ENTRY(objectHashEntry)->classHashEntry=classHashEntry;

				/* Print this object only if we are tracing this type and we want object information */
				if(OBJECT_ENTRY(objectHashEntry)->traceFlag && jvmpiAgent_isTracingHeap()) {
				  jvmpiAgent_printObjAllocElement(objectHashEntry, event->env_id, 0);
				}
			}
		}
		else {
			if(isRequested) {
				objectHashEntry = jvmpiAgent_CreateObjectSymbol(event, FALSE, FALSE);
				OBJECT_ENTRY(objectHashEntry)->classHashEntry=classHashEntry;
			}
			else if(allocatedOnTracedStackframe) {
				objectHashEntry = jvmpiAgent_CreateObjectSymbol(event, TRUE, (BOOL)(!isRequested));
				
				OBJECT_ENTRY(objectHashEntry)->classHashEntry=classHashEntry;
				jvmpiAgent_printObjAllocElement(objectHashEntry, event->env_id, 0);
			}
		}
	}
 }
 /* This is a primative array. If we requested it's alloc event then create it's entries.  Also we print
     primative array allocations if the current stack frame has been printed
 */
 else if(isRequested || allocatedOnTracedStackframe || _jvmpiAgent_Options.stackInfo==StackInfoNone) {
    ObjectEntry *objectEntry;

/* RJD. The 1.3 JVM's on Solaris and HP crash when requesting a
   JVMPI_EVENT_CLASS_LOAD event on class_id of array objects due to a bug in the JVM.
   Thus, do not look up the
   class in those cases. 
   bugzilla_56189 */


#if (defined  _HPUX || defined _SOLARIS)
	if(event->u.obj_alloc.class_id && 
		(_javaVersion != Version13 || event->u.obj_alloc.is_array!=JVMPI_CLASS)) {
#else
    if(event->u.obj_alloc.class_id) {
#endif

        classHashEntry = jvmpiAgent_FindClassSymbol(event->u.obj_alloc.class_id);
	    if(!classHashEntry) {
		    jint result;
		    result=REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, event->u.obj_alloc.class_id); /* 232010 */
		    /* Look up the class again*/
		    if(result == JVMPI_SUCCESS) {
			    classHashEntry = jvmpiAgent_FindClassSymbol(event->u.obj_alloc.class_id);
		    }

            objectHashEntry = jvmpiAgent_CreateObjectSymbol(event, FALSE, FALSE);
			OBJECT_ENTRY(objectHashEntry)->classHashEntry=classHashEntry;
	    }
    }
    else {
        classHashEntry=jvmpiAgent_getPrimativeClassEntry(event->u.obj_alloc.is_array);
        if(classHashEntry && CLASS_ENTRY(classHashEntry)->traceFlag && jvmpiAgent_isTracingHeap()) {
            jvmpiAgent_outputClassDeclaration(classHashEntry, tps);
            objectHashEntry = jvmpiAgent_CreateObjectSymbol(event, allocatedOnTracedStackframe, (BOOL)(!isRequested));
	        objectEntry=OBJECT_ENTRY(objectHashEntry);
            jvmpiAgent_printObjAllocElement(objectHashEntry, event->env_id, 0);
         }
    }
 }

 if(!isRequested) {
   /* NMM: here is where we accumulate the tracing overhead due to
      object allocation processing */
   StackEntry* tos;
   if(tps
      && (tos=jvmpiAgent_Peek(tps, 0))
      && tos->entryEventSeen) {
     timestamp_t now;
	 timestamp_t cpu_now = 0; 
     Uint64    objalloc_overhead;
	 Uint64    cpu_objalloc_overhead = 0;
	 	
	 if (_jvmpiAgent_Options.cpuTime) {
		 cpu_now = jvmpiAgent_getCurrentThreadCPUTime(); 		
		 cpu_objalloc_overhead = TIMESTAMP_SUB(cpu_now,cpu_timestamp); 
	 }

     jvmpiAgent_getCurrentTime(&now);
     objalloc_overhead        = TIMESTAMP_SUB(now,timestamp);
     tos->cumulative_overhead += objalloc_overhead;
	 tos->cumulative_cpu_overhead += cpu_objalloc_overhead; 
	 
   }
 }
}

/** PROCESS_CLASS_LOAD_HOOK_EVENT  ********************************************
  *
  */
static void processClassLoadHookEvent(JVMPI_Event *event,
									  ThreadLocalStorage *tps,
									  BOOL isRequested,
									  timestamp_t timestamp,
									  timestamp_t cpu_timestamp)
{
	/* Bug 174994 starts */
	event->u.class_load_hook.new_class_data = event->u.class_load_hook.class_data;
	event->u.class_load_hook.new_class_data_len = event->u.class_load_hook.class_data_len;
	/* Bug 174994 ends */
}

/** PROCESS_DATA_DUMP_REQUEST
 * This is the kill-3 handler
 */
static void processDataDumpRequest(JVMPI_Event *event,
				   ThreadLocalStorage *tps,
				   BOOL isRequested,
				   timestamp_t timestamp,
					timestamp_t cpu_timestamp) {
  if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
    /* status(KILL3_DUMP_REQUEST); */
	_analyseOptHeap = TRUE ;
    analyseHeap(JVMPI_DUMP_LEVEL_1);

	_analyseOptHeap = FALSE ;
	_optHeapError = 0 ;
  }
  else if (_jvmpiAgent_Options.mode == TraceHeap) {
    analyseHeap(JVMPI_DUMP_LEVEL_1);
  }
}

/* Piyush Agarwal */
/* Optimized Heap Dump from RAC */
/* We need to send the heapDumpFile Name to the RAC */
static void sendHeapDumpInformation(char *filename){

	/* Giri: <Defect 64462> Added the standalone mode check, instead of isControlled check */
	/* THe pring HD Start Element should be sent at the end after the heap dump is taken for optHeap */
	
	if ( _jvmpiAgent_Options.mode == TraceOptimizedHeap && !_jvmpiAgent_Options.standalone )
	{
		ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
#ifdef __OS400__
		/* Anandi  25 May 2004: The print routines expect ascii chars in 
		   strings */
		char *tmpname = as400_etoa(filename);
		jvmpiAgent_printHDStartElement(tps,tmpname) ;
		ra_free(tmpname);
#else
  /* Bug 64476 Added change for converting the name to ascii from ebcdic */
  #ifdef MVS
    __etoa(filename) ;
  #endif
		jvmpiAgent_printHDStartElement(tps,filename) ;
  #ifdef MVS
    __atoe(filename) ;
  #endif
#endif
	/*Bug 82214*/
	if (!_optHeapError && (optHeapContextId != -1)) {
		sendHeapDumpEndMessage(_jvmpiAgent_bindingStorage,optHeapContextId,filename);
	}
	/*Bug 82214*/

	}
}

static void processRACDataDumpRequest(BOOL finalheap,ra_uint_t contextId)
{
  optHeapContextId = contextId ;
  if (_jvmpiAgent_Options.mode ==  TraceOptimizedHeap) {
    /* Called From client */
	/* We also need to send an empty heap dump header to the workbench */
	_analyseOptHeap = TRUE ;
	
	analyseHeap(JVMPI_DUMP_LEVEL_1);
	/* If this application is being controlled/moitored remotely */
	if (!_jvmpiAgent_Options.standalone) {
		if (_optHeapError)
		{
			sendOptHeapErrorMessage(_jvmpiAgent_bindingStorage, /* Piyush 58049 */
				 			  contextId) ;
		}
		_optHeapError = 0 ;
	}
	_analyseOptHeap = FALSE ; 
  }
  optHeapContextId = -1 ;
}

/* Send message to RAC which then sends it to the client */
static void sendHeapDumpEndMessage(RA_AGENT_HANDLE handle,ra_uint_t contextId,char* filename)
{
	ra_message_t *message;
	ra_command_t *command;
	char *tmpMessageName ;

	message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage(message, NULL);

	/* Set the agent information */
	command->tag= RA_CUSTOM_COMMAND ;
	command->info.custom_command.context= contextId ;
	command->info.custom_command.processId = ra_getProcessId();

/*Bug 64476*/
#ifdef MVS
#pragma convlit(suspend)
#endif
	tmpMessageName = (char *)malloc(sizeof("RA_OPT_HEAP_DUMP_DONE")+strlen(filename)+(2*sizeof(char))) ;
	strcpy(tmpMessageName,"RA_OPT_HEAP_DUMP_DONE:") ;
	strcat(tmpMessageName,filename) ;
	ra_copyRASTRING(&command->info.custom_command.agent, &handle->agentName);
	ra_createRASTRING(&command->info.custom_command.message,tmpMessageName);
	free(tmpMessageName) ;
#ifdef MVS
#pragma convlit(resume)
#endif

	/* Inform server about heap done */
	ra_sendMessage(handle, message);
	/* Clean up the allocated memory */
	ra_destroyMessage(message, TRUE); 
}

/* Send error message to RAC which then sends it to the client */
/* Piyush 58049 */
static void sendOptHeapErrorMessage(RA_AGENT_HANDLE handle,
									ra_uint_t contextId)
{
	ra_message_t *message;
	ra_command_t *command;

	message=ra_createMessage(RA_CONTROL_MESSAGE, 0);
	command=ra_addCommandToMessage(message, NULL);

	/* Set the agent information */
	command->tag= RA_CUSTOM_COMMAND ;
	command->info.custom_command.context= contextId ;
	command->info.custom_command.processId = ra_getProcessId();

/* Giri: Defect 70308 */
#ifdef MVS
#pragma convlit(suspend)
#endif

	ra_copyRASTRING(&command->info.custom_command.agent, &handle->agentName);
	switch (_optHeapError) {
	  case RA_OPT_HEAP_DISK_FULL :
		ra_createRASTRING(&command->info.custom_command.message, "RA_OPT_HEAP_DISK_FULL");
		break ;
	  case RA_OPT_HEAP_COULD_NOT_CREATE_FILE :
		ra_createRASTRING(&command->info.custom_command.message, "RA_OPT_HEAP_COULD_NOT_CREATE_FILE");
		break ;
	}
	
/* Giri: Defect 70308 */	
#ifdef MVS
#pragma convlit(resume)
#endif

	/* Inform server about heap done */
	ra_sendMessage(handle, message);
	/* Clean up the allocated memory */
	ra_destroyMessage(message, TRUE); 
}

/** PROCESS_HEAP_DUMP_EVENT  ***************************************************
  * This is called by notify_event when we are getting a heap dump data as the
  * result of a heap dump request.
  */
static void processHeapDumpEvent(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	
        if (_jvmpiAgent_Options.mode == TraceModeNone) {
                return;
        }
	
	switch (event->u.heap_dump.dump_level) {
		case JVMPI_DUMP_LEVEL_0:  /* we must have asked for a dump population */
			if(_jvmpiAgent_Options.mode != TraceOptimizedHeap && _jvmpiAgent_isMonitored )
			{
				jvmpiAgent_markHeap(event);
			}
			break;
		case JVMPI_DUMP_LEVEL_1:  /* we must have asked for a dump for references */
			if(_jvmpiAgent_Options.mode ==  TraceOptimizedHeap && !_optHeapError ) { /*58049*/
				_optHeapError = StatelessHeapSnapshotManager_handleHeapSnapshot_FromC(event->env_id,
									  event->u.heap_dump.dump_level,
									  event->u.heap_dump.begin,
									  event->u.heap_dump.end);
			} else {
				jvmpiAgent_analyseHeap(event, _heapDefName);
			}
			break;
		default:
			break;
	} /* dump_level switch */

}





/* long getOwnerThread(jobjectID object_id)

	Obtain the owner thread of the given monitor object

  args -
    object_id - the object (monitor) whose thread owner is to be determined.
	tps - thread local storage for storing monitor dump

  returns -
    the thread that owns the given object
*/

unsigned long getOwnerThread(jobjectID object_id, ThreadLocalStorage *tps) {

	int result = JVMPI_FAIL;

	tps->monitorObj = object_id; 
	if (object_id != 0) {

		/* Requesting MONITOR_DUMP in IBM 1.4.x JVM's prior to 1.4.2 cause
		   the JVM to crash. Do not  request a JVMPI_EVENT_MONITOR_DUMP in those cases */ 
		if (!(_javaVendor == VendorIBM && 
			 (_javaVersion == Version140 || _javaVersion == Version141))) {
		
				result = REQUEST_EVENT(JVMPI_EVENT_MONITOR_DUMP,0);
		}

	}

	if (result == JVMPI_SUCCESS) {
		/* the owner thread is set in the tps->ownerThread variable upon return
		   from the monitor dump request */ 
		return tps->ownerThread; 
	}

	return 0;

}


static void processMonitorDumpEvent(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {
	
	tps->ownerThread = jvmpiAgent_analyseMonitorDump(event,tps->monitorObj); 
}

static void processMonitorContendedEnter(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {

	HashEntry *objectHashEntry = 0; /* hash entry corresponding to the monitor object */
	unsigned long thread_owner = 0;

	/* determine the thread that currently owns the monitor */ 
	thread_owner = getOwnerThread(event->u.monitor.object,tps);
	
	
	/* lookup monitor object, ensuring that it exists in the trace */
	objectHashEntry = jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(event->env_id,
		event->u.monitor.object);
	

	if (objectHashEntry) {
		jvmpiAgent_printMonitorContendedEnterElement(objectHashEntry, event,
			thread_owner, timestamp);
	}

}

static void processMonitorContendedEntered(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {

	HashEntry *objectHashEntry = 0; /* hash entry corresponding to the monitor object */	
	
	/* lookup monitor object, ensuring that it exists in the trace */
	objectHashEntry = jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(event->env_id,
		event->u.monitor.object);
	

	if (objectHashEntry) {
		jvmpiAgent_printMonitorContendedEnteredElement(objectHashEntry, event, timestamp);
	}

}

static void processMonitorContendedExit(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp) {
	/* currently not required according to BlueRat specs; stub in place since it might
	be useful */
}

static void processMonitorWait(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {

	HashEntry *objectHashEntry = 0; /* hash entry corresponding to the monitor object */
	int isThreadSleep = 0;			/* boolean indicating if monitor wait occured in thread
									   sleep */


	
	/* if the monitor object is NOT NULL, it is a regular object,
	   if it is NULL, then it indicates that the wait is in Thread.sleep() */

	if ((void *) event->u.monitor_wait.object != NULL) {
		/* lookup monitor object, ensuring that it exists in the trace */
		objectHashEntry = jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(event->env_id,
			event->u.monitor.object);
	}
	else {
		isThreadSleep = 1;
	}

	/* print the element only if we managed to resolve the hash entry for the monitor
	   object, or if the the monitor waited event was due to Thread.sleep(). */
	if (objectHashEntry || isThreadSleep) {
		jvmpiAgent_printMonitorWaitElement(objectHashEntry, event, timestamp, isThreadSleep);
	}


}

static void processMonitorWaited(JVMPI_Event *event,
								 ThreadLocalStorage *tps,
                                 BOOL isRequested,
								 timestamp_t timestamp,
									timestamp_t cpu_timestamp) {


	HashEntry *objectHashEntry = 0; /* hash entry corresponding to the monitor object */
	int isThreadSleep = 0;			/* boolean indicating if monitor wait occured in thread
									   sleep */

	/* if the monitor object is not NULL, it is a regular object,
	   if it is NULL, then it indicates that the wait is in Thread.sleep() */

	if ((void *) event->u.monitor_wait.object != NULL) {
		/* lookup monitor object, ensuring that it exists in the trace */
		objectHashEntry = jvmpiAgent_FindObjectSymbolWithAllocateAndPrint(event->env_id,
			event->u.monitor.object);
	}
	else {
		isThreadSleep = 1;
	}
	
	/* print the element only if we managed to resolve the hash entry for the monitor
	  object, or if the the monitor waited event was due to Thread.sleep(). */
	if (objectHashEntry || isThreadSleep) {
		jvmpiAgent_printMonitorWaitedElement(objectHashEntry, event, timestamp,isThreadSleep);
	}

}


/** NOTIFY_EVENT  -- JVMPI EVENT HANDLER FUNCTION  *****************************
  * This is the entry point for all events from the JVMPI interface.  These
  * events may be because of registered events or specific requested events.
  * from here we jump to the specific handler for each event.
  */
static void notify_event(JVMPI_Event *event) {
	BOOL isRequested=FALSE;
	timestamp_t timestamp;
	timestamp_t cpu_timestamp = 0; 
	jint masked_event_type = event->event_type & (0xffffffff ^  JVMPI_REQUESTED_EVENT);

	/* Get the tps */
	ThreadPrivateStorage *tps=jvmpiAgent_getThreadLocalStorage(event->env_id);

	/* Get the timestamp */
	jvmpiAgent_getCurrentTime(&timestamp);
	if (_jvmpiAgent_Options.cpuTime) {
		cpu_timestamp = jvmpiAgent_getCurrentThreadCPUTime(); 

		/* record the last CPU timestamp on this thread; we'll need this later.
		 * (Actually, it's only needed if pre-aggregating (CompressAggregate), but
		 * testing that would slow this function down unnecessarily.)
		 */
		tps->last_cpu_timestamp = cpu_timestamp;
	}

	/* Check for disablement. Note: today (3/29/04) no code disables events */
	if(tps->disableEventsForThisThread) {
        return;
    }

	/*------------------------------------------------------------
	 * Route this event to an agent extension if one is listening.
	 * This is a simple dispatcher system with no advanced logic
	 * for (e.g.) sending requested events to the requesting agent only.
	 *
	 * Agent extensions get events regardless of thread or burst timeout.
	 * They get the event pointer plus their own thread local storage pointer.
	 */
	if (agent_extension_handlers[masked_event_type] != NULL) {
		agent_extension_handlers[masked_event_type](event, &(tps->agent_extension_slot));
	}
	/*------------------------------------------------------------*/

	/* Just route the event to the appropriate event handler function */
	if(event->event_type & JVMPI_REQUESTED_EVENT)  {
		event->event_type=masked_event_type;
		isRequested=TRUE;
	}
	else {
		
		/* If we are in burst timeout then stop tracing now */
		if(_jvmpiAgent_burstTimeoutSet) {
			if(TIMESTAMP_GREATER(timestamp,_burstTimeout)) {
				suspendTracing(TracePause);
			}
		}

		/* If we are in single threaded tracing ignore this event if it isn't on the proper thread.  Can't do
		   this if we are insturmenting code though
		*/
		if(event->event_type!=JVMPI_EVENT_CLASS_LOAD_HOOK
			&& _jvmpiAgent_singleThreaded
			&& _jvmpiAgent_limitingThread!=event->env_id) {
			return;
		}
	}

	/*
	 * Bug 185988
	 *
	 * In the case of JVMPI_EVENT_CLASS_LOAD_HOOK event, we should only pass to either the piAgent handler or the agent
	 * extension handler (not both). The reason is that if we handle this event in both places the probe insertion will
	 * fail due to pointer overwrites. This event, when enabled, has to be handled or the JVM might hang or crash.
	 *
	 * Please refer to bugzilla 174994 for more details.
	 *
	 */
	if((event->event_type == JVMPI_EVENT_CLASS_LOAD_HOOK) && agent_extension_handlers[JVMPI_EVENT_CLASS_LOAD_HOOK] != NULL) {
		/* Do nothing since this event has already been handled */
	}
	else {
		/* Check for null: maybe only an agent extension has a handler */
		if (_processEventFunction[event->event_type] != NULL) {
			_processEventFunction[event->event_type](event, tps, isRequested, timestamp,cpu_timestamp);
		}
	}
}


/** AGENT_EXTENSION_SET_EVENT_HANDLER *******************************************************
  * Remember an agent extension's handler for an event.
  *
  * This function is called from agent extensions. The agent extension
  * calls this function to set a handler and enable the corresponding
  * JVMPI event.
  * 
  * In stand-alone mode, this function actually calls JVMPI to enable the event.
  *
  * But in non-standalone mode, if isJVMInitDone is still false,
  * we don't enable the event. Instead, we just record the extension's
  * desire to handle the event. Later we will loop through and enable
  * the events that the extension asked for.
  *
  * Why? Because in non-standalone mode, the agent extension's init function
  * is calling us from a different thread - not the main thread that JVM_OnLoad
  * was called on. If you try to enable events from this other thread,
  * the JVM crashes.
  *
  * Rather than needlessly attaching threads to the JVM (and risk having
  * them stay attached too long, or detach too soon), I decided to queue 
  * up the "enable" operations and process them in the main (OnLoad) thread 
  * after the listener gets unblocked - that is, when Workbench sends RESUME.
  *
  * In standalone mode, this system is not necessary because the extension
  * agent init function is called from the main thread, where OnLoad is running.
  *
  * This system isn't used in "enabled" or "application" mode. In those
  * modes, ra_startListener doesn't block, so it doesn't work to queue the 
  * requests and handle them when ra_startListener returns. But it's
  * also not a standalone mode, meaning commands will come in on the
  * listener thread (and calling enableEvent on a separate thread is bad).
  *
  * The agent_extension_init function gets a flag telling the extension
  * whether it's safe to call enable_event_handler. It's safe in standalone
  * mode, and in controlled mode, but not in application mode or enabled mode.
  * 
  * One way to make sure it's safe is to start a new thread, attach that
  * thread to the JVM, make the SetEventHandler call, detach the thread,
  * then let the thread die.
  */
static void agentExtensionSetEventHandler(jint event_type,
									AgentExtensionEventHandler handler) {
    agent_extension_handlers[event_type] = handler;
	if (_jvmpiAgent_Options.standalone || _jvmpiAgent_isListenerUnblocked) {
		_jvmpiAgent_jvmpiInterface->EnableEvent(event_type, 0);
	}
}

/** AGENT_EXTENSION_SET_COMMAND_HANDLER *******************************************************
  * Records an agent extension's handler for RAC commands.
  */
static void agentExtensionSetCommandHandler(void (*handler)(ra_command_t *command)) {
	agent_extension_command_handler = handler;
}


/** LOAD_STACK  ****************************************************************
  * The intention of this method is to materialize a single thread stack
  * within the JVM.  This would be used when doing stack sampling and when
  * starting a trace at some arbitrary point during execution.  The problem is...
  * the IBM JVM often returns the wrong stack.  Furthermore, it does not support
  * asynchronous stack traversal so this needs to be called on the thread
  * that is being loaded.
  *
  * The bug on IBM JDK's appears to be that information missing after a stack
  * load is always at the top of the stack.  Broken stack frame links due to
  * the JIT is the cause.
  */
static void loadStack(ThreadPrivateStorage *tps)
{
  int i;
  JVMPI_CallTrace callTrace;
  timestamp_t now;
  timestamp_t cpu_now = 0; 
  StackFrame *it, *calledListOfCaller; /* pre-agg: 134635 */
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
  dumpMethodEventCounts(); printf("LoadStack: tps = %8x\n", (void *)tps); fflush(stdout);
#endif

  /* Ignore the Global thread */
  if(!tps->env)
  {
	return;
  }


  jvmpiAgent_getCurrentTime(&now);
  if (_jvmpiAgent_Options.cpuTime) {
	  cpu_now = jvmpiAgent_getCurrentThreadCPUTime(); 
	  tps->last_cpu_timestamp = cpu_now; 	/* remember last known CPU time */
  }

  callTrace.frames=(JVMPI_CallFrame*)malloc(400*sizeof(JVMPI_CallFrame));
  callTrace.env_id=tps->env;
  _jvmpiAgent_jvmpiInterface->GetCallTrace(&callTrace, 400);
  calledListOfCaller=tps->calledList;  /* pre-agg: 134635 */
  for(i=callTrace.num_frames-1; i>=0; i--)
  {
	  HashEntry *methodHashEntry;
	  StackEntry *stackEntry;
	  methodHashEntry=jvmpiAgent_FindMethodSymbol((callTrace.frames[i].method_id));

	  /* If we don't know about the method we should get the class */
	  if(!methodHashEntry)
	  {
		int result;
		jobjectID clazz=_jvmpiAgent_jvmpiInterface->GetMethodClass(callTrace.frames[i].method_id);
		result=REQUEST_EVENT2(JVMPI_EVENT_CLASS_LOAD, clazz); /* 232010 */
		/* If we found the class we should have the method */
		if(result == JVMPI_SUCCESS) {
			methodHashEntry = jvmpiAgent_FindMethodSymbol((callTrace.frames[i].method_id));
		}
	  }
	  stackEntry=jvmpiAgent_Push(tps, tps->env, methodHashEntry ,NULL, now,cpu_now);
	  stackEntry->entryEventSeen=0;

      /* 134635 */
      /* Note that, before pre-agg was supported, filtering was not tested for and methodEntryEvents 
       * were not printed for these re-constructed method invocations. However, any 
       * MethodEntryEvents emitted from a previous run of this thread were still in effect and 
       * resumption of the thread picked-up where the last one left off. To get the same effect in 
       * the pre-agg case, it's necessary to determine if any of the thread's pre-existing 
       * calledList trees/sub-trees (from a previous run of the thread) has a record of the currrent 
       * call sequence being loaded (or of any starting portion thereof). If so, those stackEntries 
       * represented in the calledList tree will be connected to the corresponding stackFrames in 
       * that tree (and are marked as "realFrames" and "printed"). This occurs after a resume that 
       * follows a pause but *not* after an attach that follows a detach (because, in the 
       * detach/attach case, the calledList tree will have been cleared/freed by the detach).
       */
      if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {  /* pre-agg: 134635 */ 
         /* search for current method in calledList of Caller: if found, it will be a non-null "it"              */
         for (it=calledListOfCaller; it!=NULL && it->methodHashEntry!=methodHashEntry; it=it->next);  
         
         if (it != NULL) { /* if it!=NULL then the current method was found as a child of the caller frame        */
         	stackEntry->stackFrame = it;          /* connect the current stack entry to the found frame           */
           	stackEntry->realFrame = 1;            /* treat almost as a CreateStackFrame operation        * 139537 */
        	stackEntry->printed = 1;              /* treat almost as a CreateStackFrame operation        * 139537 */
            calledListOfCaller = it->calledList;  /* the next stack entry's method will need to one of this frame's    
                                                   * children for the repeated starting call sequence to continue */
         } else {                                                                                       /* 139537 */
         	calledListOfCaller = NULL;            /* this terminates the repeated starting call sequence * 139537 */
         }
      }
  }
  free(callTrace.frames);

  /* RKD:  Mark this thread as a valid thread only if the thread stack is depth is greater then zero.  This check
           needs to be here as the JIT in some IBM JDKS doesn't always give the stack information when you invoke
           GetCallTrace()
  */
  if(callTrace.num_frames > 0)
  {
    tps->threadStackKnown=1;
	/* pre-agg: add this thread to the list of threads that we will want to 
	 * dump data for when requested, or when they die.
	 */
    if(_jvmpiAgent_Options.compressLevel==CompressAggregate) {  /* pre-agg: 134635 */
		addThreadToList(tps);
	}
  }
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
  dumpMethodEventCounts(); printf("LoadStack: tps = %8x Done!\n", (void *)tps); fflush(stdout);
#endif


  return;
}

/** STACK_CLEANER  ************************************************************
  * Walk a stack and cleanup all the memory in use.
  */
int stackCleaner(HashEntry *hashEntry,
                 void *parm) {
	ThreadPrivateStorage *tps;
	tps=(ThreadPrivateStorage*)THREAD_ENTRY(hashEntry);

	/* Pop all the entries on the stack */
	while(tps->tos) {
		jvmpiAgent_Pop(tps);
	}
	/* Mark this thread as an invalid thread */
	tps->threadStackKnown=0;

	return 0;
}


/** DELETE_HASH_ENTRY  *********************************************************
  *
  */
int deleteHashEntry(HashEntry *entry,
                    void *param) {
	jvmpiAgent_DeleteSymbol(entry, (*(enum EntryType*)param));
	return 0;
}




/** CLEAN_UP_ALL_TRACE_ROSOURCES  **********************************************
  * Cleanup all our resources that were allocated during a trace so that we have
  * no history of events/objects to refer to.  With this done we can start a new
  * trace.
  */
static void cleanUpAllTraceResources() {
	enum EntryType type;

	_traceResourcesGone = TRUE;  /* pre-agg: 134635 */

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("cleanUpAllTraceResources\n"); fflush(stdout);
#endif
	/* Delete each of the objects */
	type=Object_t;
	jvmpiAgent_ForAll(Object_t, deleteHashEntry, &type);

	/* 174841 - RKD:  Always keep the state of threads
	   so that the stack can be re-materialized later if
	   necessary
	type=Thread_t;
	jvmpiAgent_ForAll(Thread_t, deleteHashEntry, &type);
	174841 end */

	/* pre-aggregation */
	/* Remove all threads from the "threadsRoot" list of live threads.
	 * This will free the StackFrames pointed to by the threads,
	 * and set all tps->callerList to NULL.
     *
	 * Note that threadEnds sometimes occur even after Detach/cleanup.
	 * Thus, removeThreadFromList could be underway on multiple "threads"
	 * but the ThreadList lock combined with the traceResourcesGone flag
	 * should be sufficient to provide correct operation even in that
	 * scenario.
	 */
	if (_jvmpiAgent_Options.compressLevel==CompressAggregate) { /* 134635 */ 
		while (threadsRoot != NULL) {
			removeThreadFromList(threadsRoot->data);
		}
	}	

	/* Delete each of the classes */
	type=Class_t;
	jvmpiAgent_ForAll(Class_t, deleteHashEntry, &type);

	/* Delete each of the methods */
	type=Method_t;
	jvmpiAgent_ForAll(Method_t, deleteHashEntry, &type);

	/* deleting the class hashtable will have also removed the primitive types;
	   if someone reattaches to the profiler, the primitive types will not be
	   regenerated automatically. Hence, we must regenerate them now. */
	
	createPrimitiveTypes();
}


/** START_TRACING  *************************************************************
  * This is the entry point which must be called to start a trace. It takes
  * care of emitting all the header information and turning the JVMPI events
  * on so we will start recieving events.
  */
static void startTracing(BOOL standalone) {

	_jvmpiAgent_suspendIO = 0;
	_traceResourcesGone = FALSE;  /* pre-agg: 134635 */

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("startTracing\n"); fflush(stdout);
#endif
	
	/**	Giri: <Defect 64462> We need to do some optHeap related setup, before we can request opt Heap dumps
	So we do the required set-up if the mode is TraceOptimizedHeap */

	if (_jvmpiAgent_Options.mode == TraceOptimizedHeap )	{
		jvmpiAgent_DoOptHeapSetup();
	}
	
	jvmpiAgent_PrintStartingXMLFragments();
    _xmlHeadersPrinted=TRUE;

 	/* We need to ensure the entire sybmol table is "unreferenced" to ensure we get the references
			   for all method invocations for this trace.
	 */
	jvmpiAgent_clearAllPrintFlags();

	/* Reset the trigger flag if we are not running in application mode. */
	if(!_jvmpiAgent_Options.application) {
		_triggerSqueezed=FALSE;
	}
	else {
		_jvmpiAgent_Options.startMode=TraceStartModeTriggerMultiThreaded;
	}

	resumeTracing();	
}

static void resumeTracing() {

	/* Turn on IO */
	_jvmpiAgent_suspendIO = 0;

	/* If we are burst tracing set the burst information */
	if(_jvmpiAgent_Options.burstMode>BurstModeNone) {
		switch(_jvmpiAgent_Options.burstMode) {
		case BurstModeInvocations:
			_invocationCountRemaining=_jvmpiAgent_Options.burstInvocations;
			break;
		case BurstModeSeconds:
		  {
		    timestamp_t ticks;
			jvmpiAgent_getCurrentTime(&_burstTimeout);
			ticks = timeToTicks(_jvmpiAgent_Options.burstSeconds, 0);
			TIMESTAMP_ADD(_burstTimeout,ticks);
			_jvmpiAgent_burstTimeoutSet=TRUE;
			break;
		  }
		case BurstModeSecondsAndInvocations:
		  {
		  timestamp_t ticks;
			_invocationCountRemaining=_jvmpiAgent_Options.burstInvocations;
			ticks = timeToTicks(_jvmpiAgent_Options.burstSeconds, 0);
			jvmpiAgent_getCurrentTime(&_burstTimeout);
			TIMESTAMP_ADD(_burstTimeout,ticks);
			_jvmpiAgent_burstTimeoutSet=TRUE;
		  }
		}	
	}

	_jvmpiAgent_isSuspended=FALSE;

	/* Enable the events */
  	toggleActiveJvmpiEvents(TraceON);
}


/** SUSPEND_TRACING  ***********************************************************
  *
  * toggle - TraceOFF - completely turning off tracing
  *          TracePause - pausing tracing 
  */
static void suspendTracing(TraceToggle toggle) {

	/* pre-aggregation:  
	 * Roll up stacks in live threads and report their data. This applies both 
	 * to Pause (RA_STOP_MONITORING_AGENT) and to Detach (RA_DETACH_FROM_AGENT).
	 * NB: the agPops triggered in this stack rollup break the links between the
	 *     stackEntries and corresponding stackFrames. This ensures that the 
	 *     agPops from the stackCleaner below do nothing and thus avoid a double
	 *     rollup that would produce double counts. (See jvmpiAgent_agPop.)
	 */
	if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {  /* 134635 */
		/* pre-agg mode; roll up stacks and dump data */
		timestamp_t now;
		jvmpiAgent_getCurrentTime(&now);
		rollUpAndPrintAllThreadAgData(now);
	}

	/* Turn off IO */
	_jvmpiAgent_suspendIO = 1;
	
	/* Disable the events */
	toggleActiveJvmpiEvents(toggle);

	_jvmpiAgent_burstTimeoutSet=FALSE;

	_jvmpiAgent_isSuspended=TRUE;
	_triggerSqueezed=FALSE;

	/* We need to destroy the stacks */
	jvmpiAgent_ForAll(Thread_t, stackCleaner, NULL);
}

/** STOP_TRACING  **************************************************************
  * This is where a trace is stopped.
  */
static void stopTracing() {
	/* Get the global buffer for IO */
	ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("stopTracing\n"); fflush(stdout);
#endif

	/* Print the TRACE_END element */
    jvmpiAgent_printTraceEndElement(tps);

	suspendTracing(TraceOFF);

	/* Clean up all our resources for this trace */
	cleanUpAllTraceResources();

	_jvmpiAgent_isSuspended=FALSE;

}


/** determineJavaVersion() **********************************************************
 * Determine the java vendor and version so that we can disable certain functionality
 * based on the runtime version to avoid jvm bugs. 
 *
 * @param env the JNIEnv * to use for the JNI calls 
 */ 
#ifdef __OS400__
#pragma convert(819)
#endif
static void determineJavaVersion(JNIEnv *env) {
	
	/* The basic approach is to call System.getProperty("java.version") and
	   System.getProperty("java.vendor") through JNI. If any sort of failure
	   occurs while doing JNI, the behaviour of this method is to fail immediately
	   and return to the caller. */ 

	jclass javaLangSystem;			/* java.lang.System class */ 
	jmethodID getPropertyID;		/* System.getProperty() method */ 
	jstring javaVersionProperty, javaVersionValue; 
	jstring javaVendorProperty, javaVendorValue; 
	int tmpStrLen; 
	char *javaVersionChars;
	char *javaVendorChars; 

	/* initialize javaVendor/Version to defaults */ 
	_javaVendor = VendorOther;
	_javaVersion = VersionOther; 

	/* first we need a reference to the class java.lang.System */ 

	javaLangSystem = FIND_CLASS(env, "java/lang/System");

	if (!javaLangSystem) {
		return;
	}

	/* next we need the jmethodID of the static getProperty() method in the
	   System class */ 

	getPropertyID = GET_SMETHODID(env, javaLangSystem, "getProperty", 
			"(Ljava/lang/String;)Ljava/lang/String;");

	if (!getPropertyID) {
		return;
	}

	/* we need to create a jstring version of the native string "java.version" */ 
	javaVersionProperty = CREATE_JSTRING(env, "java.version");
	if (!javaVersionProperty) {
		return;
	}

	/* we need to create a jstring version of the native string "java.vendor" */ 
	javaVendorProperty = CREATE_JSTRING(env, "java.vendor");
	if (!javaVendorProperty) {
		return;
	}

	/* Here we invoke System.getProperty("java.version") */ 
	javaVersionValue = (jstring) CALL_OBJ_SMETHOD1(env, javaLangSystem, 
		getPropertyID, javaVersionProperty);

	/* make sure there were no exceptions thrown during the preceding call */ 
	if (CHECK_EXCEPTION(env)) {	
		CLEAR_EXCEPTIONS(env);
		return; 
	}
	if (!javaVersionValue) {
		return;
	}

	/* Here we invoke System.getProperty("java.vendor") */ 
	javaVendorValue = (jstring) CALL_OBJ_SMETHOD1(env, javaLangSystem, 
		getPropertyID, javaVendorProperty);
	if (CHECK_EXCEPTION(env)) {	
		CLEAR_EXCEPTIONS(env);
		return; 
	}
	if (!javaVendorValue) {
		return; 
	}
	
	/* convert the jstring's into UTF-8 char arrays */ 
	javaVersionChars = (char *)CONVERT_TO_UTF(env, javaVersionValue);
	if (!javaVersionChars) {
		return;
	}

	javaVendorChars = (char *)CONVERT_TO_UTF(env, javaVendorValue); 
	if (!javaVendorChars) {
		RELEASE_UTF_CHARS(env,javaVersionValue,javaVersionChars); 
		return;
	}



	tmpStrLen = strlen(javaVersionChars); 
	if (tmpStrLen >= 3 && strncmp(javaVersionChars,"1.3",3) == 0) {
		_javaVersion = Version13;
	}
	else if (tmpStrLen >= 5 && strncmp(javaVersionChars,"1.4.0",5) == 0) {
		_javaVersion = Version140; 
	}
	else if (tmpStrLen >= 5 && strncmp(javaVersionChars,"1.4.1",5) == 0) {
		_javaVersion = Version141; 
	}
	else if (tmpStrLen >= 5 && strncmp(javaVersionChars,"1.4.2",5) == 0) {
		_javaVersion = Version142; 
	}
	else {
		_javaVersion = VersionOther; 
	}


	/* determine the vendor */ 
	tmpStrLen = strlen(javaVendorChars); 
	if (tmpStrLen >= IBM_VENDOR_STRING_LENGTH && 
		strncmp(javaVendorChars,IBM_VENDOR_STRING,IBM_VENDOR_STRING_LENGTH) == 0) {
		_javaVendor = VendorIBM; 
	}
	else  if (tmpStrLen >= SUN_VENDOR_STRING_LENGTH && 
		strncmp(javaVendorChars,SUN_VENDOR_STRING,SUN_VENDOR_STRING_LENGTH) == 0) {
		_javaVendor = VendorSun;
	}
	else if (tmpStrLen >= HP_VENDOR_STRING_LENGTH && 
		strncmp(javaVendorChars,HP_VENDOR_STRING,HP_VENDOR_STRING_LENGTH) == 0) {
		_javaVendor = VendorHP; 
	}
	else {
		_javaVendor = VendorOther; 
	}

	/* free up all the strings */ 
	RELEASE_UTF_CHARS(env,javaVersionValue,javaVersionChars); 
	RELEASE_UTF_CHARS(env,javaVendorValue,javaVendorChars); 

}
#ifdef __OS400__
#pragma convert(0)
#endif 



/** PROCESS_JVM_INIT_DONE_EVENT  **********************************************
  * Event called by the JVM when the JVM has completed all of it's
  * initialization.  This is the earliest pont where we can start tracing.
  */
static void processJvmInitDoneEvent(JVMPI_Event *event,
									ThreadLocalStorage *tps,
                                    BOOL isRequested,
									timestamp_t timestamp,
									timestamp_t cpu_timestamp) {


	int currentSuspendIO = _jvmpiAgent_suspendIO;
	int isStarted = 0; 

	/* determine the java runtime vendor/version */ 
	determineJavaVersion(event->env_id); 
	
	/* If we are actively profiling start the trace.  This cannot happen in any of the application mode, the
	   additional test is neccessary as applicationControlled will be monitoing at this time.

	   We start tracing even though _jvmpiAgent_suspendIO may be true in order to be able
	   to later monitor applications that were not monitored from the beginning (see
	   bugzilla 49541 and the next comment.) 

	   We cannot do this trick if we are in enabled mode, however, since this causes
	   problems with OS/400. Starting tracing actually tries forcing the RAC to write
	   data, which, if premature, can result in the problems described in bugzilla 59667.
	   There should be no need to do this trick anyhow when in enabled mode. 
	*/
	if(!_jvmpiAgent_Options.application 
		&& !(currentSuspendIO && _jvmpiAgent_Options.enabled) ) {
		startTracing(TRUE);
		isStarted = 1; 
	}

	/* This little trick is needed to be able to later on monitor 
	 * Java applications that have been  resumed but not monitored */
	
	if (currentSuspendIO && isStarted)
	{	
		stopTracing();	
		_jvmpiAgent_suspendIO = 1;
	}
	

	
	/* MARK the heap now so we have a baseline to start from.  This is nice
	   as the UI can just ask for an ANALYSE without a preceading MARK if
	   desired
	 */
	analyseHeap(JVMPI_DUMP_LEVEL_0);

    /* Set the flag to say we can now trace if we wish */
	_jvmpiAgent_isJVMInitDone=TRUE;

	if ( _jvmpiAgent_Options.mode != TraceModeNone || !_jvmpiAgent_Options.standalone ) {
		jvmpiAgent_printJvmInitDoneElement(event);
	}


}

/** SET_DYNAMIC_EVENT_HANDLERS  *******************************************************
  * Some events will be requested via the RequestEvent() call and hense we need to
  * set the function pointers for these so we don't get burned.
  */
static void setDynamicEventHandlers() {
	_processEventFunction[JVMPI_EVENT_CLASS_LOAD] = processClassLoadEvent;
	_processEventFunction[JVMPI_EVENT_OBJECT_ALLOC] = processObjAllocEvent;
#ifdef __OS400__
	_processEventFunction[JVMPI_EVENT_OBJECT_DUMP] = processObjectDumpEvent;
#endif
}

/** ENABLE_JVMPI_EVENT  *******************************************************
  * Enables an event in JVMPI so that we start recieving these types of
  * events.
  */
static int enableJvmpiEvent(jint event_type,
                            ProcessEventFunction eventHandler) {
	_processEventFunction[event_type] = eventHandler;
	return _jvmpiAgent_jvmpiInterface->EnableEvent(event_type, 0);
}


/** DISABLE_JVMPI_EVENT  ******************************************************
  * Disables an event in JVMPI so that we stop recieving these types of
  * events.
  */
static void disableJvmpiEvent(jint event_type) {
	_jvmpiAgent_jvmpiInterface->DisableEvent(event_type, NULL);
}


/** ENABLE_GLOBAL_JVMPI_EVENTS  ***********************************************
  * Enables all the JVMPI events that we need in order to start tracing at any
  * point in time.  These are the obligitory events.
  */
static void enableGlobalJvmpiEvents(enum TraceMode mode) {

	/* If Exception tracing or line coverage is enabled then activate the class load hook event so that
       we can instrument each class with our trace hooks.
    */
	if (_jvmpiAgent_Options.enableExceptionTracing || _jvmpiAgent_Options.enableLineCoverage) {
		enableJvmpiEvent(JVMPI_EVENT_CLASS_LOAD_HOOK, processClassLoadHookEvent);
	}

	/* Events we always need */
	enableJvmpiEvent(JVMPI_EVENT_JVM_INIT_DONE, processJvmInitDoneEvent);
	enableJvmpiEvent(JVMPI_EVENT_JVM_SHUT_DOWN, processesJVMShutdownEvent);
	/* Bug 62852 - We do not need to track these events for TraceOptimizedHeap Mode */
	if (_jvmpiAgent_Options.mode != TraceOptimizedHeap) {
		enableJvmpiEvent(JVMPI_EVENT_THREAD_START,  processThreadStartEvent);
		enableJvmpiEvent(JVMPI_EVENT_THREAD_END,    processThreadEndEvent);
	}
	_processEventFunction[JVMPI_EVENT_HEAP_DUMP] = processHeapDumpEvent;
	enableJvmpiEvent(JVMPI_EVENT_MONITOR_DUMP, processMonitorDumpEvent); 
	enableJvmpiEvent(JVMPI_EVENT_DATA_DUMP_REQUEST, processDataDumpRequest);
}


/** SAMPLE_STACK  **************************************************************
  * Samples the contents of a stack.  In the current impl this cleans up the
  * stack immediately after sampling its contents.  If we ever get the stack
  * traversal stuff fixed in the JVM we should change loadStack to assign
  * a weight to each stack frame so we can finish the job of providing
  * a stack sampling profiler.
  */
int sampleStack(HashEntry * hashEntry, void *parm) {
	JNIEnv *env=(JNIEnv*)parm;
	ThreadPrivateStorage *tps=THREAD_ENTRY(hashEntry);
	/* Ignore the global thread as it isn't part of the JVM threads.
	   Also don't sample ourselves as this will deadlock.  Thi condition occurs
	   because the thread is attached to the JVM
	*/
	if(tps->env  && tps->env!=env) {

		/* suspend the thread */
		_jvmpiAgent_jvmpiInterface->SuspendThread(tps->env);

		/* Load the stack and then clean it up */
		loadStack(tps);
		stackCleaner(hashEntry, NULL);

		/* Resume the thread */
		_jvmpiAgent_jvmpiInterface->ResumeThread(tps->env);
	}
	return 0;
}

/** STACK_SAMPLER  *************************************************************
  * This is the impl for the background thread that will sample each of the
  * threads in the JVM when we are running in mode=sampling.
  */
void *stackSampler(void *args) {
	JNIEnv *env;
	if(!ATTACH_THREAD(env)) {
		do {
			/* Disable GC */
			_jvmpiAgent_jvmpiInterface->DisableGC();

			/* Sample each of the threads in turn */
			jvmpiAgent_ForAll(Thread_t, sampleStack, (void *)env);

			/* Re-enable GC */
			_jvmpiAgent_jvmpiInterface->EnableGC();
			SLEEP(10);				/* Hardcoded period of 10 milliseconds */
		}
		while(_stackSamplerActive);
		DETACH_THREAD();
	}
	return 0;
}


/**
  * Proxy to the stack sampler thread for porting ease
  */
#ifdef _WIN32
DWORD WINAPI win32stackSamplerProxy(LPVOID args) {
	DWORD returnVal=0;
	stackSampler(args);
	return returnVal;
}
#endif

/** TOGGLE_ACTIVE_JVMPI_EVENTS  ************************************************
  * Toggles the optional JVMPI events on and off so that we can minimize our
  * impact on an executing application when we are not profiling.  The events
  * turned on are determined by the trace mode we are running.
  */
static void toggleActiveJvmpiEvents(TraceToggle toggle) {
#if defined MVS || defined __OS400__
	static TID stackSamplerTid;
#else
	static TID stackSamplerTid=0;
#endif

#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
	printf("toggleActiveJvmpiEvents\n"); fflush(stdout);
#endif

	/* Navid Mehregani - bugzilla 162754: Note that 'processClassLoadHookEvent' is an empty method
	   It's the class load event handler in the agent extension that does the real work. */
	if (toggle == TraceON)
		enableJvmpiEvent(JVMPI_EVENT_CLASS_LOAD_HOOK, processClassLoadHookEvent);
	else		
		disableJvmpiEvent(JVMPI_EVENT_CLASS_LOAD_HOOK);	
	/* End of bugzilla 162754 */

	/** the METHOD_COUNTS_ONLY option overrides all other options **/ 
	if (_jvmpiAgent_Options.methodCountsOnly) {
		if (toggle == TraceON) {
			enableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY, processCountingMethodEntryEvent);
		}
		else {
			disableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY); 
		}
		/* return immediately as methodCountsOnly overrides all other options */ 
		return; 
	}



  /* Register for the correct method events based upon trace mode */
  /* RKD: We should only turn on stack events if our StackInfo > StackInfoNone */
	if(_jvmpiAgent_Options.stackInfo > StackInfoNone) {
		if(_jvmpiAgent_Options.mode == TraceModeFull) {
			if(toggle == TraceON) {
				enableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY2, processMethodEntryEvent);
				enableJvmpiEvent(JVMPI_EVENT_METHOD_EXIT,   processMethodExitEvent);
			}
			else {
				disableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY2);
				disableJvmpiEvent(JVMPI_EVENT_METHOD_EXIT);
			}
		}
		else if(_jvmpiAgent_Options.mode == TraceModeNoObjectCorrelation) {
			if(toggle == TraceON) {
				enableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY, processMethodEntryEvent);
				enableJvmpiEvent(JVMPI_EVENT_METHOD_EXIT,   processMethodExitEvent);
			}
			else {
				disableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY);
				disableJvmpiEvent(JVMPI_EVENT_METHOD_EXIT);
			}
		}
		/* RKD:  This is placed as a placeholder for a stack sampling profiler.
		         This has not shipped due to problems with the IBM JDK with enumerating
				 stack's.
		*/
		else if(_jvmpiAgent_Options.mode == TraceModeStackSampling)	{
			if(toggle == TraceON) {
				_stackSamplerActive=TRUE;
#ifdef _WIN32
				CreateThread(NULL,						/* default security attributes */
					 0,							/* same stack size as current thread */
					 win32stackSamplerProxy,	/* Thread entry point */
					 NULL,						/* params */
					 0,							/* start executing immediately */
					 &stackSamplerTid);				/* the thread ID */
#else
#ifdef _AIX
				pthread_attr_t thread_attr;

				ipthread_attr_init(&thread_attr);
				pthread_attr_setstacksize( &thread_attr, 4194304 );
				pthread_create(&stackSamplerTid,
						  &thread_attr,
						  stackSampler, /* explicit cast to avoid HP compilation failure */
						  NULL);
#else
				pthread_create(&stackSamplerTid,
						  NULL,
						  stackSampler, /* explicit cast to avoid HP compilation failure */
						  NULL);
#endif

#endif

			}
			else {
				_stackSamplerActive=FALSE;
			}
		}
		else /* TraceHeap, TraceOptimizedHeap, or TraceModeNone */ {
			disableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY);
			disableJvmpiEvent(JVMPI_EVENT_METHOD_ENTRY2);
			disableJvmpiEvent(JVMPI_EVENT_METHOD_EXIT);
		}
	}

	/* Do we want object heap information */
	if (jvmpiAgent_isTracingHeap()) {
		switch(toggle) {
		case TraceON: 
			/* RJD We *always* must instrument object free's/move's/and class unloads
			   in this case, because otherwise our hash table
			   may become inconsistent */ 

			/* PA - We need to disable move,free,class Unload events when we are running in
			   TraceOptimized Mode - as in TraceOptimized mode we do not care
			   about the hashtables. If the mode is not TraceOptimizedHeap then
			   enable the moves and frees. 
			*/
			if ( _jvmpiAgent_Options.mode == TraceOptimizedHeap ) {
					disableJvmpiEvent(JVMPI_EVENT_OBJECT_FREE); 
					disableJvmpiEvent(JVMPI_EVENT_OBJECT_MOVE);
					disableJvmpiEvent(JVMPI_EVENT_CLASS_UNLOAD); 
			}
			else /* Mode is not Optmized Heap */{
				enableJvmpiEvent(JVMPI_EVENT_OBJECT_FREE, processObjFreeEvent); 
				enableJvmpiEvent(JVMPI_EVENT_OBJECT_MOVE, processObjMoveEvent);
				/*62852  The following events are not required to be tracked 
				during optimized mode */
				enableJvmpiEvent(JVMPI_EVENT_OBJECT_ALLOC,     processObjAllocEvent);
				enableJvmpiEvent(JVMPI_EVENT_GC_START,      processGcStartEvent);
				enableJvmpiEvent(JVMPI_EVENT_GC_FINISH,     processGcFinishEvent);
				enableJvmpiEvent(JVMPI_EVENT_CLASS_UNLOAD, processClassUnloadEvent); 
			}
			
			break; 
		case TraceOFF: 
			/* PA - We need to disable free events when we are running in
			   TraceOptimized Mode - as in TraceOptimized mode we do not care
			   about the hashtables. If the mode is not TraceOptimizedHeap then
			   enable the moves and frees. If we are in TraceOptimizedHeap then 
			   enable them depending on the gc options.
			*/
			if ( _jvmpiAgent_Options.mode == TraceOptimizedHeap ) {
				/*62852  The following events are not required to be tracked 
				during optimized mode */
				disableJvmpiEvent(JVMPI_EVENT_OBJECT_ALLOC);
			}
			disableJvmpiEvent(JVMPI_EVENT_OBJECT_MOVE);
			disableJvmpiEvent(JVMPI_EVENT_OBJECT_FREE); 
			disableJvmpiEvent(JVMPI_EVENT_CLASS_UNLOAD); 
			disableJvmpiEvent(JVMPI_EVENT_GC_START);
			disableJvmpiEvent(JVMPI_EVENT_GC_FINISH);

			/* fall through to the TracePause case */ 
		case TracePause: 
			/* RJD We cannot disable object free's/move's/class unloads because
			   we do not clean out the hash table; we can however disable the following
			   events */ 
			disableJvmpiEvent(JVMPI_EVENT_OBJECT_ALLOC);
			break;
		default:
			break; 
		}
	}

	/* Do we want monitor information */
	/* Events not dealt with currently:
	             JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTER,
				 JVMPI_EVENT_RAW_MONITOR_CONTENDED_ENTERED,
				 JVMPI_EVENT_RAW_MONITOR_CONTENDED_EXIT
				 (raw monitors aren't created anywhere in the profiler, so they aren't
				 presently needed)

                 JVMPI_EVENT_MONITOR_CONTENDED_EXIT (not currently required according to
			     Bluerat Specs)
	*/

	if (_jvmpiAgent_Options.monitorMode > MonitorNone) {
		if (toggle == TraceON) {
			
			enableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_ENTER, processMonitorContendedEnter);		
			enableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_ENTERED, processMonitorContendedEntered);
			/* enableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_EXIT, processMonitorContendedExit); */
			enableJvmpiEvent(JVMPI_EVENT_MONITOR_WAIT, processMonitorWait);
			enableJvmpiEvent(JVMPI_EVENT_MONITOR_WAITED, processMonitorWaited);
		}
		else {
			disableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_ENTER);
			disableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_ENTERED);
			/* disableJvmpiEvent(JVMPI_EVENT_MONITOR_CONTENDED_EXIT); */
			disableJvmpiEvent(JVMPI_EVENT_MONITOR_WAIT);
			disableJvmpiEvent(JVMPI_EVENT_MONITOR_WAITED);
		}
	}



}

/** startListenerShouldBlock -- ENCAPSULATES LOGIC FOR BLOCKING STARTLISTENER *************
 *
 * This function holds the logic telling whether the call to ra_startListener
 * should block until a RAC connects to this agent.
 * 
 * This logic is used in two places, which is why I pulled it into this
 * function rather than open-coding it. If anybody changes this logic,
 * it'll change in all the places where it's used.
 * 
 * This answer this function gives does not make sense in standalone mode,
 * because in that mode we never call ra_startListener.
 */ 
static char startListenerShouldBlock()
{
	return !_jvmpiAgent_Options.enabled;
}

/** load_agent_extension_library -- AGENT EXTENSION LOADER *************
  * This function loads whatever library is named by the 
  * _jvmpiAgent.extensionLibrary option variable, unless it's the empty string.
  */

static void load_agent_extension_library()
{
	char* extLib = _jvmpiAgent_Options.extensionLibrary;
	
	if (extLib[0] != '\0') {
		DLL_REFERENCE module;
		module = LOAD_LIBRARY(extLib);
#ifdef MVS
		if (module == NULL) {
			/* try converting ASCII to EBCDIC */
			extLib = strdup(extLib);
			__atoe(extLib);
			module = LOAD_LIBRARY(extLib);
			free(extLib);
		}
#endif

		if (module != NULL) {
			AgentExtensionInitFunction agent_extension_init;
#ifdef MVS
#pragma convlit(suspend)
#endif
			agent_extension_init = (AgentExtensionInitFunction)RESOLVE_ENTRY_POINT(module, "agent_extension_init");
#ifdef MVS
#pragma convlit(resume)
#endif
			if (agent_extension_init != NULL) {
				AgentExtensionInitArgs agent_extension_init_args;
				agent_extension_init_args.api_version = 1;
				agent_extension_init_args.options = _jvmpiAgent_Options.invocationOptions;
				agent_extension_init_args.jvm = _jvmpiAgent_jvm;
				agent_extension_init_args.jvmpi_interface = _jvmpiAgent_jvmpiInterface;
				agent_extension_init_args.set_command_handler = agentExtensionSetCommandHandler;
				agent_extension_init_args.set_event_handler = agentExtensionSetEventHandler;
				agent_extension_init_args.agent_handle = _jvmpiAgent_bindingStorage;
				
				/* 
				 * Compute the "safety flag."
				 * This flag is true if it's safe for the agent extension 
				 * to call "agentExtensionSetEventHandler" from inside
				 * "agent_extension_init" or false if it's not safe.
				 * 
				 * It's safe if ra_startListener blocks until a RAC connects,
				 * and it's safe in standalone mode, but otherwise it's not safe.
				 * 
				 * If the "blocking" flag passed to ra_startListener changes,
				 * this code should change too.
				 */
				agent_extension_init_args.event_enable_safety_flag = 
					(_jvmpiAgent_Options.standalone || startListenerShouldBlock());
				

				/* Call the agent extension init function. */
				/* See comments at agentExtensionSetEventHandler about this. */
				agent_extension_init(&agent_extension_init_args);
			}
			else {
				sendErrorMessage(RA_SEVERE, "IWAT5001E", "Profiler failed to find agent_extension_init entry point in library; continuing.\n");
			}
		}
		else {
			sendErrorMessage(RA_SEVERE, "IWAT5000E", "Profiler failed to load agent extension library; continuing.\n");
		}
	}
}



/** _JVMPIAGENT_NOTIFY_MESSAGE  -- AGENT CONTROLLER MESSAGE PUMP  *************
  * Callback function used to process messages from the Agent Controller.  This
  * is the entry point for all control messages comming from the agent controler.
  * It is the responsibility of the agent to react to these messages.
  *
  * This handler reacts to RA_AGENT_CONTROLER_AVAILABLE
  * by checking the configuration for an extension library name,
  * and loading and initializing that library if it's found.
  */

#ifdef MVS                    /* 174190 */
#pragma convlit(suspend)
#endif

static void _jvmpiAgent_notify_message(ra_command_t *command) {
	/*------------------------------------------------------------*
	 * Route this message to an agent extension if one is configured and listening
	 */
	if (agent_extension_command_handler != NULL) {
		agent_extension_command_handler(command);
	}
	/*------------------------------------------------------------*/

	/* Handle the command */
	switch(command->tag) {

	case RA_AGENT_CONTROLER_AVAILABLE:
	{
		ra_agentConfigList_t* clist = ra_getDefaultConfiguration(_jvmpiAgent_bindingStorage);
		ra_agentConfigListEntry_t* listEntry;
		for (listEntry = clist->head; listEntry != NULL; listEntry = listEntry->next) {
			ra_agentConfigEntry_t* e = &listEntry->entry;
			const char* name, *type, *value;
			if (e->name.length == 0) name = "";
			else name = e->name.data;
			if (e->type.length == 0) type = "";
			else type= e->type.data;
			if (e->value.length == 0) value = "";
			else value = e->value.data;
			if ((strcmp(name, "piAgentExtension") == 0) &&
				(strcmp(type, "libraryName") == 0))
			{
				/*
				 * An empty value string means "don't load anything"
				 */
				if (value[0] != '\0') { 
					/* only load agent extensions if we could successfully copy 
					   the value string */ 
					if (strcpyrealloc(&_jvmpiAgent_Options.extensionLibrary, value) == 0) {
						load_agent_extension_library();
						/* bugzilla 66599 -- only load agent extension once */ 
						break; 
					}
				}
			}
		}
		break;
	}
		/* Called by the local monitor to start IO at this point */
    case RA_START_MONITORING_AGENT_REMOTE:
	case RA_START_MONITORING_AGENT_LOCAL:
	{
		int result, type=0; 
		ThreadLocalStorage *tps = jvmpiAgent_getThreadLocalStorage(0);  /* Grab the I/O buffer from the static private storage   */
		
        if(!_jvmpiAgent_isMonitored && !_jvmpiAgent_isSuspended) {
        /* Set target handle type - 171664 added this line*/

            result=-1;
            if(command->tag==RA_START_MONITORING_AGENT_LOCAL) {

               _jvmpiAgent_Options.targetHdl.dtarget = RA_SHAREDMEMORY;
               type=0;

               /* transfer data via shared memory */
               result = ra_attachToShm(command->info.start_monitor_local.file.data,
                                    &_jvmpiAgent_Options.targetHdl.dtargetHdl.shmHdl);
            }
            else if(command->tag==RA_START_MONITORING_AGENT_REMOTE) {
                /* Set target handle type */
                _jvmpiAgent_Options.targetHdl.dtarget = RA_SOCKET;
               type=1;                

                /* Connect to the remote monitor's listening port as indicated in the message */
                result=ra_connectToTCPServer(command->info.start_monitor_remote.ip,
                                                                        (unsigned short)command->info.start_monitor_remote.port,
                                                                        &_jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD);
            }

			if(result != 0) {
#ifdef MVS      
#pragma convlit(resume)
#endif
				char *connectType[2]={"SharedMemory","Socket"}; /* add type/returncode to error msg if connect failed * 139537 */
				char errMsg[2000];
				sprintf(errMsg,"Profiler unable to establish connection with Agent Controller via %s. Returncode from attempt is %d.",
				        connectType[type], result);
				sendErrorMessage(RA_SEVERE,"0",errMsg);
#ifdef MVS                   
#pragma convlit(suspend)
#endif
				break;
			}
			_jvmpiAgent_isMonitored=TRUE;
		}
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
		dumpMethodEventCounts();  printf("Start Monitoring ... begin\n"); fflush(stdout);
#endif

		/* In application mode we never turn on IO here */
		if (_jvmpiAgent_suspendIO && !_jvmpiAgent_Options.application)  {

			if(_jvmpiAgent_isJVMInitDone) {
				JNIEnv *env;
				/* Attach the current thread to the JVM */
				if(ATTACH_THREAD(env)) {
					break;
				}

				/* Start tracing the application now */
				if(_jvmpiAgent_isSuspended) {
					resumeTracing();
				}
				else {
					startTracing(FALSE);
				}

				DETACH_THREAD();
			}
			else {
				/* Re-enable IO */
				_jvmpiAgent_suspendIO = 0;
			}
		}
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
		printf("Start Monitoring ... done\n"); fflush(stdout);
#endif
		break;
	}
	/* Called by the remote monitor to turn off IO */
	case RA_STOP_MONITORING_AGENT:
	{
		/* Don't do anything if IO is suspended */
		if(!_jvmpiAgent_suspendIO) {
			JNIEnv *env;
			ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
			
			if(_jvmpiAgent_isJVMInitDone) {
				if(ATTACH_THREAD(env))  {
					break;
				}

				suspendTracing(TracePause);

				DETACH_THREAD();
			}
			else {
				/* In the case where we stop traing before we get the initDone event. We need to just set the
				   IO to off so we don't start tracing when we get the initDone event
				*/
				_jvmpiAgent_suspendIO = 1;
			}
		}
		break;
	}
	case RA_DETACH_FROM_AGENT:
	{
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
		dumpMethodEventCounts(); 
		printf("Detach_Agent ... begin: suspendIO=%d, isJVMInitDone=%d\n",
			   _jvmpiAgent_suspendIO, _jvmpiAgent_isJVMInitDone); fflush(stdout);
#endif                       
		/* Don't do anything if IO is suspended */
		if(!_jvmpiAgent_suspendIO) {
			JNIEnv *env;
			ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
			
			if(_jvmpiAgent_isJVMInitDone) {
				if(ATTACH_THREAD(env))  {
					break;
				}

				stopTracing();

				DETACH_THREAD();
			}
			else {
				/* In the case where we stop tracing before we get the initDone event. We need to just set the
				   IO to off so we don't start tracing when we get the initDone event
				*/
				_jvmpiAgent_suspendIO = 1;
			}
		}
        else {
			JNIEnv *env;
			ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
			if(_jvmpiAgent_isJVMInitDone) {
				if(ATTACH_THREAD(env))  {
					break;
				}
				toggleActiveJvmpiEvents(TraceOFF); 
				DETACH_THREAD();
			}
            /* Clean up all our resources for this trace */
	        cleanUpAllTraceResources();
        }

		/* If we have been monitoring THEN */
		if (_jvmpiAgent_isMonitored) {

            if(_jvmpiAgent_Options.targetHdl.dtarget == RA_SHAREDMEMORY) {
               /* Stop flushing shm buffer */
               ra_stopFlushingShm(&_jvmpiAgent_Options.targetHdl.dtargetHdl.shmHdl); /* 175248 */
            }
            else if(_jvmpiAgent_Options.targetHdl.dtarget == RA_SOCKET) {
                ra_closeSocket(_jvmpiAgent_Options.targetHdl.dtargetHdl.socketFD);
            }
			_jvmpiAgent_isMonitored=FALSE;
			_jvmpiAgent_isSuspended=FALSE;
		}
#if defined (_DEBUG) && !defined (MVS) && !defined (__OS400__)
		printf("Detach_Agent ... done\n"); fflush(stdout);
#endif
		break;
	}
	case RA_CUSTOM_COMMAND:
	{
		jint failed;
		JNIEnv *env;
		if(strcmp(command->info.custom_command.message.data, "RESUME")==0) {
			/* Grab the I/O buffer from the static private storage   */
			ThreadLocalStorage *tps = jvmpiAgent_getThreadLocalStorage(0);
			/* RKD:  resume should be ignored from an IO standpoint.
			jvmpiAgent_printVMResume(tps);
			*/
			ra_releaseVMLock();
		}
		else if(strcmp(command->info.custom_command.message.data, "MARKHEAP")==0) {
			if(_jvmpiAgent_isJVMInitDone && !_jvmShutDown) {
				failed=ATTACH_THREAD(env);
				if(!failed)  {
					failed=analyseHeap(JVMPI_DUMP_LEVEL_0);
					DETACH_THREAD();
				}
			}
		}
		else if(strcmp(command->info.custom_command.message.data, "ANALYSEHEAP")==0) {
			if(_jvmpiAgent_isJVMInitDone && !_jvmShutDown) {
				failed=ATTACH_THREAD(env);
				if(!failed) {
					runGC();
					failed=analyseHeap(JVMPI_DUMP_LEVEL_1);
					DETACH_THREAD();
				}
			}
		}
		else if(strcmp(command->info.custom_command.message.data, "TERMINATE")==0) {
			_jvmpiAgent_jvmpiInterface->ProfilerExit(0);
		}
		else if(strcmp(command->info.custom_command.message.data, "APPLYFILTERS")==0) {
			jvmpiAgent_applyFilters();

			/* Check the current filter criteria against existing information */
			jvmpiAgent_resetTraceFlags();
		}
		else if(strcmp(command->info.custom_command.message.data, "RUNGC")==0) {
			if(_jvmpiAgent_isJVMInitDone && !_jvmShutDown) {
				failed=ATTACH_THREAD(env);
				if(!failed) {
					runGC();
					DETACH_THREAD();
				}
			}
		}
		/* Piyush  Agarwal */
		/* Change made to enable RAC connected file transfer */
		else if(strcmp(command->info.custom_command.message.data, "OPTHEAP")==0) {
			if(_jvmpiAgent_isJVMInitDone && !_jvmShutDown) {
				ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
				failed=ATTACH_THREAD(env);
				if(!failed) {
					runGC();
					processRACDataDumpRequest(FALSE,command->info.custom_command.context) ;
					DETACH_THREAD();
				}
			}
		}
		else if(strcmp(command->info.custom_command.message.data, "FINAL_OPTHEAP")==0) {
			if(_jvmpiAgent_isJVMInitDone && !_jvmShutDown) {
				ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);
				failed=ATTACH_THREAD(env);
				if(!failed) {
					runGC();
					processRACDataDumpRequest(TRUE,command->info.custom_command.context) ;
					DETACH_THREAD();
				}
			}
		}
		/* Indicates a headless client.  We will only need to take an action if we are in
		   application mode. */
		else if(strcmp(command->info.custom_command.message.data, "HEADLESS")==0) {
			/* Start profiling the agent (simulating the effect of a JNI call). */
			if (_jvmpiAgent_Options.application) {
				/* Bug 71507 : Do not start profiling if it is applicationControlled */
				if(_jvmpiAgent_Options.enabled) {
					Java_org_eclipse_hyades_collection_profiler_Profiler_startProfiling0((JNIEnv *)NULL, (JOBJECT)NULL, (jboolean)FALSE, 0);
				}
			}
		}
		/* Bug 64712 */
		else if(strcmp(command->info.custom_command.message.data, "KILLED")==0) {
			stopTracing();
		}
		/* Bug 64712 */
		else if(strcmp(command->info.custom_command.message.data, "COLLECTDATA")==0) {
			/* Pre-aggregation */
			/* Dump all the threads' statistics */
			if (_jvmpiAgent_Options.compressLevel==CompressAggregate) {  /* 134635 */
				if (!_jvmpiAgent_suspendIO) {				
					printAllThreadAgData();  /* 140009 */
				}	
			}	
		}

		break;
	}
	case RA_SET_NAME_VALUE_PAIR:
	{
		if(strcmp(command->info.set_nv_pair.type.data, "SETOPTION")==0) {
			jvmpiAgent_SetProfileOption(command->info.set_nv_pair.name.data,
										command->info.set_nv_pair.value.data);
		}
		/* RKD:  The SETFILTER name value pair is ffor complicance with the V4 UI and V5 filters not containing method detail */
		else if(strcmp(command->info.set_nv_pair.type.data, "SETFILTER")==0) {
			jvmpiAgent_addFilter(command->info.set_nv_pair.name.data, NULL, !strcmp(command->info.set_nv_pair.value.data,"INCLUDE") ? INCLUDE : EXCLUDE);
		}
		else if(strcmp(command->info.set_nv_pair.type.data, "SETMETHODFILTER")==0) {
			char *separator=strchr(command->info.set_nv_pair.name.data, ' ');
			if(separator) {
				*separator='\0';
				jvmpiAgent_addFilter(command->info.set_nv_pair.name.data, separator+1, !strcmp(command->info.set_nv_pair.value.data,"INCLUDE") ? INCLUDE : EXCLUDE);
			}
		}
		break;
	}
	default: break;
	}
}

#ifdef MVS                    /* 174190 */
#pragma convlit(resume)
#endif


/** JVM_ON_LOAD  ***************************************************************
  * The entry point for the profiler.  This function is called by the JVM if
  * the .dll is provided on the -Xrun parameter when the JVM is launched.  This
  * function is called at the very bootstrap level of the JVM before much
  * has happened.
  */



#if defined __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *vm,
                                  char *options,
                                  void *reserved) {
	int res;
	char * buffer;
	int bogus;

	initializeJvmpiAgentOptionsDefaults();

	determineTicksPerMicrosecond(&bogus);

	/* Turn IO off until we need to start tracing */
	_jvmpiAgent_suspendIO = 1;

	
	/* Establish panic exit handler */
	signal(SIGABRT, cleanupAndExit);
	signal(SIGINT, cleanupAndExit);
	signal(SIGTERM, cleanupAndExit);

	/* Initialize the global collation value */
	jvmpiAgent_initializeSegmentedValue(&_jvmpiAgent_collation, 0);

	/* Determine start time from here */
	jvmpiAgent_collectStartTimeInformation();

	/* Create the filter tables.  We will assume that this JVM uses the '/' package
	   separator.  This can be overriddden during tracing by the setpathDelimiter
	   function.
	*/
#ifdef __OS400__
#pragma convert(819) 
#endif

	jvmpiAgent_initializeFilters('/');

	/* Create the trigger tables.  We will assume that this JVM uses the '/' package
	   separator.  This can be overriddden during tracing by the setpathDelimiter
	   function.
	*/
	jvmpiAgent_initializeTriggers('/');
#ifdef __OS400__
#pragma convert(0) 
#endif


	/* Tuck away the JVM pointer for future use */
	_jvmpiAgent_jvm = vm;

	/* Resolve to the JVMPI environment */
#if defined __cplusplus && defined _HPUX
	res = ENV(_jvmpiAgent_jvm)->GetEnv((void **)&_jvmpiAgent_jvmpiInterface, JVMPI_VERSION_1);
#else
	res = (*_jvmpiAgent_jvm)->GetEnv(_jvmpiAgent_jvm, (void **)&_jvmpiAgent_jvmpiInterface, JVMPI_VERSION_1);
#endif
	if (res < 0 || !_jvmpiAgent_jvmpiInterface) {
#ifdef MVS                    /* 174190 */
#pragma convlit(suspend)
#endif
		fprintf(stderr, "  Could not resolve to JVMPI interface\n");
		fflush(stderr); 
#ifdef MVS                    /* 174190 */
#pragma convlit(resume)
#endif
		return JNI_ERR;
	}

	/* Make a global copy of the options for the agent to print later */
	if (options) {
		_jvmpiAgent_Options.invocationOptions = (char*)jvmpiAgent_Calloc(strlen(options)+1);
		strcpy(_jvmpiAgent_Options.invocationOptions, options);
	} else {
		_jvmpiAgent_Options.invocationOptions = ""; /* 101734 */		
	}	

	/* Process the command line options, continue initialization only if this is successful */
	if (jvmpiAgent_ProcessInvocationOptions(options) == 0) {
#ifdef MVS                    /* 174190 */
		char *tmpname, *tmpType;
#endif

		if (_jvmpiAgent_Options.standalone) {
			jvmpiAgent_processProfile(_jvmpiAgent_Options.profileFile);
			/* Satish: perhaps we should move following code from the bottom of this function
			 * to here.
			 */
			/* jvmpiAgent_processFilters(_jvmpiAgent_Options.filterFileName); */
			/* jvmpiAgent_processTriggers(_jvmpiAgent_Options.triggerFileName); */
		}
		
		/* Now command-line options, as well as profile has been processed, check consistency */
		if (jvmpiAgent_CheckOptionsConsistency() != 0) {
#ifdef MVS                   
#pragma convlit(suspend)
#endif
			fprintf(stderr, "Specified options are not consistent\n");
#ifdef MVS                    
#pragma convlit(resume)
#endif
			fflush(stderr); 
			printUsage();
			return JNI_ERR;
		}
	
		/* Set up the symbol table */
		jvmpiAgent_InitializeSymbolTable();

		/* Initialize the synchronization lock */
		ra_initializeLock(&_jvmpiAgent_synchLock);

		/* Grab the I/O buffer from the static private storage   */
		buffer = jvmpiAgent_getThreadLocalStorage(0)->buffer;

		/* Set the JVMPI pointer to the Agents notify_event handler function */
		_jvmpiAgent_jvmpiInterface->NotifyEvent = notify_event;

		/* Enable all of the appropriate events depending upon our mode */
		enableGlobalJvmpiEvents(_jvmpiAgent_Options.mode);

		/* Set the dynamic event pointers */
		setDynamicEventHandlers();

		/* Initialize the communication layer bindings */
#ifdef MVS                    /* 174190 */
		tmpname = (char *)malloc(strlen(_jvmpiAgent_Options.processName) + 1);
		strcpy(tmpname, _jvmpiAgent_Options.processName);
		__atoe(tmpname);
		tmpType = (char *)malloc(strlen(_jvmpiAgent_Options.processType) + 1);
		strcpy(tmpType, _jvmpiAgent_Options.processType);
		__atoe(tmpType);
		_jvmpiAgent_bindingStorage = ra_initializeBindings(tmpname,
                                                         tmpType,
                                                         _jvmpiAgent_notify_message,
                                                         _jvmpiAgent_Options.standalone);
		free(tmpname);
		free(tmpType);
#else
#ifdef  _HPUX
		_jvmpiAgent_bindingStorage=ra_initializeBindings(_jvmpiAgent_Options.processName,
                                                         _jvmpiAgent_Options.processType,
                                                         _jvmpiAgent_notify_message,
                                                         (enum _BOOL)_jvmpiAgent_Options.standalone);
#else
		_jvmpiAgent_bindingStorage=ra_initializeBindings(_jvmpiAgent_Options.processName,
                                                         _jvmpiAgent_Options.processType,
                                                         _jvmpiAgent_notify_message,
                                                         _jvmpiAgent_Options.standalone);
#endif
#endif

		/* Do setup for Agent when being controlled by the "Agent Controller" */
		if (!_jvmpiAgent_Options.standalone)  {
			/*## probably need this global so that I can kill the thread on shutdown */
			TID *tid;

			/* If the trace is "enabled" we don't want to block on invocation. Otherwise
			   we are in "controlled" mode, wait until the controller sends a
			   RA_CUSTOM_COMMAND with "RESUME" message.
			*/
			tid=ra_startListener(_jvmpiAgent_bindingStorage, startListenerShouldBlock());
			_jvmpiAgent_isListenerUnblocked = TRUE;

			/*------------------------------------------------------------*
			 * startListener returned. That means we have processed the
			 * initial message from the RAC, RA_AGENT_CONTROLER_AVAILABLE.
			 * And *that* means we have loaded and initialized the agent 
			 * extension, if one was configured.
			 *
			 * See comments at agentExtensionSetEventHandler for more
			 * about initializing agent extensions and having them
			 * enable JVMPI events.
			 */
			{
				/* Process the initial agent extension event enable requests */
				int i;
				for (i = 0; i < JVMPI_MAX_EVENT_TYPE_VAL; i++) {
					if (agent_extension_handlers[i] != NULL) {
						_jvmpiAgent_jvmpiInterface->EnableEvent(i, 0);
					}
				}
			}
		}
		else {
			/* Standalone mode */
			ThreadLocalStorage *tps=jvmpiAgent_getThreadLocalStorage(0);

			/*------------------------------------------------------------*
			 * If the extensionLibrary option is set, load and 
			 * initialize an agent extension.
			 *
			 * In non-stand-alone mode, this will be done based on
			 * the settings available when the RA_AGENT_CONTROLER_AVAILABLE
			 * message arrives.
			 *
			 * In stand-alone mode, we don't need to worry about enabling
			 * events after the agent extension initializes; see notes at
			 * agentExtensionSetEventHandler for an explanation.
			 */
			if (_jvmpiAgent_Options.extensionLibrary[0] != '\0') {
				load_agent_extension_library();
			}

			ra_initializeVMLock();
#ifdef MVS                    /* 174190 */
#pragma convlit(suspend)
			__atoe(_jvmpiAgent_Options.outputFileName);
#endif
			/* Open the output file if we are doing file I/O */
			jvmpiAgent_initializeStandaloneIO();
#ifdef MVS                    /* 174190 */
#pragma convlit(resume)
#endif
			_jvmpiAgent_suspendIO = 0;

			/* Satish: As a principle, any configuartion file must be processed before, applying
			 * any business logic. Perhaps, we should move these just processing command-line
			 * options. I am moving the call jvmpiAgent_processProfile(_jvmpiAgent_Options.profileFile)
			 * there, because profile specifications might create inconsistencies that must
			 * be checked before doing anything.
			 */
			/* jvmpiAgent_processProfile(_jvmpiAgent_Options.profileFile); */
			jvmpiAgent_processFilters(_jvmpiAgent_Options.filterFileName);
			jvmpiAgent_processTriggers(_jvmpiAgent_Options.triggerFileName);

		}
		
		return JNI_OK;
	}
	else {
		printUsage();
		return JNI_ERR;
	}
}

/**	Giri: <Defect 64462> This method is performs all the optHeap related setup necessary within 
	the agent. Previously, we were doing this set-up only within JVM_OnLoad(), so if we tried 
	to attach a running process, and then tried to do optHeap related operations, the setup 
	would not have happened, and the JVM would crash **/

static void jvmpiAgent_DoOptHeapSetup()	
{
	if (_optHeapSetupDone)	{
		return;
	}

	_optHeapSetupDone = TRUE;	/* We need to do this set-up only once */
	_jvmpiAgent_optHeapCallBack.ohdcb = sendHeapDumpInformation ;

	StatelessHeapSnapshotManager_setup_FromC(_jvmpiAgent_jvmpiInterface, TRUE);

	/* Get the LOCAL_AGENT_TEMP_DIR */
	/* Bug 59544 querying tempDir from RAC */
	if (!_jvmpiAgent_Options.standalone) {
		char tempDir[1024] ;
		int result ;
		result = ra_getEnvironmentVariable(_tempDirOptionName,tempDir,1024) ;
        /* If "LOCAL_AGENT_TEMP_DIR" is not defined then get try to look
		   for "TMP" */
		if ( !result )
			result = ra_getEnvironmentVariable("TMP",tempDir,1024) ; 
		/* If tempDir is defined then set it as the workDir .
		   else leave it as default */
		if ( result ) {
            strcpyrealloc(&_jvmpiAgent_Options.workDir,tempDir) ;
/* 64476 Convert it to ascii for consistency */
#ifdef MVS
            __etoa(_jvmpiAgent_Options.workDir) ;
#endif
		}
	}
}

static void jvmpiAgent_PrintStartingXMLFragments()
{
	
	ThreadLocalStorage *tps;

	/* Get the global buffer for IO */
	tps=jvmpiAgent_getThreadLocalStorage(0);

	/* If we are in standalone print the required additional elements */
	if((_jvmpiAgent_Options.standalone||_jvmpiAgent_Options.application))  /*94955*/
	{
		jvmpiAgent_printXMLStartElement(tps);
        jvmpiAgent_printStandaloneTraceTagOpen(tps);
	}

	/* Print the trace header */
	jvmpiAgent_printNodeElement(tps);
	jvmpiAgent_printProcessCreateElement(tps);
	jvmpiAgent_printAgentCreateElement(tps, _jvmpiAgent_Options.invocationOptions);
	jvmpiAgent_printTraceStartElement(tps, _jvmpiAgent_Options.invocationOptions);
	
	/* Print the filters and options only if the proper option is set */
	if (_jvmpiAgent_Options.filters)
	{
		jvmpiAgent_printFilters();
	}
	if (_jvmpiAgent_Options.options)
	{
		jvmpiAgent_printOptions();
	}
}


#if defined __cplusplus
}
#endif
