OSDN Git Service

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