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