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