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

#include <stdlib.h> // free
#include <string.h>

#include "StatelessHeapSnapshotManager.h"
#include "LBTimestamp.h"

static const char* CORRUPT_HEAP_DUMP   = "heap dump that the JVM gave us is corrupt!";
static const char* BOGUS_CONSTANT_POOL = "JVMPI bogus class constant pool";

StatelessHeapSnapshotManager::StatelessHeapSnapshotManager(SnapshotFiller*         filler,
							   SnapshotAgentInterface* agent,
							   JVMPI_Interface*        jvmpi,
							   bool					   optHeapDebug,
							   FILE *				   debugOut)
  : AbstractHeapSnapshotManager(filler, agent, jvmpi)
{
	_optHeapDebug = optHeapDebug;
	_debugOut = debugOut;
}
char StatelessHeapSnapshotManager::isStateless() const
{
  return 1;
}

void StatelessHeapSnapshotManager::handleHeapSnapshot(JNIEnv*      env,
						      int          dump_level,
						      char*        begin,
						      char*        end,
						      HeapDumpMode heapDumpMode)
{
  // 1. for timing...
  LBCLOCK_t now;
  getClock(&now);

  // 2. need certain class information that isn't in the heap snapshot
  if(!getClassInfos(begin, end)) return;

  if(agent().shouldIReportHeapSnapshotDuration()) {
    agent().status("part 1 (milliseconds):",
		   clockDiffInMilliseconds(&now));
  }
  getClock(&now);

  // 3. add a generation begin marker
  filler().FillJvmpiSnapshot(begin, end);
  // 4. done, report timing
  if(agent().shouldIReportHeapSnapshotDuration()) {
    agent().status("part 2 (milliseconds):",
		   clockDiffInMilliseconds(&now));
  }
}

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

void *StatelessHeapSnapshotManager::prof_dump_read_ptr(char **current) const
{
	void *p; 
#ifdef __OS400__
/* On AS/400 pointers are 16-byte aligned within the buffer. 
   Forward to the next 16 byte boundary if we currently aren't on one */ 
	unsigned int i = (unsigned int) *current;
	i &= 0x0000000f;  /* grab lowest 4 bits of i */
	if (i > 0) {  
		*current = *current + (16 - i);  /* align the pointer */ 
	}
#endif
	DUMP_READ(&p, current, sizeof(void *));
    return p;
}
	
#ifdef __OS400__
void StatelessHeapSnapshotManager::parseClassDump(unsigned int object_dump_len, 
		char *object_dump_data, int numInterfaces, int numStaticReferenceFields,
		unsigned int *instanceSize, jobjectID** classRefs) const
{
	
	char*     cursor;
	jobjectID cls;
	*instanceSize = 0;
	*classRefs = NULL;
	if ((object_dump_len = 0) ||
		(object_dump_data[0] != JVMPI_GC_CLASS_DUMP))
		return;
		
	cursor = object_dump_data + 1;
	cls = agent().dump_read_jobjectID(cursor);
	cursor += sizeof(jobjectID); /* class id */

	if(cls == NULL) {
	    // something funky happened, and we have to bail out
	    agent().error(CORRUPT_HEAP_DUMP);
	    return;
	}

	  cursor += sizeof(jobjectID); /* super class Id */
	  cursor += sizeof(jobjectID); /* class loader Id */
	  cursor += sizeof(jobjectID); /* signers Id */
	  cursor += 2*sizeof(jobjectID); /* protection domain , class name */
	  prof_dump_read_ptr(&cursor);  /* resv2 */
	  *instanceSize = agent().swap32(agent().dump_read_u4(cursor)); /* instance size */
	  cursor += sizeof(unsigned int);

	  int i;
	  for(i=0; i<numInterfaces; i++) {
	    //jobjectID iref = agent().dump_read_jobjectID(cursor);
	    cursor += sizeof(jobjectID); /* All the ids of numInterfacees */
	  }

	  // CONSTANT POOL
	  unsigned short constPoolSize = agent().swap16(agent().dump_read_u2(cursor));
	  /* size of constant pool */
	  cursor += 2;

	  for (i=0; i<constPoolSize; i++) {
	    //unsigned short constPoolIndex = agent().swap16(agent().dump_read_u2(cursor));
	    cursor += 2;

	    char constPoolType = agent().dump_read_u1(cursor);
	    cursor += 1;

	    /* skip over the constant; how much depends on its type */
	    switch(constPoolType) {
	    case JVMPI_BOOLEAN:
	    case JVMPI_BYTE:
	      cursor += 1; break;
	    case JVMPI_CHAR:
	    case JVMPI_SHORT:
	      cursor += 2; break;
	    case JVMPI_FLOAT:
	    case JVMPI_INT:
	      cursor += 4; break;
	    case JVMPI_CLASS:
		case JVMPI_NORMAL_OBJECT :
	      {
		// this is a reference case
		//jobjectID constPoolValue = agent().dump_read_jobjectID(cursor);
		cursor += sizeof(jobjectID);
		break;
	      }
	    case JVMPI_LONG:
	    case JVMPI_DOUBLE:
	      cursor += 8; break;
	    default:
	      agent().error(BOGUS_CONSTANT_POOL); break;
	    }
	  }

	  // STATIC FIELDS
	  int j;
	  int objIdSize = sizeof(jobjectID);
	  jobjectID *buildClassRefs = (jobjectID *)malloc(objIdSize * (numStaticReferenceFields));
	  *classRefs = buildClassRefs;
      for (i=0; i<numStaticReferenceFields; i++) {
		buildClassRefs[i] = agent().dump_read_jobjectID(cursor);
	    cursor += sizeof(jobjectID);
      }	
}
#endif

/*
	Anandi 1 April 2005: The OS400 jvm does not include all the classes
    in the dump. Only the user ones seem to be included. 
	1. Added code to explicitly request jvmpi to dump classes and class 
	objects (to dump static class references) whenever getClassInfos 
	comes across an instance or an object array whose class has not 
	been seen till then. 
	2. Changed FillClassInfo (only for OS400) to write out all the class
	details, including static class references. What this implies is that the
	classInfos store ALL the class related data in OS400 optHeap files and
	the parser must build the classes from these. These heaps may include
	the normal JVMPI_GC_CLASS_DUMP for some classes, the parser should ignore
	these.
 */
char StatelessHeapSnapshotManager::getClassInfos(char* begin, char* end) const
{
  char*     cursor = begin;
  char  poisonPill = 0; // found terminal error?

  if (_optHeapDebug) {
    fprintf(_debugOut, "In getClassInfos.\n");
	fflush(_debugOut);
  }
  
  while (cursor < end && !poisonPill) {

      if(!agent().stillAlive() || !agent().stillTracing()) {
	poisonPill = 1;
	break;
      }

      // the type of current entry in the bit bucket
      unsigned char type = agent().dump_read_u1(cursor);
      cursor += sizeof(char);

      switch (type) { // big switch statement
      case JVMPI_GC_ROOT_UNKNOWN:
	cursor += sizeof(jobjectID);
	break;

      case JVMPI_GC_ROOT_JNI_GLOBAL:
	cursor += sizeof(jobjectID);
	cursor += sizeof(jobject);
	break;

      case JVMPI_GC_ROOT_JNI_LOCAL:
	cursor += sizeof(jobjectID);
	prof_dump_read_ptr(&cursor);
//	cursor += sizeof(JNIEnv*);
	cursor += sizeof(int);
	break;

      case  JVMPI_GC_ROOT_JAVA_FRAME:
	cursor += sizeof(jobjectID);
	prof_dump_read_ptr(&cursor);
//	cursor += sizeof(JNIEnv*);
	cursor += sizeof(int);
	break;

      case JVMPI_GC_ROOT_NATIVE_STACK:
	cursor += sizeof(jobjectID);
	prof_dump_read_ptr(&cursor);
//	cursor += sizeof(JNIEnv*);
	break;

      case JVMPI_GC_ROOT_STICKY_CLASS:
	cursor += sizeof(jobjectID);
	break;

      case JVMPI_GC_ROOT_THREAD_BLOCK:
	cursor += sizeof(jobjectID);
	prof_dump_read_ptr(&cursor);
//	cursor += sizeof (JNIEnv*);
	break;

      case JVMPI_GC_ROOT_MONITOR_USED:
	cursor += sizeof(jobjectID);
	break;

      case JVMPI_GC_CLASS_DUMP:
	{
	  jobjectID cls = agent().dump_read_jobjectID(cursor);
	  cursor += sizeof(jobjectID); /* class id */

	  if(cls == NULL) {
	    // something funky happened, and we have to bail out
	    agent().error(CORRUPT_HEAP_DUMP);
	    poisonPill = 1;
	    if (_optHeapDebug) {
	    	fprintf(_debugOut, "In getClassInfo NULL class id.\n");
	    	fflush(_debugOut);
		}
	    break;
	  }

	  cursor += sizeof(jobjectID); /* super class Id */
	  cursor += sizeof(jobjectID); /* class loader Id */
	  cursor += sizeof(jobjectID); /* signers Id */
	  cursor += 2*sizeof(jobjectID); /* protection domain , class name */
	  prof_dump_read_ptr(&cursor);  /* resv2 */
	  unsigned int instanceSize = agent().swap32(agent().dump_read_u4(cursor)); /* instance size */
	  cursor += sizeof(unsigned int);

	  int numInterfaces, numStaticReferenceFields;
	  char* className;
	  char className_freeMe;

	  if (_optHeapDebug) {
	    fprintf(_debugOut, "In getClassInfo 0x%x.\n", cls);
	    fflush(_debugOut);
	  }

#ifdef __OS400__
	  /* 96108: Anandi On AS400, with jvm 1.4, objects are showing up
         before classes. Which, because we create classInfos from objects
         causes this to return status = 1 for such classes.
	     Handle this by calling FillClassInfo only when status =0
       */
	  char getClassInfoStatus = agent().getClassInfo(cls, &className, &className_freeMe, &numInterfaces, &numStaticReferenceFields);
	  if (getClassInfoStatus !=0 && getClassInfoStatus !=1) {
	    agent().error(CORRUPT_HEAP_DUMP);
	    fprintf(stderr, "agent().getClassInfo non 0 or 1 return.\n");
	    poisonPill = 1;
	    break;
	  }
#else
	  if(agent().getClassInfo(cls, &className, &className_freeMe, &numInterfaces, &numStaticReferenceFields)) {
	    agent().error(CORRUPT_HEAP_DUMP);
	    fprintf(stderr, "agent().getClassInfo non 0 or 1 return.\n");
	    poisonPill = 1;
	    break;
	  }
#endif

	  int i;
	  for(i=0; i<numInterfaces; i++) {
	    //jobjectID iref = agent().dump_read_jobjectID(cursor);
	    cursor += sizeof(jobjectID); /* All the ids of numInterfacees */
	  }

	  // CONSTANT POOL
	  unsigned short constPoolSize = agent().swap16(agent().dump_read_u2(cursor));
	  /* size of constant pool */
	  cursor += 2;

	  for (i=0; i<constPoolSize; i++) {
	    //unsigned short constPoolIndex = agent().swap16(agent().dump_read_u2(cursor));
	    cursor += 2;

	    char constPoolType = agent().dump_read_u1(cursor);
	    cursor += 1;

	    /* skip over the constant; how much depends on its type */
	    switch(constPoolType) {
	    case JVMPI_BOOLEAN:
	    case JVMPI_BYTE:
	      cursor += 1; break;
	    case JVMPI_CHAR:
	    case JVMPI_SHORT:
	      cursor += 2; break;
	    case JVMPI_FLOAT:
	    case JVMPI_INT:
	      cursor += 4; break;
	    case JVMPI_CLASS:
		case JVMPI_NORMAL_OBJECT :
	      {
		// this is a reference case
		//jobjectID constPoolValue = agent().dump_read_jobjectID(cursor);
		cursor += sizeof(jobjectID);
		break;
	      }
	    case JVMPI_LONG:
	    case JVMPI_DOUBLE:
	      cursor += 8; break;
	    default:
	      agent().error(BOGUS_CONSTANT_POOL); break;
	    }
	  }

	  // STATIC FIELDS
	  int j;
#ifdef __OS400__
	  // dump the classRefs here, including the null refs
	  int objIdSize = sizeof(jobjectID);
	  jobjectID *classRefs = (jobjectID *)malloc(objIdSize * (numStaticReferenceFields));
	  for (j=0; j<numStaticReferenceFields; j++) {
		classRefs[j] = agent().dump_read_jobjectID(cursor);
	    cursor += sizeof(jobjectID);
      }	
#else
	  for (j=0; j<numStaticReferenceFields; j++) {
	    cursor += sizeof(jobjectID);
	  }
#endif

#ifdef __OS400__
	  if (getClassInfoStatus !=1) {
#endif
	  filler().FillClassInfo(cls,
				 className,
				 numStaticReferenceFields,
				 numInterfaces,
#ifdef __OS400__
				 instanceSize, classRefs);
	  free(classRefs);
#else
				 instanceSize);
#endif
#ifdef __OS400__
	  }
#endif

	  if(className_freeMe) {
	    free(className);
	  }

	  break;
	}

     case JVMPI_GC_INSTANCE_DUMP:
     {

#ifdef __OS400__
/* Bug Number 73480 */
	  int numInterfaces, numStaticReferenceFields;
	  char* className;
	  char className_freeMe = 0;
 	  unsigned int instanceSize = 0;
 	  unsigned int object_dump_len = 0;
 	  char *object_dump_data = NULL;
 	  jobjectID* classRefs = NULL;
#endif

      cursor += sizeof(jobjectID);  /* object */
#ifdef __OS400__
/* Bug Number 73480 */
	  jobjectID cls = agent().dump_read_jobjectID(cursor);
#endif
	  cursor += sizeof(jobjectID); /* class id */

#ifdef __OS400__
/* Bug Number 73480 */
	  int status =  agent().getOS400ClassInfo(cls, &className, &className_freeMe, 
	  		&numInterfaces, &numStaticReferenceFields, &object_dump_len, &object_dump_data
			) ;

	  if(status < 0) {
	    agent().error(CORRUPT_HEAP_DUMP);
	    poisonPill = 1;
	    break;
	  }

	  if ( status == 0 ) {
	      parseClassDump(object_dump_len, object_dump_data, numInterfaces, 
	      	numStaticReferenceFields, &instanceSize, &classRefs);
	      filler().FillClassInfo(cls,
			 className,
			 numStaticReferenceFields,
			 numInterfaces,
			 instanceSize, classRefs);
		  free(classRefs);
	  }

  	  if(className_freeMe) {
	    free(className);
		free(object_dump_data);
	  }
#endif
       unsigned int remainingBytes =  agent().swap32(agent().dump_read_u4(cursor));
       cursor += sizeof(unsigned int);
       cursor += remainingBytes;
       break;
     }

     case JVMPI_GC_OBJ_ARRAY_DUMP:
     {
#ifdef __OS400__
/* Bug Number 73480 */
 	  int numInterfaces, numStaticReferenceFields;
	  char* className;
	  char className_freeMe = 0;
 	  unsigned int instanceSize = 0;
 	  unsigned int object_dump_len = 0;
 	  char *object_dump_data = NULL;
 	  jobjectID *classRefs = NULL;
#endif
       cursor += sizeof(jobjectID);
       int numElem = agent().swap32(agent().dump_read_u4(cursor));
       cursor += sizeof(int);
#ifdef __OS400__
/* Bug Number 73480 */
	  jobjectID cls = agent().dump_read_jobjectID(cursor);
#endif
       cursor += sizeof(jobjectID);
#ifdef __OS400__
/* Bug Number 73480 */
	  int status =  agent().getOS400ClassInfo(cls, &className, &className_freeMe, 
	  		&numInterfaces, &numStaticReferenceFields, &object_dump_len, &object_dump_data
			) ;

	  if(status < 0) {
	    agent().error(CORRUPT_HEAP_DUMP);
	    poisonPill = 1;
	    break;
	  }

	  if ( status == 0 ) {
	      parseClassDump(object_dump_len, object_dump_data, numInterfaces, 
			numStaticReferenceFields, &instanceSize, &classRefs);
	      filler().FillClassInfo(cls,
			 className,
			 numStaticReferenceFields,
			 numInterfaces,
			 instanceSize, classRefs);
		  free(classRefs);
	  }

  	  if(className_freeMe) {
	    free(className);
		free(object_dump_data);
	  }
#endif
       for (int i=0; i<numElem; i++) {
	 cursor += sizeof(jobjectID);
       }
       break;
     }

     case JVMPI_GC_PRIM_ARRAY_DUMP:
       cursor += sizeof(jobjectID);
       cursor += sizeof(int);
       cursor += sizeof(char);
       break;

      default:
	// some error in the trace, or in our processing of it
	poisonPill = 1;
	if (_optHeapDebug) {
	    fprintf(_debugOut, 
		"Possible parse error in getClassInfos. Unknown heap dump record type %d.\n",
		type);
		fflush(_debugOut);
	}
	break;
      } //switch
   } // while

  // return whether finished without error
  if (_optHeapDebug) {
    fprintf(_debugOut, "End getClassInfos: poisonPill %d\n", poisonPill);
    fflush(_debugOut);
  }
  return poisonPill==0;
}
