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         ALOGI("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     ALOGV("+++ 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     ALOGV("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     ALOGI("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     ALOGD("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     ALOGI("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         ALOGW("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         ALOGE("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         ALOGE("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         ALOGW("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         ALOGV("    --> 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         ALOGW("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         ALOGV("    --> 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         ALOGV("    --> 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, count;
   1268 
   1269     clazz = refTypeIdToClassObject(refTypeId);
   1270     assert(clazz != NULL);
   1271 
   1272     count = clazz->interfaceCount;
   1273     expandBufAdd4BE(pReply, count);
   1274     for (i = 0; i < count; i++) {
   1275         ClassObject* iface = clazz->interfaces[i];
   1276         expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
   1277     }
   1278 }
   1279 
   1280 struct DebugCallbackContext {
   1281     int numItems;
   1282     ExpandBuf* pReply;
   1283     // used by locals table
   1284     bool withGeneric;
   1285 };
   1286 
   1287 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
   1288 {
   1289     DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
   1290 
   1291     expandBufAdd8BE(pContext->pReply, address);
   1292     expandBufAdd4BE(pContext->pReply, lineNum);
   1293     pContext->numItems++;
   1294 
   1295     return 0;
   1296 }
   1297 
   1298 /*
   1299  * For Method.LineTable: output the line table.
   1300  *
   1301  * Note we operate in Dalvik's 16-bit units rather than bytes.
   1302  */
   1303 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
   1304     ExpandBuf* pReply)
   1305 {
   1306     Method* method;
   1307     u8 start, end;
   1308     DebugCallbackContext context;
   1309 
   1310     memset (&context, 0, sizeof(DebugCallbackContext));
   1311 
   1312     method = methodIdToMethod(refTypeId, methodId);
   1313     if (dvmIsNativeMethod(method)) {
   1314         start = (u8) -1;
   1315         end = (u8) -1;
   1316     } else {
   1317         start = 0;
   1318         end = dvmGetMethodInsnsSize(method);
   1319     }
   1320 
   1321     expandBufAdd8BE(pReply, start);
   1322     expandBufAdd8BE(pReply, end);
   1323 
   1324     // Add numLines later
   1325     size_t numLinesOffset = expandBufGetLength(pReply);
   1326     expandBufAdd4BE(pReply, 0);
   1327 
   1328     context.pReply = pReply;
   1329 
   1330     dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
   1331         dvmGetMethodCode(method),
   1332         method->clazz->descriptor,
   1333         method->prototype.protoIdx,
   1334         method->accessFlags,
   1335         lineTablePositionsCb, NULL, &context);
   1336 
   1337     set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
   1338 }
   1339 
   1340 /*
   1341  * Eclipse appears to expect that the "this" reference is in slot zero.
   1342  * If it's not, the "variables" display will show two copies of "this",
   1343  * possibly because it gets "this" from SF.ThisObject and then displays
   1344  * all locals with nonzero slot numbers.
   1345  *
   1346  * So, we remap the item in slot 0 to 1000, and remap "this" to zero.  On
   1347  * SF.GetValues / SF.SetValues we map them back.
   1348  */
   1349 static int tweakSlot(int slot, const char* name)
   1350 {
   1351     int newSlot = slot;
   1352 
   1353     if (strcmp(name, "this") == 0)      // only remap "this" ptr
   1354         newSlot = 0;
   1355     else if (slot == 0)                 // always remap slot 0
   1356         newSlot = kSlot0Sub;
   1357 
   1358     ALOGV("untweak: %d to %d", slot, newSlot);
   1359     return newSlot;
   1360 }
   1361 
   1362 /*
   1363  * Reverse Eclipse hack.
   1364  */
   1365 static int untweakSlot(int slot, const void* framePtr)
   1366 {
   1367     int newSlot = slot;
   1368 
   1369     if (slot == kSlot0Sub) {
   1370         newSlot = 0;
   1371     } else if (slot == 0) {
   1372         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   1373         const Method* method = saveArea->method;
   1374         newSlot = method->registersSize - method->insSize;
   1375     }
   1376 
   1377     ALOGV("untweak: %d to %d", slot, newSlot);
   1378     return newSlot;
   1379 }
   1380 
   1381 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
   1382         u4 endAddress, const char *name, const char *descriptor,
   1383         const char *signature)
   1384 {
   1385     DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
   1386 
   1387     reg = (u2) tweakSlot(reg, name);
   1388 
   1389     ALOGV("    %2d: %d(%d) '%s' '%s' slot=%d",
   1390         pContext->numItems, startAddress, endAddress - startAddress,
   1391         name, descriptor, reg);
   1392 
   1393     expandBufAdd8BE(pContext->pReply, startAddress);
   1394     expandBufAddUtf8String(pContext->pReply, (const u1*)name);
   1395     expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
   1396     if (pContext->withGeneric) {
   1397         expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
   1398     }
   1399     expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
   1400     expandBufAdd4BE(pContext->pReply, reg);
   1401 
   1402     pContext->numItems++;
   1403 }
   1404 
   1405 /*
   1406  * For Method.VariableTable[WithGeneric]: output information about local
   1407  * variables for the specified method.
   1408  */
   1409 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
   1410     bool withGeneric, ExpandBuf* pReply)
   1411 {
   1412     Method* method;
   1413     DebugCallbackContext context;
   1414 
   1415     memset (&context, 0, sizeof(DebugCallbackContext));
   1416 
   1417     method = methodIdToMethod(refTypeId, methodId);
   1418 
   1419     expandBufAdd4BE(pReply, method->insSize);
   1420 
   1421     // Add numLocals later
   1422     size_t numLocalsOffset = expandBufGetLength(pReply);
   1423     expandBufAdd4BE(pReply, 0);
   1424 
   1425     context.pReply = pReply;
   1426     context.withGeneric = withGeneric;
   1427     dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
   1428         dvmGetMethodCode(method),
   1429         method->clazz->descriptor,
   1430         method->prototype.protoIdx,
   1431         method->accessFlags,
   1432         NULL, variableTableCb, &context);
   1433 
   1434     set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
   1435 }
   1436 
   1437 /*
   1438  * Get the basic tag for an instance field.
   1439  */
   1440 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
   1441 {
   1442     Object* obj = objectIdToObject(objId);
   1443     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1444     const Field* field = fieldIdToField(classId, fieldId);
   1445     return basicTagFromDescriptor(field->signature);
   1446 }
   1447 
   1448 /*
   1449  * Get the basic tag for a static field.
   1450  */
   1451 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
   1452 {
   1453     const Field* field = fieldIdToField(refTypeId, fieldId);
   1454     return basicTagFromDescriptor(field->signature);
   1455 }
   1456 
   1457 
   1458 /*
   1459  * Copy the value of a static field into the output buffer, preceded
   1460  * by an appropriate tag.  The tag is based on the value held by the
   1461  * field, not the field's type.
   1462  */
   1463 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
   1464 {
   1465     Object* obj = objectIdToObject(objectId);
   1466     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1467     InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
   1468     u1 tag = basicTagFromDescriptor(ifield->signature);
   1469 
   1470     if (tag == JT_ARRAY || tag == JT_OBJECT) {
   1471         Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
   1472         tag = tagFromObject(objVal);
   1473         expandBufAdd1(pReply, tag);
   1474         expandBufAddObjectId(pReply, objectToObjectId(objVal));
   1475         ALOGV("    --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
   1476     } else {
   1477         ALOGV("    --> ifieldId %x --> tag '%c'", fieldId, tag);
   1478         expandBufAdd1(pReply, tag);
   1479 
   1480         switch (tag) {
   1481         case JT_BOOLEAN:
   1482             expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
   1483             break;
   1484         case JT_BYTE:
   1485             expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
   1486             break;
   1487         case JT_SHORT:
   1488             expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
   1489             break;
   1490         case JT_CHAR:
   1491             expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
   1492             break;
   1493         case JT_INT:
   1494         case JT_FLOAT:
   1495             expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
   1496             break;
   1497         case JT_LONG:
   1498         case JT_DOUBLE:
   1499             expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
   1500             break;
   1501         default:
   1502             ALOGE("ERROR: unhandled field type '%s'", ifield->signature);
   1503             assert(false);
   1504             break;
   1505         }
   1506     }
   1507 }
   1508 
   1509 /*
   1510  * Set the value of the specified field.
   1511  */
   1512 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
   1513     int width)
   1514 {
   1515     Object* obj = objectIdToObject(objectId);
   1516     RefTypeId classId = classObjectToRefTypeId(obj->clazz);
   1517     InstField* field = (InstField*) fieldIdToField(classId, fieldId);
   1518 
   1519     switch (field->signature[0]) {
   1520     case JT_BOOLEAN:
   1521         assert(width == 1);
   1522         dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
   1523         break;
   1524     case JT_BYTE:
   1525         assert(width == 1);
   1526         dvmSetFieldInt(obj, field->byteOffset, value);
   1527         break;
   1528     case JT_SHORT:
   1529     case JT_CHAR:
   1530         assert(width == 2);
   1531         dvmSetFieldInt(obj, field->byteOffset, value);
   1532         break;
   1533     case JT_INT:
   1534     case JT_FLOAT:
   1535         assert(width == 4);
   1536         dvmSetFieldInt(obj, field->byteOffset, value);
   1537         break;
   1538     case JT_ARRAY:
   1539     case JT_OBJECT:
   1540         assert(width == sizeof(ObjectId));
   1541         dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
   1542         break;
   1543     case JT_DOUBLE:
   1544     case JT_LONG:
   1545         assert(width == 8);
   1546         dvmSetFieldLong(obj, field->byteOffset, value);
   1547         break;
   1548     default:
   1549         ALOGE("ERROR: unhandled class type '%s'", field->signature);
   1550         assert(false);
   1551         break;
   1552     }
   1553 }
   1554 
   1555 /*
   1556  * Copy the value of a static field into the output buffer, preceded
   1557  * by an appropriate tag.  The tag is based on the value held by the
   1558  * field, not the field's type.
   1559  */
   1560 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
   1561     ExpandBuf* pReply)
   1562 {
   1563     StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
   1564     u1 tag = basicTagFromDescriptor(sfield->signature);
   1565 
   1566     if (tag == JT_ARRAY || tag == JT_OBJECT) {
   1567         Object* objVal = dvmGetStaticFieldObject(sfield);
   1568         tag = tagFromObject(objVal);
   1569         expandBufAdd1(pReply, tag);
   1570         expandBufAddObjectId(pReply, objectToObjectId(objVal));
   1571         ALOGV("    --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
   1572     } else {
   1573         JValue value;
   1574 
   1575         ALOGV("    --> sfieldId %x --> tag '%c'", fieldId, tag);
   1576         expandBufAdd1(pReply, tag);
   1577 
   1578         switch (tag) {
   1579         case JT_BOOLEAN:
   1580             expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
   1581             break;
   1582         case JT_BYTE:
   1583             expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
   1584             break;
   1585         case JT_SHORT:
   1586             expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
   1587             break;
   1588         case JT_CHAR:
   1589             expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
   1590             break;
   1591         case JT_INT:
   1592             expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
   1593             break;
   1594         case JT_FLOAT:
   1595             value.f = dvmGetStaticFieldFloat(sfield);
   1596             expandBufAdd4BE(pReply, value.i);
   1597             break;
   1598         case JT_LONG:
   1599             expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
   1600             break;
   1601         case JT_DOUBLE:
   1602             value.d = dvmGetStaticFieldDouble(sfield);
   1603             expandBufAdd8BE(pReply, value.j);
   1604             break;
   1605         default:
   1606             ALOGE("ERROR: unhandled field type '%s'", sfield->signature);
   1607             assert(false);
   1608             break;
   1609         }
   1610     }
   1611 }
   1612 
   1613 /*
   1614  * Set the value of a static field.
   1615  */
   1616 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
   1617     u8 rawValue, int width)
   1618 {
   1619     StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
   1620     Object* objVal;
   1621     JValue value;
   1622 
   1623     value.j = rawValue;
   1624 
   1625     switch (sfield->signature[0]) {
   1626     case JT_BOOLEAN:
   1627         assert(width == 1);
   1628         dvmSetStaticFieldBoolean(sfield, value.z);
   1629         break;
   1630     case JT_BYTE:
   1631         assert(width == 1);
   1632         dvmSetStaticFieldByte(sfield, value.b);
   1633         break;
   1634     case JT_SHORT:
   1635         assert(width == 2);
   1636         dvmSetStaticFieldShort(sfield, value.s);
   1637         break;
   1638     case JT_CHAR:
   1639         assert(width == 2);
   1640         dvmSetStaticFieldChar(sfield, value.c);
   1641         break;
   1642     case JT_INT:
   1643         assert(width == 4);
   1644         dvmSetStaticFieldInt(sfield, value.i);
   1645         break;
   1646     case JT_FLOAT:
   1647         assert(width == 4);
   1648         dvmSetStaticFieldFloat(sfield, value.f);
   1649         break;
   1650     case JT_ARRAY:
   1651     case JT_OBJECT:
   1652         assert(width == sizeof(ObjectId));
   1653         objVal = objectIdToObject(rawValue);
   1654         dvmSetStaticFieldObject(sfield, objVal);
   1655         break;
   1656     case JT_LONG:
   1657         assert(width == 8);
   1658         dvmSetStaticFieldLong(sfield, value.j);
   1659         break;
   1660     case JT_DOUBLE:
   1661         assert(width == 8);
   1662         dvmSetStaticFieldDouble(sfield, value.d);
   1663         break;
   1664     default:
   1665         ALOGE("ERROR: unhandled class type '%s'", sfield->signature);
   1666         assert(false);
   1667         break;
   1668     }
   1669 }
   1670 
   1671 /*
   1672  * Convert a string object to a UTF-8 string.
   1673  *
   1674  * Returns a newly-allocated string.
   1675  */
   1676 char* dvmDbgStringToUtf8(ObjectId strId)
   1677 {
   1678     StringObject* strObj = (StringObject*) objectIdToObject(strId);
   1679 
   1680     return dvmCreateCstrFromString(strObj);
   1681 }
   1682 
   1683 
   1684 /*
   1685  * ===========================================================================
   1686  *      Thread and ThreadGroup
   1687  * ===========================================================================
   1688  */
   1689 
   1690 /*
   1691  * Convert a thread object to a Thread ptr.
   1692  *
   1693  * This currently requires running through the list of threads and finding
   1694  * a match.
   1695  *
   1696  * IMPORTANT: grab gDvm.threadListLock before calling here.
   1697  */
   1698 static Thread* threadObjToThread(Object* threadObj)
   1699 {
   1700     Thread* thread;
   1701 
   1702     for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   1703         if (thread->threadObj == threadObj)
   1704             break;
   1705     }
   1706 
   1707     return thread;
   1708 }
   1709 
   1710 /*
   1711  * Get the status and suspend state of a thread.
   1712  */
   1713 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
   1714     u4* pSuspendStatus)
   1715 {
   1716     Object* threadObj;
   1717     Thread* thread;
   1718     bool result = false;
   1719 
   1720     threadObj = objectIdToObject(threadId);
   1721     assert(threadObj != NULL);
   1722 
   1723     /* lock the thread list, so the thread doesn't vanish while we work */
   1724     dvmLockThreadList(NULL);
   1725 
   1726     thread = threadObjToThread(threadObj);
   1727     if (thread == NULL)
   1728         goto bail;
   1729 
   1730     switch (thread->status) {
   1731     case THREAD_ZOMBIE:         *pThreadStatus = TS_ZOMBIE;     break;
   1732     case THREAD_RUNNING:        *pThreadStatus = TS_RUNNING;    break;
   1733     case THREAD_TIMED_WAIT:     *pThreadStatus = TS_SLEEPING;   break;
   1734     case THREAD_MONITOR:        *pThreadStatus = TS_MONITOR;    break;
   1735     case THREAD_WAIT:           *pThreadStatus = TS_WAIT;       break;
   1736     case THREAD_INITIALIZING:   *pThreadStatus = TS_ZOMBIE;     break;
   1737     case THREAD_STARTING:       *pThreadStatus = TS_ZOMBIE;     break;
   1738     case THREAD_NATIVE:         *pThreadStatus = TS_RUNNING;    break;
   1739     case THREAD_VMWAIT:         *pThreadStatus = TS_WAIT;       break;
   1740     case THREAD_SUSPENDED:      *pThreadStatus = TS_RUNNING;    break;
   1741     default:
   1742         assert(false);
   1743         *pThreadStatus = THREAD_ZOMBIE;
   1744         break;
   1745     }
   1746 
   1747     if (dvmIsSuspended(thread))
   1748         *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
   1749     else
   1750         *pSuspendStatus = 0;
   1751 
   1752     result = true;
   1753 
   1754 bail:
   1755     dvmUnlockThreadList();
   1756     return result;
   1757 }
   1758 
   1759 /*
   1760  * Get the thread's suspend count.
   1761  */
   1762 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
   1763 {
   1764     Object* threadObj;
   1765     Thread* thread;
   1766     u4 result = 0;
   1767 
   1768     threadObj = objectIdToObject(threadId);
   1769     assert(threadObj != NULL);
   1770 
   1771     /* lock the thread list, so the thread doesn't vanish while we work */
   1772     dvmLockThreadList(NULL);
   1773 
   1774     thread = threadObjToThread(threadObj);
   1775     if (thread == NULL)
   1776         goto bail;
   1777 
   1778     result = thread->suspendCount;
   1779 
   1780 bail:
   1781     dvmUnlockThreadList();
   1782     return result;
   1783 }
   1784 
   1785 /*
   1786  * Determine whether or not a thread exists in the VM's thread list.
   1787  *
   1788  * Returns "true" if the thread exists.
   1789  */
   1790 bool dvmDbgThreadExists(ObjectId threadId)
   1791 {
   1792     Object* threadObj;
   1793     Thread* thread;
   1794     bool result;
   1795 
   1796     threadObj = objectIdToObject(threadId);
   1797     assert(threadObj != NULL);
   1798 
   1799     /* lock the thread list, so the thread doesn't vanish while we work */
   1800     dvmLockThreadList(NULL);
   1801 
   1802     thread = threadObjToThread(threadObj);
   1803     if (thread == NULL)
   1804         result = false;
   1805     else
   1806         result = true;
   1807 
   1808     dvmUnlockThreadList();
   1809     return result;
   1810 }
   1811 
   1812 /*
   1813  * Determine whether or not a thread is suspended.
   1814  *
   1815  * Returns "false" if the thread is running or doesn't exist.
   1816  */
   1817 bool dvmDbgIsSuspended(ObjectId threadId)
   1818 {
   1819     Object* threadObj;
   1820     Thread* thread;
   1821     bool result = false;
   1822 
   1823     threadObj = objectIdToObject(threadId);
   1824     assert(threadObj != NULL);
   1825 
   1826     /* lock the thread list, so the thread doesn't vanish while we work */
   1827     dvmLockThreadList(NULL);
   1828 
   1829     thread = threadObjToThread(threadObj);
   1830     if (thread == NULL)
   1831         goto bail;
   1832 
   1833     result = dvmIsSuspended(thread);
   1834 
   1835 bail:
   1836     dvmUnlockThreadList();
   1837     return result;
   1838 }
   1839 
   1840 /*
   1841  * Return the ObjectId for the "system" thread group.
   1842  */
   1843 ObjectId dvmDbgGetSystemThreadGroupId()
   1844 {
   1845     Object* groupObj = dvmGetSystemThreadGroup();
   1846     return objectToObjectId(groupObj);
   1847 }
   1848 
   1849 /*
   1850  * Return the ObjectId for the "main" thread group.
   1851  */
   1852 ObjectId dvmDbgGetMainThreadGroupId()
   1853 {
   1854     Object* groupObj = dvmGetMainThreadGroup();
   1855     return objectToObjectId(groupObj);
   1856 }
   1857 
   1858 /*
   1859  * Get the name of a thread.
   1860  *
   1861  * Returns a newly-allocated string.
   1862  */
   1863 char* dvmDbgGetThreadName(ObjectId threadId)
   1864 {
   1865     Object* threadObj;
   1866     StringObject* nameStr;
   1867     char* str;
   1868     char* result;
   1869 
   1870     threadObj = objectIdToObject(threadId);
   1871     assert(threadObj != NULL);
   1872 
   1873     nameStr = (StringObject*) dvmGetFieldObject(threadObj,
   1874                                                 gDvm.offJavaLangThread_name);
   1875     str = dvmCreateCstrFromString(nameStr);
   1876     result = (char*) malloc(strlen(str) + 20);
   1877 
   1878     /* lock the thread list, so the thread doesn't vanish while we work */
   1879     dvmLockThreadList(NULL);
   1880     Thread* thread = threadObjToThread(threadObj);
   1881     if (thread != NULL)
   1882         sprintf(result, "<%d> %s", thread->threadId, str);
   1883     else
   1884         sprintf(result, "%s", str);
   1885     dvmUnlockThreadList();
   1886 
   1887     free(str);
   1888     return result;
   1889 }
   1890 
   1891 /*
   1892  * Get a thread's group.
   1893  */
   1894 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
   1895 {
   1896     Object* threadObj;
   1897     Object* group;
   1898 
   1899     threadObj = objectIdToObject(threadId);
   1900     assert(threadObj != NULL);
   1901 
   1902     group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
   1903     return objectToObjectId(group);
   1904 }
   1905 
   1906 
   1907 /*
   1908  * Get the name of a thread group.
   1909  *
   1910  * Returns a newly-allocated string.
   1911  */
   1912 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
   1913 {
   1914     Object* threadGroup;
   1915     StringObject* nameStr;
   1916 
   1917     threadGroup = objectIdToObject(threadGroupId);
   1918     assert(threadGroup != NULL);
   1919 
   1920     nameStr = (StringObject*)
   1921         dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
   1922     return dvmCreateCstrFromString(nameStr);
   1923 }
   1924 
   1925 /*
   1926  * Get the parent of a thread group.
   1927  *
   1928  * Returns a newly-allocated string.
   1929  */
   1930 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
   1931 {
   1932     Object* threadGroup;
   1933     Object* parent;
   1934 
   1935     threadGroup = objectIdToObject(threadGroupId);
   1936     assert(threadGroup != NULL);
   1937 
   1938     parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
   1939     return objectToObjectId(parent);
   1940 }
   1941 
   1942 /*
   1943  * Get the list of threads in the thread group.
   1944  *
   1945  * We do this by running through the full list of threads and returning
   1946  * the ones that have the ThreadGroup object as their owner.
   1947  *
   1948  * If threadGroupId is set to "kAllThreads", we ignore the group field and
   1949  * return all threads.
   1950  *
   1951  * The caller must free "*ppThreadIds".
   1952  */
   1953 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
   1954     ObjectId** ppThreadIds, u4* pThreadCount)
   1955 {
   1956     Object* targetThreadGroup = NULL;
   1957     Thread* thread;
   1958     int count;
   1959 
   1960     if (threadGroupId != THREAD_GROUP_ALL) {
   1961         targetThreadGroup = objectIdToObject(threadGroupId);
   1962         assert(targetThreadGroup != NULL);
   1963     }
   1964 
   1965     dvmLockThreadList(NULL);
   1966 
   1967     thread = gDvm.threadList;
   1968     count = 0;
   1969     for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   1970         Object* group;
   1971 
   1972         /* Skip over the JDWP support thread.  Some debuggers
   1973          * get bent out of shape when they can't suspend and
   1974          * query all threads, so it's easier if we just don't
   1975          * tell them about us.
   1976          */
   1977         if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
   1978             continue;
   1979 
   1980         /* This thread is currently being created, and isn't ready
   1981          * to be seen by the debugger yet.
   1982          */
   1983         if (thread->threadObj == NULL)
   1984             continue;
   1985 
   1986         group = dvmGetFieldObject(thread->threadObj,
   1987                     gDvm.offJavaLangThread_group);
   1988         if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
   1989             count++;
   1990     }
   1991 
   1992     *pThreadCount = count;
   1993 
   1994     if (count == 0) {
   1995         *ppThreadIds = NULL;
   1996     } else {
   1997         ObjectId* ptr;
   1998         ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
   1999 
   2000         for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
   2001             Object* group;
   2002 
   2003             /* Skip over the JDWP support thread.  Some debuggers
   2004              * get bent out of shape when they can't suspend and
   2005              * query all threads, so it's easier if we just don't
   2006              * tell them about us.
   2007              */
   2008             if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
   2009                 continue;
   2010 
   2011             /* This thread is currently being created, and isn't ready
   2012              * to be seen by the debugger yet.
   2013              */
   2014             if (thread->threadObj == NULL)
   2015                 continue;
   2016 
   2017             group = dvmGetFieldObject(thread->threadObj,
   2018                         gDvm.offJavaLangThread_group);
   2019             if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
   2020             {
   2021                 *ptr++ = objectToObjectId(thread->threadObj);
   2022                 count--;
   2023             }
   2024         }
   2025 
   2026         assert(count == 0);
   2027     }
   2028 
   2029     dvmUnlockThreadList();
   2030 }
   2031 
   2032 /*
   2033  * Get all threads.
   2034  *
   2035  * The caller must free "*ppThreadIds".
   2036  */
   2037 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
   2038 {
   2039     dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
   2040 }
   2041 
   2042 
   2043 /*
   2044  * Count up the #of frames on the thread's stack.
   2045  *
   2046  * Returns -1 on failure.
   2047  */
   2048 int dvmDbgGetThreadFrameCount(ObjectId threadId)
   2049 {
   2050     Object* threadObj;
   2051     Thread* thread;
   2052     int count = -1;
   2053 
   2054     threadObj = objectIdToObject(threadId);
   2055 
   2056     dvmLockThreadList(NULL);
   2057     thread = threadObjToThread(threadObj);
   2058     if (thread != NULL) {
   2059         count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
   2060     }
   2061     dvmUnlockThreadList();
   2062 
   2063     return count;
   2064 }
   2065 
   2066 /*
   2067  * Get info for frame N from the specified thread's stack.
   2068  */
   2069 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
   2070     JdwpLocation* pLoc)
   2071 {
   2072     Object* threadObj;
   2073     Thread* thread;
   2074     void* framePtr;
   2075     int count;
   2076 
   2077     threadObj = objectIdToObject(threadId);
   2078 
   2079     dvmLockThreadList(NULL);
   2080 
   2081     thread = threadObjToThread(threadObj);
   2082     if (thread == NULL)
   2083         goto bail;
   2084 
   2085     framePtr = thread->interpSave.curFrame;
   2086     count = 0;
   2087     while (framePtr != NULL) {
   2088         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   2089         const Method* method = saveArea->method;
   2090 
   2091         if (!dvmIsBreakFrame((u4*)framePtr)) {
   2092             if (count == num) {
   2093                 *pFrameId = frameToFrameId(framePtr);
   2094                 if (dvmIsInterfaceClass(method->clazz))
   2095                     pLoc->typeTag = TT_INTERFACE;
   2096                 else
   2097                     pLoc->typeTag = TT_CLASS;
   2098                 pLoc->classId = classObjectToRefTypeId(method->clazz);
   2099                 pLoc->methodId = methodToMethodId(method);
   2100                 if (dvmIsNativeMethod(method))
   2101                     pLoc->idx = (u8)-1;
   2102                 else
   2103                     pLoc->idx = saveArea->xtra.currentPc - method->insns;
   2104                 dvmUnlockThreadList();
   2105                 return true;
   2106             }
   2107 
   2108             count++;
   2109         }
   2110 
   2111         framePtr = saveArea->prevFrame;
   2112     }
   2113 
   2114 bail:
   2115     dvmUnlockThreadList();
   2116     return false;
   2117 }
   2118 
   2119 /*
   2120  * Get the ThreadId for the current thread.
   2121  */
   2122 ObjectId dvmDbgGetThreadSelfId()
   2123 {
   2124     Thread* self = dvmThreadSelf();
   2125     return objectToObjectId(self->threadObj);
   2126 }
   2127 
   2128 /*
   2129  * Suspend the VM.
   2130  */
   2131 void dvmDbgSuspendVM(bool isEvent)
   2132 {
   2133     dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
   2134 }
   2135 
   2136 /*
   2137  * Resume the VM.
   2138  */
   2139 void dvmDbgResumeVM()
   2140 {
   2141     dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
   2142 }
   2143 
   2144 /*
   2145  * Suspend one thread (not ourselves).
   2146  */
   2147 void dvmDbgSuspendThread(ObjectId threadId)
   2148 {
   2149     Object* threadObj = objectIdToObject(threadId);
   2150     Thread* thread;
   2151 
   2152     dvmLockThreadList(NULL);
   2153 
   2154     thread = threadObjToThread(threadObj);
   2155     if (thread == NULL) {
   2156         /* can happen if our ThreadDeath notify crosses in the mail */
   2157         ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
   2158     } else {
   2159         dvmSuspendThread(thread);
   2160     }
   2161 
   2162     dvmUnlockThreadList();
   2163 }
   2164 
   2165 /*
   2166  * Resume one thread (not ourselves).
   2167  */
   2168 void dvmDbgResumeThread(ObjectId threadId)
   2169 {
   2170     Object* threadObj = objectIdToObject(threadId);
   2171     Thread* thread;
   2172 
   2173     dvmLockThreadList(NULL);
   2174 
   2175     thread = threadObjToThread(threadObj);
   2176     if (thread == NULL) {
   2177         ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
   2178     } else {
   2179         dvmResumeThread(thread);
   2180     }
   2181 
   2182     dvmUnlockThreadList();
   2183 }
   2184 
   2185 /*
   2186  * Suspend ourselves after sending an event to the debugger.
   2187  */
   2188 void dvmDbgSuspendSelf()
   2189 {
   2190     dvmSuspendSelf(true);
   2191 }
   2192 
   2193 /*
   2194  * Get the "this" object for the specified frame.
   2195  */
   2196 static Object* getThisObject(const u4* framePtr)
   2197 {
   2198     const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
   2199     const Method* method = saveArea->method;
   2200     int argOffset = method->registersSize - method->insSize;
   2201     Object* thisObj;
   2202 
   2203     if (method == NULL) {
   2204         /* this is a "break" frame? */
   2205         assert(false);
   2206         return NULL;
   2207     }
   2208 
   2209     LOGVV("  Pulling this object for frame at %p", framePtr);
   2210     LOGVV("    Method='%s' native=%d static=%d this=%p",
   2211         method->name, dvmIsNativeMethod(method),
   2212         dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
   2213 
   2214     /*
   2215      * No "this" pointer for statics.  No args on the interp stack for
   2216      * native methods invoked directly from the VM.
   2217      */
   2218     if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
   2219         thisObj = NULL;
   2220     else
   2221         thisObj = (Object*) framePtr[argOffset];
   2222 
   2223     if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
   2224         ALOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
   2225             framePtr, method->clazz->descriptor, method->name);
   2226         thisObj = NULL;
   2227     }
   2228 
   2229     return thisObj;
   2230 }
   2231 
   2232 /*
   2233  * Return the "this" object for the specified frame.  The thread must be
   2234  * suspended.
   2235  */
   2236 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
   2237 {
   2238     const u4* framePtr = frameIdToFrame(frameId);
   2239     Object* thisObj;
   2240 
   2241     UNUSED_PARAMETER(threadId);
   2242 
   2243     thisObj = getThisObject(framePtr);
   2244 
   2245     *pThisId = objectToObjectId(thisObj);
   2246     return true;
   2247 }
   2248 
   2249 /*
   2250  * Copy the value of a method argument or local variable into the
   2251  * specified buffer.  The value will be preceeded with the tag.
   2252  *
   2253  * The debugger includes the tags in the request.  Object tags may
   2254  * be updated with a more refined type.
   2255  */
   2256 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
   2257     u1 tag, u1* buf, int expectedLen)
   2258 {
   2259     const u4* framePtr = frameIdToFrame(frameId);
   2260     Object* objVal;
   2261     u4 intVal;
   2262     u8 longVal;
   2263 
   2264     UNUSED_PARAMETER(threadId);
   2265 
   2266     slot = untweakSlot(slot, framePtr);     // Eclipse workaround
   2267 
   2268     switch (tag) {
   2269     case JT_BOOLEAN:
   2270         assert(expectedLen == 1);
   2271         intVal = framePtr[slot];
   2272         set1(buf+1, intVal != 0);
   2273         break;
   2274     case JT_BYTE:
   2275         assert(expectedLen == 1);
   2276         intVal = framePtr[slot];
   2277         set1(buf+1, intVal);
   2278         break;
   2279     case JT_SHORT:
   2280     case JT_CHAR:
   2281         assert(expectedLen == 2);
   2282         intVal = framePtr[slot];
   2283         set2BE(buf+1, intVal);
   2284         break;
   2285     case JT_INT:
   2286     case JT_FLOAT:
   2287         assert(expectedLen == 4);
   2288         intVal = framePtr[slot];
   2289         set4BE(buf+1, intVal);
   2290         break;
   2291     case JT_ARRAY:
   2292         assert(expectedLen == sizeof(ObjectId));
   2293         {
   2294             /* convert to "ObjectId" */
   2295             objVal = (Object*)framePtr[slot];
   2296             if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
   2297                 ALOGW("JDWP: slot %d expected to hold array, %p invalid",
   2298                     slot, objVal);
   2299                 dvmAbort();         // DEBUG: make it obvious
   2300                 objVal = NULL;
   2301                 tag = JT_OBJECT;    // JT_ARRAY not expected for NULL ref
   2302             }
   2303             dvmSetObjectId(buf+1, objectToObjectId(objVal));
   2304         }
   2305         break;
   2306     case JT_OBJECT:
   2307         assert(expectedLen == sizeof(ObjectId));
   2308         {
   2309             /* convert to "ObjectId" */
   2310             objVal = (Object*)framePtr[slot];
   2311 
   2312             if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
   2313                 ALOGW("JDWP: slot %d expected to hold object, %p invalid",
   2314                     slot, objVal);
   2315                 dvmAbort();         // DEBUG: make it obvious
   2316                 objVal = NULL;
   2317             }
   2318             tag = tagFromObject(objVal);
   2319             dvmSetObjectId(buf+1, objectToObjectId(objVal));
   2320         }
   2321         break;
   2322     case JT_DOUBLE:
   2323     case JT_LONG:
   2324         assert(expectedLen == 8);
   2325         memcpy(&longVal, &framePtr[slot], 8);
   2326         set8BE(buf+1, longVal);
   2327         break;
   2328     default:
   2329         ALOGE("ERROR: unhandled tag '%c'", tag);
   2330         assert(false);
   2331         break;
   2332     }
   2333 
   2334     /* prepend tag, which may have been updated */
   2335     set1(buf, tag);
   2336 }
   2337 
   2338 /*
   2339  * Copy a new value into an argument or local variable.
   2340  */
   2341 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
   2342     u8 value, int width)
   2343 {
   2344     u4* framePtr = frameIdToFrame(frameId);
   2345 
   2346     UNUSED_PARAMETER(threadId);
   2347 
   2348     slot = untweakSlot(slot, framePtr);     // Eclipse workaround
   2349 
   2350     switch (tag) {
   2351     case JT_BOOLEAN:
   2352         assert(width == 1);
   2353         framePtr[slot] = (u4)value;
   2354         break;
   2355     case JT_BYTE:
   2356         assert(width == 1);
   2357         framePtr[slot] = (u4)value;
   2358         break;
   2359     case JT_SHORT:
   2360     case JT_CHAR:
   2361         assert(width == 2);
   2362         framePtr[slot] = (u4)value;
   2363         break;
   2364     case JT_INT:
   2365     case JT_FLOAT:
   2366         assert(width == 4);
   2367         framePtr[slot] = (u4)value;
   2368         break;
   2369     case JT_STRING:
   2370         /* The debugger calls VirtualMachine.CreateString to create a new
   2371          * string, then uses this to set the object reference, when you
   2372          * edit a String object */
   2373     case JT_ARRAY:
   2374     case JT_OBJECT:
   2375         assert(width == sizeof(ObjectId));
   2376         framePtr[slot] = (u4) objectIdToObject(value);
   2377         break;
   2378     case JT_DOUBLE:
   2379     case JT_LONG:
   2380         assert(width == 8);
   2381         memcpy(&framePtr[slot], &value, 8);
   2382         break;
   2383     case JT_VOID:
   2384     case JT_CLASS_OBJECT:
   2385     case JT_THREAD:
   2386     case JT_THREAD_GROUP:
   2387     case JT_CLASS_LOADER:
   2388         /* not expecting these from debugger; fall through to failure */
   2389     default:
   2390         ALOGE("ERROR: unhandled tag '%c'", tag);
   2391         assert(false);
   2392         break;
   2393     }
   2394 }
   2395 
   2396 
   2397 /*
   2398  * ===========================================================================
   2399  *      Debugger notification
   2400  * ===========================================================================
   2401  */
   2402 
   2403 /*
   2404  * Tell JDWP that a breakpoint address has been reached.
   2405  *
   2406  * "pcOffset" will be -1 for native methods.
   2407  * "thisPtr" will be NULL for static methods.
   2408  */
   2409 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
   2410     Object* thisPtr, int eventFlags)
   2411 {
   2412     JdwpLocation loc;
   2413 
   2414     if (dvmIsInterfaceClass(method->clazz))
   2415         loc.typeTag = TT_INTERFACE;
   2416     else
   2417         loc.typeTag = TT_CLASS;
   2418     loc.classId = classObjectToRefTypeId(method->clazz);
   2419     loc.methodId = methodToMethodId(method);
   2420     loc.idx = pcOffset;
   2421 
   2422     /*
   2423      * Note we use "NoReg" so we don't keep track of references that are
   2424      * never actually sent to the debugger.  The "thisPtr" is only used to
   2425      * compare against registered events.
   2426      */
   2427 
   2428     if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
   2429             objectToObjectIdNoReg(thisPtr), eventFlags))
   2430     {
   2431         classObjectToRefTypeId(method->clazz);
   2432         objectToObjectId(thisPtr);
   2433     }
   2434 }
   2435 
   2436 /*
   2437  * Tell JDWP that an exception has occurred.
   2438  */
   2439 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
   2440     int catchRelPc, Object* exception)
   2441 {
   2442     JdwpLocation throwLoc, catchLoc;
   2443     const Method* throwMeth;
   2444     const Method* catchMeth;
   2445 
   2446     throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
   2447     if (dvmIsInterfaceClass(throwMeth->clazz))
   2448         throwLoc.typeTag = TT_INTERFACE;
   2449     else
   2450         throwLoc.typeTag = TT_CLASS;
   2451     throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
   2452     throwLoc.methodId = methodToMethodId(throwMeth);
   2453     throwLoc.idx = throwRelPc;
   2454 
   2455     if (catchRelPc < 0) {
   2456         memset(&catchLoc, 0, sizeof(catchLoc));
   2457     } else {
   2458         catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
   2459         if (dvmIsInterfaceClass(catchMeth->clazz))
   2460             catchLoc.typeTag = TT_INTERFACE;
   2461         else
   2462             catchLoc.typeTag = TT_CLASS;
   2463         catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
   2464         catchLoc.methodId = methodToMethodId(catchMeth);
   2465         catchLoc.idx = catchRelPc;
   2466     }
   2467 
   2468     /* need this for InstanceOnly filters */
   2469     Object* thisObj = getThisObject((u4*)throwFp);
   2470 
   2471     /*
   2472      * Hand the event to the JDWP exception handler.  Note we're using the
   2473      * "NoReg" objectID on the exception, which is not strictly correct --
   2474      * the exception object WILL be passed up to the debugger if the
   2475      * debugger is interested in the event.  We do this because the current
   2476      * implementation of the debugger object registry never throws anything
   2477      * away, and some people were experiencing a fatal build up of exception
   2478      * objects when dealing with certain libraries.
   2479      */
   2480     dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
   2481         objectToObjectIdNoReg(exception),
   2482         classObjectToRefTypeId(exception->clazz), &catchLoc,
   2483         objectToObjectId(thisObj));
   2484 }
   2485 
   2486 /*
   2487  * Tell JDWP and/or DDMS that a thread has started.
   2488  */
   2489 void dvmDbgPostThreadStart(Thread* thread)
   2490 {
   2491     if (gDvm.debuggerActive) {
   2492         dvmJdwpPostThreadChange(gDvm.jdwpState,
   2493             objectToObjectId(thread->threadObj), true);
   2494     }
   2495     if (gDvm.ddmThreadNotification)
   2496         dvmDdmSendThreadNotification(thread, true);
   2497 }
   2498 
   2499 /*
   2500  * Tell JDWP and/or DDMS that a thread has gone away.
   2501  */
   2502 void dvmDbgPostThreadDeath(Thread* thread)
   2503 {
   2504     if (gDvm.debuggerActive) {
   2505         dvmJdwpPostThreadChange(gDvm.jdwpState,
   2506             objectToObjectId(thread->threadObj), false);
   2507     }
   2508     if (gDvm.ddmThreadNotification)
   2509         dvmDdmSendThreadNotification(thread, false);
   2510 }
   2511 
   2512 /*
   2513  * Tell JDWP that a new class has been prepared.
   2514  */
   2515 void dvmDbgPostClassPrepare(ClassObject* clazz)
   2516 {
   2517     const char* signature;
   2518     int tag;
   2519 
   2520     if (dvmIsInterfaceClass(clazz))
   2521         tag = TT_INTERFACE;
   2522     else
   2523         tag = TT_CLASS;
   2524 
   2525     // TODO - we currently always send both "verified" and "prepared" since
   2526     // debuggers seem to like that.  There might be some advantage to honesty,
   2527     // since the class may not yet be verified.
   2528     signature = jniSignature(clazz);
   2529     dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
   2530         signature, CS_VERIFIED | CS_PREPARED);
   2531 }
   2532 
   2533 /*
   2534  * The JDWP event mechanism has registered an event with a LocationOnly
   2535  * mod.  Tell the interpreter to call us if we hit the specified
   2536  * address.
   2537  */
   2538 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
   2539 {
   2540     Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
   2541     assert(!dvmIsNativeMethod(method));
   2542     dvmAddBreakAddr(method, pLoc->idx);
   2543     return true;        /* assume success */
   2544 }
   2545 
   2546 /*
   2547  * An event with a LocationOnly mod has been removed.
   2548  */
   2549 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
   2550 {
   2551     Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
   2552     assert(!dvmIsNativeMethod(method));
   2553     dvmClearBreakAddr(method, pLoc->idx);
   2554 }
   2555 
   2556 /*
   2557  * The JDWP event mechanism has registered a single-step event.  Tell
   2558  * the interpreter about it.
   2559  */
   2560 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
   2561     JdwpStepDepth depth)
   2562 {
   2563     Object* threadObj;
   2564     Thread* thread;
   2565     bool result = false;
   2566 
   2567     threadObj = objectIdToObject(threadId);
   2568     assert(threadObj != NULL);
   2569 
   2570     /*
   2571      * Get a pointer to the Thread struct for this ID.  The pointer will
   2572      * be used strictly for comparisons against the current thread pointer
   2573      * after the setup is complete, so we can safely release the lock.
   2574      */
   2575     dvmLockThreadList(NULL);
   2576     thread = threadObjToThread(threadObj);
   2577 
   2578     if (thread == NULL) {
   2579         ALOGE("Thread for single-step not found");
   2580         goto bail;
   2581     }
   2582     if (!dvmIsSuspended(thread)) {
   2583         ALOGE("Thread for single-step not suspended");
   2584         assert(!"non-susp step");      // I want to know if this can happen
   2585         goto bail;
   2586     }
   2587 
   2588     assert(dvmIsSuspended(thread));
   2589     if (!dvmAddSingleStep(thread, size, depth))
   2590         goto bail;
   2591 
   2592     result = true;
   2593 
   2594 bail:
   2595     dvmUnlockThreadList();
   2596     return result;
   2597 }
   2598 
   2599 /*
   2600  * A single-step event has been removed.
   2601  */
   2602 void dvmDbgUnconfigureStep(ObjectId threadId)
   2603 {
   2604     UNUSED_PARAMETER(threadId);
   2605 
   2606     /* right now it's global, so don't need to find Thread */
   2607     dvmClearSingleStep(NULL);
   2608 }
   2609 
   2610 /*
   2611  * Invoke a method in a thread that has been stopped on a breakpoint or
   2612  * other debugger event.  (This function is called from the JDWP thread.)
   2613  *
   2614  * Note that access control is not enforced, per spec.
   2615  */
   2616 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
   2617     RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
   2618     u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
   2619 {
   2620     Object* threadObj = objectIdToObject(threadId);
   2621 
   2622     dvmLockThreadList(NULL);
   2623 
   2624     Thread* targetThread = threadObjToThread(threadObj);
   2625     if (targetThread == NULL) {
   2626         dvmUnlockThreadList();
   2627         return ERR_INVALID_THREAD;       /* thread does not exist */
   2628     }
   2629     if (!targetThread->invokeReq.ready) {
   2630         dvmUnlockThreadList();
   2631         return ERR_INVALID_THREAD;       /* thread not stopped by event */
   2632     }
   2633 
   2634     /*
   2635      * We currently have a bug where we don't successfully resume the
   2636      * target thread if the suspend count is too deep.  We're expected to
   2637      * require one "resume" for each "suspend", but when asked to execute
   2638      * a method we have to resume fully and then re-suspend it back to the
   2639      * same level.  (The easiest way to cause this is to type "suspend"
   2640      * multiple times in jdb.)
   2641      *
   2642      * It's unclear what this means when the event specifies "resume all"
   2643      * and some threads are suspended more deeply than others.  This is
   2644      * a rare problem, so for now we just prevent it from hanging forever
   2645      * by rejecting the method invocation request.  Without this, we will
   2646      * be stuck waiting on a suspended thread.
   2647      */
   2648     if (targetThread->suspendCount > 1) {
   2649         ALOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
   2650              "for method exec",
   2651             dvmThreadSelf()->threadId, targetThread->threadId,
   2652             targetThread->suspendCount);
   2653         dvmUnlockThreadList();
   2654         return ERR_THREAD_SUSPENDED;     /* probably not expected here */
   2655     }
   2656 
   2657     /*
   2658      * TODO: ought to screen the various IDs, and verify that the argument
   2659      * list is valid.
   2660      */
   2661 
   2662     targetThread->invokeReq.obj = objectIdToObject(objectId);
   2663     targetThread->invokeReq.thread = threadObj;
   2664     targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
   2665     targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
   2666     targetThread->invokeReq.numArgs = numArgs;
   2667     targetThread->invokeReq.argArray = argArray;
   2668     targetThread->invokeReq.options = options;
   2669     targetThread->invokeReq.invokeNeeded = true;
   2670 
   2671     /*
   2672      * This is a bit risky -- if the thread goes away we're sitting high
   2673      * and dry -- but we must release this before the dvmResumeAllThreads
   2674      * call, and it's unwise to hold it during dvmWaitForSuspend.
   2675      */
   2676     dvmUnlockThreadList();
   2677 
   2678     /*
   2679      * We change our (JDWP thread) status, which should be THREAD_RUNNING,
   2680      * so the VM can suspend for a GC if the invoke request causes us to
   2681      * run out of memory.  It's also a good idea to change it before locking
   2682      * the invokeReq mutex, although that should never be held for long.
   2683      */
   2684     Thread* self = dvmThreadSelf();
   2685     ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
   2686 
   2687     ALOGV("    Transferring control to event thread");
   2688     dvmLockMutex(&targetThread->invokeReq.lock);
   2689 
   2690     if ((options & INVOKE_SINGLE_THREADED) == 0) {
   2691         ALOGV("      Resuming all threads");
   2692         dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
   2693     } else {
   2694         ALOGV("      Resuming event thread only");
   2695         dvmResumeThread(targetThread);
   2696     }
   2697 
   2698     /*
   2699      * Wait for the request to finish executing.
   2700      */
   2701     while (targetThread->invokeReq.invokeNeeded) {
   2702         pthread_cond_wait(&targetThread->invokeReq.cv,
   2703                           &targetThread->invokeReq.lock);
   2704     }
   2705     dvmUnlockMutex(&targetThread->invokeReq.lock);
   2706     ALOGV("    Control has returned from event thread");
   2707 
   2708     /* wait for thread to re-suspend itself */
   2709     dvmWaitForSuspend(targetThread);
   2710 
   2711     /*
   2712      * Done waiting, switch back to RUNNING.
   2713      */
   2714     dvmChangeStatus(self, oldStatus);
   2715 
   2716     /*
   2717      * Suspend the threads.  We waited for the target thread to suspend
   2718      * itself, so all we need to do is suspend the others.
   2719      *
   2720      * The suspendAllThreads() call will double-suspend the event thread,
   2721      * so we want to resume the target thread once to keep the books straight.
   2722      */
   2723     if ((options & INVOKE_SINGLE_THREADED) == 0) {
   2724         ALOGV("      Suspending all threads");
   2725         dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
   2726         ALOGV("      Resuming event thread to balance the count");
   2727         dvmResumeThread(targetThread);
   2728     }
   2729 
   2730     /*
   2731      * Set up the result.
   2732      */
   2733     *pResultTag = targetThread->invokeReq.resultTag;
   2734     if (isTagPrimitive(targetThread->invokeReq.resultTag))
   2735         *pResultValue = targetThread->invokeReq.resultValue.j;
   2736     else {
   2737         Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
   2738         *pResultValue = objectToObjectId(tmpObj);
   2739     }
   2740     *pExceptObj = targetThread->invokeReq.exceptObj;
   2741     return targetThread->invokeReq.err;
   2742 }
   2743 
   2744 /*
   2745  * Return a basic tag value for the return type.
   2746  */
   2747 static u1 getReturnTypeBasicTag(const Method* method)
   2748 {
   2749     const char* descriptor = dexProtoGetReturnType(&method->prototype);
   2750     return basicTagFromDescriptor(descriptor);
   2751 }
   2752 
   2753 /*
   2754  * Execute the method described by "*pReq".
   2755  *
   2756  * We're currently in VMWAIT, because we're stopped on a breakpoint.  We
   2757  * want to switch to RUNNING while we execute.
   2758  */
   2759 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
   2760 {
   2761     Thread* self = dvmThreadSelf();
   2762     const Method* meth;
   2763     Object* oldExcept;
   2764     ThreadStatus oldStatus;
   2765 
   2766     /*
   2767      * We can be called while an exception is pending in the VM.  We need
   2768      * to preserve that across the method invocation.
   2769      */
   2770     oldExcept = dvmGetException(self);
   2771     if (oldExcept != NULL) {
   2772         dvmAddTrackedAlloc(oldExcept, self);
   2773         dvmClearException(self);
   2774     }
   2775 
   2776     oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
   2777 
   2778     /*
   2779      * Translate the method through the vtable, unless we're calling a
   2780      * direct method or the debugger wants to suppress it.
   2781      */
   2782     if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
   2783         dvmIsDirectMethod(pReq->method))
   2784     {
   2785         meth = pReq->method;
   2786     } else {
   2787         meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
   2788     }
   2789     assert(meth != NULL);
   2790 
   2791     assert(sizeof(jvalue) == sizeof(u8));
   2792 
   2793     IF_ALOGV() {
   2794         char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
   2795         ALOGV("JDWP invoking method %p/%p %s.%s:%s",
   2796             pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
   2797         free(desc);
   2798     }
   2799 
   2800     dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
   2801         (jvalue*)pReq->argArray);
   2802     pReq->exceptObj = objectToObjectId(dvmGetException(self));
   2803     pReq->resultTag = getReturnTypeBasicTag(meth);
   2804     if (pReq->exceptObj != 0) {
   2805         Object* exc = dvmGetException(self);
   2806         ALOGD("  JDWP invocation returning with exceptObj=%p (%s)",
   2807             exc, exc->clazz->descriptor);
   2808         //dvmLogExceptionStackTrace();
   2809         dvmClearException(self);
   2810         /*
   2811          * Nothing should try to use this, but it looks like something is.
   2812          * Make it null to be safe.
   2813          */
   2814         pReq->resultValue.j = 0; /*0xadadadad;*/
   2815     } else if (pReq->resultTag == JT_OBJECT) {
   2816         /* if no exception thrown, examine object result more closely */
   2817         u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
   2818         if (newTag != pReq->resultTag) {
   2819             LOGVV("  JDWP promoted result from %d to %d",
   2820                 pReq->resultTag, newTag);
   2821             pReq->resultTag = newTag;
   2822         }
   2823 
   2824         /*
   2825          * Register the object.  We don't actually need an ObjectId yet,
   2826          * but we do need to be sure that the GC won't move or discard the
   2827          * object when we switch out of RUNNING.  The ObjectId conversion
   2828          * will add the object to the "do not touch" list.
   2829          *
   2830          * We can't use the "tracked allocation" mechanism here because
   2831          * the object is going to be handed off to a different thread.
   2832          */
   2833         objectToObjectId((Object*)pReq->resultValue.l);
   2834     }
   2835 
   2836     if (oldExcept != NULL) {
   2837         dvmSetException(self, oldExcept);
   2838         dvmReleaseTrackedAlloc(oldExcept, self);
   2839     }
   2840     dvmChangeStatus(self, oldStatus);
   2841 }
   2842 
   2843 // for dvmAddressSetForLine
   2844 struct AddressSetContext {
   2845     bool lastAddressValid;
   2846     u4 lastAddress;
   2847     u4 lineNum;
   2848     AddressSet *pSet;
   2849 };
   2850 
   2851 // for dvmAddressSetForLine
   2852 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
   2853 {
   2854     AddressSetContext *pContext = (AddressSetContext *)cnxt;
   2855 
   2856     if (lineNum == pContext->lineNum) {
   2857         if (!pContext->lastAddressValid) {
   2858             // Everything from this address until the next line change is ours
   2859             pContext->lastAddress = address;
   2860             pContext->lastAddressValid = true;
   2861         }
   2862         // else, If we're already in a valid range for this lineNum,
   2863         // just keep going (shouldn't really happen)
   2864     } else if (pContext->lastAddressValid) { // and the line number is new
   2865         u4 i;
   2866         // Add everything from the last entry up until here to the set
   2867         for (i = pContext->lastAddress; i < address; i++) {
   2868             dvmAddressSetSet(pContext->pSet, i);
   2869         }
   2870 
   2871         pContext->lastAddressValid = false;
   2872     }
   2873 
   2874     // there may be multiple entries for a line
   2875     return 0;
   2876 }
   2877 /*
   2878  * Build up a set of bytecode addresses associated with a line number
   2879  */
   2880 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
   2881 {
   2882     AddressSet *result;
   2883     const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
   2884     u4 insnsSize = dvmGetMethodInsnsSize(method);
   2885     AddressSetContext context;
   2886 
   2887     result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
   2888     result->setSize = insnsSize;
   2889 
   2890     memset(&context, 0, sizeof(context));
   2891     context.pSet = result;
   2892     context.lineNum = line;
   2893     context.lastAddressValid = false;
   2894 
   2895     dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
   2896         method->clazz->descriptor,
   2897         method->prototype.protoIdx,
   2898         method->accessFlags,
   2899         addressSetCb, NULL, &context);
   2900 
   2901     // If the line number was the last in the position table...
   2902     if (context.lastAddressValid) {
   2903         u4 i;
   2904         for (i = context.lastAddress; i < insnsSize; i++) {
   2905             dvmAddressSetSet(result, i);
   2906         }
   2907     }
   2908 
   2909     return result;
   2910 }
   2911 
   2912 
   2913 /*
   2914  * ===========================================================================
   2915  *      Dalvik Debug Monitor support
   2916  * ===========================================================================
   2917  */
   2918 
   2919 /*
   2920  * We have received a DDM packet over JDWP.  Hand it off to the VM.
   2921  */
   2922 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
   2923     int* pReplyLen)
   2924 {
   2925     return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
   2926 }
   2927 
   2928 /*
   2929  * First DDM packet has arrived over JDWP.  Notify the press.
   2930  */
   2931 void dvmDbgDdmConnected()
   2932 {
   2933     dvmDdmConnected();
   2934 }
   2935 
   2936 /*
   2937  * JDWP connection has dropped.
   2938  */
   2939 void dvmDbgDdmDisconnected()
   2940 {
   2941     dvmDdmDisconnected();
   2942 }
   2943 
   2944 /*
   2945  * Send up a JDWP event packet with a DDM chunk in it.
   2946  */
   2947 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
   2948 {
   2949     assert(buf != NULL);
   2950     struct iovec vec[1] = { {(void*)buf, len} };
   2951     dvmDbgDdmSendChunkV(type, vec, 1);
   2952 }
   2953 
   2954 /*
   2955  * Send up a JDWP event packet with a DDM chunk in it.  The chunk is
   2956  * concatenated from multiple source buffers.
   2957  */
   2958 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
   2959 {
   2960     if (gDvm.jdwpState == NULL) {
   2961         ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
   2962             type);
   2963         return;
   2964     }
   2965 
   2966     dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
   2967 }
   2968