From 6500f1eff62f2e949fe527345697dbf5a186f259 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 21 Nov 2008 16:20:35 -0700 Subject: [PATCH] initial work to support boot image creation and use --- makefile | 45 +++++-- src/bootimage.cpp | 240 ++++++++++++++++++++++++++++++++++ src/bootimage.h | 34 +++++ src/finder.cpp | 112 +++++++++++++++- src/finder.h | 38 ++++++ src/heapdump.cpp | 327 ++++++++-------------------------------------- src/heapwalk.cpp | 300 ++++++++++++++++++++++++++++++++++++++++++ src/heapwalk.h | 23 ++++ src/machine.cpp | 78 +++++------ src/machine.h | 5 +- src/posix.cpp | 48 ++++++- src/system.h | 15 ++- src/types.def | 5 - src/util.cpp | 34 ----- src/util.h | 46 +++++++ 15 files changed, 975 insertions(+), 375 deletions(-) create mode 100644 src/bootimage.cpp create mode 100644 src/bootimage.h create mode 100644 src/heapwalk.cpp create mode 100644 src/heapwalk.h diff --git a/makefile b/makefile index 3e4b0e989a..946c31e62f 100644 --- a/makefile +++ b/makefile @@ -201,11 +201,6 @@ vm-sources = \ $(src)/process.cpp \ $(src)/$(asm).cpp -ifeq ($(heapdump),true) - vm-sources += $(src)/heapdump.cpp - cflags += -DAVIAN_HEAPDUMP -endif - vm-asm-sources = $(src)/$(asm).S ifeq ($(process),compile) @@ -222,6 +217,21 @@ vm-cpp-objects = $(call cpp-objects,$(vm-sources),$(src),$(native-build)) vm-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(native-build)) vm-objects = $(vm-cpp-objects) $(vm-asm-objects) +heapwalk-sources = $(src)/heapwalk.cpp +heapwalk-objects = \ + $(call cpp-objects,$(heapwalk-sources),$(src),$(native-build)) + +ifeq ($(heapdump),true) + vm-sources += $(src)/heapdump.cpp + vm-heapwalk-objects = $(heapwalk-objects) + cflags += -DAVIAN_HEAPDUMP +endif + +bootimage-sources = $(src)/bootimage.cpp +bootimage-objects = \ + $(call cpp-objects,$(bootimage-sources),$(src),$(native-build)) +bootimage = $(native-build)/bootimage + driver-source = $(src)/main.cpp driver-object = $(native-build)/main.o driver-dynamic-object = $(native-build)/main-dynamic.o @@ -259,7 +269,7 @@ args = $(flags) $(input) .PHONY: build build: $(static-library) $(executable) $(dynamic-library) \ - $(executable-dynamic) $(classpath-dep) $(test-dep) + $(executable-dynamic) $(classpath-dep) $(test-dep) $(bootimage) $(test-classes): $(classpath-dep) @@ -346,6 +356,12 @@ $(vm-cpp-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) $(vm-asm-objects): $(native-build)/%-asm.o: $(src)/%.S $(compile-asm-object) +$(bootimage-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + +$(heapwalk-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + $(driver-object): $(driver-source) $(compile-object) @@ -395,7 +411,20 @@ $(static-library): $(vm-objects) $(jni-objects) $(executable): \ $(vm-objects) $(classpath-object) $(jni-objects) $(driver-object) \ - $(boot-object) + $(vm-heapwalk-objects) $(boot-object) + @echo "linking $(@)" +ifeq ($(platform),windows) + $(dlltool) -z $(@).def $(^) + $(dlltool) -d $(@).def -e $(@).exp + $(cc) $(@).exp $(^) $(lflags) -o $(@) +else + $(cc) $(^) $(rdynamic) $(lflags) -o $(@) +endif + $(strip) $(strip-all) $(@) + +$(bootimage): \ + $(vm-objects) $(classpath-object) $(jni-objects) $(heapwalk-objects) \ + $(bootimage-objects) @echo "linking $(@)" ifeq ($(platform),windows) $(dlltool) -z $(@).def $(^) @@ -408,7 +437,7 @@ endif $(dynamic-library): \ $(vm-objects) $(classpath-object) $(dynamic-object) $(jni-objects) \ - $(boot-object) + $(vm-heapwalk-objects) $(boot-object) @echo "linking $(@)" $(cc) $(^) $(shared) $(lflags) -o $(@) $(strip) $(strip-all) $(@) diff --git a/src/bootimage.cpp b/src/bootimage.cpp new file mode 100644 index 0000000000..1936f9a65b --- /dev/null +++ b/src/bootimage.cpp @@ -0,0 +1,240 @@ +#include "bootimage.h" +#include "heapwalk.h" +#include "common.h" +#include "machine.h" +#include "util.h" + +using namespace vm; + +namespace { + +bool +endsWith(const char* suffix, const char* s, unsigned length) +{ + unsigned suffixLength = strlen(suffix); + return length >= suffixLength + and memcmp(suffix, s + (length - suffixLength), suffixLength) == 0; +} + +unsigned +codeMapSize(unsigned codeSize) +{ + return ceiling(codeSize, BitsPerWord) * BytesPerWord; +} + +object +makeCodeImage(Thread* t, BootImage* image, uint8_t* code, unsigned capacity) +{ + unsigned size; + compileThunks(t, code, &size, image); + + unsigned fixupCount = 0; + object table = makeHashMap(t, 0, 0); + PROTECT(t, table); + + for (Finder::Iterator it(t->m->finder); it.hasMore();) { + unsigned nameSize; + const char* name = it.next(&nameSize); + + if (endsWith(".class", name, nameSize)) { + object c = resolveClass + (t, makeByteArray(t, "%*s", nameSize - 5, name)); + PROTECT(t, c); + + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object method = arrayBody(t, classMethodTable(t, c), i); + if (methodCode(t, method)) { + compileMethod(t, method, code, &size, capacity, + &table, &fixupCount); + } + } + } + } + + image->codeSize = size; + + return table; +} + +unsigned +heapMapSize(unsigned heapSize) +{ + return ceiling(heapSize, BitsPerWord * 8) * BytesPerWord; +} + +unsigned +objectSize(Thread* t, object o) +{ + assert(t, not objectExtended(t, o)); + return baseSize(t, o, objectClass(t, o)); +} + +HeapMap* +makeHeapImage(Thread* t, BootImage* image, uintptr_t* heap, uintptr_t* map, + unsigned capacity) +{ + class Walker: public HeapWalker { + public: + Walker(Thread* t, uintptr_t* heap, uintptr_t* map, unsigned capacity): + t(t), currentObject(0), currentOffset(0), heap(heap), map(map), + position(0), capacity(capacity) + { } + + void visit(object p, unsigned number) { + if (currentObject) { + markBit(map, (currentObject - heap) + currentOffset); + currentObject[currentOffset] = number; + } + + currentObject = reinterpret_cast(p); + } + + virtual void root() { + currentObject = 0; + } + + virtual unsigned visitNew(object p) { + if (p) { + unsigned size = objectSize(t, p); + assert(t, position + size < capacity); + + memcpy(heap + position, p, size * BytesPerWord); + + unsigned number = position + 1; + position += size; + + visit(p, number); + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) { + visit(0, number); + } + + virtual void push(unsigned offset) { + currentOffset = offset; + } + + virtual void pop() { + currentObject = 0; + } + + Thread* t; + uintptr_t* currentObject; + unsigned currentOffset; + uintptr_t* heap; + uintptr_t* map; + unsigned position; + unsigned capacity; + } walker(t, heap, map, capacity / BytesPerWord); + + HeapMap* table = walk(t, &walker); + + image->heapSize = walker.position * BytesPerWord; + + return table; +} + +void +updateCodeTable(Thread* t, object codeTable, uint8_t* code, uintptr_t* codeMap, + HeapMap* heapTable) +{ + intptr_t i = 0; + for (HashMapIterator it(t, codeTable); it.hasMore(); ++i) { + object mapEntry = it.next(); + intptr_t target = heapTable->find(tripleFirst(t, mapEntry)); + assert(t, target >= 0); + + for (object fixup = tripleSecond(t, mapEntry); + fixup; + fixup = pairSecond(t, fixup)) + { + int32_t v = intValue(t, pairFirst(t, fixup)); + memcpy(code + v, &target, BytesPerWord); + markBit(codeMap, v); + } + } +} + +unsigned +offset(object a, uintptr_t* b) +{ + return reinterpret_cast(b) - reinterpret_cast(a); +} + +void +writeBootImage(Thread* t, FILE* out) +{ + BootImage image; + + const unsigned CodeCapacity = 32 * 1024 * 1024; + uint8_t* code = static_cast(t->m->heap->allocate(CodeCapacity)); + uintptr_t* codeMap = static_cast + (t->m->heap->allocate(codeMapSize(CodeCapacity))); + memset(codeMap, 0, codeMapSize(CodeCapacity)); + + object codeTable = makeCodeImage(t, &image, code, CodeCapacity); + + const unsigned HeapCapacity = 32 * 1024 * 1024; + uintptr_t* heap = static_cast + (t->m->heap->allocate(HeapCapacity)); + uintptr_t* heapMap = static_cast + (t->m->heap->allocate(heapMapSize(HeapCapacity))); + memset(heapMap, 0, heapMapSize(HeapCapacity)); + + HeapMap* heapTable = makeHeapImage(t, &image, heap, heapMap, HeapCapacity); + + updateCodeTable(t, codeTable, code, codeMap, heapTable); + + image.magic = BootImage::Magic; + + image.codeTable = offset(codeTable, heap); + + image.loader = offset(t->m->loader, heap); + image.bootstrapClassMap = offset(t->m->bootstrapClassMap, heap); + image.stringMap = offset(t->m->stringMap, heap); + image.types = offset(t->m->types, heap); + image.jniMethodTable = offset(t->m->jniMethodTable, heap); + image.finalizers = offset(t->m->finalizers, heap); + image.tenuredFinalizers = offset(t->m->tenuredFinalizers, heap); + image.finalizeQueue = offset(t->m->finalizeQueue, heap); + image.weakReferences = offset(t->m->weakReferences, heap); + image.tenuredWeakReferences = offset(t->m->tenuredWeakReferences, heap); + + fwrite(&image, sizeof(BootImage), 1, out); + + fwrite(heapMap, pad(heapMapSize(image.heapSize)), 1, out); + fwrite(heap, pad(image.heapSize), 1, out); + + fwrite(codeMap, pad(codeMapSize(image.codeSize)), 1, out); + fwrite(code, pad(image.codeSize), 1, out); +} + +} // namespace + +int +main(int ac, const char** av) +{ + if (ac != 2) { + fprintf(stderr, "usage: %s \n", av[0]); + return -1; + } + + System* s = makeSystem(0); + Heap* h = makeHeap(s, 128 * 1024 * 1024); + Finder* f = makeFinder(s, av[0], 0); + Processor* p = makeProcessor(s, h); + Machine* m = new (h->allocate(sizeof(Machine))) Machine(s, h, f, p, 0, 0); + Thread* t = p->makeThread(m, 0, 0); + + enter(t, Thread::ActiveState); + enter(t, Thread::IdleState); + + writeBootImage(t, stdout); + + return 0; +} diff --git a/src/bootimage.h b/src/bootimage.h new file mode 100644 index 0000000000..18c7e38012 --- /dev/null +++ b/src/bootimage.h @@ -0,0 +1,34 @@ +namespace vm { + +class BootImage { + public: + static const unsigned Magic = 0x22377322; + + unsigned magic; + + unsigned heapSize; + unsigned codeSize; + + unsigned codeTable; + + unsigned loader; + unsigned bootstrapClassMap; + unsigned stringMap; + unsigned types; + unsigned jniMethodTable; + unsigned finalizers; + unsigned tenuredFinalizers; + unsigned finalizeQueue; + unsigned weakReferences; + unsigned tenuredWeakReferences; + + unsigned defaultThunk; + unsigned nativeThunk; + unsigned aioobThunk; + +#define THUNK(s) unsigned s##Thunk; +#include "thunks.cpp" +#undef THUNK +}; + +} // namespace vm diff --git a/src/finder.cpp b/src/finder.cpp index 091593d9ea..9867c01a13 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -59,8 +59,15 @@ equal(const void* a, unsigned al, const void* b, unsigned bl) class Element { public: + class Iterator { + public: + virtual const char* next(unsigned* size) = 0; + virtual void dispose() = 0; + }; + Element(): next(0) { } + virtual Iterator* iterator() = 0; virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual void dispose() = 0; @@ -70,10 +77,45 @@ class Element { class DirectoryElement: public Element { public: + class Iterator: public Element::Iterator { + public: + Iterator(System* s, const char* name): + s(s), directory(0) + { + if (not s->success(s->open(&directory, name))) { + directory = 0; + } + } + + virtual const char* next(unsigned* size) { + if (directory) { + for (const char* v = directory->next(); v; v = directory->next()) { + if (v[0] != '.') { + *size = strlen(v); + return v; + } + } + } + return 0; + } + + virtual void dispose() { + directory->dispose(); + s->free(this); + } + + System* s; + System::Directory* directory; + }; + DirectoryElement(System* s, const char* name): s(s), name(name) { } + virtual Element::Iterator* iterator() { + return new (allocate(s, sizeof(Iterator))) Iterator(s, name); + } + virtual System::Region* find(const char* name) { const char* file = append(s, this->name, "/", name); System::Region* region; @@ -91,7 +133,7 @@ class DirectoryElement: public Element { const char* file = append(s, this->name, "/", name); System::FileType type = s->identify(file); s->free(file); - return type != System::DoesNotExist; + return type != System::TypeDoesNotExist; } virtual void dispose() { @@ -379,10 +421,37 @@ class JarIndex { class JarElement: public Element { public: + class Iterator: public Element::Iterator { + public: + Iterator(System* s, JarIndex* index): s(s), index(index), position(0) { } + + virtual const char* next(unsigned* size) { + if (position < index->position) { + JarIndex::Node* n = index->nodes + (position++); + *size = JarIndex::fileNameLength(n->entry); + return reinterpret_cast(JarIndex::fileName(n->entry)); + } else { + return 0; + } + } + + virtual void dispose() { + s->free(this); + } + + System* s; + JarIndex* index; + unsigned position; + }; + JarElement(System* s, const char* name): s(s), name(name), region(0), index(0) { } + virtual Element::Iterator* iterator() { + return new (allocate(s, sizeof(Iterator))) Iterator(s, index); + } + virtual void init() { if (index == 0) { System::Region* r; @@ -512,11 +581,11 @@ parsePath(System* s, const char* path, const char* bootLibrary) name[token.length] = 0; switch (s->identify(name)) { - case System::File: { + case System::TypeFile: { e = new (allocate(s, sizeof(JarElement))) JarElement(s, name); } break; - case System::Directory: { + case System::TypeDirectory: { e = new (allocate(s, sizeof(DirectoryElement))) DirectoryElement(s, name); } break; @@ -541,6 +610,38 @@ parsePath(System* s, const char* path, const char* bootLibrary) return first; } +class MyIterator: public Finder::IteratorImp { + public: + MyIterator(System* s, Element* path): + s(s), e(path ? path->next : 0), it(path ? path->iterator() : 0) + { } + + virtual const char* next(unsigned* size) { + while (it) { + const char* v = it->next(size); + if (v) { + return v; + } else { + it->dispose(); + if (e) { + it = e->iterator(); + e = e->next; + } + } + } + return 0; + } + + virtual void dispose() { + if (it) it->dispose(); + s->free(this); + } + + System* s; + Element* e; + Element::Iterator* it; +}; + class MyFinder: public Finder { public: MyFinder(System* system, const char* path, const char* bootLibrary): @@ -549,6 +650,11 @@ class MyFinder: public Finder { pathString(copy(system, path)) { } + virtual IteratorImp* iterator() { + return new (allocate(system, sizeof(MyIterator))) + MyIterator(system, path_); + } + virtual System::Region* find(const char* name) { for (Element* e = path_; e; e = e->next) { System::Region* r = e->find(name); diff --git a/src/finder.h b/src/finder.h index 64eb4c287b..37f6d3a9b7 100644 --- a/src/finder.h +++ b/src/finder.h @@ -19,6 +19,44 @@ namespace vm { class Finder { public: + class IteratorImp { + public: + virtual const char* next(unsigned* size) = 0; + virtual void dispose() = 0; + }; + + class Iterator { + public: + Iterator(Finder* finder): + it(finder->iterator()), + current(it->next(¤tSize)) + { } + + ~Iterator() { + it->dispose(); + } + + bool hasMore() { + return current != 0; + } + + const char* next(unsigned* size) { + if (current) { + const char* v = current; + *size = currentSize; + current = it->next(¤tSize); + return v; + } else { + return 0; + } + } + + IteratorImp* it; + const char* current; + unsigned currentSize; + }; + + virtual IteratorImp* iterator(); virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual const char* path() = 0; diff --git a/src/heapdump.cpp b/src/heapdump.cpp index 617d1d2e74..504117bfbb 100644 --- a/src/heapdump.cpp +++ b/src/heapdump.cpp @@ -8,195 +8,12 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" +#include "heapwalk.h" using namespace vm; namespace { -const uintptr_t PointerShift = log(BytesPerWord); - -class Set { - public: - class Entry { - public: - object value; - uint32_t number; - int next; - }; - - static unsigned footprint(unsigned capacity) { - return sizeof(Set) - + pad(sizeof(int) * capacity) - + pad(sizeof(Set::Entry) * capacity); - } - - Set(unsigned capacity): - size(0), - capacity(capacity), - index(reinterpret_cast - (reinterpret_cast(this) - + sizeof(Set))), - entries(reinterpret_cast - (reinterpret_cast(index) - + pad(sizeof(int) * capacity))) - { } - - unsigned size; - unsigned capacity; - int* index; - Entry* entries; -}; - -class Stack { - public: - class Entry { - public: - object value; - int offset; - }; - - static const unsigned Capacity = 4096; - - Stack(Stack* next): next(next), entryCount(0) { } - - Stack* next; - unsigned entryCount; - Entry entries[Capacity]; -}; - -class Context { - public: - Context(Thread* thread, FILE* out): - thread(thread), out(out), objects(0), stack(0), nextNumber(1) - { } - - ~Context() { - if (objects) { - thread->m->heap->free(objects, Set::footprint(objects->capacity)); - } - while (stack) { - Stack* dead = stack; - stack = dead->next; - thread->m->heap->free(stack, sizeof(Stack)); - } - } - - Thread* thread; - FILE* out; - Set* objects; - Stack* stack; - uint32_t nextNumber; -}; - -void -push(Context* c, object p, int offset) -{ - if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { - c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) - Stack(c->stack); - } - Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); - e->value = p; - e->offset = offset; -} - -bool -pop(Context* c, object* p, int* offset) -{ - if (c->stack) { - if (c->stack->entryCount == 0) { - if (c->stack->next) { - Stack* dead = c->stack; - c->stack = dead->next; - c->thread->m->heap->free(dead, sizeof(Stack)); - } else { - return false; - } - } - Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); - *p = e->value; - *offset = e->offset; - return true; - } else { - return false; - } -} - -unsigned -hash(object p, unsigned capacity) -{ - return (reinterpret_cast(p) >> PointerShift) - & (capacity - 1); -} - -Set::Entry* -find(Context* c, object p) -{ - if (c->objects == 0) return 0; - - for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { - Set::Entry* e = c->objects->entries + i; - if (e->value == p) { - return e; - } - i = e->next; - } - - return 0; -} - -Set::Entry* -add(Context* c UNUSED, Set* set, object p, uint32_t number) -{ - assert(c->thread, set->size < set->capacity); - - unsigned index = hash(p, set->capacity); - - int offset = set->size++; - Set::Entry* e = set->entries + offset; - e->value = p; - e->number = number; - e->next = set->index[index]; - set->index[index] = offset; - return e; -} - -Set::Entry* -add(Context* c, object p) -{ - if (c->objects == 0 or c->objects->size == c->objects->capacity) { - unsigned capacity; - if (c->objects) { - capacity = c->objects->capacity * 2; - } else { - capacity = 4096; // must be power of two - } - - Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) - Set(capacity); - - memset(set->index, 0xFF, sizeof(int) * capacity); - - if (c->objects) { - for (unsigned i = 0; i < c->objects->capacity; ++i) { - for (int j = c->objects->index[i]; j >= 0;) { - Set::Entry* e = c->objects->entries + j; - add(c, set, e->value, e->number); - j = e->next; - } - } - - c->thread->m->heap->free - (c->objects, Set::footprint(c->objects->capacity)); - } - - c->objects = set; - } - - return add(c, c->objects, p, 0); -} - enum { Root, Size, @@ -205,100 +22,30 @@ enum { Pop }; -inline object -get(object o, unsigned offsetInWords) +void +write1(FILE* out, uint8_t v) { - return static_cast - (mask(cast(o, offsetInWords * BytesPerWord))); + fwrite(&v, 1, 1, out); } void -write1(Context* c, uint8_t v) -{ - fwrite(&v, 1, 1, c->out); -} - -void -write4(Context* c, uint32_t v) +write4(FILE* out, uint32_t v) { uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF }; - fwrite(b, 4, 1, c->out); + fwrite(b, 4, 1, out); } void -writeString(Context* c, int8_t* p, unsigned size) +writeString(FILE* out, int8_t* p, unsigned size) { - write4(c, size); - fwrite(p, size, 1, c->out); + write4(out, size); + fwrite(p, size, 1, out); } unsigned objectSize(Thread* t, object o) { - unsigned n = baseSize(t, o, objectClass(t, o)); - if (objectExtended(t, o)) { - ++ n; - } - return n; -} - -void -visit(Context* c, object p) -{ - Thread* t = c->thread; - int nextChildOffset; - - write1(c, Root); - - visit: { - Set::Entry* e = find(c, p); - if (e) { - write4(c, e->number); - } else { - e = add(c, p); - e->number = c->nextNumber++; - - write4(c, e->number); - - write1(c, Size); - write4(c, objectSize(t, p)); - - if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) { - object name = className(t, p); - if (name) { - write1(c, ClassName); - writeString(c, &byteArrayBody(t, name, 0), - byteArrayLength(t, name) - 1); - } - } - - nextChildOffset = walkNext(t, p, -1); - if (nextChildOffset != -1) { - goto children; - } - } - } - - goto pop; - - children: { - write1(c, Push); - push(c, p, nextChildOffset); - p = get(p, nextChildOffset); - goto visit; - } - - pop: { - if (pop(c, &p, &nextChildOffset)) { - write1(c, Pop); - nextChildOffset = walkNext(t, p, nextChildOffset); - if (nextChildOffset >= 0) { - goto children; - } else { - goto pop; - } - } - } + return extendedSize(t, o, baseSize(t, o, objectClass(t, o))); } } // namespace @@ -308,22 +55,56 @@ namespace vm { void dumpHeap(Thread* t, FILE* out) { - Context context(t, out); - - class Visitor : public Heap::Visitor { + class Walker: public HeapWalker { public: - Visitor(Context* c): c(c) { } + Walker(Thread* t, FILE* out): t(t), out(out), nextNumber(1) { } - virtual void visit(void* p) { - ::visit(c, static_cast(mask(*static_cast(p)))); + virtual void root() { + write1(out, Root); } - Context* c; - } v(&context); + virtual unsigned visitNew(object p) { + if (p) { + unsigned number = nextNumber++; + write4(out, number); - add(&context, 0)->number = 0; + write1(out, Size); + write4(out, objectSize(t, p)); - visitRoots(t->m, &v); + if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) + { + object name = className(t, p); + if (name) { + write1(out, ClassName); + writeString(out, &byteArrayBody(t, name, 0), + byteArrayLength(t, name) - 1); + } + } + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) { + write4(out, number); + } + + virtual void push(unsigned) { + write1(out, Push); + } + + virtual void pop() { + write1(out, Pop); + } + + Thread* t; + FILE* out; + unsigned nextNumber; + } walker(t, out); + + walk(t, &walker)->dispose(); } } // namespace vm diff --git a/src/heapwalk.cpp b/src/heapwalk.cpp new file mode 100644 index 0000000000..4a240af4a2 --- /dev/null +++ b/src/heapwalk.cpp @@ -0,0 +1,300 @@ +#include "heapwalk.h" + +using namespace vm; + +namespace { + +const uintptr_t PointerShift = log(BytesPerWord); + +class Context; + +class Set: public HeapMap { + public: + class Entry { + public: + object value; + uint32_t number; + int next; + }; + + static unsigned footprint(unsigned capacity) { + return sizeof(Set) + + pad(sizeof(int) * capacity) + + pad(sizeof(Set::Entry) * capacity); + } + + Set(Context* context, unsigned capacity): + context(context), + index(reinterpret_cast + (reinterpret_cast(this) + + sizeof(Set))), + entries(reinterpret_cast + (reinterpret_cast(index) + + pad(sizeof(int) * capacity))), + size(0), + capacity(capacity) + { } + + virtual int find(object value); + + virtual void dispose(); + + Context* context; + int* index; + Entry* entries; + unsigned size; + unsigned capacity; +}; + +class Stack { + public: + class Entry { + public: + object value; + int offset; + }; + + static const unsigned Capacity = 4096; + + Stack(Stack* next): next(next), entryCount(0) { } + + Stack* next; + unsigned entryCount; + Entry entries[Capacity]; +}; + +class Context { + public: + Context(Thread* thread): + thread(thread), objects(0), stack(0) + { } + + ~Context() { + while (stack) { + Stack* dead = stack; + stack = dead->next; + thread->m->heap->free(stack, sizeof(Stack)); + } + } + + Thread* thread; + Set* objects; + Stack* stack; +}; + +void +push(Context* c, object p, int offset) +{ + if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { + c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) + Stack(c->stack); + } + Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); + e->value = p; + e->offset = offset; +} + +bool +pop(Context* c, object* p, int* offset) +{ + if (c->stack) { + if (c->stack->entryCount == 0) { + if (c->stack->next) { + Stack* dead = c->stack; + c->stack = dead->next; + c->thread->m->heap->free(dead, sizeof(Stack)); + } else { + return false; + } + } + Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); + *p = e->value; + *offset = e->offset; + return true; + } else { + return false; + } +} + +unsigned +hash(object p, unsigned capacity) +{ + return (reinterpret_cast(p) >> PointerShift) + & (capacity - 1); +} + +Set::Entry* +find(Context* c, object p) +{ + if (c->objects == 0) return 0; + + for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { + Set::Entry* e = c->objects->entries + i; + if (e->value == p) { + return e; + } + i = e->next; + } + + return 0; +} + +int +Set::find(object value) +{ + Set::Entry* e = ::find(context, value); + if (e) { + return e->number; + } else { + return -1; + } +} + +void +Set::dispose() +{ + context->thread->m->heap->free(this, footprint(capacity)); +} + +Set::Entry* +add(Context* c UNUSED, Set* set, object p, uint32_t number) +{ + assert(c->thread, set->size < set->capacity); + + unsigned index = hash(p, set->capacity); + + int offset = set->size++; + Set::Entry* e = set->entries + offset; + e->value = p; + e->number = number; + e->next = set->index[index]; + set->index[index] = offset; + return e; +} + +Set::Entry* +add(Context* c, object p) +{ + if (c->objects == 0 or c->objects->size == c->objects->capacity) { + unsigned capacity; + if (c->objects) { + capacity = c->objects->capacity * 2; + } else { + capacity = 4096; // must be power of two + } + + Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) + Set(c, capacity); + + memset(set->index, 0xFF, sizeof(int) * capacity); + + if (c->objects) { + for (unsigned i = 0; i < c->objects->capacity; ++i) { + for (int j = c->objects->index[i]; j >= 0;) { + Set::Entry* e = c->objects->entries + j; + add(c, set, e->value, e->number); + j = e->next; + } + } + + c->thread->m->heap->free + (c->objects, Set::footprint(c->objects->capacity)); + } + + c->objects = set; + } + + return add(c, c->objects, p, 0); +} + +inline object +get(object o, unsigned offsetInWords) +{ + return static_cast + (mask(cast(o, offsetInWords * BytesPerWord))); +} + +unsigned +objectSize(Thread* t, object o) +{ + unsigned n = baseSize(t, o, objectClass(t, o)); + if (objectExtended(t, o)) { + ++ n; + } + return n; +} + +void +walk(Context* c, HeapWalker* w, object p) +{ + Thread* t = c->thread; + int nextChildOffset; + + w->root(); + + visit: { + Set::Entry* e = find(c, p); + if (e) { + w->visitOld(p, e->number); + } else { + e = add(c, p); + e->number = w->visitNew(p); + + nextChildOffset = walkNext(t, p, -1); + if (nextChildOffset != -1) { + goto children; + } + } + } + + goto pop; + + children: { + w->push(nextChildOffset); + push(c, p, nextChildOffset); + p = get(p, nextChildOffset); + goto visit; + } + + pop: { + if (pop(c, &p, &nextChildOffset)) { + w->pop(); + nextChildOffset = walkNext(t, p, nextChildOffset); + if (nextChildOffset >= 0) { + goto children; + } else { + goto pop; + } + } + } +} + +} // namespace + +namespace vm { + +HeapMap* +walk(Thread* t, HeapWalker* w) +{ + Context context(t); + + class Visitor: public Heap::Visitor { + public: + Visitor(Context* c, HeapWalker* w): c(c), w(w) { } + + virtual void visit(void* p) { + walk(c, w, static_cast(mask(*static_cast(p)))); + } + + Context* c; + HeapWalker* w; + } v(&context, w); + + add(&context, 0)->number = w->visitNew(0); + + visitRoots(t->m, &v); + + return context.objects; +} + +} // namespace vm diff --git a/src/heapwalk.h b/src/heapwalk.h new file mode 100644 index 0000000000..f18cb021ea --- /dev/null +++ b/src/heapwalk.h @@ -0,0 +1,23 @@ +#include "machine.h" + +namespace vm { + +class HeapMap { + public: + virtual int find(object value) = 0; + virtual void dispose() = 0; +}; + +class HeapWalker { + public: + virtual void root() = 0; + virtual unsigned visitNew(object value) = 0; + virtual void visitOld(object value, unsigned number) = 0; + virtual void push(unsigned offset) = 0; + virtual void pop() = 0; +}; + +HeapMap* +walk(Thread* t, HeapWalker* w); + +} // namespace vm diff --git a/src/machine.cpp b/src/machine.cpp index 713e4ac0e7..3935ed893a 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -248,38 +248,6 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, } } -void -walk(Thread* t, Heap::Walker* w, object o, unsigned start) -{ - object class_ = static_cast(t->m->heap->follow(objectClass(t, o))); - object objectMask = static_cast - (t->m->heap->follow(classObjectMask(t, class_))); - - if (objectMask) { - unsigned fixedSize = classFixedSize(t, class_); - unsigned arrayElementSize = classArrayElementSize(t, class_); - unsigned arrayLength - = (arrayElementSize ? - cast(o, fixedSize - BytesPerWord) : 0); - - uint32_t mask[intArrayLength(t, objectMask)]; - memcpy(mask, &intArrayBody(t, objectMask, 0), - intArrayLength(t, objectMask) * 4); - - walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); - } else if (classVmFlags(t, class_) & SingletonFlag) { - unsigned length = singletonLength(t, o); - if (length) { - walk(t, w, singletonMask(t, o), - (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); - } else if (start == 0) { - w->visit(0); - } - } else if (start == 0) { - w->visit(0); - } -} - void finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p) { @@ -770,12 +738,8 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) PROTECT(t, interfaceTable); unsigned i = 0; - object it = hashMapIterator(t, map); - PROTECT(t, it); - - for (; it; it = hashMapIteratorNext(t, it)) { - object interface = resolveClass - (t, tripleFirst(t, hashMapIteratorNode(t, it))); + for (HashMapIterator it(t, map); it.hasMore();) { + object interface = resolveClass(t, tripleFirst(t, it.next())); if (UNLIKELY(t->exception)) return; set(t, interfaceTable, ArrayBody + (i * BytesPerWord), interface); @@ -1243,10 +1207,8 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) if (classFlags(t, class_) & ACC_INTERFACE) { PROTECT(t, vtable); - for (object it = hashMapIterator(t, virtualMap); it; - it = hashMapIteratorNext(t, it)) - { - object method = tripleFirst(t, hashMapIteratorNode(t, it)); + for (HashMapIterator it(t, virtualMap); it.hasMore();) { + object method = tripleFirst(t, it.next()); assert(t, arrayBody(t, vtable, methodOffset(t, method)) == 0); set(t, vtable, ArrayBody + (methodOffset(t, method) * BytesPerWord), method); @@ -2769,6 +2731,38 @@ collect(Thread* t, Heap::CollectionType type) #endif } +void +walk(Thread* t, Heap::Walker* w, object o, unsigned start) +{ + object class_ = static_cast(t->m->heap->follow(objectClass(t, o))); + object objectMask = static_cast + (t->m->heap->follow(classObjectMask(t, class_))); + + if (objectMask) { + unsigned fixedSize = classFixedSize(t, class_); + unsigned arrayElementSize = classArrayElementSize(t, class_); + unsigned arrayLength + = (arrayElementSize ? + cast(o, fixedSize - BytesPerWord) : 0); + + uint32_t mask[intArrayLength(t, objectMask)]; + memcpy(mask, &intArrayBody(t, objectMask, 0), + intArrayLength(t, objectMask) * 4); + + ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); + } else if (classVmFlags(t, class_) & SingletonFlag) { + unsigned length = singletonLength(t, o); + if (length) { + ::walk(t, w, singletonMask(t, o), + (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); + } else if (start == 0) { + w->visit(0); + } + } else if (start == 0) { + w->visit(0); + } +} + int walkNext(Thread* t, object o, int previous) { diff --git a/src/machine.h b/src/machine.h index 3c12722c21..1c18fa9770 100644 --- a/src/machine.h +++ b/src/machine.h @@ -1128,7 +1128,7 @@ class Machine { dispose(); } - static const unsigned HeapPoolSize = 8; + static const unsigned HeapPoolSize = 64; static const unsigned FixedFootprintThresholdInBytes = 256 * 1024; @@ -2208,6 +2208,9 @@ intern(Thread* t, object s); void exit(Thread* t); +void +walk(Thread* t, Heap::Walker* w, object o, unsigned start); + int walkNext(Thread* t, object o, int previous); diff --git a/src/posix.cpp b/src/posix.cpp index 8f480aee26..e92dd23e14 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -25,6 +25,7 @@ #include "signal.h" #include "ucontext.h" #include "stdint.h" +#include "dirent.h" #include "x86.h" #include "system.h" @@ -463,6 +464,31 @@ class MySystem: public System { size_t length_; }; + class Directory: public System::Directory { + public: + Directory(System* s, DIR* directory): s(s), directory(directory) { } + + virtual const char* next() { + if (directory) { + dirent* e = readdir(directory); + if (e) { + return e->d_name; + } + } + return 0; + } + + virtual void dispose() { + if (directory) { + closedir(directory); + } + s->free(this); + } + + System* s; + DIR* directory; + }; + class Library: public System::Library { public: Library(System* s, void* p, const char* name, unsigned nameLength, @@ -658,7 +684,7 @@ class MySystem: public System { virtual Status map(System::Region** region, const char* name) { Status status = 1; - int fd = open(name, O_RDONLY); + int fd = ::open(name, O_RDONLY); if (fd != -1) { struct stat s; int r = fstat(fd, &s); @@ -676,19 +702,31 @@ class MySystem: public System { return status; } + virtual Status open(System::Directory** directory, const char* name) { + Status status = 1; + + DIR* d = opendir(name); + if (d) { + *directory = new (allocate(this, sizeof(Directory))) Directory(this, d); + status = 0; + } + + return status; + } + virtual FileType identify(const char* name) { struct stat s; int r = stat(name, &s); if (r == 0) { if (S_ISREG(s.st_mode)) { - return File; + return TypeFile; } else if (S_ISDIR(s.st_mode)) { - return Directory; + return TypeDirectory; } else { - return Unknown; + return TypeUnknown; } } else { - return DoesNotExist; + return TypeDoesNotExist; } } diff --git a/src/system.h b/src/system.h index 3a5cb3cf22..932a5ec4bc 100644 --- a/src/system.h +++ b/src/system.h @@ -20,10 +20,10 @@ class System { typedef intptr_t Status; enum FileType { - Unknown, - DoesNotExist, - File, - Directory + TypeUnknown, + TypeDoesNotExist, + TypeFile, + TypeDirectory }; class Thread { @@ -79,6 +79,12 @@ class System { virtual void dispose() = 0; }; + class Directory { + public: + virtual const char* next() = 0; + virtual void dispose() = 0; + }; + class Library { public: virtual void* resolve(const char* function) = 0; @@ -128,6 +134,7 @@ class System { unsigned returnType) = 0; virtual Status map(Region**, const char* name) = 0; virtual FileType identify(const char* name) = 0; + virtual Status open(Directory**, const char* name) = 0; virtual Status load(Library**, const char* name, bool mapName) = 0; virtual char pathSeparator() = 0; virtual int64_t now() = 0; diff --git a/src/types.def b/src/types.def index bfe450e16a..35fff05cd1 100644 --- a/src/types.def +++ b/src/types.def @@ -73,11 +73,6 @@ (type weakHashMap (extends hashMap)) -(type hashMapIterator - (object map) - (object node) - (unsigned index)) - (type list (uint32_t size) (object front) diff --git a/src/util.cpp b/src/util.cpp index 219695a536..720709fe8e 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -442,40 +442,6 @@ hashMapRemove(Thread* t, object map, object key, return o; } -object -hashMapIterator(Thread* t, object map) -{ - object array = hashMapArray(t, map); - if (array) { - for (unsigned i = 0; i < arrayLength(t, array); ++i) { - if (arrayBody(t, array, i)) { - return makeHashMapIterator(t, map, arrayBody(t, array, i), i + 1); - } - } - } - return 0; -} - -object -hashMapIteratorNext(Thread* t, object it) -{ - object map = hashMapIteratorMap(t, it); - object node = hashMapIteratorNode(t, it); - unsigned index = hashMapIteratorIndex(t, it); - - if (tripleThird(t, node)) { - return makeHashMapIterator(t, map, tripleThird(t, node), index); - } else { - object array = hashMapArray(t, map); - for (unsigned i = index; i < arrayLength(t, array); ++i) { - if (arrayBody(t, array, i)) { - return makeHashMapIterator(t, map, arrayBody(t, array, i), i + 1); - } - } - return 0; - } -} - void listAppend(Thread* t, object list, object value) { diff --git a/src/util.h b/src/util.h index adf8487723..10279a69db 100644 --- a/src/util.h +++ b/src/util.h @@ -92,6 +92,52 @@ treeInsertNode(Thread* t, object tree, intptr_t key, object node, object sentinal, intptr_t (*compare)(Thread* t, intptr_t key, object b)); +class HashMapIterator: public Thread::Protector { + public: + HashMapIterator(Thread* t, object map): + Protector(t), map(map), node(0), index(0) + { + find(); + } + + void find() { + object array = hashMapArray(t, map); + for (unsigned i = index; i < arrayLength(t, array); ++i) { + if (arrayBody(t, array, i)) { + node = arrayBody(t, array, i); + index = i + 1; + } + } + } + + bool hasMore() { + return node != 0; + } + + object next() { + if (node) { + object n = node; + if (tripleThird(t, node)) { + node = tripleThird(t, node); + } else { + find(); + } + return n; + } else { + return 0; + } + } + + virtual void visit(Heap::Visitor* v) { + v->visit(&map); + v->visit(&node); + } + + object map; + object node; + unsigned index; +}; + } // vm #endif//UTIL_H