/* -*- 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" #include "sm.h" #include "smobj.h" #include "smpage.h" #include "smheap.h" #include "smtrav.h" #include #include #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; } /******************************************************************************/