1998-12-17 10:00:17 +03:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
|
|
|
|
*
|
1999-11-02 09:38:29 +03:00
|
|
|
* 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/
|
1998-12-17 10:00:17 +03:00
|
|
|
*
|
1999-11-02 09:38:29 +03:00
|
|
|
* 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.
|
1998-12-17 10:00:17 +03:00
|
|
|
*
|
1999-11-02 09:38:29 +03:00
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
1998-12-17 10:00:17 +03:00
|
|
|
* Communications Corporation. Portions created by Netscape are
|
1999-11-02 09:38:29 +03:00
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
* Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
1998-12-17 10:00:17 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
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"
|
|
|
|
#include "sm.h"
|
|
|
|
#include "smobj.h"
|
|
|
|
#include "smpage.h"
|
|
|
|
#include "smheap.h"
|
|
|
|
#include "smtrav.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#define K 1024
|
|
|
|
#define M (K*K)
|
|
|
|
#define PAGE (4*K)
|
|
|
|
#define MIN_HEAP_SIZE (4*M)
|
|
|
|
#define MAX_HEAP_SIZE (16*M)
|
|
|
|
|
|
|
|
FILE* out;
|
|
|
|
int toFile = 1;
|
|
|
|
int verbose = 0;
|
|
|
|
int testLargeObjects = 1;
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
//PRUword dumpFlags = SMDumpFlag_Detailed | SMDumpFlag_GCState | SMDumpFlag_Abbreviate;
|
|
|
|
PRUword dumpFlags = SMDumpFlag_Detailed;
|
|
|
|
//PRUword dumpFlags = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
SMObjectClass* MyClass;
|
|
|
|
SMObjectClass* MyLargeClass;
|
|
|
|
SMArrayClass* MyArrayClass;
|
|
|
|
|
|
|
|
typedef struct MyClassStruct {
|
|
|
|
SMObjStruct super;
|
|
|
|
struct MyClassStruct* next;
|
|
|
|
int value;
|
|
|
|
// void* foo[2]; /* to see internal fragmentation */
|
|
|
|
} MyClassStruct;
|
|
|
|
|
|
|
|
static void
|
|
|
|
MyClass_finalize(SMObject* self)
|
|
|
|
{
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
fprintf(out, "finalizing %d\n", SM_GET_FIELD(MyClassStruct, self, value));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
InitClasses(void)
|
|
|
|
{
|
|
|
|
SMFieldDesc* fds;
|
|
|
|
MyClass = (SMObjectClass*)malloc(sizeof(SMObjectClass)
|
|
|
|
+ (1 * sizeof(SMFieldDesc)));
|
|
|
|
SM_InitObjectClass(MyClass, NULL, sizeof(SMObjectClass),
|
|
|
|
sizeof(MyClassStruct), 1, 0);
|
|
|
|
fds = SM_CLASS_GET_INST_FIELD_DESCS((SMClass*)MyClass);
|
|
|
|
fds[0].offset = offsetof(MyClassStruct, next) / sizeof(void*);
|
|
|
|
fds[0].count = 1;
|
|
|
|
MyClass->data.finalize = MyClass_finalize;
|
|
|
|
|
|
|
|
MyLargeClass = (SMObjectClass*)malloc(sizeof(SMObjectClass)
|
|
|
|
+ (1 * sizeof(SMFieldDesc)));
|
|
|
|
SM_InitObjectClass(MyLargeClass, NULL, sizeof(SMObjectClass),
|
|
|
|
4097, 1, 0);
|
|
|
|
fds = SM_CLASS_GET_INST_FIELD_DESCS((SMClass*)MyLargeClass);
|
|
|
|
fds[0].offset = offsetof(MyClassStruct, next) / sizeof(void*);
|
|
|
|
fds[0].count = 1;
|
|
|
|
MyLargeClass->data.finalize = MyClass_finalize;
|
|
|
|
|
|
|
|
MyArrayClass = (SMArrayClass*)malloc(sizeof(SMArrayClass));
|
|
|
|
SM_InitArrayClass(MyArrayClass, NULL, (SMClass*)MyClass);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
MyClassStruct* root = NULL;
|
|
|
|
|
|
|
|
static void
|
|
|
|
MarkRoots(SMGenNum genNum, PRBool copyCycle, void* data)
|
|
|
|
{
|
|
|
|
// SM_MarkThreadsConservatively(genNum, copyCycle, data); /* must be first */
|
|
|
|
SM_MarkRoots((SMObject**)&root, 1, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)" : ""));
|
|
|
|
SM_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)" : ""));
|
|
|
|
SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ITERATIONS 10000
|
|
|
|
|
|
|
|
int
|
|
|
|
main(void)
|
|
|
|
{
|
|
|
|
PRStatus status;
|
|
|
|
SMArray* arr;
|
|
|
|
MyClassStruct* obj;
|
|
|
|
MyClassStruct* conserv;
|
|
|
|
int i, cnt = 0;
|
|
|
|
struct tm *newtime;
|
|
|
|
time_t aclock;
|
|
|
|
void* randomPtr;
|
|
|
|
PRIntervalTime t1, t2;
|
|
|
|
int failure = 0;
|
|
|
|
|
|
|
|
if (toFile) {
|
|
|
|
out = fopen("smtest.out", "w+");
|
|
|
|
if (out == NULL) {
|
|
|
|
perror("Can't open smtest.out");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
out = stdout;
|
|
|
|
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
SM_SetTraceOutputFile(out);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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();
|
|
|
|
status = SM_InitGC(MIN_HEAP_SIZE, MAX_HEAP_SIZE,
|
|
|
|
BeforeHook, out,
|
|
|
|
AfterHook, out,
|
|
|
|
MarkRoots, NULL);
|
|
|
|
if (status != PR_SUCCESS)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
InitClasses();
|
|
|
|
|
|
|
|
t1 = PR_IntervalNow();
|
|
|
|
|
|
|
|
arr = SM_AllocArray(MyArrayClass, 13); /* gets collected */
|
|
|
|
|
|
|
|
randomPtr = (char*)SM_DECRYPT_ARRAY(arr) + (4096 * 200);
|
|
|
|
|
|
|
|
arr = SM_AllocArray(MyArrayClass, 13); /* try an array */
|
|
|
|
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
|
|
|
root = (MyClassStruct*)arr;
|
|
|
|
arr = NULL;
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
|
|
|
|
if (testLargeObjects) {
|
|
|
|
/* test large object allocation */
|
|
|
|
obj = (MyClassStruct*)SM_AllocObject(MyLargeClass); /* gets collected */
|
|
|
|
if (obj) {
|
|
|
|
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Large Object\n");
|
|
|
|
failure = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = (MyClassStruct*)SM_AllocObject(MyLargeClass);
|
|
|
|
if (obj) {
|
|
|
|
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
|
|
|
SM_SET_FIELD(MyClassStruct, obj, next, root);
|
|
|
|
root = obj;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Large Object\n");
|
|
|
|
failure = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = (MyClassStruct*)SM_AllocObject(MyClass); /* gets collected */
|
|
|
|
if (obj) {
|
|
|
|
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Small Object\n");
|
|
|
|
failure = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
obj = (MyClassStruct*)SM_AllocObject(MyClass);
|
|
|
|
if (obj) {
|
|
|
|
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
|
|
|
SM_SET_FIELD(MyClassStruct, obj, next, root); /* link this into the root list */
|
|
|
|
root = obj;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Small Object\n");
|
|
|
|
failure = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
conserv = (MyClassStruct*)SM_AllocObject(MyClass); /* keep a conservative root */
|
|
|
|
((MyClassStruct*)SM_DECRYPT(conserv))->value = cnt++;
|
|
|
|
|
|
|
|
arr = SM_AllocArray(MyArrayClass, 13); /* try an array */
|
|
|
|
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
|
|
|
root = (MyClassStruct*)arr;
|
|
|
|
arr = NULL;
|
|
|
|
|
|
|
|
obj = NULL;
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
|
|
|
|
/* Force collection to occur for every subsequent object,
|
|
|
|
for testing purposes. */
|
|
|
|
#if 0
|
|
|
|
SM_SetCollectThresholds(1*M/*sizeof(MyClassStruct)*/,
|
|
|
|
sizeof(MyClassStruct),
|
|
|
|
sizeof(MyClassStruct),
|
|
|
|
sizeof(MyClassStruct),
|
|
|
|
0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (i = 0; i < ITERATIONS; i++) {
|
|
|
|
int size = rand() / 100 + 4;
|
|
|
|
/* allocate some random garbage */
|
|
|
|
// fprintf(out, "allocating array of size %d\n", size);
|
|
|
|
arr = SM_AllocArray(MyArrayClass, size);
|
|
|
|
if (arr) {
|
|
|
|
SM_ARRAY_ELEMENTS(arr)[3] = (SMObject*)root;
|
|
|
|
root = (MyClassStruct*)arr;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Array size %u index %d\n", size, i);
|
|
|
|
/* continue */
|
|
|
|
root = NULL;
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
SM_DumpPageMap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = (MyClassStruct*)SM_AllocObject(MyClass);
|
|
|
|
if (obj) {
|
|
|
|
((MyClassStruct*)SM_DECRYPT(obj))->value = cnt++;
|
|
|
|
SM_SET_FIELD(MyClassStruct, obj, next, root); /* link this into the root list */
|
|
|
|
root = obj;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "ALLOCATION FAILURE: Small Object index %d\n", i);
|
|
|
|
/* continue */
|
|
|
|
root = NULL;
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
SM_DumpPageMap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//SM_DumpStats(out, dumpFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
SM_Collect();
|
|
|
|
}
|
|
|
|
SM_RunFinalization();
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
|
|
|
|
t2 = PR_IntervalNow();
|
|
|
|
fprintf(out, "Test completed in %ums\n", PR_IntervalToMilliseconds(t2 - t1));
|
|
|
|
|
|
|
|
done:
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
SM_DumpPageMap(out, dumpFlags);
|
|
|
|
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (verbose) {
|
|
|
|
/* list out all the objects */
|
|
|
|
i = 0;
|
|
|
|
obj = root;
|
|
|
|
while (obj) {
|
|
|
|
MyClassStruct* next;
|
|
|
|
if (SM_IS_OBJECT((SMObject*)obj)) {
|
|
|
|
fprintf(out, "%4d ", SM_GET_FIELD(MyClassStruct, obj, value));
|
|
|
|
next = SM_GET_FIELD(MyClassStruct, obj, next);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(out, "[arr %p] ", obj);
|
|
|
|
next = (MyClassStruct*)SM_ARRAY_ELEMENTS((SMArray*)obj)[3];
|
|
|
|
}
|
|
|
|
if (i++ % 20 == 19)
|
|
|
|
fprintf(out, "\n");
|
|
|
|
obj = next;
|
|
|
|
}
|
|
|
|
fprintf(out, "\nconserv = %d\n", SM_GET_FIELD(MyClassStruct, conserv, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
randomPtr = NULL;
|
|
|
|
root = NULL;
|
|
|
|
obj = NULL;
|
|
|
|
conserv = NULL;
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
// SM_DumpPageMap(out, dumpFlags);
|
|
|
|
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
#if 0
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
// SM_DumpPageMap(out, dumpFlags);
|
|
|
|
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
#if 0
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
// SM_DumpPageMap(out, dumpFlags);
|
|
|
|
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SM_Collect();
|
|
|
|
SM_RunFinalization();
|
|
|
|
#if defined(DEBUG) || defined(SM_DUMP)
|
|
|
|
// SM_DumpStats(out, dumpFlags);
|
|
|
|
SM_DumpPageMap(out, dumpFlags);
|
|
|
|
if (failure) SM_DumpGCHeap(out, dumpFlags);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SM_CleanupGC(PR_TRUE);
|
|
|
|
status = PR_Cleanup();
|
|
|
|
if (status != PR_SUCCESS)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|