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