/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Dalvik.h"
#include "alloc/HeapInternal.h"
#include "alloc/Visit.h"
#include "alloc/VisitInlines.h"
/*
* Visits all of the reference locations in an object.
*/
void dvmVisitObject(Visitor *visitor, Object *obj, void *arg)
{
assert(visitor != NULL);
assert(obj != NULL);
assert(obj->clazz != NULL);
visitObject(visitor, obj, arg);
}
/*
* Applies a verification function to all present values in the hash table.
*/
static void visitHashTable(RootVisitor *visitor, HashTable *table,
RootType type, void *arg)
{
assert(visitor != NULL);
assert(table != NULL);
dvmHashTableLock(table);
for (int i = 0; i < table->tableSize; ++i) {
HashEntry *entry = &table->pEntries[i];
if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
(*visitor)(&entry->data, 0, type, arg);
}
}
dvmHashTableUnlock(table);
}
/*
* Visits all entries in the reference table.
*/
static void visitReferenceTable(RootVisitor *visitor, ReferenceTable *table,
u4 threadId, RootType type, void *arg)
{
assert(visitor != NULL);
assert(table != NULL);
for (Object **entry = table->table; entry < table->nextEntry; ++entry) {
assert(entry != NULL);
(*visitor)(entry, threadId, type, arg);
}
}
/*
* Visits all entries in the indirect reference table.
*/
static void visitIndirectRefTable(RootVisitor *visitor, IndirectRefTable *table,
u4 threadId, RootType type, void *arg)
{
assert(visitor != NULL);
assert(table != NULL);
typedef IndirectRefTable::iterator It; // TODO: C++0x auto
for (It it = table->begin(), end = table->end(); it != end; ++it) {
(*visitor)(*it, threadId, type, arg);
}
}
/*
* Visits all stack slots except those belonging to native method
* arguments.
*/
static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg)
{
assert(visitor != NULL);
assert(thread != NULL);
u4 threadId = thread->threadId;
const StackSaveArea *saveArea;
for (u4 *fp = (u4 *)thread->interpSave.curFrame;
fp != NULL;
fp = (u4 *)saveArea->prevFrame) {
Method *method;
saveArea = SAVEAREA_FROM_FP(fp);
method = (Method *)saveArea->method;
if (method != NULL && !dvmIsNativeMethod(method)) {
const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
const u1* regVector = NULL;
if (pMap != NULL) {
/* found map, get registers for this address */
int addr = saveArea->xtra.currentPc - method->insns;
regVector = dvmRegisterMapGetLine(pMap, addr);
}
if (regVector == NULL) {
/*
* Either there was no register map or there is no
* info for the current PC. Perform a conservative
* scan.
*/
for (size_t i = 0; i < method->registersSize; ++i) {
if (dvmIsValidObject((Object *)fp[i])) {
(*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
}
}
} else {
/*
* Precise scan. v0 is at the lowest address on the
* interpreted stack, and is the first bit in the
* register vector, so we can walk through the
* register map and memory in the same direction.
*
* A '1' bit indicates a live reference.
*/
u2 bits = 1 << 1;
for (size_t i = 0; i < method->registersSize; ++i) {
bits >>= 1;
if (bits == 1) {
/* set bit 9 so we can tell when we're empty */
bits = *regVector++ | 0x0100;
}
if ((bits & 0x1) != 0) {
/*
* Register is marked as live, it's a valid root.
*/
#if WITH_EXTRA_GC_CHECKS
if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) {
/* this is very bad */
LOGE("PGC: invalid ref in reg %d: %#x",
method->registersSize - 1 - i, fp[i]);
LOGE("PGC: %s.%s addr %#x",
method->clazz->descriptor, method->name,
saveArea->xtra.currentPc - method->insns);
continue;
}
#endif
(*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
}
}
dvmReleaseRegisterMapLine(pMap, regVector);
}
}
/*
* Don't fall into an infinite loop if things get corrupted.
*/
assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp ||
saveArea->prevFrame == NULL);
}
}
/*
* Visits all roots associated with a thread.
*/
static void visitThread(RootVisitor *visitor, Thread *thread, void *arg)
{
u4 threadId;
assert(visitor != NULL);
assert(thread != NULL);
threadId = thread->threadId;
(*visitor)(&thread->threadObj, threadId, ROOT_THREAD_OBJECT, arg);
(*visitor)(&thread->exception, threadId, ROOT_NATIVE_STACK, arg);
visitReferenceTable(visitor, &thread->internalLocalRefTable, threadId, ROOT_NATIVE_STACK, arg);
visitIndirectRefTable(visitor, &thread->jniLocalRefTable, threadId, ROOT_JNI_LOCAL, arg);
if (thread->jniMonitorRefTable.table != NULL) {
visitReferenceTable(visitor, &thread->jniMonitorRefTable, threadId, ROOT_JNI_MONITOR, arg);
}
visitThreadStack(visitor, thread, arg);
}
/*
* Visits all threads on the thread list.
*/
static void visitThreads(RootVisitor *visitor, void *arg)
{
Thread *thread;
assert(visitor != NULL);
dvmLockThreadList(dvmThreadSelf());
thread = gDvm.threadList;
while (thread) {
visitThread(visitor, thread, arg);
thread = thread->next;
}
dvmUnlockThreadList();
}
static void visitPrimitiveTypes(RootVisitor *visitor, void *arg)
{
(*visitor)(&gDvm.typeVoid, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeBoolean, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeByte, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeShort, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeChar, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeInt, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeLong, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeFloat, 0, ROOT_STICKY_CLASS, arg);
(*visitor)(&gDvm.typeDouble, 0, ROOT_STICKY_CLASS, arg);
}
/*
* Visits roots. TODO: visit cached global references.
*/
void dvmVisitRoots(RootVisitor *visitor, void *arg)
{
assert(visitor != NULL);
visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg);
visitPrimitiveTypes(visitor, arg);
if (gDvm.dbgRegistry != NULL) {
visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
}
if (gDvm.literalStrings != NULL) {
visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
}
dvmLockMutex(&gDvm.jniGlobalRefLock);
visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
dvmLockMutex(&gDvm.jniPinRefLock);
visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
dvmUnlockMutex(&gDvm.jniPinRefLock);
visitThreads(visitor, arg);
(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
(*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
}