OSDN Git Service

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