2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.
22 * Collecting all debugger-related pieces here will also allow us to #ifdef
23 * the JDWP code out of release builds.
28 Notes on garbage collection and object registration
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.
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.)
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.
52 There are various approaches we can take to achieve correct behavior:
54 (1) Disable garbage collection entirely while the debugger is attached.
55 This is very easy, but doesn't allow extended debugging sessions on
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
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".
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.
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.
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
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
90 #define THREAD_GROUP_ALL ((ObjectId) 0x12345) // magic, internal-only value
92 #define kSlot0Sub 1000 // Eclipse workaround
95 * System init. We don't allocate the registry until first use.
96 * Make sure we do this before initializing JDWP.
98 bool dvmDebuggerStartup(void)
100 if (!dvmBreakpointStartup())
103 gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
104 return (gDvm.dbgRegistry != NULL);
108 * Free registry storage.
110 void dvmDebuggerShutdown(void)
112 dvmHashTableFree(gDvm.dbgRegistry);
113 gDvm.dbgRegistry = NULL;
114 dvmBreakpointShutdown();
119 * Pass these through to the VM functions. Allows extended checking
120 * (e.g. "errorcheck" mutexes). If nothing else we can assert() success.
122 void dvmDbgInitMutex(pthread_mutex_t* pMutex)
124 dvmInitMutex(pMutex);
126 void dvmDbgLockMutex(pthread_mutex_t* pMutex)
128 dvmLockMutex(pMutex);
130 void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
132 dvmUnlockMutex(pMutex);
134 void dvmDbgInitCond(pthread_cond_t* pCond)
136 pthread_cond_init(pCond, NULL);
138 void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
140 int cc = pthread_cond_wait(pCond, pMutex);
143 void dvmDbgCondSignal(pthread_cond_t* pCond)
145 int cc = pthread_cond_signal(pCond);
148 void dvmDbgCondBroadcast(pthread_cond_t* pCond)
150 int cc = pthread_cond_broadcast(pCond);
155 /* keep track of type, in case we need to distinguish them someday */
156 typedef enum RegistryType {
157 kObjectId = 0xc1, kRefTypeId
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.
165 static inline u4 registryHash(u4 val)
171 * (This is a dvmHashTableLookup() callback.)
173 static int registryCompare(const void* obj1, const void* obj2)
175 return (int) obj1 - (int) obj2;
180 * Determine if an id is already in the list.
182 * If the list doesn't yet exist, this creates it.
184 * Lock the registry before calling here.
186 static bool lookupId(ObjectId id)
190 found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
191 (void*)(u4) id, registryCompare, false);
194 assert(found == (void*)(u4) id);
199 * Register an object, if it hasn't already been.
201 * This is used for both ObjectId and RefTypeId. In theory we don't have
202 * to register RefTypeIds unless we're worried about classes unloading.
204 * Null references must be represented as zero, or the debugger will get
207 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
214 assert((u4) obj != 0xcccccccc);
215 assert((u4) obj > 0x100);
217 id = (ObjectId)(u4)obj | ((u8) type) << 32;
221 dvmHashTableLock(gDvm.dbgRegistry);
222 if (!gDvm.debuggerConnected) {
223 /* debugger has detached while we were doing stuff? */
224 LOGI("ignoring registerObject request in thread=%d\n",
225 dvmThreadSelf()->threadId);
230 (void) dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
231 (void*)(u4) id, registryCompare, true);
234 dvmHashTableUnlock(gDvm.dbgRegistry);
239 * (This is a HashForeachFunc callback.)
241 static int markRef(void* data, void* arg)
243 UNUSED_PARAMETER(arg);
245 //LOGI("dbg mark %p\n", data);
246 dvmMarkObjectNonNull(data);
250 /* Mark all of the registered debugger references so the
251 * GC doesn't collect them.
253 void dvmGcMarkDebuggerRefs()
255 /* dvmDebuggerStartup() may not have been called before the first GC.
257 if (gDvm.dbgRegistry != NULL) {
258 dvmHashTableLock(gDvm.dbgRegistry);
259 dvmHashForeach(gDvm.dbgRegistry, markRef, NULL);
260 dvmHashTableUnlock(gDvm.dbgRegistry);
265 * Verify that an object has been registered. If it hasn't, the debugger
266 * is asking for something we didn't send it, which means something
267 * somewhere is broken.
269 * If speed is an issue we can encode the registry index in the high
270 * four bytes. We could also just hard-wire this to "true".
272 * Note this actually takes both ObjectId and RefTypeId.
274 static bool objectIsRegistered(ObjectId id, RegistryType type)
276 UNUSED_PARAMETER(type);
278 if (id == 0) // null reference?
281 dvmHashTableLock(gDvm.dbgRegistry);
282 bool result = lookupId(id);
283 dvmHashTableUnlock(gDvm.dbgRegistry);
288 * Convert to/from a RefTypeId.
290 * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
292 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
294 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
296 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
298 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
300 static ClassObject* refTypeIdToClassObject(RefTypeId id)
302 assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
303 return (ClassObject*)(u4) id;
307 * Convert to/from an ObjectId.
309 static ObjectId objectToObjectId(const Object* obj)
311 return registerObject(obj, kObjectId, true);
313 static ObjectId objectToObjectIdNoReg(const Object* obj)
315 return registerObject(obj, kObjectId, false);
317 static Object* objectIdToObject(ObjectId id)
319 assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
320 return (Object*)(u4) id;
324 * Register an object ID that might not have been registered previously.
326 * Normally this wouldn't happen -- the conversion to an ObjectId would
327 * have added the object to the registry -- but in some cases (e.g.
328 * throwing exceptions) we really want to do the registration late.
330 void dvmDbgRegisterObjectId(ObjectId id)
332 Object* obj = (Object*)(u4) id;
333 LOGV("+++ registering %p (%s)\n", obj, obj->clazz->descriptor);
334 registerObject(obj, kObjectId, true);
338 * Convert to/from a MethodId.
340 * These IDs are only guaranteed unique within a class, so they could be
341 * an enumeration index. For now we just use the Method*.
343 static MethodId methodToMethodId(const Method* meth)
345 return (MethodId)(u4) meth;
347 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
349 // TODO? verify "id" is actually a method in "refTypeId"
350 return (Method*)(u4) id;
354 * Convert to/from a FieldId.
356 * These IDs are only guaranteed unique within a class, so they could be
357 * an enumeration index. For now we just use the Field*.
359 static FieldId fieldToFieldId(const Field* field)
361 return (FieldId)(u4) field;
363 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
365 // TODO? verify "id" is actually a field in "refTypeId"
366 return (Field*)(u4) id;
370 * Convert to/from a FrameId.
372 * We just return a pointer to the stack frame.
374 static FrameId frameToFrameId(const void* frame)
376 return (FrameId)(u4) frame;
378 static void* frameIdToFrame(FrameId id)
380 return (void*)(u4) id;
385 * Get the invocation request state.
387 DebugInvokeReq* dvmDbgGetInvokeReq(void)
389 return &dvmThreadSelf()->invokeReq;
393 * Enable the object registry, but don't enable debugging features yet.
395 * Only called from the JDWP handler thread.
397 void dvmDbgConnected(void)
399 assert(!gDvm.debuggerConnected);
401 LOGV("JDWP has attached\n");
402 assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
403 gDvm.debuggerConnected = true;
407 * Enable all debugging features, including scans for breakpoints.
409 * This is a no-op if we're already active.
411 * Only called from the JDWP handler thread.
413 void dvmDbgActive(void)
415 if (gDvm.debuggerActive)
418 LOGI("Debugger is active\n");
419 dvmInitBreakpoints();
420 gDvm.debuggerActive = true;
421 #if defined(WITH_JIT)
422 dvmCompilerStateRefresh();
427 * Disable debugging features.
429 * Set "debuggerConnected" to false, which disables use of the object
432 * Only called from the JDWP handler thread.
434 void dvmDbgDisconnected(void)
436 assert(gDvm.debuggerConnected);
438 gDvm.debuggerActive = false;
440 dvmHashTableLock(gDvm.dbgRegistry);
441 gDvm.debuggerConnected = false;
443 LOGD("Debugger has detached; object registry had %d entries\n",
444 dvmHashTableNumEntries(gDvm.dbgRegistry));
446 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
447 // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
449 dvmHashTableClear(gDvm.dbgRegistry);
450 dvmHashTableUnlock(gDvm.dbgRegistry);
451 #if defined(WITH_JIT)
452 dvmCompilerStateRefresh();
457 * Returns "true" if a debugger is connected.
459 * Does not return "true" if it's just a DDM server.
461 bool dvmDbgIsDebuggerConnected(void)
463 return gDvm.debuggerActive;
467 * Get time since last debugger activity. Used when figuring out if the
468 * debugger has finished configuring us.
470 s8 dvmDbgLastDebuggerActivity(void)
472 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
476 * JDWP thread is running, don't allow GC.
478 int dvmDbgThreadRunning(void)
480 return dvmChangeStatus(NULL, THREAD_RUNNING);
484 * JDWP thread is idle, allow GC.
486 int dvmDbgThreadWaiting(void)
488 return dvmChangeStatus(NULL, THREAD_VMWAIT);
492 * Restore state returned by Running/Waiting calls.
494 int dvmDbgThreadContinuing(int status)
496 return dvmChangeStatus(NULL, status);
500 * The debugger wants us to exit.
502 void dvmDbgExit(int status)
504 // TODO? invoke System.exit() to perform exit processing; ends up
505 // in System.exitInternal(), which can call JNI exit hook
507 LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
508 if (CALC_CACHE_STATS) {
509 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
510 dvmDumpBootClassPath();
513 #ifdef PROFILE_FIELD_ACCESS
514 dvmDumpFieldAccessCounts();
522 * ===========================================================================
523 * Class, Object, Array
524 * ===========================================================================
528 * Get the class's type descriptor from a reference type ID.
530 const char* dvmDbgGetClassDescriptor(RefTypeId id)
534 clazz = refTypeIdToClassObject(id);
535 return clazz->descriptor;
539 * Convert a RefTypeId to an ObjectId.
541 ObjectId dvmDbgGetClassObject(RefTypeId id)
543 ClassObject* clazz = refTypeIdToClassObject(id);
544 return objectToObjectId((Object*) clazz);
548 * Return the superclass of a class (will be NULL for java/lang/Object).
550 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
552 ClassObject* clazz = refTypeIdToClassObject(id);
553 return classObjectToRefTypeId(clazz->super);
557 * Return a class's defining class loader.
559 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
561 ClassObject* clazz = refTypeIdToClassObject(id);
562 return objectToObjectId(clazz->classLoader);
566 * Return a class's access flags.
568 u4 dvmDbgGetAccessFlags(RefTypeId id)
570 ClassObject* clazz = refTypeIdToClassObject(id);
571 return clazz->accessFlags & JAVA_FLAGS_MASK;
575 * Is this class an interface?
577 bool dvmDbgIsInterface(RefTypeId id)
579 ClassObject* clazz = refTypeIdToClassObject(id);
580 return dvmIsInterfaceClass(clazz);
584 * dvmHashForeach callback
586 static int copyRefType(void* vclazz, void* varg)
588 RefTypeId** pRefType = (RefTypeId**)varg;
589 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
595 * Get the complete list of reference classes (i.e. all classes except
596 * the primitive types).
598 * Returns a newly-allocated buffer full of RefTypeId values.
600 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
604 dvmHashTableLock(gDvm.loadedClasses);
605 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
606 pRefType = *pClassRefBuf = malloc(sizeof(RefTypeId) * *pNumClasses);
608 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
609 LOGW("Warning: problem getting class list\n");
610 /* not really expecting this to happen */
612 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
615 dvmHashTableUnlock(gDvm.loadedClasses);
619 * Get the list of reference classes "visible" to the specified class
620 * loader. A class is visible to a class loader if the ClassLoader object
621 * is the defining loader or is listed as an initiating loader.
623 * Returns a newly-allocated buffer full of RefTypeId values.
625 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
626 RefTypeId** pClassRefBuf)
629 int numClasses = 0, maxClasses;
631 classLoader = objectIdToObject(classLoaderId);
632 // I don't think classLoader can be NULL, but the spec doesn't say
634 LOGVV("GetVisibleList: comparing to %p\n", classLoader);
636 dvmHashTableLock(gDvm.loadedClasses);
638 /* over-allocate the return buffer */
639 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
640 *pClassRefBuf = malloc(sizeof(RefTypeId) * maxClasses);
643 * Run through the list, looking for matches.
646 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
647 dvmHashIterNext(&iter))
649 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
651 if (clazz->classLoader == classLoader ||
652 dvmLoaderInInitiatingList(clazz, classLoader))
654 LOGVV(" match '%s'\n", clazz->descriptor);
655 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
658 *pNumClasses = numClasses;
660 dvmHashTableUnlock(gDvm.loadedClasses);
664 * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
666 * Our class descriptors are in the correct format, so we just copy that.
667 * TODO: figure out if we can avoid the copy now that we're using
668 * descriptors instead of unadorned class names.
670 * Returns a newly-allocated string.
672 static char* generateJNISignature(ClassObject* clazz)
674 return strdup(clazz->descriptor);
678 * Get information about a class.
680 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
683 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
686 ClassObject* clazz = refTypeIdToClassObject(classId);
688 if (clazz->descriptor[0] == '[') {
689 /* generated array class */
690 *pStatus = CS_VERIFIED | CS_PREPARED;
691 *pTypeTag = TT_ARRAY;
693 if (clazz->status == CLASS_ERROR)
696 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
697 if (dvmIsInterfaceClass(clazz))
698 *pTypeTag = TT_INTERFACE;
700 *pTypeTag = TT_CLASS;
702 if (pSignature != NULL)
703 *pSignature = generateJNISignature(clazz);
707 * Search the list of loaded classes for a match.
709 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
710 RefTypeId* pRefTypeId)
714 clazz = dvmFindLoadedClass(classDescriptor);
716 *pRefTypeId = classObjectToRefTypeId(clazz);
724 * Get an object's class and "type tag".
726 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
727 RefTypeId* pRefTypeId)
729 Object* obj = objectIdToObject(objectId);
731 if (dvmIsArrayClass(obj->clazz))
732 *pRefTypeTag = TT_ARRAY;
733 else if (dvmIsInterfaceClass(obj->clazz))
734 *pRefTypeTag = TT_INTERFACE;
736 *pRefTypeTag = TT_CLASS;
737 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
741 * Get a class object's "type tag".
743 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
745 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
747 if (dvmIsArrayClass(clazz))
749 else if (dvmIsInterfaceClass(clazz))
756 * Get a class' signature.
758 * Returns a newly-allocated string.
760 char* dvmDbgGetSignature(RefTypeId refTypeId)
764 clazz = refTypeIdToClassObject(refTypeId);
765 assert(clazz != NULL);
767 return generateJNISignature(clazz);
771 * Get class' source file.
773 * Returns a newly-allocated string.
775 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
779 clazz = refTypeIdToClassObject(refTypeId);
780 assert(clazz != NULL);
782 return clazz->sourceFile;
786 * Get an object's type name. Converted to a "JNI signature".
788 * Returns a newly-allocated string.
790 char* dvmDbgGetObjectTypeName(ObjectId objectId)
792 Object* obj = objectIdToObject(objectId);
796 return generateJNISignature(obj->clazz);
800 * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
803 * In many cases this is necessary but not sufficient. For example, if
804 * we have a NULL String object, we want to return JT_STRING. If we have
805 * a java/lang/Object that holds a String reference, we also want to
806 * return JT_STRING. See dvmDbgGetObjectTag().
808 int dvmDbgGetSignatureTag(const char* type)
811 * We're not checking the class loader here (to guarantee that JT_STRING
812 * is truly the one and only String), but it probably doesn't matter
815 if (strcmp(type, "Ljava/lang/String;") == 0)
817 else if (strcmp(type, "Ljava/lang/Class;") == 0)
818 return JT_CLASS_OBJECT;
819 else if (strcmp(type, "Ljava/lang/Thread;") == 0)
821 else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
822 return JT_THREAD_GROUP;
823 else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
824 return JT_CLASS_LOADER;
827 case '[': return JT_ARRAY;
828 case 'B': return JT_BYTE;
829 case 'C': return JT_CHAR;
830 case 'L': return JT_OBJECT;
831 case 'F': return JT_FLOAT;
832 case 'D': return JT_DOUBLE;
833 case 'I': return JT_INT;
834 case 'J': return JT_LONG;
835 case 'S': return JT_SHORT;
836 case 'V': return JT_VOID;
837 case 'Z': return JT_BOOLEAN;
839 LOGE("ERROR: unhandled type '%s'\n", type);
846 * Methods declared to return Object might actually be returning one
847 * of the "refined types". We need to check the object explicitly.
849 static u1 resultTagFromObject(Object* obj)
859 * Comparing against the known classes is faster than string
860 * comparisons. It ensures that we only find the classes in the
861 * bootstrap class loader, which may or may not be what we want.
863 if (clazz == gDvm.classJavaLangString)
865 else if (clazz == gDvm.classJavaLangClass)
866 return JT_CLASS_OBJECT;
867 else if (clazz == gDvm.classJavaLangThread)
869 else if (clazz == gDvm.classJavaLangThreadGroup)
870 return JT_THREAD_GROUP;
871 else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
872 return JT_CLASS_LOADER;
873 else if (clazz->descriptor[0] == '[')
880 * Determine the tag for an object with a known type.
882 int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
886 tag = dvmDbgGetSignatureTag(type);
887 if (tag == JT_OBJECT && objectId != 0)
888 tag = resultTagFromObject(objectIdToObject(objectId));
894 * Get the widths of the specified JDWP.Tag value.
896 int dvmDbgGetTagWidth(int tag)
914 case JT_THREAD_GROUP:
915 case JT_CLASS_LOADER:
916 case JT_CLASS_OBJECT:
917 return sizeof(ObjectId);
922 LOGE("ERROR: unhandled tag '%c'\n", tag);
929 * Determine whether or not a tag represents a primitive type.
931 static bool isTagPrimitive(u1 tag)
947 case JT_CLASS_OBJECT:
949 case JT_THREAD_GROUP:
950 case JT_CLASS_LOADER:
953 LOGE("ERROR: unhandled tag '%c'\n", tag);
961 * Return the length of the specified array.
963 int dvmDbgGetArrayLength(ObjectId arrayId)
965 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
966 assert(dvmIsArray(arrayObj));
967 return arrayObj->length;
971 * Return a tag indicating the general type of elements in the array.
973 int dvmDbgGetArrayElementTag(ObjectId arrayId)
975 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
977 assert(dvmIsArray(arrayObj));
979 return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
983 * Copy a series of values with the specified width, changing the byte
984 * ordering to big-endian.
986 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
992 memcpy(out, in, count);
995 for (i = 0; i < count; i++)
996 *(((u2*) out)+i) = get2BE(in + i*2);
999 for (i = 0; i < count; i++)
1000 *(((u4*) out)+i) = get4BE(in + i*4);
1003 for (i = 0; i < count; i++)
1004 *(((u8*) out)+i) = get8BE(in + i*8);
1012 * Copy a series of values with the specified with, changing the
1013 * byte order from big-endian.
1015 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
1021 memcpy(out, in, count);
1024 for (i = 0; i < count; i++)
1025 set2BE(out + i*2, *((u2*)in + i));
1028 for (i = 0; i < count; i++)
1029 set4BE(out + i*4, *((u4*)in + i));
1032 for (i = 0; i < count; i++)
1033 set8BE(out + i*8, *((u8*)in + i));
1041 * Output a piece of an array to the reply buffer.
1043 * Returns "false" if something looks fishy.
1045 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
1048 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1049 const u1* data = (const u1*)arrayObj->contents;
1052 assert(dvmIsArray(arrayObj));
1054 if (firstIndex + count > (int)arrayObj->length) {
1055 LOGW("Request for index=%d + count=%d excceds length=%d\n",
1056 firstIndex, count, arrayObj->length);
1060 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1062 if (isTagPrimitive(tag)) {
1063 int width = dvmDbgGetTagWidth(tag);
1066 outBuf = expandBufAddSpace(pReply, count * width);
1068 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1073 pObjects = (Object**) data;
1074 pObjects += firstIndex;
1076 LOGV(" --> copying %d object IDs\n", count);
1077 //assert(tag == JT_OBJECT); // could be object or "refined" type
1079 for (i = 0; i < count; i++, pObjects++) {
1081 if (*pObjects != NULL)
1082 thisTag = resultTagFromObject(*pObjects);
1085 expandBufAdd1(pReply, thisTag);
1086 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1094 * Set a range of elements in an array from the data in "buf".
1096 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1099 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1100 u1* data = (u1*)arrayObj->contents;
1103 assert(dvmIsArray(arrayObj));
1105 if (firstIndex + count > (int)arrayObj->length) {
1106 LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
1107 firstIndex, count, arrayObj->length);
1111 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1113 if (isTagPrimitive(tag)) {
1114 int width = dvmDbgGetTagWidth(tag);
1116 LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
1118 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1123 pObjects = (Object**) data;
1124 pObjects += firstIndex;
1126 LOGV(" --> setting %d objects", count);
1128 /* should do array type check here */
1129 for (i = 0; i < count; i++) {
1130 ObjectId id = dvmReadObjectId(&buf);
1131 *pObjects++ = objectIdToObject(id);
1139 * Create a new string.
1141 * The only place the reference will be held in the VM is in our registry.
1143 ObjectId dvmDbgCreateString(const char* str)
1145 StringObject* strObj;
1147 strObj = dvmCreateStringFromCstr(str, ALLOC_DEFAULT);
1148 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1149 return objectToObjectId((Object*) strObj);
1153 * Allocate a new object of the specified type.
1155 * Add it to the registry to prevent it from being GCed.
1157 ObjectId dvmDbgCreateObject(RefTypeId classId)
1159 ClassObject* clazz = refTypeIdToClassObject(classId);
1160 Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
1161 dvmReleaseTrackedAlloc(newObj, NULL);
1162 return objectToObjectId(newObj);
1166 * Determine if "instClassId" is an instance of "classId".
1168 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1170 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1171 ClassObject* clazz = refTypeIdToClassObject(classId);
1173 return dvmInstanceof(instClazz, clazz);
1178 * ===========================================================================
1180 * ===========================================================================
1184 * Get the method name from a MethodId.
1186 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1190 meth = methodIdToMethod(refTypeId, id);
1195 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1196 * output all fields declared by the class. Inerhited fields are
1199 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1202 static const u1 genericSignature[1] = "";
1208 clazz = refTypeIdToClassObject(refTypeId);
1209 assert(clazz != NULL);
1211 declared = clazz->sfieldCount + clazz->ifieldCount;
1212 expandBufAdd4BE(pReply, declared);
1214 for (i = 0; i < clazz->sfieldCount; i++) {
1215 field = (Field*) &clazz->sfields[i];
1217 expandBufAddFieldId(pReply, fieldToFieldId(field));
1218 expandBufAddUtf8String(pReply, (const u1*) field->name);
1219 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1221 expandBufAddUtf8String(pReply, genericSignature);
1222 expandBufAdd4BE(pReply, field->accessFlags);
1224 for (i = 0; i < clazz->ifieldCount; i++) {
1225 field = (Field*) &clazz->ifields[i];
1227 expandBufAddFieldId(pReply, fieldToFieldId(field));
1228 expandBufAddUtf8String(pReply, (const u1*) field->name);
1229 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1231 expandBufAddUtf8String(pReply, genericSignature);
1232 expandBufAdd4BE(pReply, field->accessFlags);
1237 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1238 * output all methods declared by the class. Inherited methods are
1241 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1244 DexStringCache stringCache;
1245 static const u1 genericSignature[1] = "";
1251 dexStringCacheInit(&stringCache);
1253 clazz = refTypeIdToClassObject(refTypeId);
1254 assert(clazz != NULL);
1256 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1257 expandBufAdd4BE(pReply, declared);
1259 for (i = 0; i < clazz->directMethodCount; i++) {
1260 meth = &clazz->directMethods[i];
1262 expandBufAddMethodId(pReply, methodToMethodId(meth));
1263 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1265 expandBufAddUtf8String(pReply,
1266 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1270 expandBufAddUtf8String(pReply, genericSignature);
1271 expandBufAdd4BE(pReply, meth->accessFlags);
1273 for (i = 0; i < clazz->virtualMethodCount; i++) {
1274 meth = &clazz->virtualMethods[i];
1276 expandBufAddMethodId(pReply, methodToMethodId(meth));
1277 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1279 expandBufAddUtf8String(pReply,
1280 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1284 expandBufAddUtf8String(pReply, genericSignature);
1285 expandBufAdd4BE(pReply, meth->accessFlags);
1288 dexStringCacheRelease(&stringCache);
1292 * Output all interfaces directly implemented by the class.
1294 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1297 int i, start, count;
1299 clazz = refTypeIdToClassObject(refTypeId);
1300 assert(clazz != NULL);
1302 if (clazz->super == NULL)
1305 start = clazz->super->iftableCount;
1307 count = clazz->iftableCount - start;
1308 expandBufAdd4BE(pReply, count);
1309 for (i = start; i < clazz->iftableCount; i++) {
1310 ClassObject* iface = clazz->iftable[i].clazz;
1311 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1315 typedef struct DebugCallbackContext {
1318 // used by locals table
1320 } DebugCallbackContext;
1322 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1324 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1326 expandBufAdd8BE(pContext->pReply, address);
1327 expandBufAdd4BE(pContext->pReply, lineNum);
1328 pContext->numItems++;
1334 * For Method.LineTable: output the line table.
1336 * Note we operate in Dalvik's 16-bit units rather than bytes.
1338 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1344 DebugCallbackContext context;
1346 memset (&context, 0, sizeof(DebugCallbackContext));
1348 method = methodIdToMethod(refTypeId, methodId);
1349 if (dvmIsNativeMethod(method)) {
1354 end = dvmGetMethodInsnsSize(method);
1357 expandBufAdd8BE(pReply, start);
1358 expandBufAdd8BE(pReply, end);
1360 // Add numLines later
1361 size_t numLinesOffset = expandBufGetLength(pReply);
1362 expandBufAdd4BE(pReply, 0);
1364 context.pReply = pReply;
1366 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1367 dvmGetMethodCode(method),
1368 method->clazz->descriptor,
1369 method->prototype.protoIdx,
1370 method->accessFlags,
1371 lineTablePositionsCb, NULL, &context);
1373 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1377 * Eclipse appears to expect that the "this" reference is in slot zero.
1378 * If it's not, the "variables" display will show two copies of "this",
1379 * possibly because it gets "this" from SF.ThisObject and then displays
1380 * all locals with nonzero slot numbers.
1382 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1383 * SF.GetValues / SF.SetValues we map them back.
1385 static int tweakSlot(int slot, const char* name)
1389 if (strcmp(name, "this") == 0) // only remap "this" ptr
1391 else if (slot == 0) // always remap slot 0
1392 newSlot = kSlot0Sub;
1394 LOGV("untweak: %d to %d\n", slot, newSlot);
1399 * Reverse Eclipse hack.
1401 static int untweakSlot(int slot, const void* framePtr)
1405 if (slot == kSlot0Sub) {
1407 } else if (slot == 0) {
1408 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1409 const Method* method = saveArea->method;
1410 newSlot = method->registersSize - method->insSize;
1413 LOGV("untweak: %d to %d\n", slot, newSlot);
1417 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1418 u4 endAddress, const char *name, const char *descriptor,
1419 const char *signature)
1421 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1423 reg = (u2) tweakSlot(reg, name);
1425 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
1426 pContext->numItems, startAddress, endAddress - startAddress,
1427 name, descriptor, reg);
1429 expandBufAdd8BE(pContext->pReply, startAddress);
1430 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1431 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1432 if (pContext->withGeneric) {
1433 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1435 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1436 expandBufAdd4BE(pContext->pReply, reg);
1438 pContext->numItems++;
1442 * For Method.VariableTable[WithGeneric]: output information about local
1443 * variables for the specified method.
1445 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1446 bool withGeneric, ExpandBuf* pReply)
1449 DebugCallbackContext context;
1451 memset (&context, 0, sizeof(DebugCallbackContext));
1453 method = methodIdToMethod(refTypeId, methodId);
1455 expandBufAdd4BE(pReply, method->insSize);
1457 // Add numLocals later
1458 size_t numLocalsOffset = expandBufGetLength(pReply);
1459 expandBufAdd4BE(pReply, 0);
1461 context.pReply = pReply;
1462 context.withGeneric = withGeneric;
1463 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1464 dvmGetMethodCode(method),
1465 method->clazz->descriptor,
1466 method->prototype.protoIdx,
1467 method->accessFlags,
1468 NULL, variableTableCb, &context);
1470 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1474 * Get the type tag for the field's type.
1476 int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
1478 Object* obj = objectIdToObject(objId);
1479 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1480 Field* field = fieldIdToField(classId, fieldId);
1482 return dvmDbgGetSignatureTag(field->signature);
1486 * Get the type tag for the static field's type.
1488 int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
1490 Field* field = fieldIdToField(refTypeId, fieldId);
1491 return dvmDbgGetSignatureTag(field->signature);
1495 * Copy the value of a field into the specified buffer.
1497 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
1500 Object* obj = objectIdToObject(objectId);
1501 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1502 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1507 switch (field->field.signature[0]) {
1509 assert(expectedLen == 1);
1510 intVal = dvmGetFieldBoolean(obj, field->byteOffset);
1511 set1(buf, intVal != 0);
1514 assert(expectedLen == 1);
1515 intVal = dvmGetFieldInt(obj, field->byteOffset);
1520 assert(expectedLen == 2);
1521 intVal = dvmGetFieldInt(obj, field->byteOffset);
1522 set2BE(buf, intVal);
1526 assert(expectedLen == 4);
1527 intVal = dvmGetFieldInt(obj, field->byteOffset);
1528 set4BE(buf, intVal);
1532 assert(expectedLen == sizeof(ObjectId));
1533 objVal = dvmGetFieldObject(obj, field->byteOffset);
1534 dvmSetObjectId(buf, objectToObjectId(objVal));
1538 assert(expectedLen == 8);
1539 longVal = dvmGetFieldLong(obj, field->byteOffset);
1540 set8BE(buf, longVal);
1543 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1550 * Set the value of the specified field.
1552 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1555 Object* obj = objectIdToObject(objectId);
1556 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1557 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1559 switch (field->field.signature[0]) {
1562 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1566 dvmSetFieldInt(obj, field->byteOffset, value);
1571 dvmSetFieldInt(obj, field->byteOffset, value);
1576 dvmSetFieldInt(obj, field->byteOffset, value);
1580 assert(width == sizeof(ObjectId));
1581 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1586 dvmSetFieldLong(obj, field->byteOffset, value);
1589 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1596 * Copy the value of a static field into the specified buffer.
1598 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
1601 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1605 switch (sfield->field.signature[0]) {
1607 assert(expectedLen == 1);
1608 set1(buf, dvmGetStaticFieldBoolean(sfield));
1611 assert(expectedLen == 1);
1612 set1(buf, dvmGetStaticFieldByte(sfield));
1615 assert(expectedLen == 2);
1616 set2BE(buf, dvmGetStaticFieldShort(sfield));
1619 assert(expectedLen == 2);
1620 set2BE(buf, dvmGetStaticFieldChar(sfield));
1623 assert(expectedLen == 4);
1624 set4BE(buf, dvmGetStaticFieldInt(sfield));
1627 assert(expectedLen == 4);
1628 value.f = dvmGetStaticFieldFloat(sfield);
1629 set4BE(buf, value.i);
1633 assert(expectedLen == sizeof(ObjectId));
1634 objVal = dvmGetStaticFieldObject(sfield);
1635 dvmSetObjectId(buf, objectToObjectId(objVal));
1638 assert(expectedLen == 8);
1639 set8BE(buf, dvmGetStaticFieldLong(sfield));
1642 assert(expectedLen == 8);
1643 value.d = dvmGetStaticFieldDouble(sfield);
1644 set8BE(buf, value.j);
1647 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1654 * Set the value of a static field.
1656 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1657 u8 rawValue, int width)
1659 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1665 switch (sfield->field.signature[0]) {
1668 dvmSetStaticFieldBoolean(sfield, value.z);
1672 dvmSetStaticFieldByte(sfield, value.b);
1676 dvmSetStaticFieldShort(sfield, value.s);
1680 dvmSetStaticFieldChar(sfield, value.c);
1684 dvmSetStaticFieldInt(sfield, value.i);
1688 dvmSetStaticFieldFloat(sfield, value.f);
1692 assert(width == sizeof(ObjectId));
1693 objVal = objectIdToObject(rawValue);
1694 dvmSetStaticFieldObject(sfield, objVal);
1698 dvmSetStaticFieldLong(sfield, value.j);
1702 dvmSetStaticFieldDouble(sfield, value.d);
1705 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1712 * Convert a string object to a UTF-8 string.
1714 * Returns a newly-allocated string.
1716 char* dvmDbgStringToUtf8(ObjectId strId)
1718 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1720 return dvmCreateCstrFromString(strObj);
1725 * ===========================================================================
1726 * Thread and ThreadGroup
1727 * ===========================================================================
1731 * Convert a thread object to a Thread ptr.
1733 * This currently requires running through the list of threads and finding
1736 * IMPORTANT: grab gDvm.threadListLock before calling here.
1738 static Thread* threadObjToThread(Object* threadObj)
1742 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1743 if (thread->threadObj == threadObj)
1751 * Get the status and suspend state of a thread.
1753 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1758 bool result = false;
1760 threadObj = objectIdToObject(threadId);
1761 assert(threadObj != NULL);
1763 /* lock the thread list, so the thread doesn't vanish while we work */
1764 dvmLockThreadList(NULL);
1766 thread = threadObjToThread(threadObj);
1770 switch (thread->status) {
1771 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1772 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1773 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1774 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1775 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1776 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1777 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1778 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1779 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1782 *pThreadStatus = THREAD_ZOMBIE;
1786 if (dvmIsSuspended(thread))
1787 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1789 *pSuspendStatus = 0;
1794 dvmUnlockThreadList();
1799 * Get the thread's suspend count.
1801 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1807 threadObj = objectIdToObject(threadId);
1808 assert(threadObj != NULL);
1810 /* lock the thread list, so the thread doesn't vanish while we work */
1811 dvmLockThreadList(NULL);
1813 thread = threadObjToThread(threadObj);
1817 result = thread->suspendCount;
1820 dvmUnlockThreadList();
1825 * Determine whether or not a thread exists in the VM's thread list.
1827 * Returns "true" if the thread exists.
1829 bool dvmDbgThreadExists(ObjectId threadId)
1835 threadObj = objectIdToObject(threadId);
1836 assert(threadObj != NULL);
1838 /* lock the thread list, so the thread doesn't vanish while we work */
1839 dvmLockThreadList(NULL);
1841 thread = threadObjToThread(threadObj);
1847 dvmUnlockThreadList();
1852 * Determine whether or not a thread is suspended.
1854 * Returns "false" if the thread is running or doesn't exist.
1856 bool dvmDbgIsSuspended(ObjectId threadId)
1860 bool result = false;
1862 threadObj = objectIdToObject(threadId);
1863 assert(threadObj != NULL);
1865 /* lock the thread list, so the thread doesn't vanish while we work */
1866 dvmLockThreadList(NULL);
1868 thread = threadObjToThread(threadObj);
1872 result = dvmIsSuspended(thread);
1875 dvmUnlockThreadList();
1881 * Wait until a thread suspends.
1883 * We stray from the usual pattern here, and release the thread list lock
1884 * before we use the Thread. This is necessary and should be safe in this
1885 * circumstance; see comments in dvmWaitForSuspend().
1887 void dvmDbgWaitForSuspend(ObjectId threadId)
1892 threadObj = objectIdToObject(threadId);
1893 assert(threadObj != NULL);
1895 dvmLockThreadList(NULL);
1896 thread = threadObjToThread(threadObj);
1897 dvmUnlockThreadList();
1900 dvmWaitForSuspend(thread);
1906 * Return the ObjectId for the "system" thread group.
1908 ObjectId dvmDbgGetSystemThreadGroupId(void)
1910 Object* groupObj = dvmGetSystemThreadGroup();
1911 return objectToObjectId(groupObj);
1915 * Return the ObjectId for the "system" thread group.
1917 ObjectId dvmDbgGetMainThreadGroupId(void)
1919 Object* groupObj = dvmGetMainThreadGroup();
1920 return objectToObjectId(groupObj);
1924 * Get the name of a thread.
1926 * Returns a newly-allocated string.
1928 char* dvmDbgGetThreadName(ObjectId threadId)
1931 StringObject* nameStr;
1935 threadObj = objectIdToObject(threadId);
1936 assert(threadObj != NULL);
1938 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1939 gDvm.offJavaLangThread_name);
1940 str = dvmCreateCstrFromString(nameStr);
1941 result = (char*) malloc(strlen(str) + 20);
1943 /* lock the thread list, so the thread doesn't vanish while we work */
1944 dvmLockThreadList(NULL);
1945 Thread* thread = threadObjToThread(threadObj);
1947 sprintf(result, "<%d> %s", thread->threadId, str);
1949 sprintf(result, "%s", str);
1950 dvmUnlockThreadList();
1957 * Get a thread's group.
1959 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1964 threadObj = objectIdToObject(threadId);
1965 assert(threadObj != NULL);
1967 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1968 return objectToObjectId(group);
1973 * Get the name of a thread group.
1975 * Returns a newly-allocated string.
1977 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1979 Object* threadGroup;
1980 InstField* nameField;
1981 StringObject* nameStr;
1983 threadGroup = objectIdToObject(threadGroupId);
1984 assert(threadGroup != NULL);
1986 nameField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
1987 "name", "Ljava/lang/String;");
1988 if (nameField == NULL) {
1989 LOGE("unable to find name field in ThreadGroup\n");
1993 nameStr = (StringObject*) dvmGetFieldObject(threadGroup,
1994 nameField->byteOffset);
1995 return dvmCreateCstrFromString(nameStr);
1999 * Get the parent of a thread group.
2001 * Returns a newly-allocated string.
2003 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
2005 Object* threadGroup;
2006 InstField* parentField;
2009 threadGroup = objectIdToObject(threadGroupId);
2010 assert(threadGroup != NULL);
2012 parentField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
2013 "parent", "Ljava/lang/ThreadGroup;");
2014 if (parentField == NULL) {
2015 LOGE("unable to find parent field in ThreadGroup\n");
2018 parent = dvmGetFieldObject(threadGroup, parentField->byteOffset);
2020 return objectToObjectId(parent);
2024 * Get the list of threads in the thread group.
2026 * We do this by running through the full list of threads and returning
2027 * the ones that have the ThreadGroup object as their owner.
2029 * If threadGroupId is set to "kAllThreads", we ignore the group field and
2030 * return all threads.
2032 * The caller must free "*ppThreadIds".
2034 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
2035 ObjectId** ppThreadIds, u4* pThreadCount)
2037 Object* targetThreadGroup = NULL;
2038 InstField* groupField = NULL;
2042 if (threadGroupId != THREAD_GROUP_ALL) {
2043 targetThreadGroup = objectIdToObject(threadGroupId);
2044 assert(targetThreadGroup != NULL);
2047 groupField = dvmFindInstanceField(gDvm.classJavaLangThread,
2048 "group", "Ljava/lang/ThreadGroup;");
2050 dvmLockThreadList(NULL);
2052 thread = gDvm.threadList;
2054 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2057 /* Skip over the JDWP support thread. Some debuggers
2058 * get bent out of shape when they can't suspend and
2059 * query all threads, so it's easier if we just don't
2060 * tell them about us.
2062 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2065 /* This thread is currently being created, and isn't ready
2066 * to be seen by the debugger yet.
2068 if (thread->threadObj == NULL)
2071 group = dvmGetFieldObject(thread->threadObj, groupField->byteOffset);
2072 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2076 *pThreadCount = count;
2079 *ppThreadIds = NULL;
2082 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2084 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2087 /* Skip over the JDWP support thread. Some debuggers
2088 * get bent out of shape when they can't suspend and
2089 * query all threads, so it's easier if we just don't
2090 * tell them about us.
2092 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2095 /* This thread is currently being created, and isn't ready
2096 * to be seen by the debugger yet.
2098 if (thread->threadObj == NULL)
2101 group = dvmGetFieldObject(thread->threadObj,groupField->byteOffset);
2102 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2104 *ptr++ = objectToObjectId(thread->threadObj);
2112 dvmUnlockThreadList();
2118 * The caller must free "*ppThreadIds".
2120 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2122 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2127 * Count up the #of frames on the thread's stack.
2129 * Returns -1 on failure;
2131 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2138 threadObj = objectIdToObject(threadId);
2140 dvmLockThreadList(NULL);
2142 thread = threadObjToThread(threadObj);
2146 framePtr = thread->curFrame;
2147 while (framePtr != NULL) {
2148 if (!dvmIsBreakFrame(framePtr))
2151 framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
2155 dvmUnlockThreadList();
2160 * Get info for frame N from the specified thread's stack.
2162 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2170 threadObj = objectIdToObject(threadId);
2172 dvmLockThreadList(NULL);
2174 thread = threadObjToThread(threadObj);
2178 framePtr = thread->curFrame;
2180 while (framePtr != NULL) {
2181 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2182 const Method* method = saveArea->method;
2184 if (!dvmIsBreakFrame(framePtr)) {
2186 *pFrameId = frameToFrameId(framePtr);
2187 if (dvmIsInterfaceClass(method->clazz))
2188 pLoc->typeTag = TT_INTERFACE;
2190 pLoc->typeTag = TT_CLASS;
2191 pLoc->classId = classObjectToRefTypeId(method->clazz);
2192 pLoc->methodId = methodToMethodId(method);
2193 if (dvmIsNativeMethod(method))
2196 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2197 dvmUnlockThreadList();
2204 framePtr = saveArea->prevFrame;
2208 dvmUnlockThreadList();
2213 * Get the ThreadId for the current thread.
2215 ObjectId dvmDbgGetThreadSelfId(void)
2217 Thread* self = dvmThreadSelf();
2218 return objectToObjectId(self->threadObj);
2224 void dvmDbgSuspendVM(bool isEvent)
2226 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2232 void dvmDbgResumeVM()
2234 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2238 * Suspend one thread (not ourselves).
2240 void dvmDbgSuspendThread(ObjectId threadId)
2242 Object* threadObj = objectIdToObject(threadId);
2245 dvmLockThreadList(NULL);
2247 thread = threadObjToThread(threadObj);
2248 if (thread == NULL) {
2249 /* can happen if our ThreadDeath notify crosses in the mail */
2250 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2252 dvmSuspendThread(thread);
2255 dvmUnlockThreadList();
2259 * Resume one thread (not ourselves).
2261 void dvmDbgResumeThread(ObjectId threadId)
2263 Object* threadObj = objectIdToObject(threadId);
2266 dvmLockThreadList(NULL);
2268 thread = threadObjToThread(threadObj);
2269 if (thread == NULL) {
2270 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2272 dvmResumeThread(thread);
2275 dvmUnlockThreadList();
2279 * Suspend ourselves after sending an event to the debugger.
2281 void dvmDbgSuspendSelf(void)
2283 dvmSuspendSelf(true);
2287 * Get the "this" object for the specified frame.
2289 static Object* getThisObject(const u4* framePtr)
2291 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2292 const Method* method = saveArea->method;
2293 int argOffset = method->registersSize - method->insSize;
2296 if (method == NULL) {
2297 /* this is a "break" frame? */
2302 LOGVV(" Pulling this object for frame at %p\n", framePtr);
2303 LOGVV(" Method='%s' native=%d static=%d this=%p\n",
2304 method->name, dvmIsNativeMethod(method),
2305 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2308 * No "this" pointer for statics. No args on the interp stack for
2309 * native methods invoked directly from the VM.
2311 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2314 thisObj = (Object*) framePtr[argOffset];
2316 if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
2317 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
2318 framePtr, method->clazz->descriptor, method->name);
2326 * Return the "this" object for the specified frame. The thread must be
2329 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2331 const u4* framePtr = frameIdToFrame(frameId);
2334 UNUSED_PARAMETER(threadId);
2336 thisObj = getThisObject(framePtr);
2338 *pThisId = objectToObjectId(thisObj);
2343 * Copy the value of a method argument or local variable into the
2344 * specified buffer. The value will be preceeded with the tag.
2346 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2347 u1 tag, u1* buf, int expectedLen)
2349 const u4* framePtr = frameIdToFrame(frameId);
2354 UNUSED_PARAMETER(threadId);
2356 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2360 assert(expectedLen == 1);
2361 intVal = framePtr[slot];
2362 set1(buf+1, intVal != 0);
2365 assert(expectedLen == 1);
2366 intVal = framePtr[slot];
2367 set1(buf+1, intVal);
2371 assert(expectedLen == 2);
2372 intVal = framePtr[slot];
2373 set2BE(buf+1, intVal);
2377 assert(expectedLen == 4);
2378 intVal = framePtr[slot];
2379 set4BE(buf+1, intVal);
2382 assert(expectedLen == 8);
2384 /* convert to "ObjectId" */
2385 objVal = (Object*)framePtr[slot];
2386 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2387 LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
2389 dvmAbort(); // DEBUG: make it obvious
2391 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2393 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2397 assert(expectedLen == 8);
2399 /* convert to "ObjectId" */
2400 objVal = (Object*)framePtr[slot];
2403 if (objVal != NULL) {
2404 if (!dvmIsValidObject(objVal)) {
2405 LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
2407 dvmAbort(); // DEBUG: make it obvious
2410 //name = generateJNISignature(objVal->clazz);
2411 tag = resultTagFromObject(objVal);
2416 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2421 assert(expectedLen == 8);
2422 longVal = *(u8*)(&framePtr[slot]);
2423 set8BE(buf+1, longVal);
2426 LOGE("ERROR: unhandled tag '%c'\n", tag);
2435 * Copy a new value into an argument or local variable.
2437 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2438 u8 value, int width)
2440 u4* framePtr = frameIdToFrame(frameId);
2442 UNUSED_PARAMETER(threadId);
2444 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2449 framePtr[slot] = (u4)value;
2453 framePtr[slot] = (u4)value;
2458 framePtr[slot] = (u4)value;
2463 framePtr[slot] = (u4)value;
2466 /* The debugger calls VirtualMachine.CreateString to create a new
2467 * string, then uses this to set the object reference, when you
2468 * edit a String object */
2471 assert(width == sizeof(ObjectId));
2472 framePtr[slot] = (u4) objectIdToObject(value);
2477 *(u8*)(&framePtr[slot]) = value;
2480 case JT_CLASS_OBJECT:
2482 case JT_THREAD_GROUP:
2483 case JT_CLASS_LOADER:
2485 LOGE("ERROR: unhandled tag '%c'\n", tag);
2493 * ===========================================================================
2494 * Debugger notification
2495 * ===========================================================================
2499 * Tell JDWP that a breakpoint address has been reached.
2501 * "pcOffset" will be -1 for native methods.
2502 * "thisPtr" will be NULL for static methods.
2504 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2505 Object* thisPtr, int eventFlags)
2509 if (dvmIsInterfaceClass(method->clazz))
2510 loc.typeTag = TT_INTERFACE;
2512 loc.typeTag = TT_CLASS;
2513 loc.classId = classObjectToRefTypeId(method->clazz);
2514 loc.methodId = methodToMethodId(method);
2518 * Note we use "NoReg" so we don't keep track of references that are
2519 * never actually sent to the debugger. The "thisPtr" is only used to
2520 * compare against registered events.
2523 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2524 objectToObjectIdNoReg(thisPtr), eventFlags))
2526 classObjectToRefTypeId(method->clazz);
2527 objectToObjectId(thisPtr);
2532 * Tell JDWP that an exception has occurred.
2534 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2535 int catchRelPc, Object* exception)
2537 JdwpLocation throwLoc, catchLoc;
2538 const Method* throwMeth;
2539 const Method* catchMeth;
2541 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2542 if (dvmIsInterfaceClass(throwMeth->clazz))
2543 throwLoc.typeTag = TT_INTERFACE;
2545 throwLoc.typeTag = TT_CLASS;
2546 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2547 throwLoc.methodId = methodToMethodId(throwMeth);
2548 throwLoc.idx = throwRelPc;
2550 if (catchRelPc < 0) {
2551 memset(&catchLoc, 0, sizeof(catchLoc));
2553 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2554 if (dvmIsInterfaceClass(catchMeth->clazz))
2555 catchLoc.typeTag = TT_INTERFACE;
2557 catchLoc.typeTag = TT_CLASS;
2558 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2559 catchLoc.methodId = methodToMethodId(catchMeth);
2560 catchLoc.idx = catchRelPc;
2563 /* need this for InstanceOnly filters */
2564 Object* thisObj = getThisObject(throwFp);
2567 * Hand the event to the JDWP exception handler. Note we're using the
2568 * "NoReg" objectID on the exception, which is not strictly correct --
2569 * the exception object WILL be passed up to the debugger if the
2570 * debugger is interested in the event. We do this because the current
2571 * implementation of the debugger object registry never throws anything
2572 * away, and some people were experiencing a fatal build up of exception
2573 * objects when dealing with certain libraries.
2575 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2576 objectToObjectIdNoReg(exception),
2577 classObjectToRefTypeId(exception->clazz), &catchLoc,
2578 objectToObjectId(thisObj));
2582 * Tell JDWP and/or DDMS that a thread has started.
2584 void dvmDbgPostThreadStart(Thread* thread)
2586 if (gDvm.debuggerActive) {
2587 dvmJdwpPostThreadChange(gDvm.jdwpState,
2588 objectToObjectId(thread->threadObj), true);
2590 if (gDvm.ddmThreadNotification)
2591 dvmDdmSendThreadNotification(thread, true);
2595 * Tell JDWP and/or DDMS that a thread has gone away.
2597 void dvmDbgPostThreadDeath(Thread* thread)
2599 if (gDvm.debuggerActive) {
2600 dvmJdwpPostThreadChange(gDvm.jdwpState,
2601 objectToObjectId(thread->threadObj), false);
2603 if (gDvm.ddmThreadNotification)
2604 dvmDdmSendThreadNotification(thread, false);
2608 * Tell JDWP that a new class has been prepared.
2610 void dvmDbgPostClassPrepare(ClassObject* clazz)
2615 if (dvmIsInterfaceClass(clazz))
2620 // TODO - we currently always send both "verified" and "prepared" since
2621 // debuggers seem to like that. There might be some advantage to honesty,
2622 // since the class may not yet be verified.
2623 signature = generateJNISignature(clazz);
2624 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2625 signature, CS_VERIFIED | CS_PREPARED);
2630 * The JDWP event mechanism has registered an event with a LocationOnly
2631 * mod. Tell the interpreter to call us if we hit the specified
2634 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2636 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2637 assert(!dvmIsNativeMethod(method));
2638 dvmAddBreakAddr(method, pLoc->idx);
2639 return true; /* assume success */
2643 * An event with a LocationOnly mod has been removed.
2645 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2647 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2648 assert(!dvmIsNativeMethod(method));
2649 dvmClearBreakAddr(method, pLoc->idx);
2653 * The JDWP event mechanism has registered a single-step event. Tell
2654 * the interpreter about it.
2656 bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
2657 enum JdwpStepDepth depth)
2661 bool result = false;
2663 threadObj = objectIdToObject(threadId);
2664 assert(threadObj != NULL);
2667 * Get a pointer to the Thread struct for this ID. The pointer will
2668 * be used strictly for comparisons against the current thread pointer
2669 * after the setup is complete, so we can safely release the lock.
2671 dvmLockThreadList(NULL);
2672 thread = threadObjToThread(threadObj);
2674 if (thread == NULL) {
2675 LOGE("Thread for single-step not found\n");
2678 if (!dvmIsSuspended(thread)) {
2679 LOGE("Thread for single-step not suspended\n");
2680 assert(!"non-susp step"); // I want to know if this can happen
2684 assert(dvmIsSuspended(thread));
2685 if (!dvmAddSingleStep(thread, size, depth))
2691 dvmUnlockThreadList();
2696 * A single-step event has been removed.
2698 void dvmDbgUnconfigureStep(ObjectId threadId)
2700 UNUSED_PARAMETER(threadId);
2702 /* right now it's global, so don't need to find Thread */
2703 dvmClearSingleStep(NULL);
2707 * Invoke a method in a thread that has been stopped on a breakpoint or
2708 * other debugger event. (This function is called from the JDWP thread.)
2710 * Note that access control is not enforced, per spec.
2712 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2713 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2714 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2716 Object* threadObj = objectIdToObject(threadId);
2717 Thread* targetThread;
2718 JdwpError err = ERR_NONE;
2720 dvmLockThreadList(NULL);
2722 targetThread = threadObjToThread(threadObj);
2723 if (targetThread == NULL) {
2724 err = ERR_INVALID_THREAD; /* thread does not exist */
2725 dvmUnlockThreadList();
2728 if (!targetThread->invokeReq.ready) {
2729 err = ERR_INVALID_THREAD; /* thread not stopped by event */
2730 dvmUnlockThreadList();
2735 * We currently have a bug where we don't successfully resume the
2736 * target thread if the suspend count is too deep. We're expected to
2737 * require one "resume" for each "suspend", but when asked to execute
2738 * a method we have to resume fully and then re-suspend it back to the
2739 * same level. (The easiest way to cause this is to type "suspend"
2740 * multiple times in jdb.)
2742 * It's unclear what this means when the event specifies "resume all"
2743 * and some threads are suspended more deeply than others. This is
2744 * a rare problem, so for now we just prevent it from hanging forever
2745 * by rejecting the method invocation request. Without this, we will
2746 * be stuck waiting on a suspended thread.
2748 if (targetThread->suspendCount > 1) {
2749 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2750 "for method exec\n",
2751 dvmThreadSelf()->threadId, targetThread->threadId,
2752 targetThread->suspendCount);
2753 err = ERR_THREAD_SUSPENDED; /* probably not expected here */
2754 dvmUnlockThreadList();
2759 * TODO: ought to screen the various IDs, and verify that the argument
2763 targetThread->invokeReq.obj = objectIdToObject(objectId);
2764 targetThread->invokeReq.thread = threadObj;
2765 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2766 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2767 targetThread->invokeReq.numArgs = numArgs;
2768 targetThread->invokeReq.argArray = argArray;
2769 targetThread->invokeReq.options = options;
2770 targetThread->invokeReq.invokeNeeded = true;
2773 * This is a bit risky -- if the thread goes away we're sitting high
2774 * and dry -- but we must release this before the dvmResumeAllThreads
2775 * call, and it's unwise to hold it during dvmWaitForSuspend.
2777 dvmUnlockThreadList();
2780 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2781 * so the VM can suspend for a GC if the invoke request causes us to
2782 * run out of memory. It's also a good idea to change it before locking
2783 * the invokeReq mutex, although that should never be held for long.
2785 Thread* self = dvmThreadSelf();
2786 int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2788 LOGV(" Transferring control to event thread\n");
2789 dvmLockMutex(&targetThread->invokeReq.lock);
2791 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2792 LOGV(" Resuming all threads\n");
2793 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2795 LOGV(" Resuming event thread only\n");
2796 dvmResumeThread(targetThread);
2800 * Wait for the request to finish executing.
2802 while (targetThread->invokeReq.invokeNeeded) {
2803 pthread_cond_wait(&targetThread->invokeReq.cv,
2804 &targetThread->invokeReq.lock);
2806 dvmUnlockMutex(&targetThread->invokeReq.lock);
2807 LOGV(" Control has returned from event thread\n");
2809 /* wait for thread to re-suspend itself */
2810 dvmWaitForSuspend(targetThread);
2813 * Done waiting, switch back to RUNNING.
2815 dvmChangeStatus(self, oldStatus);
2818 * Suspend the threads. We waited for the target thread to suspend
2819 * itself, so all we need to do is suspend the others.
2821 * The suspendAllThreads() call will double-suspend the event thread,
2822 * so we want to resume the target thread once to keep the books straight.
2824 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2825 LOGV(" Suspending all threads\n");
2826 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2827 LOGV(" Resuming event thread to balance the count\n");
2828 dvmResumeThread(targetThread);
2832 * Set up the result.
2834 *pResultTag = targetThread->invokeReq.resultTag;
2835 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2836 *pResultValue = targetThread->invokeReq.resultValue.j;
2838 *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
2839 *pExceptObj = targetThread->invokeReq.exceptObj;
2840 err = targetThread->invokeReq.err;
2847 * Determine the tag type for the return value for this method.
2849 static u1 resultTagFromSignature(const Method* method)
2851 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2852 return dvmDbgGetSignatureTag(descriptor);
2856 * Execute the method described by "*pReq".
2858 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2859 * want to switch to RUNNING while we execute.
2861 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2863 Thread* self = dvmThreadSelf();
2869 * We can be called while an exception is pending in the VM. We need
2870 * to preserve that across the method invocation.
2872 oldExcept = dvmGetException(self);
2873 dvmClearException(self);
2875 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2878 * Translate the method through the vtable, unless we're calling a
2879 * direct method or the debugger wants to suppress it.
2881 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2882 dvmIsDirectMethod(pReq->method))
2884 meth = pReq->method;
2886 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2888 assert(meth != NULL);
2890 assert(sizeof(jvalue) == sizeof(u8));
2893 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2894 LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
2895 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2899 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2900 (jvalue*)pReq->argArray);
2901 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2902 pReq->resultTag = resultTagFromSignature(meth);
2903 if (pReq->exceptObj != 0) {
2904 Object* exc = dvmGetException(self);
2905 LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
2906 exc, exc->clazz->descriptor);
2907 //dvmLogExceptionStackTrace();
2908 dvmClearException(self);
2910 * Nothing should try to use this, but it looks like something is.
2911 * Make it null to be safe.
2913 pReq->resultValue.j = 0; /*0xadadadad;*/
2914 } else if (pReq->resultTag == JT_OBJECT) {
2915 /* if no exception thrown, examine object result more closely */
2916 u1 newTag = resultTagFromObject(pReq->resultValue.l);
2917 if (newTag != pReq->resultTag) {
2918 LOGVV(" JDWP promoted result from %d to %d\n",
2919 pReq->resultTag, newTag);
2920 pReq->resultTag = newTag;
2924 if (oldExcept != NULL)
2925 dvmSetException(self, oldExcept);
2926 dvmChangeStatus(self, oldStatus);
2929 // for dvmAddressSetForLine
2930 typedef struct AddressSetContext {
2931 bool lastAddressValid;
2935 } AddressSetContext;
2937 // for dvmAddressSetForLine
2938 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2940 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2942 if (lineNum == pContext->lineNum) {
2943 if (!pContext->lastAddressValid) {
2944 // Everything from this address until the next line change is ours
2945 pContext->lastAddress = address;
2946 pContext->lastAddressValid = true;
2948 // else, If we're already in a valid range for this lineNum,
2949 // just keep going (shouldn't really happen)
2950 } else if (pContext->lastAddressValid) { // and the line number is new
2952 // Add everything from the last entry up until here to the set
2953 for (i = pContext->lastAddress; i < address; i++) {
2954 dvmAddressSetSet(pContext->pSet, i);
2957 pContext->lastAddressValid = false;
2960 // there may be multiple entries for a line
2964 * Build up a set of bytecode addresses associated with a line number
2966 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2969 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2970 u4 insnsSize = dvmGetMethodInsnsSize(method);
2971 AddressSetContext context;
2973 result = calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2974 result->setSize = insnsSize;
2976 memset(&context, 0, sizeof(context));
2977 context.pSet = result;
2978 context.lineNum = line;
2979 context.lastAddressValid = false;
2981 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2982 method->clazz->descriptor,
2983 method->prototype.protoIdx,
2984 method->accessFlags,
2985 addressSetCb, NULL, &context);
2987 // If the line number was the last in the position table...
2988 if (context.lastAddressValid) {
2990 for (i = context.lastAddress; i < insnsSize; i++) {
2991 dvmAddressSetSet(result, i);
3000 * ===========================================================================
3001 * Dalvik Debug Monitor support
3002 * ===========================================================================
3006 * We have received a DDM packet over JDWP. Hand it off to the VM.
3008 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
3011 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
3015 * First DDM packet has arrived over JDWP. Notify the press.
3017 void dvmDbgDdmConnected(void)
3023 * JDWP connection has dropped.
3025 void dvmDbgDdmDisconnected(void)
3027 dvmDdmDisconnected();
3031 * Send up a JDWP event packet with a DDM chunk in it.
3033 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
3035 assert(buf != NULL);
3036 struct iovec vec[1] = { {(void*)buf, len} };
3037 dvmDbgDdmSendChunkV(type, vec, 1);
3041 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
3042 * concatenated from multiple source buffers.
3044 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
3046 if (gDvm.jdwpState == NULL) {
3047 LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x l=%d)\n",
3052 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);