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