OSDN Git Service

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