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