gecko-dev/ef/gc/test/msgctest.c

661 строка
17 KiB
C
Исходник Обычный вид История

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*******************************************************************************
S P O R T M O D E L
_____
____/_____\____
/__o____o____o__\ __
\_______________/ (@@)/
/\_____|_____/\ x~[]~
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
Advanced Technology Garbage Collector
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
Author: Warren Harris
*******************************************************************************/
#include "prinit.h"
#ifdef XP_MAC
#include "pprthred.h"
#else
#include "private/pprthred.h"
#endif
#include "prgc.h"
#include <stdio.h>
#include <time.h>
#define K 1024
#define M (K*K)
#define PAGE (4*K)
#define MIN_HEAP_SIZE (128*PAGE)
#define MAX_HEAP_SIZE (16*M)
FILE* out;
int toFile = 1;
int verbose = 1;
int testLargeObjects = 1;
GCInfo *gcInfo = 0;
/******************************************************************************/
typedef void (PR_CALLBACK *FinalizeFun)(void* obj);
typedef struct fieldblock {
int type;
int access;
int offset;
} fieldblock;
typedef enum AType {
T_NORMAL_OBJECT, /* must be 0 */
T_CLASS, /* aka, array of object */
T_INT,
T_LONG,
T_DOUBLE
} AType;
#define fieldIsArray(fb) ((fb)->type == T_CLASS)
#define fieldIsClass(fb) ((fb)->type == T_NORMAL_OBJECT)
typedef struct ClassClass {
PRUword instanceSize;
int fieldsCount;
fieldblock* fields;
struct methodtable* methodTable;
FinalizeFun finalize;
} ClassClass;
#define cbSuperclass(cb) NULL
#define cbFields(cb) ((cb)->fields)
#define cbFieldsCount(cb) ((cb)->fieldsCount)
#define ACC_STATIC 1
#define cbFinalizer(cb) ((cb)->finalize)
#define cbInstanceSize(cb) ((cb)->instanceSize)
#define cbMethodTable(cb) ((cb)->methodTable)
#define class_offset(o) obj_length(o)
struct methodblock;
struct methodtable {
ClassClass* classdescriptor;
/* methodtables must be at 32-byte aligned -- adding 7 will get around this */
struct methodblock* methods[7];
};
#define ALIGN_MT(mt) ((struct methodtable*)((((PRUword)(mt)) + FLAG_MASK) & ~FLAG_MASK))
#define obj_methodtable(obj) ((obj)->methods)
#define obj_classblock(obj) ((obj)->methods->classdescriptor)
#define METHOD_FLAG_BITS 5L
#define FLAG_MASK ((1L<<METHOD_FLAG_BITS)-1L) /* valid flag bits */
#define METHOD_MASK (~FLAG_MASK) /* valid mtable ptr bits */
#define LENGTH_MASK METHOD_MASK
#define rawtype(o) (((unsigned long) (o)->methods) & FLAG_MASK)
#define obj_flags(o) rawtype(o)
#define obj_length(o) \
(((unsigned long) (o)->methods) >> METHOD_FLAG_BITS)
#define tt2(m) ((m) & FLAG_MASK)
#define atype(m) tt2(m)
#define mkatype(t,l) ((struct methodtable *) (((l) << METHOD_FLAG_BITS)|(t)))
typedef struct ClassObject ClassObject;
typedef struct JHandle {
struct methodtable* methods;
ClassObject* obj;
} JHandle;
typedef JHandle HObject;
#define OBJECT HObject*
#define unhand(h) ((h)->obj)
typedef struct HArrayOfObject {
JHandle super;
PRUword length;
HObject* body[1];
} HArrayOfObject;
static void PR_CALLBACK
ScanJavaHandle(void GCPTR *gch)
{
JHandle *h = (JHandle *) gch;
ClassClass *cb;
void **sub;
ClassObject *p;
void (*livePointer)(void *base);
void (*liveBlock)(void **base, PRInt32 count);
int32 n;
liveBlock = gcInfo->liveBlock;
livePointer = gcInfo->livePointer;
(*livePointer)((void*) h->obj);
p = unhand(h);
switch (obj_flags(h)) {
case T_NORMAL_OBJECT:
if (obj_methodtable(h)) {
cb = obj_classblock(h);
do {
struct fieldblock *fb = cbFields(cb);
/* Scan the fields of the class */
n = cbFieldsCount(cb);
while (--n >= 0) {
if ((fieldIsArray(fb) || fieldIsClass(fb))
&& !(fb->access & ACC_STATIC)) {
sub = (void **) ((char *) p + fb->offset);
(*livePointer)(*sub);
}
fb++;
}
if (cbSuperclass(cb) == 0) {
break;
}
cb = 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 = class_offset(h) + 1;
(*liveBlock)((void**)((HArrayOfObject *) h)->body, n);
break;
}
}
static void PR_CALLBACK
DumpJavaHandle(FILE *out, void GCPTR *gch, PRBool detailed, int indent)
{
}
static void PR_CALLBACK
SummarizeJavaHandle(void GCPTR *obj, size_t bytes)
{
}
static void PR_CALLBACK
FinalJavaHandle(void GCPTR *gch)
{
JHandle *handle = (JHandle GCPTR *) gch;
ClassClass* cb = obj_classblock(handle);
cbFinalizer(cb)(handle);
}
GCType unscannedType = {
0,
0,
DumpJavaHandle,
SummarizeJavaHandle,
0,
0,
'U',
0
};
int unscannedTIX;
GCType scannedType = {
ScanJavaHandle,
0,
DumpJavaHandle,
SummarizeJavaHandle,
0,
0,
'S',
NULL
};
int scannedTIX;
GCType scannedFinalType = {
ScanJavaHandle,
FinalJavaHandle,
DumpJavaHandle,
SummarizeJavaHandle,
0,
0,
'F',
NULL
};
int scannedFinalTIX;
static void*
realObjAlloc(struct methodtable *mptr, long bytes, PRBool inIsClass)
{
ClassClass *cb;
HObject *h;
int tix, flags;
/* See if the object requires finalization or scanning */
if (!inIsClass) {
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;
cb = mptr->classdescriptor;
if (cbFinalizer(cb)) {
tix = scannedFinalTIX;
#if 0
} else if (cbFlags(cb) & CCF_IsWeakLink) {
tix = weakLinkTIX;
#endif
} 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;
}
}
else {
flags = PR_ALLOC_CLEAN;
tix = unscannedTIX; // classClassTIX;
}
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)
*/
/*
Achtung Explorer!! Proceed carefully!!!
On certain machines a JHandle MUST be double aligned.
*/
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);
return h;
}
#define T_ELEMENT_SIZE(t) \
(((t) == T_DOUBLE) ? sizeof(double) : sizeof(void*))
#define MAX_INT 0x7fffL
static PRInt32
sizearray(PRInt32 t, PRInt32 l)
{
PRInt32 size = 0;
switch(t){
case T_CLASS:
size = sizeof(OBJECT);
break;
default:
size = T_ELEMENT_SIZE(t);
break;
}
/* Check for overflow of PRInt32 size */
if ( l && (size > MAX_INT/l )) {
return -1;
}
size *= l;
return size;
}
static HObject*
AllocArray(PRInt32 t, PRInt32 l)
{
HObject *handle;
PRInt32 bytes;
bytes = sizearray(t, l);
if (bytes < 0) {
/* This is probably an overflow error - t*l > MAX_INT */
return NULL;
}
bytes += (t == T_CLASS ? sizeof(OBJECT) : 0);
handle = realObjAlloc((struct methodtable *) mkatype(t, l), bytes, FALSE);
return handle;
}
static HObject*
AllocObject(ClassClass *cb, long n0)
{
HObject *handle;
n0 = cbInstanceSize(cb);
handle = realObjAlloc(cbMethodTable(cb), n0, FALSE);
return handle;
}
PR_EXTERN(void)
PR_PrintGCStats(void);
/******************************************************************************/
ClassClass* MyClass;
ClassClass* MyLargeClass;
typedef struct MyClassStruct {
HObject super;
struct MyClassStruct* next;
int value;
// void* foo[2]; /* to see internal fragmentation */
} MyClassStruct;
static void
MyClass_finalize(HObject* self)
{
#ifdef DEBUG
fprintf(out, "finalizing %d\n", ((MyClassStruct*)self)->value);
#endif
}
static void
InitClasses(void)
{
MyClass = (ClassClass*)malloc(sizeof(ClassClass));
MyClass->instanceSize = sizeof(MyClassStruct);
MyClass->fieldsCount = 2;
MyClass->fields = (fieldblock*)calloc(sizeof(fieldblock), 2);
MyClass->fields[0].type = T_NORMAL_OBJECT;
MyClass->fields[0].access = 0;
MyClass->fields[0].offset = offsetof(MyClassStruct, next) - sizeof(HObject);
MyClass->fields[1].type = T_INT;
MyClass->fields[1].access = 0;
MyClass->fields[1].offset = offsetof(MyClassStruct, value) - sizeof(HObject);
MyClass->methodTable = ALIGN_MT(malloc(sizeof(struct methodtable)));
MyClass->methodTable->classdescriptor = MyClass;
MyClass->finalize = MyClass_finalize;
MyLargeClass = (ClassClass*)malloc(sizeof(ClassClass));
MyLargeClass->instanceSize = 4097;
MyLargeClass->fieldsCount = 2;
MyLargeClass->fields = (fieldblock*)calloc(sizeof(fieldblock), 2);
MyLargeClass->fields[0].type = T_NORMAL_OBJECT;
MyLargeClass->fields[0].access = 0;
MyLargeClass->fields[0].offset = offsetof(MyClassStruct, next) - sizeof(HObject);
MyLargeClass->fields[1].type = T_INT;
MyLargeClass->fields[1].access = 0;
MyLargeClass->fields[1].offset = offsetof(MyClassStruct, value) - sizeof(HObject);
MyLargeClass->methodTable = ALIGN_MT(malloc(sizeof(struct methodtable)));
MyLargeClass->methodTable->classdescriptor = MyLargeClass;
MyLargeClass->finalize = MyClass_finalize;
}
/******************************************************************************/
MyClassStruct* root = NULL;
static void PR_CALLBACK
MarkRoots(void* data)
{
void (*livePointer)(void *base);
livePointer = gcInfo->livePointer;
gcInfo->livePointer(root);
}
#if 0
static void
BeforeHook(SMGenNum genNum, PRUword collectCount,
PRBool copyCycle, void* closure)
{
#ifdef xDEBUG
FILE* out = (FILE*)closure;
if (verbose) {
fprintf(out, "Before collection %u gen %u %s\n", collectCount, genNum,
(copyCycle ? "(copy cycle)" : ""));
PR_DumpGCHeap(out, dumpFlags);
}
#endif
}
static void
AfterHook(SMGenNum genNum, PRUword collectCount,
PRBool copyCycle, void* closure)
{
#ifdef xDEBUG
FILE* out = (FILE*)closure;
if (verbose) {
fprintf(out, "After collection %u gen %u %s\n", collectCount, genNum,
(copyCycle ? "(copy cycle)" : ""));
PR_DumpGCHeap(out, dumpFlags);
}
#endif
}
#endif
#define ITERATIONS 10000
int
main(void)
{
PRStatus status;
HObject* arr;
MyClassStruct* obj;
MyClassStruct* conserv;
int i, cnt = 0;
struct tm *newtime;
time_t aclock;
void* randomPtr;
PRIntervalTime t1, t2;
if (toFile) {
out = fopen("msgctest.out", "w+");
if (out == NULL) {
perror("Can't open msgctest.out");
return -1;
}
}
else
out = stdout;
time(&aclock);
newtime = localtime(&aclock);
fprintf(out, "SportModel Garbage Collector Test Program: %s\n", asctime(newtime));
PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
PR_SetThreadGCAble();
PR_InitGC(0, MIN_HEAP_SIZE, 4*K, 0);
gcInfo = PR_GetGCInfo();
PR_RegisterRootFinder(MarkRoots, "mark msgctest roots", 0);
scannedTIX = PR_RegisterType(&scannedType);
scannedFinalTIX = PR_RegisterType(&scannedFinalType);
InitClasses();
t1 = PR_IntervalNow();
arr = AllocArray(T_CLASS, 13); /* gets collected */
randomPtr = (char*)arr + (4096 * 200);
arr = AllocArray(T_CLASS, 13); /* try an array */
((HArrayOfObject*)arr)->body[3] = (HObject*)root;
root = (MyClassStruct*)arr;
arr = NULL;
PR_GC();
if (testLargeObjects) {
/* test large object allocation */
obj = (MyClassStruct*)AllocObject(MyLargeClass, 0); /* gets collected */
obj->value = cnt++;
obj = (MyClassStruct*)AllocObject(MyLargeClass, 0);
obj->value = cnt++;
obj->next = root;
root = obj;
PR_GC();
}
obj = (MyClassStruct*)AllocObject(MyClass, 0); /* gets collected */
obj->value = cnt++;
obj = (MyClassStruct*)AllocObject(MyClass, 0);
obj->value = cnt++;
obj->next = root; /* link this into the root list */
root = obj;
conserv = (MyClassStruct*)AllocObject(MyClass, 0); /* keep a conservative root */
conserv->value = cnt++;
arr = AllocArray(T_CLASS, 13); /* try an array */
((HArrayOfObject*)arr)->body[3] = (HObject*)root;
root = (MyClassStruct*)arr;
arr = NULL;
obj = NULL;
PR_GC();
PR_ForceFinalize();
/* Force collection to occur for every subsequent object,
for testing purposes. */
#if 0
SM_SetCollectThresholds(sizeof(MyClassStruct),
sizeof(MyClassStruct),
sizeof(MyClassStruct),
sizeof(MyClassStruct),
0);
#endif
for (i = 0; i < ITERATIONS; i++) {
int size = rand() / 100;
/* allocate some random garbage */
// fprintf(out, "allocating array of size %d\n", size);
AllocArray(T_CLASS, size);
obj = (MyClassStruct*)AllocObject(MyClass, 0); /* gets collected */
obj->value = cnt++;
obj->next = root;
root = obj;
//SM_DumpStats(out, dumpFlags);
}
#ifdef DEBUG
for (i = 0; i < 10; i++) {
PR_GC();
}
// PR_PrintGCStats();
#endif
t2 = PR_IntervalNow();
fprintf(out, "Test completed in %ums\n", PR_IntervalToMilliseconds(t2 - t1));
if (verbose) {
/* list out all the objects */
i = 0;
obj = root;
while (obj != NULL) {
MyClassStruct* next;
if (obj_flags((HObject*)obj) == T_NORMAL_OBJECT) {
fprintf(out, "%4d ", obj->value);
next = obj->next;
}
else {
fprintf(out, "[arr %p] ", obj);
next = (MyClassStruct*)((HArrayOfObject*)obj)->body[3];
}
if (i++ % 20 == 19)
fprintf(out, "\n");
obj = next;
}
fprintf(out, "\nconserv = %d\n", conserv->value);
}
randomPtr = NULL;
root = NULL;
obj = NULL;
conserv = NULL;
PR_GC();
PR_ForceFinalize();
PR_GC();
PR_ForceFinalize();
#ifdef DEBUG
// PR_DumpGCHeap(out, 0);
#endif
PR_ShutdownGC(PR_TRUE);
status = PR_Cleanup();
if (status != PR_SUCCESS)
return -1;
return 0;
}
/******************************************************************************/