OSDN Git Service

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