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