Separate HPROF from the GC.

In the beginning, the only way to traverse the roots and heap was to
piggyback off the garbage collector.  As such, HPROF was implemented
by instrumenting the root- and object traversal routines to check a
mode flag and call into HPROF during a GC when the flag was set.

This change moves the HPROF calls out of the GC and into callbacks
invoked through the visitor.  Notably, it allows HPROF dumps to be
computed at any point in time without invoking a GC and potentially
destroying evidence relating to the cause of an OOM.

Change-Id: I2b74c4f10f35af3ca33b7c0bbfe470a8b586ff66
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
index 240e657..4f90371 100644
--- a/vm/alloc/CardTable.c
+++ b/vm/alloc/CardTable.c
@@ -228,7 +228,8 @@
 /*
  * Root visitor that looks for matching references.
  */
-static void dumpReferencesRootVisitor(void *ptr, void *arg)
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
 {
     Object *obj = *(Object **)ptr;
     Object *lookingFor = *(Object **)arg;
diff --git a/vm/alloc/GC.h b/vm/alloc/GC.h
index 62e9aa6..c65c807 100644
--- a/vm/alloc/GC.h
+++ b/vm/alloc/GC.h
@@ -138,18 +138,4 @@
  */
 void dvmGcMarkDebuggerRefs(void);
 
-/*
- * Optional heap profiling.
- */
-#if WITH_HPROF && !defined(_DALVIK_HPROF_HPROF)
-#include "hprof/Hprof.h"
-#define HPROF_SET_GC_SCAN_STATE(tag_, thread_) \
-    dvmHeapSetHprofGcScanState((tag_), (thread_))
-#define HPROF_CLEAR_GC_SCAN_STATE() \
-    dvmHeapSetHprofGcScanState(0, 0)
-#else
-#define HPROF_SET_GC_SCAN_STATE(tag_, thread_)  do {} while (false)
-#define HPROF_CLEAR_GC_SCAN_STATE()  do {} while (false)
-#endif
-
 #endif  // _DALVIK_ALLOC_GC
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 843ee38..bd782bd 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -25,7 +25,6 @@
 #include "alloc/DdmHeap.h"
 #include "alloc/HeapSource.h"
 #include "alloc/MarkSweep.h"
-#include "alloc/Visit.h"
 
 #include "utils/threads.h"      // need Android thread priorities
 #define kInvalidPriority        10000
@@ -41,8 +40,7 @@
     [GC_FOR_MALLOC] = "GC_FOR_MALLOC",
     [GC_CONCURRENT] = "GC_CONCURRENT",
     [GC_EXPLICIT] = "GC_EXPLICIT",
-    [GC_EXTERNAL_ALLOC] = "GC_EXTERNAL_ALLOC",
-    [GC_HPROF_DUMP_HEAP] = "GC_HPROF_DUMP_HEAP"
+    [GC_EXTERNAL_ALLOC] = "GC_EXTERNAL_ALLOC"
 };
 
 /*
@@ -71,10 +69,6 @@
     gcHeap->ddmHpsgWhat = 0;
     gcHeap->ddmNhsgWhen = 0;
     gcHeap->ddmNhsgWhat = 0;
-#if WITH_HPROF
-    gcHeap->hprofDumpOnGc = false;
-    gcHeap->hprofContext = NULL;
-#endif
     gDvm.gcHeap = gcHeap;
 
     /* Set up the lists and lock we'll use for finalizable
@@ -661,46 +655,6 @@
 
     dvmMethodTraceGCBegin();
 
-#if WITH_HPROF
-
-/* Set DUMP_HEAP_ON_DDMS_UPDATE to 1 to enable heap dumps
- * whenever DDMS requests a heap update (HPIF chunk).
- * The output files will appear in /data/misc, which must
- * already exist.
- * You must define "WITH_HPROF := true" in your buildspec.mk
- * and recompile libdvm for this to work.
- *
- * To enable stack traces for each allocation, define
- * "WITH_HPROF_STACK := true" in buildspec.mk.  This option slows down
- * allocations and also requires 8 additional bytes per object on the
- * GC heap.
- */
-#define DUMP_HEAP_ON_DDMS_UPDATE 0
-#if DUMP_HEAP_ON_DDMS_UPDATE
-    gcHeap->hprofDumpOnGc |= (gcHeap->ddmHpifWhen != 0);
-#endif
-
-    if (gcHeap->hprofDumpOnGc) {
-        char nameBuf[128];
-
-        gcHeap->hprofResult = -1;
-
-        if (gcHeap->hprofFileName == NULL) {
-            /* no filename was provided; invent one */
-            sprintf(nameBuf, "/data/misc/heap-dump-tm%d-pid%d.hprof",
-                (int) time(NULL), (int) getpid());
-            gcHeap->hprofFileName = nameBuf;
-        }
-        gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName,
-                gcHeap->hprofFd, gcHeap->hprofDirectToDdms);
-        if (gcHeap->hprofContext != NULL) {
-            hprofStartHeapDump(gcHeap->hprofContext);
-        }
-        gcHeap->hprofDumpOnGc = false;
-        gcHeap->hprofFileName = NULL;
-    }
-#endif
-
     /* Set up the marking context.
      */
     if (!dvmHeapBeginMarkStep(gcMode)) {
@@ -852,16 +806,6 @@
     currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
     currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
 
-#if WITH_HPROF
-    if (gcHeap->hprofContext != NULL) {
-        hprofFinishHeapDump(gcHeap->hprofContext);
-//TODO: write a HEAP_SUMMARY record
-        if (hprofShutdown(gcHeap->hprofContext))
-            gcHeap->hprofResult = 0;    /* indicate success */
-        gcHeap->hprofContext = NULL;
-    }
-#endif
-
     /* Now that we've freed up the GC heap, return any large
      * free chunks back to the system.  They'll get paged back
      * in the next time they're used.  Don't do it immediately,
@@ -963,45 +907,3 @@
     dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
     dvmChangeStatus(self, oldStatus);
 }
-
-#if WITH_HPROF
-/*
- * Perform garbage collection, writing heap information to the specified file.
- *
- * If "fd" is >= 0, the output will be written to that file descriptor.
- * Otherwise, "fileName" is used to create an output file.
- *
- * If "fileName" is NULL, a suitable name will be generated automatically.
- * (TODO: remove this when the SIGUSR1 feature goes away)
- *
- * If "directToDdms" is set, the other arguments are ignored, and data is
- * sent directly to DDMS.
- *
- * Returns 0 on success, or an error code on failure.
- */
-int hprofDumpHeap(const char* fileName, int fd, bool directToDdms)
-{
-    int result;
-
-    dvmLockMutex(&gDvm.gcHeapLock);
-
-    gDvm.gcHeap->hprofDumpOnGc = true;
-    gDvm.gcHeap->hprofFileName = fileName;
-    gDvm.gcHeap->hprofFd = fd;
-    gDvm.gcHeap->hprofDirectToDdms = directToDdms;
-    dvmCollectGarbageInternal(false, GC_HPROF_DUMP_HEAP);
-    result = gDvm.gcHeap->hprofResult;
-
-    dvmUnlockMutex(&gDvm.gcHeapLock);
-
-    return result;
-}
-
-void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber)
-{
-    if (gDvm.gcHeap->hprofContext != NULL) {
-        hprofSetGcScanState(gDvm.gcHeap->hprofContext, state,
-                threadSerialNumber);
-    }
-}
-#endif
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 0298f84..5a7e43d 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -110,15 +110,6 @@
     int             ddmHpsgWhat;
     int             ddmNhsgWhen;
     int             ddmNhsgWhat;
-
-#if WITH_HPROF
-    bool            hprofDumpOnGc;
-    const char*     hprofFileName;
-    int             hprofFd;
-    hprof_context_t *hprofContext;
-    int             hprofResult;
-    bool            hprofDirectToDdms;
-#endif
 };
 
 bool dvmLockHeap(void);
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 91365ad..84c111a 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -131,12 +131,6 @@
              */
             MARK_STACK_PUSH(ctx->stack, obj);
         }
-
-#if WITH_HPROF
-        if (gDvm.gcHeap->hprofContext != NULL) {
-            hprofMarkRootObject(gDvm.gcHeap->hprofContext, obj, 0);
-        }
-#endif
     }
 }
 
@@ -198,8 +192,6 @@
 {
     GcHeap *gcHeap = gDvm.gcHeap;
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0);
-
     LOG_SCAN("immune objects");
     dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
 
@@ -208,41 +200,24 @@
     LOG_SCAN("primitive classes\n");
     dvmGcScanPrimitiveClasses();
 
-    /* dvmGcScanRootThreadGroups() sets a bunch of
-     * different scan states internally.
-     */
-    HPROF_CLEAR_GC_SCAN_STATE();
-
     LOG_SCAN("root thread groups\n");
     dvmGcScanRootThreadGroups();
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0);
-
     LOG_SCAN("interned strings\n");
     dvmGcScanInternedStrings();
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0);
-
     LOG_SCAN("JNI global refs\n");
     dvmGcMarkJniGlobalRefs();
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
-
     LOG_SCAN("pending reference operations\n");
     dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations);
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
-
     LOG_SCAN("pending finalizations\n");
     dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs);
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0);
-
     LOG_SCAN("debugger refs\n");
     dvmGcMarkDebuggerRefs();
 
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0);
-
     /* Mark any special objects we have sitting around.
      */
     LOG_SCAN("special objects\n");
@@ -250,15 +225,13 @@
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
     dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
-
-    HPROF_CLEAR_GC_SCAN_STATE();
 }
 
 /*
  * Callback applied to root references.  If the root location contains
  * a white reference it is pushed on the mark stack and grayed.
  */
-static void markObjectVisitor(void *addr, void *arg)
+static void markObjectVisitor(void *addr, RootType type, u4 thread, void *arg)
 {
     Object *obj;
 
@@ -519,11 +492,6 @@
     assert(obj != NULL);
     assert(ctx != NULL);
     assert(obj->clazz != NULL);
-#if WITH_HPROF
-    if (gDvm.gcHeap->hprofContext != NULL) {
-        hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
-    }
-#endif
     /* Dispatch a type-specific scan routine. */
     if (obj->clazz == gDvm.classJavaLangClass) {
         scanClassObject((ClassObject *)obj, ctx);
@@ -986,13 +954,11 @@
     ref = newPendingRefs.table;
     lastRef = newPendingRefs.nextEntry;
     assert(ref < lastRef);
-    HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
     while (ref < lastRef) {
         assert(*ref != NULL);
         markObject(*ref, ctx);
         ref++;
     }
-    HPROF_CLEAR_GC_SCAN_STATE();
     processMarkStack(ctx);
     dvmSignalHeapWorker(false);
 }
diff --git a/vm/alloc/Verify.c b/vm/alloc/Verify.c
index a48ff29..5ce692c 100644
--- a/vm/alloc/Verify.c
+++ b/vm/alloc/Verify.c
@@ -42,7 +42,8 @@
     }
 }
 
-static void dumpReferencesRootVisitor(void *ptr, void *arg)
+static void dumpReferencesRootVisitor(void *ptr, u4 threadId,
+                                      RootType type, void *arg)
 {
     Object *obj = *(Object **)ptr;
     Object *lookingFor = *(Object **)arg;
@@ -120,9 +121,18 @@
 }
 
 /*
+ * Helper function to call verifyReference from the root verifier.
+ */
+static void verifyRootReference(void *addr, u4 threadId,
+                                RootType type, void *arg)
+{
+    verifyReference(addr, arg);
+}
+
+/*
  * Verifies references in the roots.
  */
 void dvmVerifyRoots(void)
 {
-    dvmVisitRoots(verifyReference, NULL);
+    dvmVisitRoots(verifyRootReference, NULL);
 }
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
index 0af27da..5371177 100644
--- a/vm/alloc/Visit.c
+++ b/vm/alloc/Visit.c
@@ -34,7 +34,8 @@
 /*
  * Applies a verification function to all present values in the hash table.
  */
-static void visitHashTable(Visitor *visitor, HashTable *table, void *arg)
+static void visitHashTable(RootVisitor *visitor, HashTable *table,
+                           RootType type, void *arg)
 {
     int i;
 
@@ -44,17 +45,32 @@
     for (i = 0; i < table->tableSize; ++i) {
         HashEntry *entry = &table->pEntries[i];
         if (entry->data != NULL && entry->data != HASH_TOMBSTONE) {
-            (*visitor)(&entry->data, arg);
+            (*visitor)(&entry->data, 0, type, arg);
         }
     }
     dvmHashTableUnlock(table);
 }
 
 /*
+ * Applies a verification function to all elements in the array.
+ */
+static void visitArray(RootVisitor *visitor, Object **array, size_t length,
+                       RootType type, void *arg)
+{
+    size_t i;
+
+    assert(visitor != NULL);
+    assert(array != NULL);
+    for (i = 0; i < length; ++i) {
+        (*visitor)(&array[i], 0, type, arg);
+    }
+}
+
+/*
  * Visits all entries in the reference table.
  */
-static void visitReferenceTable(Visitor *visitor, const ReferenceTable *table,
-                                void *arg)
+static void visitReferenceTable(RootVisitor *visitor, ReferenceTable *table,
+                                u4 threadId, RootType type, void *arg)
 {
     Object **entry;
 
@@ -62,7 +78,7 @@
     assert(table != NULL);
     for (entry = table->table; entry < table->nextEntry; ++entry) {
         assert(entry != NULL);
-        (*visitor)(entry, arg);
+        (*visitor)(entry, threadId, type, arg);
     }
 }
 
@@ -70,29 +86,32 @@
  * Visits a large heap reference table.  These objects are list heads.
  * As such, it is valid for table to be NULL.
  */
-static void visitLargeHeapRefTable(Visitor *visitor, LargeHeapRefTable *table,
-                                   void *arg)
+static void visitLargeHeapRefTable(RootVisitor *visitor,
+                                   LargeHeapRefTable *table,
+                                   RootType type, void *arg)
 {
     assert(visitor != NULL);
     for (; table != NULL; table = table->next) {
-        visitReferenceTable(visitor, &table->refs, arg);
+        visitReferenceTable(visitor, &table->refs, 0, type, arg);
     }
 }
 
 /*
  * Visits all stack slots. TODO: visit native methods.
  */
-static void visitThreadStack(Visitor *visitor, Thread *thread, void *arg)
+static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg)
 {
     const StackSaveArea *saveArea;
-    u4 *framePtr;
+    u4 *fp;
+    u4 threadId;
 
     assert(visitor != NULL);
     assert(thread != NULL);
-    framePtr = (u4 *)thread->curFrame;
-    for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
+    threadId = thread->threadId;
+    fp = (u4 *)thread->curFrame;
+    for (; fp != NULL; fp = saveArea->prevFrame) {
         Method *method;
-        saveArea = SAVEAREA_FROM_FP(framePtr);
+        saveArea = SAVEAREA_FROM_FP(fp);
         method = (Method *)saveArea->method;
         if (method != NULL && !dvmIsNativeMethod(method)) {
             const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
@@ -111,8 +130,8 @@
                  * scan.
                  */
                 for (i = 0; i < method->registersSize; ++i) {
-                    if (dvmIsValidObject((Object *)framePtr[i])) {
-                        (*visitor)(&framePtr[i], arg);
+                    if (dvmIsValidObject((Object *)fp[i])) {
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
                     }
                 }
             } else {
@@ -135,7 +154,7 @@
                         /*
                          * Register is marked as live, it's a valid root.
                          */
-                        (*visitor)(&framePtr[i], arg);
+                        (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
                     }
                 }
                 dvmReleaseRegisterMapLine(pMap, regVector);
@@ -144,7 +163,7 @@
         /*
          * Don't fall into an infinite loop if things get corrupted.
          */
-        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)fp ||
                saveArea->prevFrame == NULL);
     }
 }
@@ -152,16 +171,19 @@
 /*
  * Visits all roots associated with a thread.
  */
-static void visitThread(Visitor *visitor, Thread *thread, void *arg)
+static void visitThread(RootVisitor *visitor, Thread *thread, void *arg)
 {
+    u4 threadId;
+
     assert(visitor != NULL);
     assert(thread != NULL);
-    (*visitor)(&thread->threadObj, arg);
-    (*visitor)(&thread->exception, arg);
-    visitReferenceTable(visitor, &thread->internalLocalRefTable, arg);
-    visitReferenceTable(visitor, &thread->jniLocalRefTable, arg);
-    if (thread->jniMonitorRefTable.table) {
-        visitReferenceTable(visitor, &thread->jniMonitorRefTable, arg);
+    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);
+    visitReferenceTable(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);
 }
@@ -169,7 +191,7 @@
 /*
  * Visits all threads on the thread list.
  */
-static void visitThreads(Visitor *visitor, void *arg)
+static void visitThreads(RootVisitor *visitor, void *arg)
 {
     Thread *thread;
 
@@ -184,22 +206,22 @@
 }
 
 /*
- * Visits roots.  TODO: visit all roots.
+ * Visits roots.  TODO: visit cached global references.
  */
-void dvmVisitRoots(Visitor *visitor, void *arg)
+void dvmVisitRoots(RootVisitor *visitor, void *arg)
 {
     assert(visitor != NULL);
-    visitHashTable(visitor, gDvm.loadedClasses, arg);
-    visitHashTable(visitor, gDvm.dbgRegistry, arg);
-    visitHashTable(visitor, gDvm.internedStrings, arg);
-    visitHashTable(visitor, gDvm.literalStrings, arg);
-    visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, arg);
-    visitReferenceTable(visitor, &gDvm.jniPinRefTable, arg);
-    visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, arg);
-    visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, arg);
+    visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg);
+    visitArray(visitor, (Object **)gDvm.primitiveClass, NELEM(gDvm.primitiveClass), ROOT_STICKY_CLASS, arg);
+    visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
+    visitHashTable(visitor, gDvm.internedStrings, ROOT_INTERNED_STRING, arg);
+    visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
+    visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
+    visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_NATIVE_STACK, arg);
+    visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, ROOT_REFERENCE_CLEANUP, arg);
+    visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, ROOT_FINALIZING, arg);
     visitThreads(visitor, arg);
-    (*visitor)(&gDvm.outOfMemoryObj, arg);
-    (*visitor)(&gDvm.internalErrorObj, arg);
-    (*visitor)(&gDvm.noClassDefFoundErrorObj, arg);
-    /* TODO: visit cached global references. */
+    (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
+    (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
 }
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
index 488c721..e7c52e5 100644
--- a/vm/alloc/Visit.h
+++ b/vm/alloc/Visit.h
@@ -19,6 +19,24 @@
 
 #include "Dalvik.h"
 
+typedef enum {
+  ROOT_UNKNOWN = 0,
+  ROOT_JNI_GLOBAL,
+  ROOT_JNI_LOCAL,
+  ROOT_JAVA_FRAME,
+  ROOT_NATIVE_STACK,
+  ROOT_STICKY_CLASS,
+  ROOT_THREAD_BLOCK,
+  ROOT_MONITOR_USED,
+  ROOT_THREAD_OBJECT,
+  ROOT_INTERNED_STRING,
+  ROOT_FINALIZING,
+  ROOT_DEBUGGER,
+  ROOT_REFERENCE_CLEANUP,
+  ROOT_VM_INTERNAL,
+  ROOT_JNI_MONITOR,
+} RootType;
+
 /*
  * Callback invoked with the address of a reference and a user
  * supplied context argument.
@@ -26,6 +44,13 @@
 typedef void Visitor(void *addr, void *arg);
 
 /*
+ * Like a Visitor, but passes root specific information such as the
+ * containing thread id and the root type.  In cases where a root is
+ * not specific to a thread, 0, an invalid thread id is provided.
+ */
+typedef void RootVisitor(void *addr, u4 threadId, RootType type, void *arg);
+
+/*
  * Visits references in an object.
  */
 void dvmVisitObject(Visitor *visitor, Object *obj, void *arg);
@@ -33,6 +58,6 @@
 /*
  * Visits references in the root set.
  */
-void dvmVisitRoots(Visitor *visitor, void *arg);
+void dvmVisitRoots(RootVisitor *visitor, void *arg);
 
 #endif /* _DALVIK_ALLOC_VISIT */