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