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