#include "typedefs.h"
#include "oobj.h"
#include "monitor.h"
#include "javaThreads.h"
#include "finalize.h"
#include "prmem.h"
#include "prlog.h"
#include "prmon.h"
#include "prprf.h"
#include "prthread.h"
#include "prarena.h"
#include "prgc.h"
#include "utf.h"
#include "prlink.h"
#if XP_MAC
#include "prmacos.h"
#endif
static void PR_CALLBACK ScanJavaHandle(void GCPTR *obj);
static void PR_CALLBACK FinalJavaHandle(void GCPTR *obj);
static void PR_CALLBACK DumpJavaHandle(FILE *out, void GCPTR *obj, PRBool detailed, int indent);
static void PR_CALLBACK SummarizeJavaHandle(void GCPTR *obj, size_t bytes);
static void PR_CALLBACK ScanClassClass(ClassClass *cb);
static void PR_CALLBACK FreeClassClass(ClassClass *cb);
static void PR_CALLBACK DumpClassClass(FILE *out, void GCPTR *ptr, PRBool detailed, int indent);
static void PR_CALLBACK SummarizeClassClass(void GCPTR *obj, size_t bytes);
extern void ScanVerifyContext(void *ptr);
GCInfo *gcInfo = 0;
extern int verbose_class_loading;
PR_LOG_DEFINE(UnloadClass);
PR_LOG_DEFINE(Allocation);
/*
** GC type's for java. Java uses handles (the GC doesn't) so handles are
** allocated out of the front of an object. All "pointers" in java's are
** actually handle addresses so there normally will be no direct pointers
** to the java object. However, because C code is used to implement java
** sometimes there will be a C pointer into the java object. The GC
** handles this "offset pointer" (because it has to) and can find the
** appropriate handle.
**
** We use 3 types for java objects: java objects which are not scanned
** (things which have no embedded pointers like array's of int's), java
** objects which are scanned but not finalized, and java objects which
** are scanned and finalized.
*/
GCType unscannedType = {
0,
0,
DumpJavaHandle,
SummarizeJavaHandle,
0,
};
int unscannedTIX;
GCType scannedType = {
ScanJavaHandle,
0,
DumpJavaHandle,
SummarizeJavaHandle,
0,
};
int scannedTIX;
GCType scannedFinalType = {
ScanJavaHandle,
FinalJavaHandle,
DumpJavaHandle,
SummarizeJavaHandle,
0,
};
int scannedFinalTIX;
GCType classClassType = {
(void (*)(void*)) ScanClassClass,
0,
DumpClassClass,
SummarizeClassClass,
(void (*)(void*)) FreeClassClass,
};
int classClassTIX;
GCType verifyContextType = {
(void (*)(void*)) ScanVerifyContext,
0,
0,
0,
};
int verifyContextTIX;
static const char *TYPEOF(HObject *h)
{
static char buf[200];
static char *tag[] =
{
"??? object",
"??? 1",
"object",
"??? 3",
"boolean",
"char",
"float",
"double",
"byte",
"short",
"int",
"long",
"unsigned byte",
"unsigned short",
"unsigned int",
"unsigned long",
};
if (h)
{
switch (obj_flags(h))
{
case T_NORMAL_OBJECT:
return obj_classblock(h)->name;
case T_CLASS:
{
ArrayOfObject *array = (ArrayOfObject *)unhand(h);
ClassClass *fromClass = (ClassClass *)(array->body[obj_length(h)]);
char *fromName = fromClass->name;
sprintf(buf, "%s[%d]", fromName, obj_length(h));
return buf;
}
case T_BOOLEAN:
case T_CHAR:
case T_FLOAT:
case T_DOUBLE:
case T_BYTE:
case T_SHORT:
case T_INT:
case T_LONG:
sprintf(buf, "%s[%d]", tag[obj_flags(h) & 15], obj_length(h));
return buf;
default:
sprintf(buf, "UNKNOWN KIND %d", obj_flags(h));
return buf;
}
}
else
return "null";
}
#include "prgc.h"
/************************************************************************/
void
RefTable_processRoots(RefTable* refTable)
{
if (refTable->element) {
#ifdef xDEBUG_warren
/*
** Change the above ifdef to see what's left in the global ref
** tables during gc.
*/
void RefTable_describe(RefTable* refTable);
RefTable_describe(refTable);
#endif /* DEBUG_warren */
(*gcInfo->processRoot)((void**)refTable->element, refTable->top);
}
}
/*
** Scan a java thread looking for GC roots. This doesn't scan the java
** thread object (the java thread object is marked by the gc code when
** the nspr thread object's private data areas are processed)
*/
static int PR_CALLBACK ScanJavaThread(PRThread *t, void *notused)
{
JHandle *p = (JHandle *) sysThreadGetBackPtr(t);/* XXX inline */
void **ssc, **limit;
ExecEnv *ee;
JavaFrame *frame;
void (*liveObject)(void **base, int32 count);
PR_ASSERT(p != 0);
/* Its possible that a thread has exited, and is waiting
to be killed (its a zombie), but we do a garbage collection.
In this case, PrivateInfo will be null, we will throw
the following assert, and will exit (bad). So when
PrivateInfo is 0, do nothing... the thread is dead. */
if (THREAD(p)->PrivateInfo == 0)
return 1;
PR_ASSERT(THREAD(p)->PrivateInfo == (long)t);
PR_PROCESS_ROOT_LOG(("Scanning thread %s", t->name));
/* Mark thread object */
ee = (ExecEnv *)THREAD(p)->eetop;
liveObject = gcInfo->liveObject;
if (ee != 0) {
if (((frame = ee->current_frame) != 0)) {
struct methodblock *mb = frame->current_method;
stack_item *top_top_stack = mb
? &frame->ostack[mb->maxstack]
: frame->optop;
PR_ASSERT(frame->optop >= &frame->ostack[0]);
if (mb) {
PR_ASSERT(frame->optop <= &frame->ostack[mb->maxstack]);
}
limit = (void **)top_top_stack;
for (;;) {
if (mb) {
/* Mark the class that contains the method as busy
when we are busy executing code in it */
PR_PROCESS_ROOT_LOG(("Marking class %s busy while in method %s", classname(fieldclass(&mb->fb)), fieldname(&mb->fb)));
(*liveObject)((void**) &fieldclass(&mb->fb), 1);
}
ssc = (void **)(frame->ostack);
if (ssc < limit) {
(*liveObject)(ssc, limit - ssc);
}
ssc = (void **)(frame->vars);
if (ssc) {
limit = (void **)frame;
if (limit > ssc) {
(*liveObject)(ssc, limit - ssc);
}
}
frame = frame->prev;
if (!frame) break;
limit = (void **)(frame->optop);
mb = frame->current_method;
}
}
/*
** Mark the exception object (if any)...
*/
if (ee->exception.exc) {
(*liveObject)((void**)&(ee->exception.exc), 1);
}
/*
** JRI Stuff: Scavenge the global refTable
*/
if (ee->globalRefs.element)
RefTable_processRoots(&ee->globalRefs);
}
return 1;
}
/*
** Root finder to find root's into the GC heap from outside the GC heap.
** This is used to specialy handle the thread execution stack.
*/
void PR_CALLBACK ScanJavaThreads(void *notused)
{
JHandle *self;
self = (JHandle *) sysThreadGetBackPtr(sysThreadSelf());
(void) sysThreadEnumerateOver(ScanJavaThread, (void *) self);
}
/************************************************************************/
/*
** Code for GC scanning verifier objects.
*/
void *AllocContext(size_t size)
{
void *cx;
cx = (void*) PR_AllocMemory(size, verifyContextTIX, PR_ALLOC_CLEAN);
return cx;
}
/************************************************************************/
extern int class_lock_depth;
/*
** Scan an instance of ClassClass.
*/
void PR_CALLBACK ScanClassClass(ClassClass *cb)
{
int32_t i;
void (*liveObject)(void **base, int32 count);
struct fieldblock *fb;
liveObject = gcInfo->liveObject;
if (CCIs(cb, Resolved)) {
union cp_item_type *constant_pool = cbConstantPool(cb);
union cp_item_type *cpp = constant_pool+ CONSTANT_POOL_UNUSED_INDEX;
union cp_item_type *end_cpp = &constant_pool[cb->constantpool_count];
unsigned char *type_tab = (unsigned char *)
constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
PR_PROCESS_ROOT_LOG(("Scanning class %s constant pool",
classname(cb)));
for (i = CONSTANT_POOL_UNUSED_INDEX; cpp < end_cpp; cpp++, i++) {
ClassClass *cb2;
switch (type_tab[i]) {
case CONSTANT_String:
case CONSTANT_String|CONSTANT_POOL_ENTRY_RESOLVED:
PR_PROCESS_ROOT_LOG(("Constant pool slot %d: String %p",
i, (*cpp).p));
(*liveObject)(&(*cpp).p, 1);
break;
case CONSTANT_Class|CONSTANT_POOL_ENTRY_RESOLVED:
cb2 = (ClassClass*) (*cpp).p;
if (cb2 != cb) {
PR_PROCESS_ROOT_LOG(("Constant pool slot %d: Class %s",
i, classname(cb2)));
(*liveObject)(&(*cpp).p, 1);
}
break;
case CONSTANT_Long|CONSTANT_POOL_ENTRY_RESOLVED:
case CONSTANT_Double|CONSTANT_POOL_ENTRY_RESOLVED:
i++;
cpp++;
break;
}
}
}
/* Scan class definitions looking for statics */
for (i = (int32_t) cb->fields_count, fb = cbFields(cb); --i >= 0; fb++) {
if ((fieldIsArray(fb) || fieldIsClass(fb))
&& (fb->access & ACC_STATIC)) {
PR_PROCESS_ROOT_LOG(("Scanning class %s static %s",
classname(cb), fieldname(fb)));
(*liveObject)((void**) normal_static_address(fb), 1);
}
}
/* Scan superclass, HandleToSelf, loader and classname_array */
(*liveObject)((void**) &cb->superclass, 4);
}
ClassArena *free_class_arenas;
extern void CompilerFreeMethod(struct methodblock *mb);
void FreeClassArenas(void) {
ClassArena *arena;
while (free_class_arenas) {
/* Steal away one of the free arenas (under the cover of the GC lock) */
PR_EnterMonitor(gcInfo->lock);
arena = free_class_arenas;
if (arena) {
free_class_arenas = arena->next;
}
PR_ExitMonitor(gcInfo->lock);
if (arena) {
int i;
/* Free up JIT memory */
for (i = 0; i < arena->methods_count; i++) {
struct methodblock* mb = &arena->methods[i];
CompilerFreeMethod(mb);
/* Free native method libraries too */
if (mb->lib != NULL) {
int s = PR_UnloadLibrary(mb->lib);
PR_ASSERT(s == 0);
}
}
/* Now free it */
PR_FinishArenaPool(&arena->pool);
free(arena);
}
}
}
#if XP_MAC
unsigned char ClassArenaFlusherProc(size_t size)
{
FreeClassArenas();
return 0; /* We don't know how much we freed */
}
#endif
ClassClass *AllocClass(void)
{
ClassClass *cb;
ClassArena *arena;
/* Free up any unloaded classes arenas */
FreeClassArenas();
cb = (ClassClass*) PR_AllocMemory(sizeof(ClassClass), classClassTIX,
PR_ALLOC_CLEAN);
if (cb != NULL) {
arena = PR_NEWZAP(ClassArena);
if (!arena) {
return 0;
}
PR_InitArenaPool(&arena->pool, "class pool", 1 * 512, sizeof(void*));
cb->classStorageArena = arena;
}
return cb;
}
/*
** Prepare classes for GC'ing. This is run before any pointers are
** examined.
*/
void PR_CALLBACK PrepareClassesForGC(void)
{
ClassClass **pcb;
int j;
/* Clear the instance counting stuff */
pcb = binclasses;
for (j = nbinclasses; --j >= 0; pcb++) {
ClassClass *cb = *pcb;
if (!cb) continue;
cb->instances = 0;
}
}
/*
** Scan class table for GC roots. Only sticky classes constitute roots.
*/
void PR_CALLBACK ProcessRootsInClassTable(void *notused)
{
ClassClass **pcb;
int j;
void (*liveObject)(void **base, int32 count);
liveObject = gcInfo->liveObject;
/* Clear the instance counting stuff */
pcb = binclasses;
for (j = nbinclasses; --j >= 0; pcb++) {
ClassClass *cb = *pcb;
if (!cb) continue;
if ((cb->flags & (CCF_Sticky | CCF_IsResolving)) ||
(!cb->loader && (cb->flags & CCF_HasStatics))) {
/*
** Make a root reference to everything that a sticky class
** refers to. We also make references to anything that has
** static variables (that doesn't have a class loader).
**
** XXX the unloading policy is currently in design review!
*/
for (;;) {
(*liveObject)((void**) &cb, 1);
if (!cbSuperclass(cb))
break;
cb = unhand(cbSuperclass(cb));
}
}
}
}
/*
** This is called by the GC for a ClassClass that has no references and
** is ready to be released.
**
** NOTE: this is called during the sweep cycle so no GC operations are
** allowed to be performed. When this call returns the memory referred to
** by "cb" must no longer be referenced (which means we need to carefully
** remove it from the binclasses table).
**
** NOTE 2: because we are conservatively collecting, we know that there
** are no other references to the cb anywhere, except for the table. This
** means that even if the table is locked it is safe to zero out the cell
** in the table because no thread has dereferenced it (yet).
*/
void PR_CALLBACK FreeClassClass(ClassClass *cb)
{
ClassClass **pcb, **epcb;
pcb = binclasses;
epcb = pcb + nbinclasses;
for (; pcb < epcb; pcb++) {
if (*pcb == cb) {
/* Found a class to unload */
#ifdef DEBUG
PR_LOG(UnloadClass, out,
("FreeClassClass: %s ( 0x%p )", classname(cb), cb));
if (verbose_class_loading)
fprintf(stderr, "Unloaded %s (%x)\n", classname(cb), cb);
#endif
/* Rip it out of the table */
*pcb = 0;
/*
* Put the indirectly held memory (the arena) on the freelist
* of classes. We can't free the indirectly held memory right
* now because some other suspended thread may have the malloc
* lock (thus causing us to deadlock).
*/
cb->classStorageArena->next = free_class_arenas;
free_class_arenas = cb->classStorageArena;
return;
}
}
}
static void PR_CALLBACK DumpClassClass(FILE *out, void GCPTR *ptr, PRBool detailed, int indent)
{
ClassClass *cb = (ClassClass *) ptr;
fprintf(out, "Class record <%s>\n", classname(cb));
}
/************************************************************************/
/*
** Scan a java object for pointers to other java objects.
**
** Note: the code below handles a few special cases:
** (1) the handle has been allocated but not fully initialized
** because the GC was run before the handle object and methodtable
** cells were filled in. Either h->obj or h->methodtable can be
** NULL.
** (2) the handle was created via AllocHandle. In this case the
** methodtable will be T_BYTE and the object will be the ClassClass
** (again, it might not be filled in yet). In the latter case
** processing the handles object pointer will mark the ClassClass
** object busy (which is what we want). Later, the class code will
** change the methodtable pointer for the class to refer to the
** methods for java.lang.Class at which point the T_NORMAL_OBJECT
** case in the switch below will be used.
*/
static void PR_CALLBACK ScanJavaHandle(void GCPTR *gch)
{
JHandle *h = (JHandle *) gch;
ClassClass *cb;
void **sub;
ClassObject *p;
void (*liveObject)(void **base, int32 count);
int32 n;
liveObject = gcInfo->liveObject;
(*liveObject)((void**) &h->obj, 1);
p = unhand(h);
switch (obj_flags(h)) {
case T_NORMAL_OBJECT:
if (obj_methodtable(h)) {
cb = obj_classblock(h);
/* Count the direct instance */
if (cb->instances++ == 0) {
/* Mark class as alive */
(*liveObject)((void**) &cb, 1);
}
do {
struct fieldblock *fb = cbFields(cb);
/* Scan the fields of the class */
n = cb->fields_count;
while (--n >= 0) {
if ((fieldIsArray(fb) || fieldIsClass(fb))
&& !(fb->access & ACC_STATIC)) {
sub = (void **) ((char *) p + fb->u.offset);
(*liveObject)(sub, 1);
}
fb++;
}
if (cbSuperclass(cb) == 0) {
break;
}
cb = unhand(cbSuperclass(cb));
} while (cb);
} else {
/*
** If the object doesn't yet have it's method table, we can't
** scan it. This means the GC examined the handle before the
** allocation code (realObjAlloc/AllocHandle) has initialized
** it.
*/
}
break;
case T_CLASS: /* an array of objects */
/*
** Have the GC scan all of the elements and the ClassClass*
** stored at the end.
*/
n = obj_length(h) + 1;
(*liveObject)((void**)((ArrayOfObject *) p)->body, n);
break;
}
}
/************************************************************************/
ExecEnv java_finalizer_ee;
/*
** Finalize a java handle and it's object. Called by the nspr finalizer
** thread.
*/
static void PR_CALLBACK FinalJavaHandle(void GCPTR *gch)
{
JHandle GCPTR *h = (JHandle GCPTR *) gch;
if (h->obj != 0) {
struct methodblock *mb;
PR_ASSERT(obj_flags(h) == T_NORMAL_OBJECT);
mb = obj_classblock(h)->finalizer;
PR_ASSERT(mb != 0);
GCTRACE(GC_FINAL, ("java final of %p (type=%s)", h, TYPEOF(h)));
do_execute_java_method(&java_finalizer_ee, h, 0, 0, mb, FALSE);
if(exceptionOccurred(&java_finalizer_ee)) {
#if 0
/* XXX Probably should get treated the same way as static
class initializers */
exceptionDescribe(&java_finalizer_ee);
#endif
exceptionClear(&java_finalizer_ee);
}
}
}
/*
** Reflect the nspr finalizer thread into java
*/
void InitializeFinalizerThread()
{
ClassClass *cb;
HThread *finalizer;
struct Classjava_lang_Thread *tid;
ExecEnv *ee;
extern ClassClass *Thread_classblock;
char *name = "Finalizer thread";
if (java_finalizer_ee.thread) {
return;
}
cb = Thread_classblock;
finalizer = (HThread*) ObjAlloc(cb, 0);
if (finalizer == 0) {
out_of_memory();
}
tid = THREAD(finalizer);
memset((char *)tid, 0, cbInstanceSize(cb));
InitializeExecEnv(&java_finalizer_ee, (JHandle *)finalizer);
/*
** Setup linkage between system thread and java thread for the Garbage Collector
** If these pointers are not set the GC will ignore any java stack frames that
** may be present in the java_finalizer_ee
*/
unhand(finalizer)->PrivateInfo = (long) gcInfo->finalizer;
sysThreadSetBackPtr(gcInfo->finalizer, finalizer);
/*
** Get the EE from the java thread... This is not the java_finalizer_ee
** because this method is called by the primordial thread during java initialization.
*/
ee = EE();
sysAssert(ee != &java_finalizer_ee);
if (ee->initial_stack == 0) {
out_of_memory();
}
/*
** Invoke thread class constructor so that finalizer get's added to
** the system thread group
*/
do_execute_java_method(ee, finalizer, "<init>", "()V", 0, 0);
PR_ASSERT(unhand(finalizer)->group != 0);
/* Setup linkage between system thread and java thread */
unhand(finalizer)->name = MakeString(name, strlen(name));
unhand(finalizer)->daemon = 1;
#if defined(XP_PC) && !defined(_WIN32)
unhand(finalizer)->priority = MaximumPriority;
threadSetPriority(finalizer, MaximumPriority);
#else
unhand(finalizer)->priority = MinimumPriority;
threadSetPriority(finalizer, MinimumPriority);
#endif
/* Point to java dump routine */
gcInfo->finalizer->dump = sysThreadDumpInfo;
}
void runFinalization(void)
{
PR_ForceFinalize();
}
/************************************************************************/
static void DumpUnicodeString(FILE *out, const unicode *str, int length)
{
fputc('\'', out);
while (length-- > 0)
{
unicode ch = *str++;
if (ch == '\n')
fprintf(out, "\\n");
else if (ch == '\0')
fprintf(out, "\\0");
else if (ch < ' ' || ch > 0x7E)
fprintf(out, "\\u%.4X", ch);
else if (ch == '\'')
fprintf(out, "\\'");
else if (ch == '\\')
fprintf(out, "\\\\");
else
fputc(ch, out);
}
fputc('\'', out);
}
static void DumpObjectContentsShallow(FILE *out, ClassObject *p, ClassClass *cb, int indent)
{
struct fieldblock *fb = cbFields(cb);
long n = cb->fields_count;
/* Scan the fields of the class */
while (--n >= 0)
{
if (!(fb->access & ACC_STATIC))
{
char fieldKind = fieldsig(fb)[0];
char *fp = (char *)p + fb->u.offset;
PR_DumpIndent(out, indent);
fprintf(out, "+0x%.3X: .%s = ", fb->u.offset, fieldname(fb));
switch (fieldKind)
{
case SIGNATURE_ARRAY:
case SIGNATURE_CLASS:
{
JHandle *v = *(JHandle **)fp;
fprintf(out, "<%s> 0x%p", TYPEOF(v), v);
}
break;
case SIGNATURE_BYTE:
fprintf(out, "<byte> %d", *(int8 *)fp);
break;
case SIGNATURE_SHORT:
fprintf(out, "<short> %d", *(int16 *)fp);
break;
case SIGNATURE_INT:
fprintf(out, "<int> %d", *(int32 *)fp);
break;
case SIGNATURE_LONG:
{
char str[64];
ll2str(*(int64 *)fp, str, str + 64);
fprintf(out, "<long> %s", str);
}
break;
case SIGNATURE_CHAR:
fprintf(out, "<char> ");
DumpUnicodeString(out, (unicode *)fp, 1);
break;
case SIGNATURE_BOOLEAN:
{
uint8 v = *(uint8 *)fp;
fprintf(out, "<boolean> ");
switch (v)
{
case 0:
fprintf(out, "false");
break;
case 1:
fprintf(out, "true");
break;
default:
fprintf(out, "%d", v);
break;
}
}
break;
case SIGNATURE_FLOAT:
fprintf(out, "<float> %d", *(float *)fp);
break;
case SIGNATURE_DOUBLE:
fprintf(out, "<double> %d", *(double *)fp);
break;
default:
fprintf(out, "UNKNOWN FIELD KIND '%c'", fieldKind);
}
fputc('\n', out);
}
fb++;
}
}
static void DumpObjectContents(FILE *out, JHandle *h, int indent)
{
ClassObject *p = unhand(h);
ClassClass *cb = obj_classblock(h);
ClassClass *superclasses[256];
int nSuperclasses = 0;
while (cb && nSuperclasses != 256)
{
HClass *s;
superclasses[nSuperclasses++] = cb;
s = cbSuperclass(cb);
if (!s)
break;
cb = unhand(s);
}
while (nSuperclasses--)
DumpObjectContentsShallow(out, p, superclasses[nSuperclasses], indent);
}
static void FormatNextObject(char *out, const void **src)
{
JHandle *elt = *(*(JHandle *const **)src)++;
if (elt)
sprintf(out, "%s @ %p", TYPEOF(elt), elt);
else
strcpy(out, "null");
}
static void FormatNextByte(char *out, const void **src)
{
int8 elt = *(*(const int8 **)src)++;
sprintf(out, "%d", elt);
}
static void FormatNextShort(char *out, const void **src)
{
int16 elt = *(*(const int16 **)src)++;
sprintf(out, "%d", elt);
}
static void FormatNextInt(char *out, const void **src)
{
int32 elt = *(*(const int32 **)src)++;
sprintf(out, "%ld", elt);
}
static void FormatNextLong(char *out, const void **src)
{
int64 elt = *(*(const int64 **)src)++;
ll2str(elt, out, out + 256);
}
static void FormatNextFloat(char *out, const void **src)
{
float elt = *(*(const float **)src)++;
sprintf(out, "%g", elt);
}
static void FormatNextDouble(char *out, const void **src)
{
double elt = *(*(const double **)src)++;
sprintf(out, "%g", elt);
}
static void DumpArrayEltRange(FILE *out, uint32 startIndex, uint32 stopIndex, const char *str, int indent)
{
PR_DumpIndent(out, indent);
stopIndex--;
if (stopIndex == startIndex)
fprintf(out, "[%d]", startIndex);
else
fprintf(out, "[%d..%d]", startIndex, stopIndex);
fprintf(out, " = %s\n", str);
}
static void DumpArrayContents(FILE *out, JHandle *h, void (*formatNext)(char *out, const void **src), int indent)
{
uint32 nElts = obj_length(h);
const void *p = unhand(h);
uint32 n = 0;
int lastEltStrIndex = -1;
uint32 firstRepeatN = 0;
char eltStr[2][256];
while (n != nElts)
{
int thisEltStrIndex = !lastEltStrIndex;
(*formatNext)(eltStr[thisEltStrIndex], &p);
if (lastEltStrIndex < 0 || strcmp(eltStr[0], eltStr[1]))
{
if (lastEltStrIndex >= 0)
DumpArrayEltRange(out, firstRepeatN, n, eltStr[lastEltStrIndex], indent);
lastEltStrIndex = thisEltStrIndex;
firstRepeatN = n;
}
n++;
}
if (lastEltStrIndex >= 0)
DumpArrayEltRange(out, firstRepeatN, n, eltStr[lastEltStrIndex], indent);
}
static void DumpArrayHandle(FILE *out, JHandle *h, void (*formatNext)(char *out, const void **src), PRBool detailed, int indent)
{
fprintf(out, "Array <%s> handle=0x%p, data=0x%p\n", TYPEOF(h), h, h->obj);
if (detailed)
DumpArrayContents(out, h, formatNext, indent+22);
}
static void PR_CALLBACK DumpJavaHandle(FILE *out, void GCPTR *gch, PRBool detailed, int indent)
{
JHandle *h = (JHandle *) gch;
if (obj_methodtable(h))
{
uint32 flags = obj_flags(h);
switch (flags)
{
case T_NORMAL_OBJECT:
fprintf(out, "Object <%s> handle=0x%p, data=0x%p, methods=0x%p\n", TYPEOF(h), h, h->obj, h->methods);
if (detailed)
DumpObjectContents(out, h, indent+22);
break;
case T_CLASS:
DumpArrayHandle(out, h, &FormatNextObject, detailed, indent);
break;
case T_BOOLEAN:
case T_BYTE:
DumpArrayHandle(out, h, &FormatNextByte, detailed, indent);
break;
case T_CHAR:
fprintf(out, "Array <%s> handle=0x%p, data=0x%p ", TYPEOF(h), h, h->obj);
DumpUnicodeString(out, unhand((HArrayOfChar*)h)->body, obj_length(h));
fputc('\n', out);
break;
case T_FLOAT:
DumpArrayHandle(out, h, &FormatNextFloat, detailed, indent);
break;
case T_DOUBLE:
DumpArrayHandle(out, h, &FormatNextDouble, detailed, indent);
break;
case T_SHORT:
DumpArrayHandle(out, h, &FormatNextShort, detailed, indent);
break;
case T_INT:
DumpArrayHandle(out, h, &FormatNextInt, detailed, indent);
break;
case T_LONG:
DumpArrayHandle(out, h, &FormatNextLong, detailed, indent);
break;
default:
fprintf(out, "UNKNOWN OBJECT flags=%lu\n", flags);
break;
}
}
else
fprintf(out, "UNFINISHED JAVA OBJECT %p\n", gch);
}
/************************************************************************/
#include "prhash.h"
PRHashTable* summaryTable = NULL;
PRSummaryEntry* summaries = NULL;
int32 summarySize = 0;
int32 summaryIndex = 0;
#define SUMMARY_INCREMENT 1024
/* XXX Move this to prhash? */
PRHashNumber
PR_HashAddress(const void* key)
{
return (PRHashNumber)key;
}
static void
Summarize(ClassClass* clazz, size_t bytes)
{
PRSummaryEntry* entry = NULL;
if (!summaryTable) {
summaryTable = PR_NewHashTable(1024, PR_HashAddress,
PR_CompareValues, PR_CompareValues,
NULL, NULL);
if (!summaryTable) return;
} else {
entry = PR_HashTableLookup(summaryTable, clazz);
}
if (!entry) {
PRHashEntry *he;
if (!summaries) {
summarySize = SUMMARY_INCREMENT;
summaries = (PRSummaryEntry*)calloc(summarySize, sizeof(PRSummaryEntry));
if (!summaries) return;
summaryIndex = 0;
}
if (summaryIndex == summarySize) {
PRSummaryEntry* s =
(PRSummaryEntry*)realloc(summaries, summarySize + SUMMARY_INCREMENT);
if (!s) return;
summaries = s;
summarySize += SUMMARY_INCREMENT;
}
entry = &summaries[summaryIndex++];
entry->clazz = clazz;
entry->instancesCount = 0;
entry->totalSize = 0;
he = PR_HashTableAdd(summaryTable, clazz, entry);
}
PR_ASSERT(entry->clazz == clazz);
entry->instancesCount++;
entry->totalSize += bytes;
}
static void PR_CALLBACK
SummarizeJavaHandle(void GCPTR *obj, size_t bytes)
{
extern HClass* java_lang_Object_getClass(HObject *p); /* from object.c */
Summarize(unhand(java_lang_Object_getClass((JHandle*)obj)), bytes);
}
static void PR_CALLBACK
SummarizeClassClass(void GCPTR *obj, size_t bytes)
{
Summarize(get_classClass(), bytes);
}
static void PR_CALLBACK
PrintSummary(FILE *out, void* closure)
{
int32 i;
fprintf(out, "Count Total Size Average Size Class\n");
for (i = 0; i < summaryIndex; i++) {
PRSummaryEntry* entry = &summaries[i];
fprintf(out, "%12ld %12ld %12ld %s\n", entry->instancesCount,
entry->totalSize, (entry->totalSize / entry->instancesCount),
classname((ClassClass*)entry->clazz));
}
fprintf(out, "End of memory summary.\n");
PR_HashTableDestroy(summaryTable);
summaryTable = NULL;
free(summaries);
summaries = NULL;
}
/************************************************************************/
/*
* Rough counter of heap modification events: a lack of known permu-
* tations of the heap is used to suppress async GC. While this isn't
* a perfect indicator of good times to GC, it is unlikely that there
* will be many good times to GC when we aren't turning over memory.
* Current incrementers of heap_memory_changes include realObjectAlloc()
* and execute_finalizer().
*/
int heap_memory_changes = 0;
int tracegc = 0;
struct arrayinfo arrayinfo[] = {
{ 0, 0, "N/A", 0},
{ 0, 0, "N/A", 0},
{ T_CLASS, SIGNATURE_CLASS, "class[]", sizeof(OBJECT)},
{ 0, 0, "N/A", 0},
{ T_BOOLEAN, SIGNATURE_BOOLEAN, "bool[]", sizeof(char)},
{ T_CHAR, SIGNATURE_CHAR, "char[]", sizeof(unicode)},
{ T_FLOAT, SIGNATURE_FLOAT, "float[]", sizeof(float)},
{ T_DOUBLE, SIGNATURE_DOUBLE, "double[]", sizeof(double)},
{ T_BYTE, SIGNATURE_BYTE, "byte[]", sizeof(char)},
{ T_SHORT, SIGNATURE_SHORT, "short[]", sizeof(short)},
{ T_INT, SIGNATURE_INT, "int[]", sizeof(int32_t)},
{ T_LONG, SIGNATURE_LONG, "long[]", sizeof(int64_t)},
{ 0, 0, "Obsolete", 0},
{ 0, 0, "Obsolete", 0},
{ 0, 0, "Obsolete", 0},
{ 0, 0, "Obsolete", 0}
};
#define ARRAYTYPES (sizeof(arrayinfo) / sizeof(arrayinfo[0]))
void prof_heap(FILE *fp)
{
}
/*
* Support for DumpMonitors(): we want to print whose monitor this is
* if it is associated with an object. To check whether that's so we
* need to see whether the handle is in the handle pool, and the limits
* of the handle pool aren't visible out of gc.c.
*/
void
monitorDumpHelper(monitor_t *mid, void *name)
{
unsigned int key = mid->key;
if (mid && (mid->flags & MON_IN_USE) != 0) {
if (name == 0) {
if (key) {
name = Object2CString((JHandle *) key);
} else {
name = "unknown key";
}
}
fprintf(stderr, " %s (key=%p): ", name, key);
sysMonitorDumpInfo((sys_mon_t*) mid->mid);
}
return;
}
/*
* Calculate the size in bytes of an array of type t and length l.
*/
int32_t sizearray(int32_t t, int32_t l)
{
int size = 0;
switch(t){
case T_CLASS:
size = sizeof(OBJECT);
break;
default:
size = T_ELEMENT_SIZE(t);
break;
}
size *= l;
return size;
}
/*
** In order to return instances of class Class, the classresolver code
** allocates a handle whose mptr equals the class Class mptr and whose
** obj value points to the runtime version of the class. This call is
** here for just that purpose.
*/
HObject *AllocHandle(struct methodtable *mptr, ClassObject *p)
{
JHandle *hp;
hp = (JHandle*) PR_AllocMemory(sizeof(JHandle), scannedTIX,
PR_ALLOC_CLEAN);
if (hp) {
/*
** Note: If the gc runs before these two stores complete, it's ok
** because it will find the references to the new handle and to
** the ClassClass object on the C stack.
*/
hp->methods = mptr;
hp->obj = (ClassObject *) p;
}
return (HObject *) hp;
}
/*
** Allocate a handle and object memory for an object. bytes is the size
** of the object memory. Allocate them as a single unit since java only
** deals with handles not objects...The underyling GC has to be able to
** deal with offset pointers anyway (that is, pointers which point
** somewhere in the middle of an object), so it's ok.
**
** Caller arranges to clean the memory returned.
*/
static HObject *realObjAlloc(struct methodtable *mptr, long bytes)
{
HObject *h;
int tix, flags;
heap_memory_changes++;
/* See if the object requires finalization or scanning */
switch (atype((unsigned long) mptr)) {
case T_NORMAL_OBJECT:
/* Normal object that may require finalization or double alignment. */
/* XXX pessimistic assumption for now */
flags = PR_ALLOC_DOUBLE | PR_ALLOC_CLEAN;
if (mptr->classdescriptor->finalizer) {
tix = scannedFinalTIX;
} else {
tix = scannedTIX;
}
break;
case T_CLASS: /* An array of objects */
flags = PR_ALLOC_CLEAN;
tix = scannedTIX;
break;
case T_LONG: /* An array of java long's */
case T_DOUBLE: /* An array of double's */
flags = PR_ALLOC_DOUBLE | PR_ALLOC_CLEAN;
tix = unscannedTIX;
break;
default:
/* An array of something (char, byte, ...) */
flags = PR_ALLOC_CLEAN;
tix = unscannedTIX;
break;
}
if (flags & PR_ALLOC_DOUBLE) {
/* Double align bytes */
bytes = (bytes + BYTES_PER_DWORD - 1) >> BYTES_PER_DWORD_LOG2;
bytes <<= BYTES_PER_DWORD_LOG2;
} else {
/* Word align bytes */
bytes = (bytes + BYTES_PER_WORD - 1) >> BYTES_PER_WORD_LOG2;
bytes <<= BYTES_PER_WORD_LOG2;
}
/*
** Add in space for the handle (which is two words so we won't mess
** up the double alignment)
*/
bytes += sizeof(JHandle);
/* Allocate object and handle memory */
h = (JHandle*) PR_AllocMemory(bytes, tix, flags);
if (!h) {
return 0;
}
/*
** Fill in handle.
**
** Note: if the gc runs before these two stores happen, it's ok
** because it will find the reference to the new object on the C
** stack. The reference to the class object will be found in the
** calling frames C stack (see ObjAlloc below).
*/
h->methods = mptr;
h->obj = (ClassObject*) (h + 1);
GCTRACE(GC_ALLOC, ("handle=%p type=%s %s",
((prword_t*)h) - 1, TYPEOF(h),
(tix == scannedFinalTIX) ? "final" : ""));
return h;
}
/*
* Allocate an object. This routine is in flux. For now, the second
* parameter should always be zero and the first must alwaysd point to
* a valid classblock. This routine should not be used to allocate
* arrays; use ArrayAlloc for that.
*/
HObject *ObjAlloc(ClassClass *cb, long n0)
{
HObject *handle;
n0 = cbInstanceSize(cb);
handle = realObjAlloc(cbMethodTable(cb), n0);
#ifdef LOG_JAVA_ALLOCS
if (handle)
{
PR_LOG(Allocation, debug, ("Allocated object %p\n", handle));
sysThreadDumpInfo(sysThreadSelf(), stderr);
}
#endif
return handle;
}
JRI_PUBLIC_API(HObject *)
ArrayAlloc(int32_t t, int32_t l)
{
HObject *handle;
PR_ASSERT(t >= T_CLASS && t < T_MAXNUMERIC);
/*
* Uncomment me to find all the places where the code creates zero length
* arrays.
*
* if (l == 0) {
* extern void DumpThreads();
* printf("zero length array created %s[%d]\n", arrayinfo[t].name, l);
* DumpThreads();
* }
*/
handle = realObjAlloc((struct methodtable *) mkatype(t, l),
sizearray(t, l) + (t == T_CLASS ? sizeof(OBJECT) : 0));
#ifdef LOG_JAVA_ALLOCS
if (handle)
{
fprintf(stderr, "Allocated array %p\n", handle);
sysThreadDumpInfo(sysThreadSelf(), stderr);
}
#endif
return handle;
}
void InitializeAlloc(long max_request, long min_request)
{
PR_InitGC(0, min_request);
gcInfo = PR_GetGCInfo();
PR_SetBeginGCHook((GCBeginGCHook*) PrepareClassesForGC, 0);
PR_RegisterRootFinder(ScanJavaThreads, "scan java threads", 0);
PR_RegisterRootFinder(ProcessRootsInClassTable, "scan class table", 0);
unscannedTIX = PR_RegisterType(&unscannedType);
scannedTIX = PR_RegisterType(&scannedType);
scannedFinalTIX = PR_RegisterType(&scannedFinalType);
classClassTIX = PR_RegisterType(&classClassType);
verifyContextTIX = PR_RegisterType(&verifyContextType);
#if XP_MAC
InstallMemoryCacheFlusher(&ClassArenaFlusherProc);
#endif
PR_RegisterSummaryPrinter(PrintSummary, NULL);
}
void gc(int async_call, unsigned int free_space_goal)
{
/* XXX async_call ignored */
/* XXX free_space_goal ignored */
PR_GC();
}
int64 TotalObjectMemory(void)
{
int32 sum = gcInfo->busyMemory + gcInfo->freeMemory;
int64 rv;
LL_I2L(rv, sum);
return rv;
}
int64 FreeObjectMemory(void)
{
int64 rv;
LL_I2L(rv, gcInfo->freeMemory);
return rv;
}
int64 TotalHandleMemory(void)
{
return ll_zero_const;
}
int64 FreeHandleMemory(void)
{
return ll_zero_const;
}