OSDN Git Service

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