Home | History | Annotate | Download | only in vm
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Link between JDWP and the VM.  The code here only runs as a result of
     19  * requests from the debugger, so speed is not essential.  Maintaining
     20  * isolation of the JDWP code should make it easier to maintain and reuse.
     21  *
     22  * Collecting all debugger-related pieces here will also allow us to #ifdef
     23  * the JDWP code out of release builds.
     24  */
     25 #include "Dalvik.h"
     26 
     27 /*
     28 Notes on garbage collection and object registration
     29 
     30 JDWP does not allow the debugger to assume that objects passed to it
     31 will not be garbage collected.  It specifies explicit commands (e.g.
     32 ObjectReference.DisableCollection) to allow the debugger to manage
     33 object lifetime.  It does, however, require that the VM not re-use an
     34 object ID unless an explicit "dispose" call has been made, and if the
     35 VM asks for a now-collected object we must return INVALID_OBJECT.
     36 
     37 JDWP also requires that, while the VM is suspended, no garbage collection
     38 occur.  The JDWP docs suggest that this is obvious, because no threads
     39 can be running.  Unfortunately it's not entirely clear how to deal
     40 with situations where the debugger itself allocates strings or executes
     41 code as part of displaying variables.  The easiest way to enforce this,
     42 short of disabling GC whenever the debugger is connected, is to ensure
     43 that the debugger thread can't cause a GC: it has to expand the heap or
     44 fail to allocate.  (Might want to make that "is debugger thread AND all
     45 other threads are suspended" to avoid unnecessary heap expansion by a
     46 poorly-timed JDWP request.)
     47 
     48 We use an "object registry" so that we can separate our internal
     49 representation from what we show the debugger.  This allows us to
     50 return a registry table index instead of a pointer or handle.
     51 
     52 There are various approaches we can take to achieve correct behavior:
     53 
     54 (1) Disable garbage collection entirely while the debugger is attached.
     55 This is very easy, but doesn't allow extended debugging sessions on
     56 small devices.
     57 
     58 (2) Keep a list of all object references requested by or sent to the
     59 debugger, and include the list in the GC root set.  This ensures that
     60 objects the debugger might care about don't go away.  This is straightforward,
     61 but it can cause us to hold on to large objects and prevent finalizers from
     62 being executed.
     63 
     64 (3) Keep a list of what amount to weak object references.  This way we
     65 don't interfere with the GC, and can support JDWP requests like
     66 "ObjectReference.IsCollected".
     67 
     68 The current implementation is #2.  The set should be reasonably small and
     69 performance isn't critical, so a simple expanding array can be used.
     70 
     71 
     72 Notes on threads:
     73 
     74 The VM has a Thread struct associated with every active thread.  The
     75 ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
     76 object, so to retrieve the VM's Thread struct we have to scan through the
     77 list looking for a match.
     78 
     79 When a thread goes away, we lock the list and free the struct.  To
     80 avoid having the thread list updated or Thread structs freed out from
     81 under us, we want to acquire and hold the thread list lock while we're
     82 performing operations on Threads.  Exceptions to this rule are noted in
     83 a couple of places.
     84 
     85 We can speed this up a bit by adding a Thread struct pointer to the
     86 java/lang/Thread object, and ensuring that both are discarded at the
     87 same time.
     88 */
     89 
     90 #define THREAD_GROUP_ALL ((ObjectId) 0x12345)   // magic, internal-only value
     91 
     92 #define kSlot0Sub   1000    // Eclipse workaround
     93 
     94 /*
     95  * System init.  We don't allocate the registry until first use.
     96  * Make sure we do this before initializing JDWP.
     97  */
     98 bool dvmDebuggerStartup()
     99 {
    100     if (!dvmBreakpointStartup())
    101         return false;
    102 
    103     gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
    104     return (gDvm.dbgRegistry != NULL);
    105 }
    106 
    107 /*
    108  * Free registry storage.
    109  */
    110 void dvmDebuggerShutdown()
    111 {
    112     dvmHashTableFree(gDvm.dbgRegistry);
    113     gDvm.dbgRegistry = NULL;
    114     dvmBreakpointShutdown();
    115 }
    116 
    117 
    118 /*
    119  * Pass these through to the VM functions.  Allows extended checking
    120  * (e.g. "errorcheck" mutexes).  If nothing else we can assert() success.
    121  */
    122 void dvmDbgInitMutex(pthread_mutex_t* pMutex)
    123 {
    124     dvmInitMutex(pMutex);
    125 }
    126 void dvmDbgLockMutex(pthread_mutex_t* pMutex)
    127 {
    128     dvmLockMutex(pMutex);
    129 }
    130 void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
    131 {
    132     dvmUnlockMutex(pMutex);
    133 }
    134 void dvmDbgInitCond(pthread_cond_t* pCond)
    135 {
    136     pthread_cond_init(pCond, NULL);
    137 }
    138 void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
    139 {
    140     int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
    141     assert(cc == 0);
    142 }
    143 void dvmDbgCondSignal(pthread_cond_t* pCond)
    144 {
    145     int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
    146     assert(cc == 0);
    147 }
    148 void dvmDbgCondBroadcast(pthread_cond_t* pCond)
    149 {
    150     int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
    151     assert(cc == 0);
    152 }
    153 
    154 
    155 /* keep track of type, in case we need to distinguish them someday */
    156 enum RegistryType {
    157     kObjectId = 0xc1, kRefTypeId
    158 };
    159 
    160 /*
    161  * Hash function for object IDs.  Since objects are at least 8 bytes, and
    162  * could someday be allocated on 16-byte boundaries, we don't want to use
    163  * the low 4 bits in our hash.
    164  */
    165 static inline u4 registryHash(u4 val)
    166 {
    167     return val >> 4;
    168 }
    169 
    170 /*
    171  * (This is a dvmHashTableLookup() callback.)
    172  */
    173 static int registryCompare(const void* obj1, const void* obj2)
    174 {
    175     return (int) obj1 - (int) obj2;
    176 }
    177 
    178 
    179 /*
    180  * Determine if an id is already in the list.
    181  *
    182  * If the list doesn't yet exist, this creates it.
    183  *
    184  * Lock the registry before calling here.
    185  */
    186 #ifndef NDEBUG
    187 static bool lookupId(ObjectId id)
    188 {
    189     void* found;
    190 
    191     found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
    192                 (void*)(u4) id, registryCompare, false);
    193     if (found == NULL)
    194         return false;
    195     assert(found == (void*)(u4) id);
    196     return true;
    197 }
    198 #endif
    199 
    200 /*
    201  * Register an object, if it hasn't already been.
    202  *
    203  * This is used for both ObjectId and RefTypeId.  In theory we don't have
    204  * to register RefTypeIds unless we're worried about classes unloading.
    205  *
    206  * Null references must be represented as zero, or the debugger will get
    207  * very confused.
    208  */
    209 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
    210 {
    211     ObjectId id;
    212 
    213     if (obj == NULL)
    214         return 0;
    215 
    216     assert((u4) obj != 0xcccccccc);
    217     assert((u4) obj > 0x100);
    218 
    219     id = (ObjectId)(u4)obj | ((u8) type) << 32;
    220     if (!reg)
    221         return id;
    222 
    223     dvmHashTableLock(gDvm.dbgRegistry);
    224     if (!gDvm.debuggerConnected) {
    225         /* debugger has detached while we were doing stuff? */
    226         LOGI("ignoring registerObject request in thread=%d",
    227             dvmThreadSelf()->threadId);
    228         //dvmAbort();
    229         goto bail;
    230     }
    231 
    232     dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
    233                 (void*)(u4) id, registryCompare, true);
    234 
    235 bail:
    236     dvmHashTableUnlock(gDvm.dbgRegistry);
    237     return id;
    238 }
    239 
    240 /*
    241  * Verify that an object has been registered.  If it hasn't, the debugger
    242  * is asking for something we didn't send it, which means something
    243  * somewhere is broken.
    244  *
    245  * If speed is an issue we can encode the registry index in the high
    246  * four bytes.  We could also just hard-wire this to "true".
    247  *
    248  * Note this actually takes both ObjectId and RefTypeId.
    249  */
    250 #ifndef NDEBUG
    251 static bool objectIsRegistered(ObjectId id, RegistryType type)
    252 {
    253     UNUSED_PARAMETER(type);
    254 
    255     if (id == 0)        // null reference?
    256         return true;
    257 
    258     dvmHashTableLock(gDvm.dbgRegistry);
    259     bool result = lookupId(id);
    260     dvmHashTableUnlock(gDvm.dbgRegistry);
    261     return result;
    262 }
    263 #endif
    264 
    265 /*
    266  * Convert to/from a RefTypeId.
    267  *
    268  * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
    269  */
    270 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
    271 {
    272     return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
    273 }
    274 #if 0
    275 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
    276 {
    277     return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
    278 }
    279 #endif
    280 static ClassObject* refTypeIdToClassObject(RefTypeId id)
    281 {
    282     assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
    283     return (ClassObject*)(u4) id;
    284 }
    285 
    286 /*
    287  * Convert to/from an ObjectId.
    288  */
    289 static ObjectId objectToObjectId(const Object* obj)
    290 {
    291     return registerObject(obj, kObjectId, true);
    292 }
    293 static ObjectId objectToObjectIdNoReg(const Object* obj)
    294 {
    295     return registerObject(obj, kObjectId, false);
    296 }
    297 static Object* objectIdToObject(ObjectId id)
    298 {
    299     assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
    300     return (Object*)(u4) id;
    301 }
    302 
    303 /*
    304  * Register an object ID that might not have been registered previously.
    305  *
    306  * Normally this wouldn't happen -- the conversion to an ObjectId would
    307  * have added the object to the registry -- but in some cases (e.g.
    308  * throwing exceptions) we really want to do the registration late.
    309  */
    310 void dvmDbgRegisterObjectId(ObjectId id)
    311 {
    312     Object* obj = (Object*)(u4) id;
    313     LOGV("+++ registering %p (%s)", obj, obj->clazz->descriptor);
    314     registerObject(obj, kObjectId, true);
    315 }
    316 
    317 /*
    318  * Convert to/from a MethodId.
    319  *
    320  * These IDs are only guaranteed unique within a class, so they could be
    321  * an enumeration index.  For now we just use the Method*.
    322  */
    323 static MethodId methodToMethodId(const Method* meth)
    324 {
    325     return (MethodId)(u4) meth;
    326 }
    327 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
    328 {
    329     // TODO? verify "id" is actually a method in "refTypeId"
    330     return (Method*)(u4) id;
    331 }
    332 
    333 /*
    334  * Convert to/from a FieldId.
    335  *
    336  * These IDs are only guaranteed unique within a class, so they could be
    337  * an enumeration index.  For now we just use the Field*.
    338  */
    339 static FieldId fieldToFieldId(const Field* field)
    340 {
    341     return (FieldId)(u4) field;
    342 }
    343 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
    344 {
    345     // TODO? verify "id" is actually a field in "refTypeId"
    346     return (Field*)(u4) id;
    347 }
    348 
    349 /*
    350  * Convert to/from a FrameId.
    351  *
    352  * We just return a pointer to the stack frame.
    353  */
    354 static FrameId frameToFrameId(const void* frame)
    355 {
    356     return (FrameId)(u4) frame;
    357 }
    358 static u4* frameIdToFrame(FrameId id)
    359 {
    360     return (u4*)(u4) id;
    361 }
    362 
    363 
    364 /*
    365  * Get the invocation request state.
    366  */
    367 DebugInvokeReq* dvmDbgGetInvokeReq()
    368 {
    369     return &dvmThreadSelf()->invokeReq;
    370 }
    371 
    372 /*
    373  * Enable the object registry, but don't enable debugging features yet.
    374  *
    375  * Only called from the JDWP handler thread.
    376  */
    377 void dvmDbgConnected()
    378 {
    379     assert(!gDvm.debuggerConnected);
    380 
    381     LOGV("JDWP has attached");
    382     assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
    383     gDvm.debuggerConnected = true;
    384 }
    385 
    386 /*
    387  * Enable all debugging features, including scans for breakpoints.
    388  *
    389  * This is a no-op if we're already active.
    390  *
    391  * Only called from the JDWP handler thread.
    392  */
    393 void dvmDbgActive()
    394 {
    395     if (gDvm.debuggerActive)
    396         return;
    397 
    398     LOGI("Debugger is active");
    399     dvmInitBreakpoints();
    400     gDvm.debuggerActive = true;
    401     dvmEnableAllSubMode(kSubModeDebuggerActive);
    402 #if defined(WITH_JIT)
    403     dvmCompilerUpdateGlobalState();
    404 #endif
    405 }
    406 
    407 /*
    408  * Disable debugging features.
    409  *
    410  * Set "debuggerConnected" to false, which disables use of the object
    411  * registry.
    412  *
    413  * Only called from the JDWP handler thread.
    414  */
    415 void dvmDbgDisconnected()
    416 {
    417     assert(gDvm.debuggerConnected);
    418 
    419     gDvm.debuggerActive = false;
    420     dvmDisableAllSubMode(kSubModeDebuggerActive);
    421 #if defined(WITH_JIT)
    422     dvmCompilerUpdateGlobalState();
    423 #endif
    424 
    425     dvmHashTableLock(gDvm.dbgRegistry);
    426     gDvm.debuggerConnected = false;
    427 
    428     LOGD("Debugger has detached; object registry had %d entries",
    429         dvmHashTableNumEntries(gDvm.dbgRegistry));
    430     //int i;
    431     //for (i = 0; i < gDvm.dbgRegistryNext; i++)
    432     //    LOGVV("%4d: 0x%llx", i, gDvm.dbgRegistryTable[i]);
    433 
    434     dvmHashTableClear(gDvm.dbgRegistry);
    435     dvmHashTableUnlock(gDvm.dbgRegistry);
    436 }
    437 
    438 /*
    439  * Returns "true" if a debugger is connected.
    440  *
    441  * Does not return "true" if it's just a DDM server.
    442  */
    443 bool dvmDbgIsDebuggerConnected()
    444 {
    445     return gDvm.debuggerActive;
    446 }
    447 
    448 /*
    449  * Get time since last debugger activity.  Used when figuring out if the
    450  * debugger has finished configuring us.
    451  */
    452 s8 dvmDbgLastDebuggerActivity()
    453 {
    454     return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
    455 }
    456 
    457 /*
    458  * JDWP thread is running, don't allow GC.
    459  */
    460 int dvmDbgThreadRunning()
    461 {
    462     ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
    463     return static_cast<int>(oldStatus);
    464 }
    465 
    466 /*
    467  * JDWP thread is idle, allow GC.
    468  */
    469 int dvmDbgThreadWaiting()
    470 {
    471     ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
    472     return static_cast<int>(oldStatus);
    473 }
    474 
    475 /*
    476  * Restore state returned by Running/Waiting calls.
    477  */
    478 int dvmDbgThreadContinuing(int status)
    479 {
    480     ThreadStatus newStatus = static_cast<ThreadStatus>(status);
    481     ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
    482     return static_cast<int>(oldStatus);
    483 }
    484 
    485 /*
    486  * The debugger wants us to exit.
    487  */
    488 void dvmDbgExit(int status)
    489 {
    490     // TODO? invoke System.exit() to perform exit processing; ends up
    491     // in System.exitInternal(), which can call JNI exit hook
    492     LOGI("GC lifetime allocation: %d bytes", gDvm.allocProf.allocCount);
    493     if (CALC_CACHE_STATS) {
    494         dvmDumpAtomicCacheStats(gDvm.instanceofCache);
    495         dvmDumpBootClassPath();
    496     }
    497     exit(status);
    498 }
    499 
    500 
    501 /*
    502  * ===========================================================================
    503  *      Class, Object, Array
    504  * ===========================================================================
    505  */
    506 
    507 /*
    508  * Get the class's type descriptor from a reference type ID.
    509  */
    510 const char* dvmDbgGetClassDescriptor(RefTypeId id)
    511 {
    512     ClassObject* clazz;
    513 
    514     clazz = refTypeIdToClassObject(id);
    515     return clazz->descriptor;
    516 }
    517 
    518 /*
    519  * Convert a RefTypeId to an ObjectId.
    520  */
    521 ObjectId dvmDbgGetClassObject(RefTypeId id)
    522 {
    523     ClassObject* clazz = refTypeIdToClassObject(id);
    524     return objectToObjectId((Object*) clazz);
    525 }
    526 
    527 /*
    528  * Return the superclass of a class (will be NULL for java/lang/Object).
    529  */
    530 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
    531 {
    532     ClassObject* clazz = refTypeIdToClassObject(id);
    533     return classObjectToRefTypeId(clazz->super);
    534 }
    535 
    536 /*
    537  * Return a class's defining class loader.
    538  */
    539 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
    540 {
    541     ClassObject* clazz = refTypeIdToClassObject(id);
    542     return objectToObjectId(clazz->classLoader);
    543 }
    544 
    545 /*
    546  * Return a class's access flags.
    547  */
    548 u4 dvmDbgGetAccessFlags(RefTypeId id)
    549 {
    550     ClassObject* clazz = refTypeIdToClassObject(id);
    551     return clazz->accessFlags & JAVA_FLAGS_MASK;
    552 }
    553 
    554 /*
    555  * Is this class an interface?
    556  */
    557 bool dvmDbgIsInterface(RefTypeId id)
    558 {
    559     ClassObject* clazz = refTypeIdToClassObject(id);
    560     return dvmIsInterfaceClass(clazz);
    561 }
    562 
    563 /*
    564  * dvmHashForeach callback
    565  */
    566 static int copyRefType(void* vclazz, void* varg)
    567 {
    568     RefTypeId** pRefType = (RefTypeId**)varg;
    569     **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
    570     (*pRefType)++;
    571     return 0;
    572 }
    573 
    574 /*
    575  * Get the complete list of reference classes (i.e. all classes except
    576  * the primitive types).
    577  *
    578  * Returns a newly-allocated buffer full of RefTypeId values.
    579  */
    580 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
    581 {
    582     RefTypeId* pRefType;
    583 
    584     dvmHashTableLock(gDvm.loadedClasses);
    585     *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
    586     pRefType = *pClassRefBuf =
    587         (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
    588 
    589     if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
    590         LOGW("Warning: problem getting class list");
    591         /* not really expecting this to happen */
    592     } else {
    593         assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
    594     }
    595 
    596     dvmHashTableUnlock(gDvm.loadedClasses);
    597 }
    598 
    599 /*
    600  * Get the list of reference classes "visible" to the specified class
    601  * loader.  A class is visible to a class loader if the ClassLoader object
    602  * is the defining loader or is listed as an initiating loader.
    603  *
    604  * Returns a newly-allocated buffer full of RefTypeId values.
    605  */
    606 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
    607     RefTypeId** pClassRefBuf)
    608 {
    609     Object* classLoader;
    610     int numClasses = 0, maxClasses;
    611 
    612     classLoader = objectIdToObject(classLoaderId);
    613     // I don't think classLoader can be NULL, but the spec doesn't say
    614 
    615     LOGVV("GetVisibleList: comparing to %p", classLoader);
    616 
    617     dvmHashTableLock(gDvm.loadedClasses);
    618 
    619     /* over-allocate the return buffer */
    620     maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
    621     *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
    622 
    623     /*
    624      * Run through the list, looking for matches.
    625      */
    626     HashIter iter;
    627     for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
    628         dvmHashIterNext(&iter))
    629     {
    630         ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
    631 
    632         if (clazz->classLoader == classLoader ||
    633             dvmLoaderInInitiatingList(clazz, classLoader))
    634         {
    635             LOGVV("  match '%s'", clazz->descriptor);
    636             (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
    637         }
    638     }
    639     *pNumClasses = numClasses;
    640 
    641     dvmHashTableUnlock(gDvm.loadedClasses);
    642 }
    643 
    644 /*
    645  * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
    646  *
    647  * Our class descriptors are in the correct format, so we just return that.
    648  */
    649 static const char* jniSignature(ClassObject* clazz)
    650 {
    651     return clazz->descriptor;
    652 }
    653 
    654 /*
    655  * Get information about a class.
    656  *
    657  * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
    658  * the class.
    659  */
    660 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
    661     const char** pSignature)
    662 {
    663     ClassObject* clazz = refTypeIdToClassObject(classId);
    664 
    665     if (clazz->descriptor[0] == '[') {
    666         /* generated array class */
    667         *pStatus = CS_VERIFIED | CS_PREPARED;
    668         *pTypeTag = TT_ARRAY;
    669     } else {
    670         if (clazz->status == CLASS_ERROR)
    671             *pStatus = CS_ERROR;
    672         else
    673             *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
    674         if (dvmIsInterfaceClass(clazz))
    675             *pTypeTag = TT_INTERFACE;
    676         else
    677             *pTypeTag = TT_CLASS;
    678     }
    679     if (pSignature != NULL)
    680         *pSignature = jniSignature(clazz);
    681 }
    682 
    683 /*
    684  * Search the list of loaded classes for a match.
    685  */
    686 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
    687         RefTypeId* pRefTypeId)
    688 {
    689     ClassObject* clazz;
    690 
    691     clazz = dvmFindLoadedClass(classDescriptor);
    692     if (clazz != NULL) {
    693         *pRefTypeId = classObjectToRefTypeId(clazz);
    694         return true;
    695     } else
    696         return false;
    697 }
    698 
    699 
    700 /*
    701  * Get an object's class and "type tag".
    702  */
    703 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
    704     RefTypeId* pRefTypeId)
    705 {
    706     Object* obj = objectIdToObject(objectId);
    707 
    708     if (dvmIsArrayClass(obj->clazz))
    709         *pRefTypeTag = TT_ARRAY;
    710     else if (dvmIsInterfaceClass(obj->clazz))
    711         *pRefTypeTag = TT_INTERFACE;
    712     else
    713         *pRefTypeTag = TT_CLASS;
    714     *pRefTypeId = classObjectToRefTypeId(obj->clazz);
    715 }
    716 
    717 /*
    718  * Get a class object's "type tag".
    719  */
    720 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
    721 {
    722     ClassObject* clazz = refTypeIdToClassObject(refTypeId);
    723 
    724     if (dvmIsArrayClass(clazz))
    725         return TT_ARRAY;
    726     else if (dvmIsInterfaceClass(clazz))
    727         return TT_INTERFACE;
    728     else
    729         return TT_CLASS;
    730 }
    731 
    732 /*
    733  * Get a class' signature.
    734  */
    735 const char* dvmDbgGetSignature(RefTypeId refTypeId)
    736 {
    737     ClassObject* clazz;
    738 
    739     clazz = refTypeIdToClassObject(refTypeId);
    740     assert(clazz != NULL);
    741 
    742     return jniSignature(clazz);
    743 }
    744 
    745 /*
    746  * Get class' source file.
    747  *
    748  * Returns a newly-allocated string.
    749  */
    750 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
    751 {
    752     ClassObject* clazz;
    753 
    754     clazz = refTypeIdToClassObject(refTypeId);
    755     assert(clazz != NULL);
    756 
    757     return clazz->sourceFile;
    758 }
    759 
    760 /*
    761  * Get an object's type name.  (For log message display only.)
    762  */
    763 const char* dvmDbgGetObjectTypeName(ObjectId objectId)
    764 {
    765     if (objectId == 0)
    766         return "(null)";
    767 
    768     Object* obj = objectIdToObject(objectId);
    769     return jniSignature(obj->clazz);
    770 }
    771 
    772 /*
    773  * Determine whether or not a tag represents a primitive type.
    774  */
    775 static bool isTagPrimitive(u1 tag)
    776 {
    777     switch (tag) {
    778     case JT_BYTE:
    779     case JT_CHAR:
    780     case JT_FLOAT:
    781     case JT_DOUBLE:
    782     case JT_INT:
    783     case JT_LONG:
    784     case JT_SHORT:
    785     case JT_VOID:
    786     case JT_BOOLEAN:
    787         return true;
    788     case JT_ARRAY:
    789     case JT_OBJECT:
    790     case JT_STRING:
    791     case JT_CLASS_OBJECT:
    792     case JT_THREAD:
    793     case JT_THREAD_GROUP:
    794     case JT_CLASS_LOADER:
    795         return false;
    796     default:
    797         LOGE("ERROR: unhandled tag '%c'", tag);
    798         assert(false);
    799         return false;
    800     }
    801 }
    802 
    803 /*
    804  * Determine the best tag type given an object's class.
    805  */
    806 static u1 tagFromClass(ClassObject* clazz)
    807 {
    808     if (dvmIsArrayClass(clazz))
    809         return JT_ARRAY;
    810 
    811     if (clazz == gDvm.classJavaLangString) {
    812         return JT_STRING;
    813     } else if (dvmIsTheClassClass(clazz)) {
    814         return JT_CLASS_OBJECT;
    815     } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
    816         return JT_THREAD;
    817     } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
    818         return JT_THREAD_GROUP;
    819     } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
    820         return JT_CLASS_LOADER;
    821     } else {
    822         return JT_OBJECT;
    823     }
    824 }
    825 
    826 /*
    827  * Return a basic tag value based solely on a type descriptor.
    828  *
    829  * The ASCII value maps directly to the JDWP tag constants, so we don't
    830  * need to do much here.  This does not return the fancier tags like
    831  * JT_THREAD.
    832  */
    833 static u1 basicTagFromDescriptor(const char* descriptor)
    834 {
    835     return descriptor[0];
    836 }
    837 
    838 /*
    839  * Objects declared to hold Object might actually hold a more specific
    840  * type.  The debugger may take a special interest in these (e.g. it
    841  * wants to display the contents of Strings), so we want to return an
    842  * appropriate tag.
    843  *
    844  * Null objects are tagged JT_OBJECT.
    845  */
    846 static u1 tagFromObject(const Object* obj)
    847 {
    848     if (obj == NULL)
    849         return JT_OBJECT;
    850     return tagFromClass(obj->clazz);
    851 }
    852 
    853 /*
    854  * Determine the tag for an object.
    855  *
    856  * "objectId" may be 0 (i.e. NULL reference).
    857  */
    858 u1 dvmDbgGetObjectTag(ObjectId objectId)
    859 {
    860     return tagFromObject(objectIdToObject(objectId));
    861 }
    862 
    863 /*
    864  * Get the widths of the specified JDWP.Tag value.
    865  */
    866 int dvmDbgGetTagWidth(int tag)
    867 {
    868     switch (tag) {
    869     case JT_VOID:
    870         return 0;
    871     case JT_BYTE:
    872     case JT_BOOLEAN:
    873         return 1;
    874     case JT_CHAR:
    875     case JT_SHORT:
    876         return 2;
    877     case JT_FLOAT:
    878     case JT_INT:
    879         return 4;
    880     case JT_ARRAY:
    881     case JT_OBJECT:
    882     case JT_STRING:
    883     case JT_THREAD:
    884     case JT_THREAD_GROUP:
    885     case JT_CLASS_LOADER:
    886     case JT_CLASS_OBJECT:
    887         return sizeof(ObjectId);
    888     case JT_DOUBLE:
    889     case JT_LONG:
    890         return 8;
    891     default:
    892         LOGE("ERROR: unhandled tag '%c'", tag);
    893         assert(false);
    894         return -1;
    895     }
    896 }
    897 
    898 
    899 /*
    900  * Return the length of the specified array.
    901  */
    902 int dvmDbgGetArrayLength(ObjectId arrayId)
    903 {
    904     ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
    905     assert(dvmIsArray(arrayObj));
    906     return arrayObj->length;
    907 }
    908 
    909 /*
    910  * Return a tag indicating the general type of elements in the array.
    911  */
    912 u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
    913 {
    914     ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
    915 
    916     ClassObject* arrayClass = arrayObj->clazz;
    917     u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
    918     if (!isTagPrimitive(tag)) {
    919         /* try to refine it */
    920         tag = tagFromClass(arrayClass->elementClass);
    921     }
    922 
    923     return tag;
    924 }
    925 
    926 /*
    927  * Copy a series of values with the specified width, changing the byte
    928  * ordering to big-endian.
    929  */
    930 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
    931 {
    932     int i;
    933 
    934     switch (width) {
    935     case 1:
    936         memcpy(out, in, count);
    937         break;
    938     case 2:
    939         for (i = 0; i < count; i++)
    940             *(((u2*) out)+i) = get2BE(in + i*2);
    941         break;
    942     case 4:
    943         for (i = 0; i < count; i++)
    944             *(((u4*) out)+i) = get4BE(in + i*4);
    945         break;
    946     case 8:
    947         for (i = 0; i < count; i++)
    948             *(((u8*) out)+i) = get8BE(in + i*8);
    949         break;
    950     default:
    951         assert(false);
    952     }
    953 }
    954 
    955 /*
    956  * Copy a series of values with the specified width, changing the
    957  * byte order from big-endian.
    958  */
    959 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
    960 {
    961     int i;
    962 
    963     switch (width) {
    964     case 1:
    965         memcpy(out, in, count);
    966         break;
    967     case 2:
    968         for (i = 0; i < count; i++)
    969             set2BE(out + i*2, *((u2*)in + i));
    970         break;
    971     case 4:
    972         for (i = 0; i < count; i++)
    973             set4BE(out + i*4, *((u4*)in + i));
    974         break;
    975     case 8:
    976         for (i = 0; i < count; i++)
    977             set8BE(out + i*8, *((u8*)in + i));
    978         break;
    979     default:
    980         assert(false);
    981     }
    982 }
    983 
    984 /*
    985  * Output a piece of an array to the reply buffer.
    986  *
    987  * Returns "false" if something looks fishy.
    988  */
    989 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
    990     ExpandBuf* pReply)
    991 {
    992     ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
    993     const u1* data = (const u1*)arrayObj->contents;
    994     u1 tag;
    995 
    996     assert(dvmIsArray(arrayObj));
    997 
    998     if (firstIndex + count > (int)arrayObj->length) {
    999         LOGW("Request for index=%d + count=%d excceds length=%d",
   1000             firstIndex, count, arrayObj->length);
   1001         return false;
   1002     }
   1003 
   1004     tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
   1005 
   1006     if (isTagPrimitive(tag)) {
   1007         int width = dvmDbgGetTagWidth(tag);
   1008         u1* outBuf;
   1009 
   1010         outBuf = expandBufAddSpace(pReply, count * width);
   1011 
   1012         copyValuesToBE(outBuf, data + firstIndex*width, count, width);
   1013     } else {
   1014         Object** pObjects;
   1015         int i;
   1016 
   1017         pObjects = (Object**) data;
   1018         pObjects += firstIndex;
   1019 
   1020         LOGV("    --> copying %d object IDs", count);
   1021         //assert(tag == JT_OBJECT);     // could be object or "refined" type
   1022 
   1023         for (i = 0; i < count; i++, pObjects++) {
   1024             u1 thisTag;
   1025             if (*pObjects != NULL)
   1026                 thisTag = tagFromObject(*pObjects);
   1027             else
   1028                 thisTag = tag;
   1029             expandBufAdd1(pReply, thisTag);
   1030             expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
   1031         }
   1032     }
   1033 
   1034     return true;
   1035 }
   1036 
   1037 /*
   1038  * Set a range of elements in an array from the data in "buf".
   1039  */
   1040 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
   1041     const u1* buf)
   1042 {
   1043     ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
   1044     u1* data = (u1*)arrayObj->contents;
   1045     u1 tag;
   1046 
   1047     assert(dvmIsArray(arrayObj));
   1048 
   1049     if (firstIndex + count > (int)arrayObj->length) {
   1050         LOGW("Attempt to set index=%d + count=%d excceds length=%d",
   1051             firstIndex, count, arrayObj->length);
   1052         return false;
   1053     }
   1054 
   1055     tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
   1056 
   1057     if (isTagPrimitive(tag)) {
   1058         int width = dvmDbgGetTagWidth(tag);
   1059 
   1060         LOGV("    --> setting %d '%c' width=%d", count, tag, width);
   1061 
   1062         copyValuesFromBE(data + firstIndex*width, buf, count, width);
   1063     } else {
   1064         Object** pObjects;
   1065         int i;
   1066 
   1067         pObjects = (Object**) data;
   1068         pObjects += firstIndex;
   1069 
   1070         LOGV("    --> setting %d objects", count);
   1071 
   1072         /* should do array type check here */
   1073         for (i = 0; i < count; i++) {
   1074             ObjectId id = dvmReadObjectId(&buf);
   1075             *pObjects++ = objectIdToObject(id);
   1076         }
   1077     }
   1078 
   1079     return true;
   1080 }
   1081 
   1082 /*
   1083  * Create a new string.
   1084  *
   1085  * The only place the reference will be held in the VM is in our registry.
   1086  */
   1087 ObjectId dvmDbgCreateString(const char* str)
   1088 {
   1089     StringObject* strObj;
   1090 
   1091     strObj = dvmCreateStringFromCstr(str);
   1092     dvmReleaseTrackedAlloc((Object*) strObj, NULL);
   1093     return objectToObjectId((Object*) strObj);
   1094 }
   1095 
   1096 /*
   1097  * Allocate a new object of the specified type.
   1098  *
   1099  * Add it to the registry to prevent it from being GCed.
   1100  */
   1101 ObjectId dvmDbgCreateObject(RefTypeId classId)
   1102 {
   1103     ClassObject* clazz = refTypeIdToClassObject(classId);
   1104     Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
   1105     dvmReleaseTrackedAlloc(newObj, NULL);
   1106     return objectToObjectId(newObj);
   1107 }
   1108 
   1109 /*
   1110  * Allocate a new array object of the specified type and length.  The
   1111  * type is the array type, not the element type.
   1112  *
   1113  * Add it to the registry to prevent it from being GCed.
   1114  */
   1115 ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
   1116 {
   1117     ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
   1118     Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
   1119     dvmReleaseTrackedAlloc(newObj, NULL);
   1120     return objectToObjectId(newObj);
   1121 }
   1122 
   1123 /*
   1124  * Determine if "instClassId" is an instance of "classId".
   1125  */
   1126 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
   1127 {
   1128     ClassObject* instClazz = refTypeIdToClassObject(instClassId);
   1129     ClassObject* clazz = refTypeIdToClassObject(classId);
   1130 
   1131     return dvmInstanceof(instClazz, clazz);
   1132 }
   1133 
   1134 
   1135 /*
   1136  * ===========================================================================
   1137  *      Method and Field
   1138  * ===========================================================================
   1139  */
   1140 
   1141 /*
   1142  * Get the method name from a MethodId.
   1143  */
   1144 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
   1145 {
   1146     Method* meth;
   1147 
   1148     meth = methodIdToMethod(refTypeId, id);
   1149     return meth->name;
   1150 }
   1151 
   1152 /*
   1153  * Augment the access flags for synthetic methods and fields by setting
   1154  * the (as described by the spec) "0xf0000000 bit".  Also, strip out any
   1155  * flags not specified by the Java programming language.
   1156  */
   1157 static u4 augmentedAccessFlags(u4 accessFlags)
   1158 {
   1159     accessFlags &= JAVA_FLAGS_MASK;
   1160 
   1161     if ((accessFlags & ACC_SYNTHETIC) != 0) {
   1162         return accessFlags | 0xf0000000;
   1163     } else {
   1164         return accessFlags;
   1165     }
   1166 }
   1167 
   1168 /*
   1169  * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
   1170  * output all fields declared by the class.  Inherited fields are
   1171  * not included.
   1172  */
   1173 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
   1174     ExpandBuf* pReply)
   1175 {
   1176     ClassObject* clazz = refTypeIdToClassObject(refTypeId);
   1177     assert(clazz != NULL);
   1178 
   1179     u4 declared = clazz->sfieldCount + clazz->ifieldCount;
   1180     expandBufAdd4BE(pReply, declared);
   1181 
   1182     for (int i = 0; i < clazz->sfieldCount; i++) {
   1183         Field* field = &clazz->sfields[i];
   1184         expandBufAddFieldId(pReply, fieldToFieldId(field));
   1185         expandBufAddUtf8String(pReply, (const u1*) field->name);
   1186         expandBufAddUtf8String(pReply, (const u1*) field->signature);
   1187         if (withGeneric) {
   1188             static const u1 genericSignature[1] = "";
   1189             expandBufAddUtf8String(pReply, genericSignature);
   1190         }
   1191         expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
   1192     }
   1193     for (int i = 0; i < clazz->ifieldCount; i++) {
   1194         Field* field = &clazz->ifields[i];
   1195         expandBufAddFieldId(pReply, fieldToFieldId(field));
   1196         expandBufAddUtf8String(pReply, (const u1*) field->name);
   1197         expandBufAddUtf8String(pReply, (const u1*) field->signature);
   1198         if (withGeneric) {
   1199             static const u1 genericSignature[1] = "";
   1200             expandBufAddUtf8String(pReply, genericSignature);
   1201         }
   1202         expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
   1203     }
   1204 }
   1205 
   1206 /*
   1207  * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
   1208  * output all methods declared by the class.  Inherited methods are
   1209  * not included.
   1210  */
   1211 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
   1212     ExpandBuf* pReply)
   1213 {
   1214     DexStringCache stringCache;
   1215     static const u1 genericSignature[1] = "";
   1216     ClassObject* clazz;
   1217     Method* meth;
   1218     u4 declared;
   1219     int i;
   1220 
   1221     dexStringCacheInit(&stringCache);
   1222 
   1223     clazz = refTypeIdToClassObject(refTypeId);
   1224     assert(clazz != NULL);
   1225 
   1226     declared = clazz->directMethodCount + clazz->virtualMethodCount;
   1227     expandBufAdd4BE(pReply, declared);
   1228 
   1229     for (i = 0; i < clazz->directMethodCount; i++) {
   1230         meth = &clazz->directMethods[i];
   1231 
   1232         expandBufAddMethodId(pReply, methodToMethodId(meth));
   1233         expandBufAddUtf8String(pReply, (const u1*) meth->name);
   1234 
   1235         expandBufAddUtf8String(pReply,
   1236             (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
   1237                     &stringCache));
   1238 
   1239         if (withGeneric)
   1240             expandBufAddUtf8String(pReply, genericSignature);
   1241         expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
   1242     }
   1243     for (i = 0; i < clazz->virtualMethodCount; i++) {
   1244         meth = &clazz->virtualMethods[i];
   1245 
   1246         expandBufAddMethodId(pReply, methodToMethodId(meth));
   1247         expandBufAddUtf8String(pReply, (const u1*) meth->name);
   1248 
   1249         expandBufAddUtf8String(pReply,
   1250             (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
   1251                     &stringCache));
   1252 
   1253         if (withGeneric)
   1254             expandBufAddUtf8String(pReply, genericSignature);
   1255         expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
   1256     }
   1257 
   1258     dexStringCacheRelease(&stringCache);
   1259 }
   1260 
   1261 /*
   1262  * Output all interfaces directly implemented by the class.
   1263  */
   1264 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
   1265 {
   1266     ClassObject* clazz;
   1267     int i, start, count;
   1268 
   1269     clazz = refTypeIdToClassObject(refTypeId);
   1270     assert(clazz != NULL);
   1271 
   1272     if (clazz->super == NULL)
   1273         start = 0;
   1274     else
   1275         start = clazz->super->iftableCount;
   1276 
   1277     count = clazz->iftableCount - start;
   1278     expandBufAdd4BE(pReply, count);
   1279     for (i = start; i < clazz->iftableCount; i++) {
   1280         ClassObject* iface = clazz->iftable[i].clazz;
   1281         expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
   1282     }
   1283 }
   1284 
   1285 struct DebugCallbackContext {
   1286     int numItems;
   1287     ExpandBuf* pReply;
   1288     // used by locals table
   1289     bool withGeneric;
   1290 };
   1291 
   1292 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
   1293 {
   1294     DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
   1295 
   1296     expandBufAdd8BE(pContext->pReply, address);
   1297     expandBufAdd4BE(pContext->pReply, lineNum);
   1298     pContext->numItems++;
   1299 
   1300     return 0;
   1301 }
   1302 
   1303 /*
   1304  * For Method.LineTable: output the line table.
   1305  *
   1306  * Note we operate in Dalvik's 16-bit units rather than bytes.
   1307  */
   1308 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
   1309     ExpandBuf* pReply)
   1310 {
   1311     Method* method;
   1312     u8 start, end;
   1313     DebugCallbackContext context;
   1314 
   1315     memset (&context, 0, sizeof(DebugCallbackContext));
   1316 
   1317     method = methodIdToMethod(refTypeId, methodId);
   1318     if (dvmIsNativeMethod(method)) {
   1319         start = (u8) -1;
   1320         end = (u8) -1;
   1321     } else {
   1322         start = 0;
   1323         end = dvmGetMethodInsnsSize(method);
   1324     }
   1325 
   1326     expandBufAdd8BE(pReply, start);
   1327     expandBufAdd8BE(pReply, end);
   1328 
   1329     // Add numLines later
   1330     size_t numLinesOffset = expandBufGetLength(pReply);
   1331     expandBufAdd4BE(pReply, 0);
   1332 
   1333     context.pReply = pReply;
   1334 
   1335     dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
   1336         dvmGetMethodCode(method),
   1337         method->clazz->descriptor,
   1338         method->prototype.protoIdx,
   1339         method->accessFlags,
   1340         lineTablePositionsCb, NULL, &context);
   1341 
   1342     set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
   1343 }
   1344 
   1345 /*
   1346  * Eclipse appears to expect that the "this" reference is in slot zero.
   1347  * If it's not, the "variables" display will show two copies of "this",
   1348  * possibly because it gets "this" from SF.ThisObject and then displays
   1349  * all locals with nonzero slot numbers.
   1350  *
   1351  * So, we remap the item in slot 0 to 1000, and remap "this" to zero.  On
   1352  * SF.GetValues / SF.SetValues we map them back.
   1353  */
   1354 static int tweakSlot(int slot, const char* name)
   1355 {
   1356     int newSlot = slot;
   1357 
   1358     if (strcmp(name, "this") == 0)      // only remap "this" ptr
   1359         newSlot = 0;
   1360     else if (slot == 0)                 // always remap slot 0
   1361         newSlot = kSlot0Sub;
   1362 
   1363     LOGV("untweak: %d to %d", slot, newSlot);
   1364     return newSlot;
   1365 }
   1366 
   1367 /*
   1368  * Reverse Eclipse hack.
   1369  */
   1370 static int untweakSlot(int slot, const void* framePtr)
   1371 {
   1372     int newSlot = slot;
   1373 
   1374     if (slot == kSlot0Sub) {
   1375         newSlot = 0;
   1376     } else if (slot == 0) {
   1377         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   1378         const Method* method = saveArea->method;
   1379         newSlot = method->registersSize - method->insSize;
   1380     }
   1381 
   1382     LOGV("untweak: %d to %d", slot, newSlot);
   1383     return newSlot;
   1384 }
   1385 
   1386 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
   1387         u4 endAddress, const char *name, const char *descriptor,
   1388         const char *signature)
   1389 {
   1390     DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
   1391 
   1392     reg = (u2) tweakSlot(reg, name);
   1393 
   1394     LOGV("    %2d: %d(%d) '%s' '%s' slot=%d",
   1395         pContext->numItems, startAddress, endAddress - startAddress,
   1396         name, descriptor, reg);
   1397 
   1398     expandBufAdd8BE(pContext->pReply, startAddress);
   1399     expandBufAddUtf8String(pContext->pReply, (const u1*)name);
   1400     expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
   1401     if (pContext->withGeneric) {
   1402         expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
   1403     }
   1404     expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
   1405     expandBufAdd4BE(pContext->pReply, reg);
   1406 
   1407     pContext->numItems++;
   1408 }
   1409 
   1410 /*
   1411  * For Method.VariableTable[WithGeneric]: output information about local
   1412  * variables for the specified method.
   1413  */
   1414 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
   1415     bool withGeneric, ExpandBuf* pReply)
   1416 {
   1417     Method* method;
   1418     DebugCallbackContext context;
   1419 
   1420     memset (&context, 0, sizeof(DebugCallbackContext));
   1421 
   1422     method = methodIdToMethod(refTypeId, methodId);
   1423 
   1424     expandBufAdd4BE(pReply, method->insSize);
   1425 
   1426     // Add numLocals later
   1427     size_t numLocalsOffset = expandBufGetLength(pReply);
   1428     expandBufAdd4BE(pReply, 0);
   1429 
   1430     context.pReply = pReply;
   1431     context.withGeneric = withGeneric;
   1432     dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
   1433         dvmGetMethodCode(method),
   1434         method->clazz->descriptor,
   1435         method->prototype.protoIdx,
   1436         method->accessFlags,
   1437         NULL, variableTableCb, &context);
   1438 
   1439     set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
   1440 }
   1441 
   1442 /*
   1443  * Get the basic tag for an instance field.
   1444  */
   1445 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
   1446 {
   1447     Object* obj = objectIdToObject(objId);
   1448     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1449     const Field* field = fieldIdToField(classId, fieldId);
   1450     return basicTagFromDescriptor(field->signature);
   1451 }
   1452 
   1453 /*
   1454  * Get the basic tag for a static field.
   1455  */
   1456 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
   1457 {
   1458     const Field* field = fieldIdToField(refTypeId, fieldId);
   1459     return basicTagFromDescriptor(field->signature);
   1460 }
   1461 
   1462 
   1463 /*
   1464  * Copy the value of a static field into the output buffer, preceded
   1465  * by an appropriate tag.  The tag is based on the value held by the
   1466  * field, not the field's type.
   1467  */
   1468 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
   1469 {
   1470     Object* obj = objectIdToObject(objectId);
   1471     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1472     InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
   1473     u1 tag = basicTagFromDescriptor(ifield->signature);
   1474 
   1475     if (tag == JT_ARRAY || tag == JT_OBJECT) {
   1476         Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
   1477         tag = tagFromObject(objVal);
   1478         expandBufAdd1(pReply, tag);
   1479         expandBufAddObjectId(pReply, objectToObjectId(objVal));
   1480         LOGV("    --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
   1481     } else {
   1482         LOGV("    --> ifieldId %x --> tag '%c'", fieldId, tag);
   1483         expandBufAdd1(pReply, tag);
   1484 
   1485         switch (tag) {
   1486         case JT_BOOLEAN:
   1487             expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
   1488             break;
   1489         case JT_BYTE:
   1490             expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
   1491             break;
   1492         case JT_SHORT:
   1493             expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
   1494             break;
   1495         case JT_CHAR:
   1496             expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
   1497             break;
   1498         case JT_INT:
   1499         case JT_FLOAT:
   1500             expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
   1501             break;
   1502         case JT_LONG:
   1503         case JT_DOUBLE:
   1504             expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
   1505             break;
   1506         default:
   1507             LOGE("ERROR: unhandled field type '%s'", ifield->signature);
   1508             assert(false);
   1509             break;
   1510         }
   1511     }
   1512 }
   1513 
   1514 /*
   1515  * Set the value of the specified field.
   1516  */
   1517 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
   1518     int width)
   1519 {
   1520     Object* obj = objectIdToObject(objectId);
   1521     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1522     InstField* field = (InstField*) fieldIdToField(classId, fieldId);
   1523 
   1524     switch (field->signature[0]) {
   1525     case JT_BOOLEAN:
   1526         assert(width == 1);
   1527         dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
   1528         break;
   1529     case JT_BYTE:
   1530         assert(width == 1);
   1531         dvmSetFieldInt(obj, field->byteOffset, value);
   1532         break;
   1533     case JT_SHORT:
   1534     case JT_CHAR:
   1535         assert(width == 2);
   1536         dvmSetFieldInt(obj, field->byteOffset, value);
   1537         break;
   1538     case JT_INT:
   1539     case JT_FLOAT:
   1540         assert(width == 4);
   1541         dvmSetFieldInt(obj, field->byteOffset, value);
   1542         break;
   1543     case JT_ARRAY:
   1544     case JT_OBJECT:
   1545         assert(width == sizeof(ObjectId));
   1546         dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
   1547         break;
   1548     case JT_DOUBLE:
   1549     case JT_LONG:
   1550         assert(width == 8);
   1551         dvmSetFieldLong(obj, field->byteOffset, value);
   1552         break;
   1553     default:
   1554         LOGE("ERROR: unhandled class type '%s'", field->signature);
   1555         assert(false);
   1556         break;
   1557     }
   1558 }
   1559 
   1560 /*
   1561  * Copy the value of a static field into the output buffer, preceded
   1562  * by an appropriate tag.  The tag is based on the value held by the
   1563  * field, not the field's type.
   1564  */
   1565 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
   1566     ExpandBuf* pReply)
   1567 {
   1568     StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
   1569     u1 tag = basicTagFromDescriptor(sfield->signature);
   1570 
   1571     if (tag == JT_ARRAY || tag == JT_OBJECT) {
   1572         Object* objVal = dvmGetStaticFieldObject(sfield);
   1573         tag = tagFromObject(objVal);
   1574         expandBufAdd1(pReply, tag);
   1575         expandBufAddObjectId(pReply, objectToObjectId(objVal));
   1576         LOGV("    --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
   1577     } else {
   1578         JValue value;
   1579 
   1580         LOGV("    --> sfieldId %x --> tag '%c'", fieldId, tag);
   1581         expandBufAdd1(pReply, tag);
   1582 
   1583         switch (tag) {
   1584         case JT_BOOLEAN:
   1585             expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
   1586             break;
   1587         case JT_BYTE:
   1588             expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
   1589             break;
   1590         case JT_SHORT:
   1591             expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
   1592             break;
   1593         case JT_CHAR:
   1594             expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
   1595             break;
   1596         case JT_INT:
   1597             expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
   1598             break;
   1599         case JT_FLOAT:
   1600             value.f = dvmGetStaticFieldFloat(sfield);
   1601             expandBufAdd4BE(pReply, value.i);
   1602             break;
   1603         case JT_LONG:
   1604             expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
   1605             break;
   1606         case JT_DOUBLE:
   1607             value.d = dvmGetStaticFieldDouble(sfield);
   1608             expandBufAdd8BE(pReply, value.j);
   1609             break;
   1610         default:
   1611             LOGE("ERROR: unhandled field type '%s'", sfield->signature);
   1612             assert(false);
   1613             break;
   1614         }
   1615     }
   1616 }
   1617 
   1618 /*
   1619  * Set the value of a static field.
   1620  */
   1621 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
   1622     u8 rawValue, int width)
   1623 {
   1624     StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
   1625     Object* objVal;
   1626     JValue value;
   1627 
   1628     value.j = rawValue;
   1629 
   1630     switch (sfield->signature[0]) {
   1631     case JT_BOOLEAN:
   1632         assert(width == 1);
   1633         dvmSetStaticFieldBoolean(sfield, value.z);
   1634         break;
   1635     case JT_BYTE:
   1636         assert(width == 1);
   1637         dvmSetStaticFieldByte(sfield, value.b);
   1638         break;
   1639     case JT_SHORT:
   1640         assert(width == 2);
   1641         dvmSetStaticFieldShort(sfield, value.s);
   1642         break;
   1643     case JT_CHAR:
   1644         assert(width == 2);
   1645         dvmSetStaticFieldChar(sfield, value.c);
   1646         break;
   1647     case JT_INT:
   1648         assert(width == 4);
   1649         dvmSetStaticFieldInt(sfield, value.i);
   1650         break;
   1651     case JT_FLOAT:
   1652         assert(width == 4);
   1653         dvmSetStaticFieldFloat(sfield, value.f);
   1654         break;
   1655     case JT_ARRAY:
   1656     case JT_OBJECT:
   1657         assert(width == sizeof(ObjectId));
   1658         objVal = objectIdToObject(rawValue);
   1659         dvmSetStaticFieldObject(sfield, objVal);
   1660         break;
   1661     case JT_LONG:
   1662         assert(width == 8);
   1663         dvmSetStaticFieldLong(sfield, value.j);
   1664         break;
   1665     case JT_DOUBLE:
   1666         assert(width == 8);
   1667         dvmSetStaticFieldDouble(sfield, value.d);
   1668         break;
   1669     default:
   1670         LOGE("ERROR: unhandled class type '%s'", sfield->signature);
   1671         assert(false);
   1672         break;
   1673     }
   1674 }
   1675 
   1676 /*
   1677  * Convert a string object to a UTF-8 string.
   1678  *
   1679  * Returns a newly-allocated string.
   1680  */
   1681 char* dvmDbgStringToUtf8(ObjectId strId)
   1682 {
   1683     StringObject* strObj = (StringObject*) objectIdToObject(strId);
   1684 
   1685     return dvmCreateCstrFromString(strObj);
   1686 }
   1687 
   1688 
   1689 /*
   1690  * ===========================================================================
   1691  *      Thread and ThreadGroup
   1692  * ===========================================================================
   1693  */
   1694 
   1695 /*
   1696  * Convert a thread object to a Thread ptr.
   1697  *
   1698  * This currently requires running through the list of threads and finding
   1699  * a match.
   1700  *
   1701  * IMPORTANT: grab gDvm.threadListLock before calling here.
   1702  */
   1703 static Thread* threadObjToThread(Object* threadObj)
   1704 {
   1705     Thread* thread;
   1706 
   1707     for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   1708         if (thread->threadObj == threadObj)
   1709             break;
   1710     }
   1711 
   1712     return thread;
   1713 }
   1714 
   1715 /*
   1716  * Get the status and suspend state of a thread.
   1717  */
   1718 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
   1719     u4* pSuspendStatus)
   1720 {
   1721     Object* threadObj;
   1722     Thread* thread;
   1723     bool result = false;
   1724 
   1725     threadObj = objectIdToObject(threadId);
   1726     assert(threadObj != NULL);
   1727 
   1728     /* lock the thread list, so the thread doesn't vanish while we work */
   1729     dvmLockThreadList(NULL);
   1730 
   1731     thread = threadObjToThread(threadObj);
   1732     if (thread == NULL)
   1733         goto bail;
   1734 
   1735     switch (thread->status) {
   1736     case THREAD_ZOMBIE:         *pThreadStatus = TS_ZOMBIE;     break;
   1737     case THREAD_RUNNING:        *pThreadStatus = TS_RUNNING;    break;
   1738     case THREAD_TIMED_WAIT:     *pThreadStatus = TS_SLEEPING;   break;
   1739     case THREAD_MONITOR:        *pThreadStatus = TS_MONITOR;    break;
   1740     case THREAD_WAIT:           *pThreadStatus = TS_WAIT;       break;
   1741     case THREAD_INITIALIZING:   *pThreadStatus = TS_ZOMBIE;     break;
   1742     case THREAD_STARTING:       *pThreadStatus = TS_ZOMBIE;     break;
   1743     case THREAD_NATIVE:         *pThreadStatus = TS_RUNNING;    break;
   1744     case THREAD_VMWAIT:         *pThreadStatus = TS_WAIT;       break;
   1745     case THREAD_SUSPENDED:      *pThreadStatus = TS_RUNNING;    break;
   1746     default:
   1747         assert(false);
   1748         *pThreadStatus = THREAD_ZOMBIE;
   1749         break;
   1750     }
   1751 
   1752     if (dvmIsSuspended(thread))
   1753         *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
   1754     else
   1755         *pSuspendStatus = 0;
   1756 
   1757     result = true;
   1758 
   1759 bail:
   1760     dvmUnlockThreadList();
   1761     return result;
   1762 }
   1763 
   1764 /*
   1765  * Get the thread's suspend count.
   1766  */
   1767 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
   1768 {
   1769     Object* threadObj;
   1770     Thread* thread;
   1771     u4 result = 0;
   1772 
   1773     threadObj = objectIdToObject(threadId);
   1774     assert(threadObj != NULL);
   1775 
   1776     /* lock the thread list, so the thread doesn't vanish while we work */
   1777     dvmLockThreadList(NULL);
   1778 
   1779     thread = threadObjToThread(threadObj);
   1780     if (thread == NULL)
   1781         goto bail;
   1782 
   1783     result = thread->suspendCount;
   1784 
   1785 bail:
   1786     dvmUnlockThreadList();
   1787     return result;
   1788 }
   1789 
   1790 /*
   1791  * Determine whether or not a thread exists in the VM's thread list.
   1792  *
   1793  * Returns "true" if the thread exists.
   1794  */
   1795 bool dvmDbgThreadExists(ObjectId threadId)
   1796 {
   1797     Object* threadObj;
   1798     Thread* thread;
   1799     bool result;
   1800 
   1801     threadObj = objectIdToObject(threadId);
   1802     assert(threadObj != NULL);
   1803 
   1804     /* lock the thread list, so the thread doesn't vanish while we work */
   1805     dvmLockThreadList(NULL);
   1806 
   1807     thread = threadObjToThread(threadObj);
   1808     if (thread == NULL)
   1809         result = false;
   1810     else
   1811         result = true;
   1812 
   1813     dvmUnlockThreadList();
   1814     return result;
   1815 }
   1816 
   1817 /*
   1818  * Determine whether or not a thread is suspended.
   1819  *
   1820  * Returns "false" if the thread is running or doesn't exist.
   1821  */
   1822 bool dvmDbgIsSuspended(ObjectId threadId)
   1823 {
   1824     Object* threadObj;
   1825     Thread* thread;
   1826     bool result = false;
   1827 
   1828     threadObj = objectIdToObject(threadId);
   1829     assert(threadObj != NULL);
   1830 
   1831     /* lock the thread list, so the thread doesn't vanish while we work */
   1832     dvmLockThreadList(NULL);
   1833 
   1834     thread = threadObjToThread(threadObj);
   1835     if (thread == NULL)
   1836         goto bail;
   1837 
   1838     result = dvmIsSuspended(thread);
   1839 
   1840 bail:
   1841     dvmUnlockThreadList();
   1842     return result;
   1843 }
   1844 
   1845 /*
   1846  * Return the ObjectId for the "system" thread group.
   1847  */
   1848 ObjectId dvmDbgGetSystemThreadGroupId()
   1849 {
   1850     Object* groupObj = dvmGetSystemThreadGroup();
   1851     return objectToObjectId(groupObj);
   1852 }
   1853 
   1854 /*
   1855  * Return the ObjectId for the "main" thread group.
   1856  */
   1857 ObjectId dvmDbgGetMainThreadGroupId()
   1858 {
   1859     Object* groupObj = dvmGetMainThreadGroup();
   1860     return objectToObjectId(groupObj);
   1861 }
   1862 
   1863 /*
   1864  * Get the name of a thread.
   1865  *
   1866  * Returns a newly-allocated string.
   1867  */
   1868 char* dvmDbgGetThreadName(ObjectId threadId)
   1869 {
   1870     Object* threadObj;
   1871     StringObject* nameStr;
   1872     char* str;
   1873     char* result;
   1874 
   1875     threadObj = objectIdToObject(threadId);
   1876     assert(threadObj != NULL);
   1877 
   1878     nameStr = (StringObject*) dvmGetFieldObject(threadObj,
   1879                                                 gDvm.offJavaLangThread_name);
   1880     str = dvmCreateCstrFromString(nameStr);
   1881     result = (char*) malloc(strlen(str) + 20);
   1882 
   1883     /* lock the thread list, so the thread doesn't vanish while we work */
   1884     dvmLockThreadList(NULL);
   1885     Thread* thread = threadObjToThread(threadObj);
   1886     if (thread != NULL)
   1887         sprintf(result, "<%d> %s", thread->threadId, str);
   1888     else
   1889         sprintf(result, "%s", str);
   1890     dvmUnlockThreadList();
   1891 
   1892     free(str);
   1893     return result;
   1894 }
   1895 
   1896 /*
   1897  * Get a thread's group.
   1898  */
   1899 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
   1900 {
   1901     Object* threadObj;
   1902     Object* group;
   1903 
   1904     threadObj = objectIdToObject(threadId);
   1905     assert(threadObj != NULL);
   1906 
   1907     group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
   1908     return objectToObjectId(group);
   1909 }
   1910 
   1911 
   1912 /*
   1913  * Get the name of a thread group.
   1914  *
   1915  * Returns a newly-allocated string.
   1916  */
   1917 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
   1918 {
   1919     Object* threadGroup;
   1920     StringObject* nameStr;
   1921 
   1922     threadGroup = objectIdToObject(threadGroupId);
   1923     assert(threadGroup != NULL);
   1924 
   1925     nameStr = (StringObject*)
   1926         dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
   1927     return dvmCreateCstrFromString(nameStr);
   1928 }
   1929 
   1930 /*
   1931  * Get the parent of a thread group.
   1932  *
   1933  * Returns a newly-allocated string.
   1934  */
   1935 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
   1936 {
   1937     Object* threadGroup;
   1938     Object* parent;
   1939 
   1940     threadGroup = objectIdToObject(threadGroupId);
   1941     assert(threadGroup != NULL);
   1942 
   1943     parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
   1944     return objectToObjectId(parent);
   1945 }
   1946 
   1947 /*
   1948  * Get the list of threads in the thread group.
   1949  *
   1950  * We do this by running through the full list of threads and returning
   1951  * the ones that have the ThreadGroup object as their owner.
   1952  *
   1953  * If threadGroupId is set to "kAllThreads", we ignore the group field and
   1954  * return all threads.
   1955  *
   1956  * The caller must free "*ppThreadIds".
   1957  */
   1958 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
   1959     ObjectId** ppThreadIds, u4* pThreadCount)
   1960 {
   1961     Object* targetThreadGroup = NULL;
   1962     Thread* thread;
   1963     int count;
   1964 
   1965     if (threadGroupId != THREAD_GROUP_ALL) {
   1966         targetThreadGroup = objectIdToObject(threadGroupId);
   1967         assert(targetThreadGroup != NULL);
   1968     }
   1969 
   1970     dvmLockThreadList(NULL);
   1971 
   1972     thread = gDvm.threadList;
   1973     count = 0;
   1974     for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   1975         Object* group;
   1976 
   1977         /* Skip over the JDWP support thread.  Some debuggers
   1978          * get bent out of shape when they can't suspend and
   1979          * query all threads, so it's easier if we just don't
   1980          * tell them about us.
   1981          */
   1982         if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
   1983             continue;
   1984 
   1985         /* This thread is currently being created, and isn't ready
   1986          * to be seen by the debugger yet.
   1987          */
   1988         if (thread->threadObj == NULL)
   1989             continue;
   1990 
   1991         group = dvmGetFieldObject(thread->threadObj,
   1992                     gDvm.offJavaLangThread_group);
   1993         if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
   1994             count++;
   1995     }
   1996 
   1997     *pThreadCount = count;
   1998 
   1999     if (count == 0) {
   2000         *ppThreadIds = NULL;
   2001     } else {
   2002         ObjectId* ptr;
   2003         ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
   2004 
   2005         for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   2006             Object* group;
   2007 
   2008             /* Skip over the JDWP support thread.  Some debuggers
   2009              * get bent out of shape when they can't suspend and
   2010              * query all threads, so it's easier if we just don't
   2011              * tell them about us.
   2012              */
   2013             if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
   2014                 continue;
   2015 
   2016             /* This thread is currently being created, and isn't ready
   2017              * to be seen by the debugger yet.
   2018              */
   2019             if (thread->threadObj == NULL)
   2020                 continue;
   2021 
   2022             group = dvmGetFieldObject(thread->threadObj,
   2023                         gDvm.offJavaLangThread_group);
   2024             if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
   2025             {
   2026                 *ptr++ = objectToObjectId(thread->threadObj);
   2027                 count--;
   2028             }
   2029         }
   2030 
   2031         assert(count == 0);
   2032     }
   2033 
   2034     dvmUnlockThreadList();
   2035 }
   2036 
   2037 /*
   2038  * Get all threads.
   2039  *
   2040  * The caller must free "*ppThreadIds".
   2041  */
   2042 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
   2043 {
   2044     dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
   2045 }
   2046 
   2047 
   2048 /*
   2049  * Count up the #of frames on the thread's stack.
   2050  *
   2051  * Returns -1 on failure.
   2052  */
   2053 int dvmDbgGetThreadFrameCount(ObjectId threadId)
   2054 {
   2055     Object* threadObj;
   2056     Thread* thread;
   2057     int count = -1;
   2058 
   2059     threadObj = objectIdToObject(threadId);
   2060 
   2061     dvmLockThreadList(NULL);
   2062     thread = threadObjToThread(threadObj);
   2063     if (thread != NULL) {
   2064         count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
   2065     }
   2066     dvmUnlockThreadList();
   2067 
   2068     return count;
   2069 }
   2070 
   2071 /*
   2072  * Get info for frame N from the specified thread's stack.
   2073  */
   2074 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
   2075     JdwpLocation* pLoc)
   2076 {
   2077     Object* threadObj;
   2078     Thread* thread;
   2079     void* framePtr;
   2080     int count;
   2081 
   2082     threadObj = objectIdToObject(threadId);
   2083 
   2084     dvmLockThreadList(NULL);
   2085 
   2086     thread = threadObjToThread(threadObj);
   2087     if (thread == NULL)
   2088         goto bail;
   2089 
   2090     framePtr = thread->interpSave.curFrame;
   2091     count = 0;
   2092     while (framePtr != NULL) {
   2093         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   2094         const Method* method = saveArea->method;
   2095 
   2096         if (!dvmIsBreakFrame((u4*)framePtr)) {
   2097             if (count == num) {
   2098                 *pFrameId = frameToFrameId(framePtr);
   2099                 if (dvmIsInterfaceClass(method->clazz))
   2100                     pLoc->typeTag = TT_INTERFACE;
   2101                 else
   2102                     pLoc->typeTag = TT_CLASS;
   2103                 pLoc->classId = classObjectToRefTypeId(method->clazz);
   2104                 pLoc->methodId = methodToMethodId(method);
   2105                 if (dvmIsNativeMethod(method))
   2106                     pLoc->idx = (u8)-1;
   2107                 else
   2108                     pLoc->idx = saveArea->xtra.currentPc - method->insns;
   2109                 dvmUnlockThreadList();
   2110                 return true;
   2111             }
   2112 
   2113             count++;
   2114         }
   2115 
   2116         framePtr = saveArea->prevFrame;
   2117     }
   2118 
   2119 bail:
   2120     dvmUnlockThreadList();
   2121     return false;
   2122 }
   2123 
   2124 /*
   2125  * Get the ThreadId for the current thread.
   2126  */
   2127 ObjectId dvmDbgGetThreadSelfId()
   2128 {
   2129     Thread* self = dvmThreadSelf();
   2130     return objectToObjectId(self->threadObj);
   2131 }
   2132 
   2133 /*
   2134  * Suspend the VM.
   2135  */
   2136 void dvmDbgSuspendVM(bool isEvent)
   2137 {
   2138     dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
   2139 }
   2140 
   2141 /*
   2142  * Resume the VM.
   2143  */
   2144 void dvmDbgResumeVM()
   2145 {
   2146     dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
   2147 }
   2148 
   2149 /*
   2150  * Suspend one thread (not ourselves).
   2151  */
   2152 void dvmDbgSuspendThread(ObjectId threadId)
   2153 {
   2154     Object* threadObj = objectIdToObject(threadId);
   2155     Thread* thread;
   2156 
   2157     dvmLockThreadList(NULL);
   2158 
   2159     thread = threadObjToThread(threadObj);
   2160     if (thread == NULL) {
   2161         /* can happen if our ThreadDeath notify crosses in the mail */
   2162         LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
   2163     } else {
   2164         dvmSuspendThread(thread);
   2165     }
   2166 
   2167     dvmUnlockThreadList();
   2168 }
   2169 
   2170 /*
   2171  * Resume one thread (not ourselves).
   2172  */
   2173 void dvmDbgResumeThread(ObjectId threadId)
   2174 {
   2175     Object* threadObj = objectIdToObject(threadId);
   2176     Thread* thread;
   2177 
   2178     dvmLockThreadList(NULL);
   2179 
   2180     thread = threadObjToThread(threadObj);
   2181     if (thread == NULL) {
   2182         LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
   2183     } else {
   2184         dvmResumeThread(thread);
   2185     }
   2186 
   2187     dvmUnlockThreadList();
   2188 }
   2189 
   2190 /*
   2191  * Suspend ourselves after sending an event to the debugger.
   2192  */
   2193 void dvmDbgSuspendSelf()
   2194 {
   2195     dvmSuspendSelf(true);
   2196 }
   2197 
   2198 /*
   2199  * Get the "this" object for the specified frame.
   2200  */
   2201 static Object* getThisObject(const u4* framePtr)
   2202 {
   2203     const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   2204     const Method* method = saveArea->method;
   2205     int argOffset = method->registersSize - method->insSize;
   2206     Object* thisObj;
   2207 
   2208     if (method == NULL) {
   2209         /* this is a "break" frame? */
   2210         assert(false);
   2211         return NULL;
   2212     }
   2213 
   2214     LOGVV("  Pulling this object for frame at %p", framePtr);
   2215     LOGVV("    Method='%s' native=%d static=%d this=%p",
   2216         method->name, dvmIsNativeMethod(method),
   2217         dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
   2218 
   2219     /*
   2220      * No "this" pointer for statics.  No args on the interp stack for
   2221      * native methods invoked directly from the VM.
   2222      */
   2223     if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
   2224         thisObj = NULL;
   2225     else
   2226         thisObj = (Object*) framePtr[argOffset];
   2227 
   2228     if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
   2229         LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
   2230             framePtr, method->clazz->descriptor, method->name);
   2231         thisObj = NULL;
   2232     }
   2233 
   2234     return thisObj;
   2235 }
   2236 
   2237 /*
   2238  * Return the "this" object for the specified frame.  The thread must be
   2239  * suspended.
   2240  */
   2241 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
   2242 {
   2243     const u4* framePtr = frameIdToFrame(frameId);
   2244     Object* thisObj;
   2245 
   2246     UNUSED_PARAMETER(threadId);
   2247 
   2248     thisObj = getThisObject(framePtr);
   2249 
   2250     *pThisId = objectToObjectId(thisObj);
   2251     return true;
   2252 }
   2253 
   2254 /*
   2255  * Copy the value of a method argument or local variable into the
   2256  * specified buffer.  The value will be preceeded with the tag.
   2257  *
   2258  * The debugger includes the tags in the request.  Object tags may
   2259  * be updated with a more refined type.
   2260  */
   2261 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
   2262     u1 tag, u1* buf, int expectedLen)
   2263 {
   2264     const u4* framePtr = frameIdToFrame(frameId);
   2265     Object* objVal;
   2266     u4 intVal;
   2267     u8 longVal;
   2268 
   2269     UNUSED_PARAMETER(threadId);
   2270 
   2271     slot = untweakSlot(slot, framePtr);     // Eclipse workaround
   2272 
   2273     switch (tag) {
   2274     case JT_BOOLEAN:
   2275         assert(expectedLen == 1);
   2276         intVal = framePtr[slot];
   2277         set1(buf+1, intVal != 0);
   2278         break;
   2279     case JT_BYTE:
   2280         assert(expectedLen == 1);
   2281         intVal = framePtr[slot];
   2282         set1(buf+1, intVal);
   2283         break;
   2284     case JT_SHORT:
   2285     case JT_CHAR:
   2286         assert(expectedLen == 2);
   2287         intVal = framePtr[slot];
   2288         set2BE(buf+1, intVal);
   2289         break;
   2290     case JT_INT:
   2291     case JT_FLOAT:
   2292         assert(expectedLen == 4);
   2293         intVal = framePtr[slot];
   2294         set4BE(buf+1, intVal);
   2295         break;
   2296     case JT_ARRAY:
   2297         assert(expectedLen == sizeof(ObjectId));
   2298         {
   2299             /* convert to "ObjectId" */
   2300             objVal = (Object*)framePtr[slot];
   2301             if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
   2302                 LOGW("JDWP: slot %d expected to hold array, %p invalid",
   2303                     slot, objVal);
   2304                 dvmAbort();         // DEBUG: make it obvious
   2305                 objVal = NULL;
   2306                 tag = JT_OBJECT;    // JT_ARRAY not expected for NULL ref
   2307             }
   2308             dvmSetObjectId(buf+1, objectToObjectId(objVal));
   2309         }
   2310         break;
   2311     case JT_OBJECT:
   2312         assert(expectedLen == sizeof(ObjectId));
   2313         {
   2314             /* convert to "ObjectId" */
   2315             objVal = (Object*)framePtr[slot];
   2316 
   2317             if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
   2318                 LOGW("JDWP: slot %d expected to hold object, %p invalid",
   2319                     slot, objVal);
   2320                 dvmAbort();         // DEBUG: make it obvious
   2321                 objVal = NULL;
   2322             }
   2323             tag = tagFromObject(objVal);
   2324             dvmSetObjectId(buf+1, objectToObjectId(objVal));
   2325         }
   2326         break;
   2327     case JT_DOUBLE:
   2328     case JT_LONG:
   2329         assert(expectedLen == 8);
   2330         memcpy(&longVal, &framePtr[slot], 8);
   2331         set8BE(buf+1, longVal);
   2332         break;
   2333     default:
   2334         LOGE("ERROR: unhandled tag '%c'", tag);
   2335         assert(false);
   2336         break;
   2337     }
   2338 
   2339     /* prepend tag, which may have been updated */
   2340     set1(buf, tag);
   2341 }
   2342 
   2343 /*
   2344  * Copy a new value into an argument or local variable.
   2345  */
   2346 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
   2347     u8 value, int width)
   2348 {
   2349     u4* framePtr = frameIdToFrame(frameId);
   2350 
   2351     UNUSED_PARAMETER(threadId);
   2352 
   2353     slot = untweakSlot(slot, framePtr);     // Eclipse workaround
   2354 
   2355     switch (tag) {
   2356     case JT_BOOLEAN:
   2357         assert(width == 1);
   2358         framePtr[slot] = (u4)value;
   2359         break;
   2360     case JT_BYTE:
   2361         assert(width == 1);
   2362         framePtr[slot] = (u4)value;
   2363         break;
   2364     case JT_SHORT:
   2365     case JT_CHAR:
   2366         assert(width == 2);
   2367         framePtr[slot] = (u4)value;
   2368         break;
   2369     case JT_INT:
   2370     case JT_FLOAT:
   2371         assert(width == 4);
   2372         framePtr[slot] = (u4)value;
   2373         break;
   2374     case JT_STRING:
   2375         /* The debugger calls VirtualMachine.CreateString to create a new
   2376          * string, then uses this to set the object reference, when you
   2377          * edit a String object */
   2378     case JT_ARRAY:
   2379     case JT_OBJECT:
   2380         assert(width == sizeof(ObjectId));
   2381         framePtr[slot] = (u4) objectIdToObject(value);
   2382         break;
   2383     case JT_DOUBLE:
   2384     case JT_LONG:
   2385         assert(width == 8);
   2386         memcpy(&framePtr[slot], &value, 8);
   2387         break;
   2388     case JT_VOID:
   2389     case JT_CLASS_OBJECT:
   2390     case JT_THREAD:
   2391     case JT_THREAD_GROUP:
   2392     case JT_CLASS_LOADER:
   2393         /* not expecting these from debugger; fall through to failure */
   2394     default:
   2395         LOGE("ERROR: unhandled tag '%c'", tag);
   2396         assert(false);
   2397         break;
   2398     }
   2399 }
   2400 
   2401 
   2402 /*
   2403  * ===========================================================================
   2404  *      Debugger notification
   2405  * ===========================================================================
   2406  */
   2407 
   2408 /*
   2409  * Tell JDWP that a breakpoint address has been reached.
   2410  *
   2411  * "pcOffset" will be -1 for native methods.
   2412  * "thisPtr" will be NULL for static methods.
   2413  */
   2414 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
   2415     Object* thisPtr, int eventFlags)
   2416 {
   2417     JdwpLocation loc;
   2418 
   2419     if (dvmIsInterfaceClass(method->clazz))
   2420         loc.typeTag = TT_INTERFACE;
   2421     else
   2422         loc.typeTag = TT_CLASS;
   2423     loc.classId = classObjectToRefTypeId(method->clazz);
   2424     loc.methodId = methodToMethodId(method);
   2425     loc.idx = pcOffset;
   2426 
   2427     /*
   2428      * Note we use "NoReg" so we don't keep track of references that are
   2429      * never actually sent to the debugger.  The "thisPtr" is only used to
   2430      * compare against registered events.
   2431      */
   2432 
   2433     if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
   2434             objectToObjectIdNoReg(thisPtr), eventFlags))
   2435     {
   2436         classObjectToRefTypeId(method->clazz);
   2437         objectToObjectId(thisPtr);
   2438     }
   2439 }
   2440 
   2441 /*
   2442  * Tell JDWP that an exception has occurred.
   2443  */
   2444 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
   2445     int catchRelPc, Object* exception)
   2446 {
   2447     JdwpLocation throwLoc, catchLoc;
   2448     const Method* throwMeth;
   2449     const Method* catchMeth;
   2450 
   2451     throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
   2452     if (dvmIsInterfaceClass(throwMeth->clazz))
   2453         throwLoc.typeTag = TT_INTERFACE;
   2454     else
   2455         throwLoc.typeTag = TT_CLASS;
   2456     throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
   2457     throwLoc.methodId = methodToMethodId(throwMeth);
   2458     throwLoc.idx = throwRelPc;
   2459 
   2460     if (catchRelPc < 0) {
   2461         memset(&catchLoc, 0, sizeof(catchLoc));
   2462     } else {
   2463         catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
   2464         if (dvmIsInterfaceClass(catchMeth->clazz))
   2465             catchLoc.typeTag = TT_INTERFACE;
   2466         else
   2467             catchLoc.typeTag = TT_CLASS;
   2468         catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
   2469         catchLoc.methodId = methodToMethodId(catchMeth);
   2470         catchLoc.idx = catchRelPc;
   2471     }
   2472 
   2473     /* need this for InstanceOnly filters */
   2474     Object* thisObj = getThisObject((u4*)throwFp);
   2475 
   2476     /*
   2477      * Hand the event to the JDWP exception handler.  Note we're using the
   2478      * "NoReg" objectID on the exception, which is not strictly correct --
   2479      * the exception object WILL be passed up to the debugger if the
   2480      * debugger is interested in the event.  We do this because the current
   2481      * implementation of the debugger object registry never throws anything
   2482      * away, and some people were experiencing a fatal build up of exception
   2483      * objects when dealing with certain libraries.
   2484      */
   2485     dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
   2486         objectToObjectIdNoReg(exception),
   2487         classObjectToRefTypeId(exception->clazz), &catchLoc,
   2488         objectToObjectId(thisObj));
   2489 }
   2490 
   2491 /*
   2492  * Tell JDWP and/or DDMS that a thread has started.
   2493  */
   2494 void dvmDbgPostThreadStart(Thread* thread)
   2495 {
   2496     if (gDvm.debuggerActive) {
   2497         dvmJdwpPostThreadChange(gDvm.jdwpState,
   2498             objectToObjectId(thread->threadObj), true);
   2499     }
   2500     if (gDvm.ddmThreadNotification)
   2501         dvmDdmSendThreadNotification(thread, true);
   2502 }
   2503 
   2504 /*
   2505  * Tell JDWP and/or DDMS that a thread has gone away.
   2506  */
   2507 void dvmDbgPostThreadDeath(Thread* thread)
   2508 {
   2509     if (gDvm.debuggerActive) {
   2510         dvmJdwpPostThreadChange(gDvm.jdwpState,
   2511             objectToObjectId(thread->threadObj), false);
   2512     }
   2513     if (gDvm.ddmThreadNotification)
   2514         dvmDdmSendThreadNotification(thread, false);
   2515 }
   2516 
   2517 /*
   2518  * Tell JDWP that a new class has been prepared.
   2519  */
   2520 void dvmDbgPostClassPrepare(ClassObject* clazz)
   2521 {
   2522     const char* signature;
   2523     int tag;
   2524 
   2525     if (dvmIsInterfaceClass(clazz))
   2526         tag = TT_INTERFACE;
   2527     else
   2528         tag = TT_CLASS;
   2529 
   2530     // TODO - we currently always send both "verified" and "prepared" since
   2531     // debuggers seem to like that.  There might be some advantage to honesty,
   2532     // since the class may not yet be verified.
   2533     signature = jniSignature(clazz);
   2534     dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
   2535         signature, CS_VERIFIED | CS_PREPARED);
   2536 }
   2537 
   2538 /*
   2539  * The JDWP event mechanism has registered an event with a LocationOnly
   2540  * mod.  Tell the interpreter to call us if we hit the specified
   2541  * address.
   2542  */
   2543 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
   2544 {
   2545     Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
   2546     assert(!dvmIsNativeMethod(method));
   2547     dvmAddBreakAddr(method, pLoc->idx);
   2548     return true;        /* assume success */
   2549 }
   2550 
   2551 /*
   2552  * An event with a LocationOnly mod has been removed.
   2553  */
   2554 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
   2555 {
   2556     Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
   2557     assert(!dvmIsNativeMethod(method));
   2558     dvmClearBreakAddr(method, pLoc->idx);
   2559 }
   2560 
   2561 /*
   2562  * The JDWP event mechanism has registered a single-step event.  Tell
   2563  * the interpreter about it.
   2564  */
   2565 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
   2566     JdwpStepDepth depth)
   2567 {
   2568     Object* threadObj;
   2569     Thread* thread;
   2570     bool result = false;
   2571 
   2572     threadObj = objectIdToObject(threadId);
   2573     assert(threadObj != NULL);
   2574 
   2575     /*
   2576      * Get a pointer to the Thread struct for this ID.  The pointer will
   2577      * be used strictly for comparisons against the current thread pointer
   2578      * after the setup is complete, so we can safely release the lock.
   2579      */
   2580     dvmLockThreadList(NULL);
   2581     thread = threadObjToThread(threadObj);
   2582 
   2583     if (thread == NULL) {
   2584         LOGE("Thread for single-step not found");
   2585         goto bail;
   2586     }
   2587     if (!dvmIsSuspended(thread)) {
   2588         LOGE("Thread for single-step not suspended");
   2589         assert(!"non-susp step");      // I want to know if this can happen
   2590         goto bail;
   2591     }
   2592 
   2593     assert(dvmIsSuspended(thread));
   2594     if (!dvmAddSingleStep(thread, size, depth))
   2595         goto bail;
   2596 
   2597     result = true;
   2598 
   2599 bail:
   2600     dvmUnlockThreadList();
   2601     return result;
   2602 }
   2603 
   2604 /*
   2605  * A single-step event has been removed.
   2606  */
   2607 void dvmDbgUnconfigureStep(ObjectId threadId)
   2608 {
   2609     UNUSED_PARAMETER(threadId);
   2610 
   2611     /* right now it's global, so don't need to find Thread */
   2612     dvmClearSingleStep(NULL);
   2613 }
   2614 
   2615 /*
   2616  * Invoke a method in a thread that has been stopped on a breakpoint or
   2617  * other debugger event.  (This function is called from the JDWP thread.)
   2618  *
   2619  * Note that access control is not enforced, per spec.
   2620  */
   2621 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
   2622     RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
   2623     u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
   2624 {
   2625     Object* threadObj = objectIdToObject(threadId);
   2626 
   2627     dvmLockThreadList(NULL);
   2628 
   2629     Thread* targetThread = threadObjToThread(threadObj);
   2630     if (targetThread == NULL) {
   2631         dvmUnlockThreadList();
   2632         return ERR_INVALID_THREAD;       /* thread does not exist */
   2633     }
   2634     if (!targetThread->invokeReq.ready) {
   2635         dvmUnlockThreadList();
   2636         return ERR_INVALID_THREAD;       /* thread not stopped by event */
   2637     }
   2638 
   2639     /*
   2640      * We currently have a bug where we don't successfully resume the
   2641      * target thread if the suspend count is too deep.  We're expected to
   2642      * require one "resume" for each "suspend", but when asked to execute
   2643      * a method we have to resume fully and then re-suspend it back to the
   2644      * same level.  (The easiest way to cause this is to type "suspend"
   2645      * multiple times in jdb.)
   2646      *
   2647      * It's unclear what this means when the event specifies "resume all"
   2648      * and some threads are suspended more deeply than others.  This is
   2649      * a rare problem, so for now we just prevent it from hanging forever
   2650      * by rejecting the method invocation request.  Without this, we will
   2651      * be stuck waiting on a suspended thread.
   2652      */
   2653     if (targetThread->suspendCount > 1) {
   2654         LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
   2655              "for method exec",
   2656             dvmThreadSelf()->threadId, targetThread->threadId,
   2657             targetThread->suspendCount);
   2658         dvmUnlockThreadList();
   2659         return ERR_THREAD_SUSPENDED;     /* probably not expected here */
   2660     }
   2661 
   2662     /*
   2663      * TODO: ought to screen the various IDs, and verify that the argument
   2664      * list is valid.
   2665      */
   2666 
   2667     targetThread->invokeReq.obj = objectIdToObject(objectId);
   2668     targetThread->invokeReq.thread = threadObj;
   2669     targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
   2670     targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
   2671     targetThread->invokeReq.numArgs = numArgs;
   2672     targetThread->invokeReq.argArray = argArray;
   2673     targetThread->invokeReq.options = options;
   2674     targetThread->invokeReq.invokeNeeded = true;
   2675 
   2676     /*
   2677      * This is a bit risky -- if the thread goes away we're sitting high
   2678      * and dry -- but we must release this before the dvmResumeAllThreads
   2679      * call, and it's unwise to hold it during dvmWaitForSuspend.
   2680      */
   2681     dvmUnlockThreadList();
   2682 
   2683     /*
   2684      * We change our (JDWP thread) status, which should be THREAD_RUNNING,
   2685      * so the VM can suspend for a GC if the invoke request causes us to
   2686      * run out of memory.  It's also a good idea to change it before locking
   2687      * the invokeReq mutex, although that should never be held for long.
   2688      */
   2689     Thread* self = dvmThreadSelf();
   2690     ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
   2691 
   2692     LOGV("    Transferring control to event thread");
   2693     dvmLockMutex(&targetThread->invokeReq.lock);
   2694 
   2695     if ((options & INVOKE_SINGLE_THREADED) == 0) {
   2696         LOGV("      Resuming all threads");
   2697         dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
   2698     } else {
   2699         LOGV("      Resuming event thread only");
   2700         dvmResumeThread(targetThread);
   2701     }
   2702 
   2703     /*
   2704      * Wait for the request to finish executing.
   2705      */
   2706     while (targetThread->invokeReq.invokeNeeded) {
   2707         pthread_cond_wait(&targetThread->invokeReq.cv,
   2708                           &targetThread->invokeReq.lock);
   2709     }
   2710     dvmUnlockMutex(&targetThread->invokeReq.lock);
   2711     LOGV("    Control has returned from event thread");
   2712 
   2713     /* wait for thread to re-suspend itself */
   2714     dvmWaitForSuspend(targetThread);
   2715 
   2716     /*
   2717      * Done waiting, switch back to RUNNING.
   2718      */
   2719     dvmChangeStatus(self, oldStatus);
   2720 
   2721     /*
   2722      * Suspend the threads.  We waited for the target thread to suspend
   2723      * itself, so all we need to do is suspend the others.
   2724      *
   2725      * The suspendAllThreads() call will double-suspend the event thread,
   2726      * so we want to resume the target thread once to keep the books straight.
   2727      */
   2728     if ((options & INVOKE_SINGLE_THREADED) == 0) {
   2729         LOGV("      Suspending all threads");
   2730         dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
   2731         LOGV("      Resuming event thread to balance the count");
   2732         dvmResumeThread(targetThread);
   2733     }
   2734 
   2735     /*
   2736      * Set up the result.
   2737      */
   2738     *pResultTag = targetThread->invokeReq.resultTag;
   2739     if (isTagPrimitive(targetThread->invokeReq.resultTag))
   2740         *pResultValue = targetThread->invokeReq.resultValue.j;
   2741     else {
   2742         Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
   2743         *pResultValue = objectToObjectId(tmpObj);
   2744     }
   2745     *pExceptObj = targetThread->invokeReq.exceptObj;
   2746     return targetThread->invokeReq.err;
   2747 }
   2748 
   2749 /*
   2750  * Return a basic tag value for the return type.
   2751  */
   2752 static u1 getReturnTypeBasicTag(const Method* method)
   2753 {
   2754     const char* descriptor = dexProtoGetReturnType(&method->prototype);
   2755     return basicTagFromDescriptor(descriptor);
   2756 }
   2757 
   2758 /*
   2759  * Execute the method described by "*pReq".
   2760  *
   2761  * We're currently in VMWAIT, because we're stopped on a breakpoint.  We
   2762  * want to switch to RUNNING while we execute.
   2763  */
   2764 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
   2765 {
   2766     Thread* self = dvmThreadSelf();
   2767     const Method* meth;
   2768     Object* oldExcept;
   2769     ThreadStatus oldStatus;
   2770 
   2771     /*
   2772      * We can be called while an exception is pending in the VM.  We need
   2773      * to preserve that across the method invocation.
   2774      */
   2775     oldExcept = dvmGetException(self);
   2776     if (oldExcept != NULL) {
   2777         dvmAddTrackedAlloc(oldExcept, self);
   2778         dvmClearException(self);
   2779     }
   2780 
   2781     oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
   2782 
   2783     /*
   2784      * Translate the method through the vtable, unless we're calling a
   2785      * direct method or the debugger wants to suppress it.
   2786      */
   2787     if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
   2788         dvmIsDirectMethod(pReq->method))
   2789     {
   2790         meth = pReq->method;
   2791     } else {
   2792         meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
   2793     }
   2794     assert(meth != NULL);
   2795 
   2796     assert(sizeof(jvalue) == sizeof(u8));
   2797 
   2798     IF_LOGV() {
   2799         char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
   2800         LOGV("JDWP invoking method %p/%p %s.%s:%s",
   2801             pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
   2802         free(desc);
   2803     }
   2804 
   2805     dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
   2806         (jvalue*)pReq->argArray);
   2807     pReq->exceptObj = objectToObjectId(dvmGetException(self));
   2808     pReq->resultTag = getReturnTypeBasicTag(meth);
   2809     if (pReq->exceptObj != 0) {
   2810         Object* exc = dvmGetException(self);
   2811         LOGD("  JDWP invocation returning with exceptObj=%p (%s)",
   2812             exc, exc->clazz->descriptor);
   2813         //dvmLogExceptionStackTrace();
   2814         dvmClearException(self);
   2815         /*
   2816          * Nothing should try to use this, but it looks like something is.
   2817          * Make it null to be safe.
   2818          */
   2819         pReq->resultValue.j = 0; /*0xadadadad;*/
   2820     } else if (pReq->resultTag == JT_OBJECT) {
   2821         /* if no exception thrown, examine object result more closely */
   2822         u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
   2823         if (newTag != pReq->resultTag) {
   2824             LOGVV("  JDWP promoted result from %d to %d",
   2825                 pReq->resultTag, newTag);
   2826             pReq->resultTag = newTag;
   2827         }
   2828 
   2829         /*
   2830          * Register the object.  We don't actually need an ObjectId yet,
   2831          * but we do need to be sure that the GC won't move or discard the
   2832          * object when we switch out of RUNNING.  The ObjectId conversion
   2833          * will add the object to the "do not touch" list.
   2834          *
   2835          * We can't use the "tracked allocation" mechanism here because
   2836          * the object is going to be handed off to a different thread.
   2837          */
   2838         objectToObjectId((Object*)pReq->resultValue.l);
   2839     }
   2840 
   2841     if (oldExcept != NULL) {
   2842         dvmSetException(self, oldExcept);
   2843         dvmReleaseTrackedAlloc(oldExcept, self);
   2844     }
   2845     dvmChangeStatus(self, oldStatus);
   2846 }
   2847 
   2848 // for dvmAddressSetForLine
   2849 struct AddressSetContext {
   2850     bool lastAddressValid;
   2851     u4 lastAddress;
   2852     u4 lineNum;
   2853     AddressSet *pSet;
   2854 };
   2855 
   2856 // for dvmAddressSetForLine
   2857 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
   2858 {
   2859     AddressSetContext *pContext = (AddressSetContext *)cnxt;
   2860 
   2861     if (lineNum == pContext->lineNum) {
   2862         if (!pContext->lastAddressValid) {
   2863             // Everything from this address until the next line change is ours
   2864             pContext->lastAddress = address;
   2865             pContext->lastAddressValid = true;
   2866         }
   2867         // else, If we're already in a valid range for this lineNum,
   2868         // just keep going (shouldn't really happen)
   2869     } else if (pContext->lastAddressValid) { // and the line number is new
   2870         u4 i;
   2871         // Add everything from the last entry up until here to the set
   2872         for (i = pContext->lastAddress; i < address; i++) {
   2873             dvmAddressSetSet(pContext->pSet, i);
   2874         }
   2875 
   2876         pContext->lastAddressValid = false;
   2877     }
   2878 
   2879     // there may be multiple entries for a line
   2880     return 0;
   2881 }
   2882 /*
   2883  * Build up a set of bytecode addresses associated with a line number
   2884  */
   2885 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
   2886 {
   2887     AddressSet *result;
   2888     const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
   2889     u4 insnsSize = dvmGetMethodInsnsSize(method);
   2890     AddressSetContext context;
   2891 
   2892     result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
   2893     result->setSize = insnsSize;
   2894 
   2895     memset(&context, 0, sizeof(context));
   2896     context.pSet = result;
   2897     context.lineNum = line;
   2898     context.lastAddressValid = false;
   2899 
   2900     dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
   2901         method->clazz->descriptor,
   2902         method->prototype.protoIdx,
   2903         method->accessFlags,
   2904         addressSetCb, NULL, &context);
   2905 
   2906     // If the line number was the last in the position table...
   2907     if (context.lastAddressValid) {
   2908         u4 i;
   2909         for (i = context.lastAddress; i < insnsSize; i++) {
   2910             dvmAddressSetSet(result, i);
   2911         }
   2912     }
   2913 
   2914     return result;
   2915 }
   2916 
   2917 
   2918 /*
   2919  * ===========================================================================
   2920  *      Dalvik Debug Monitor support
   2921  * ===========================================================================
   2922  */
   2923 
   2924 /*
   2925  * We have received a DDM packet over JDWP.  Hand it off to the VM.
   2926  */
   2927 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
   2928     int* pReplyLen)
   2929 {
   2930     return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
   2931 }
   2932 
   2933 /*
   2934  * First DDM packet has arrived over JDWP.  Notify the press.
   2935  */
   2936 void dvmDbgDdmConnected()
   2937 {
   2938     dvmDdmConnected();
   2939 }
   2940 
   2941 /*
   2942  * JDWP connection has dropped.
   2943  */
   2944 void dvmDbgDdmDisconnected()
   2945 {
   2946     dvmDdmDisconnected();
   2947 }
   2948 
   2949 /*
   2950  * Send up a JDWP event packet with a DDM chunk in it.
   2951  */
   2952 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
   2953 {
   2954     assert(buf != NULL);
   2955     struct iovec vec[1] = { {(void*)buf, len} };
   2956     dvmDbgDdmSendChunkV(type, vec, 1);
   2957 }
   2958 
   2959 /*
   2960  * Send up a JDWP event packet with a DDM chunk in it.  The chunk is
   2961  * concatenated from multiple source buffers.
   2962  */
   2963 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
   2964 {
   2965     if (gDvm.jdwpState == NULL) {
   2966         LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
   2967             type);
   2968         return;
   2969     }
   2970 
   2971     dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
   2972 }
   2973