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()
100 if (!dvmBreakpointStartup())
103 gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
104 return (gDvm.dbgRegistry != NULL);
108 * Free registry storage.
110 void dvmDebuggerShutdown()
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 __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
143 void dvmDbgCondSignal(pthread_cond_t* pCond)
145 int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
148 void dvmDbgCondBroadcast(pthread_cond_t* pCond)
150 int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
155 /* keep track of type, in case we need to distinguish them someday */
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.
187 static bool lookupId(ObjectId id)
191 found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
192 (void*)(u4) id, registryCompare, false);
195 assert(found == (void*)(u4) id);
201 * Register an object, if it hasn't already been.
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.
206 * Null references must be represented as zero, or the debugger will get
209 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
216 assert((u4) obj != 0xcccccccc);
217 assert((u4) obj > 0x100);
219 id = (ObjectId)(u4)obj | ((u8) type) << 32;
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);
232 dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
233 (void*)(u4) id, registryCompare, true);
236 dvmHashTableUnlock(gDvm.dbgRegistry);
241 * Verify that an object has been registered. If it hasn't, the debugger
242 * is asking for something we didn't send it, which means something
243 * somewhere is broken.
245 * If speed is an issue we can encode the registry index in the high
246 * four bytes. We could also just hard-wire this to "true".
248 * Note this actually takes both ObjectId and RefTypeId.
251 static bool objectIsRegistered(ObjectId id, RegistryType type)
253 UNUSED_PARAMETER(type);
255 if (id == 0) // null reference?
258 dvmHashTableLock(gDvm.dbgRegistry);
259 bool result = lookupId(id);
260 dvmHashTableUnlock(gDvm.dbgRegistry);
266 * Convert to/from a RefTypeId.
268 * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
270 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
272 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
275 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
277 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
280 static ClassObject* refTypeIdToClassObject(RefTypeId id)
282 assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
283 return (ClassObject*)(u4) id;
287 * Convert to/from an ObjectId.
289 static ObjectId objectToObjectId(const Object* obj)
291 return registerObject(obj, kObjectId, true);
293 static ObjectId objectToObjectIdNoReg(const Object* obj)
295 return registerObject(obj, kObjectId, false);
297 static Object* objectIdToObject(ObjectId id)
299 assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
300 return (Object*)(u4) id;
304 * Register an object ID that might not have been registered previously.
306 * Normally this wouldn't happen -- the conversion to an ObjectId would
307 * have added the object to the registry -- but in some cases (e.g.
308 * throwing exceptions) we really want to do the registration late.
310 void dvmDbgRegisterObjectId(ObjectId id)
312 Object* obj = (Object*)(u4) id;
313 LOGV("+++ registering %p (%s)\n", obj, obj->clazz->descriptor);
314 registerObject(obj, kObjectId, true);
318 * Convert to/from a MethodId.
320 * These IDs are only guaranteed unique within a class, so they could be
321 * an enumeration index. For now we just use the Method*.
323 static MethodId methodToMethodId(const Method* meth)
325 return (MethodId)(u4) meth;
327 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
329 // TODO? verify "id" is actually a method in "refTypeId"
330 return (Method*)(u4) id;
334 * Convert to/from a FieldId.
336 * These IDs are only guaranteed unique within a class, so they could be
337 * an enumeration index. For now we just use the Field*.
339 static FieldId fieldToFieldId(const Field* field)
341 return (FieldId)(u4) field;
343 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
345 // TODO? verify "id" is actually a field in "refTypeId"
346 return (Field*)(u4) id;
350 * Convert to/from a FrameId.
352 * We just return a pointer to the stack frame.
354 static FrameId frameToFrameId(const void* frame)
356 return (FrameId)(u4) frame;
358 static u4* frameIdToFrame(FrameId id)
365 * Get the invocation request state.
367 DebugInvokeReq* dvmDbgGetInvokeReq()
369 return &dvmThreadSelf()->invokeReq;
373 * Enable the object registry, but don't enable debugging features yet.
375 * Only called from the JDWP handler thread.
377 void dvmDbgConnected()
379 assert(!gDvm.debuggerConnected);
381 LOGV("JDWP has attached\n");
382 assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
383 gDvm.debuggerConnected = true;
387 * Enable all debugging features, including scans for breakpoints.
389 * This is a no-op if we're already active.
391 * Only called from the JDWP handler thread.
395 if (gDvm.debuggerActive)
398 LOGI("Debugger is active\n");
399 dvmInitBreakpoints();
400 gDvm.debuggerActive = true;
401 dvmEnableAllSubMode(kSubModeDebuggerActive);
402 #if defined(WITH_JIT)
403 dvmCompilerUpdateGlobalState();
408 * Disable debugging features.
410 * Set "debuggerConnected" to false, which disables use of the object
413 * Only called from the JDWP handler thread.
415 void dvmDbgDisconnected()
417 assert(gDvm.debuggerConnected);
419 gDvm.debuggerActive = false;
420 dvmDisableAllSubMode(kSubModeDebuggerActive);
421 #if defined(WITH_JIT)
422 dvmCompilerUpdateGlobalState();
425 dvmHashTableLock(gDvm.dbgRegistry);
426 gDvm.debuggerConnected = false;
428 LOGD("Debugger has detached; object registry had %d entries\n",
429 dvmHashTableNumEntries(gDvm.dbgRegistry));
431 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
432 // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
434 dvmHashTableClear(gDvm.dbgRegistry);
435 dvmHashTableUnlock(gDvm.dbgRegistry);
439 * Returns "true" if a debugger is connected.
441 * Does not return "true" if it's just a DDM server.
443 bool dvmDbgIsDebuggerConnected()
445 return gDvm.debuggerActive;
449 * Get time since last debugger activity. Used when figuring out if the
450 * debugger has finished configuring us.
452 s8 dvmDbgLastDebuggerActivity()
454 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
458 * JDWP thread is running, don't allow GC.
460 int dvmDbgThreadRunning()
462 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
463 return static_cast<int>(oldStatus);
467 * JDWP thread is idle, allow GC.
469 int dvmDbgThreadWaiting()
471 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
472 return static_cast<int>(oldStatus);
476 * Restore state returned by Running/Waiting calls.
478 int dvmDbgThreadContinuing(int status)
480 ThreadStatus newStatus = static_cast<ThreadStatus>(status);
481 ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
482 return static_cast<int>(oldStatus);
486 * The debugger wants us to exit.
488 void dvmDbgExit(int status)
490 // TODO? invoke System.exit() to perform exit processing; ends up
491 // in System.exitInternal(), which can call JNI exit hook
492 LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
493 if (CALC_CACHE_STATS) {
494 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
495 dvmDumpBootClassPath();
502 * ===========================================================================
503 * Class, Object, Array
504 * ===========================================================================
508 * Get the class's type descriptor from a reference type ID.
510 const char* dvmDbgGetClassDescriptor(RefTypeId id)
514 clazz = refTypeIdToClassObject(id);
515 return clazz->descriptor;
519 * Convert a RefTypeId to an ObjectId.
521 ObjectId dvmDbgGetClassObject(RefTypeId id)
523 ClassObject* clazz = refTypeIdToClassObject(id);
524 return objectToObjectId((Object*) clazz);
528 * Return the superclass of a class (will be NULL for java/lang/Object).
530 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
532 ClassObject* clazz = refTypeIdToClassObject(id);
533 return classObjectToRefTypeId(clazz->super);
537 * Return a class's defining class loader.
539 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
541 ClassObject* clazz = refTypeIdToClassObject(id);
542 return objectToObjectId(clazz->classLoader);
546 * Return a class's access flags.
548 u4 dvmDbgGetAccessFlags(RefTypeId id)
550 ClassObject* clazz = refTypeIdToClassObject(id);
551 return clazz->accessFlags & JAVA_FLAGS_MASK;
555 * Is this class an interface?
557 bool dvmDbgIsInterface(RefTypeId id)
559 ClassObject* clazz = refTypeIdToClassObject(id);
560 return dvmIsInterfaceClass(clazz);
564 * dvmHashForeach callback
566 static int copyRefType(void* vclazz, void* varg)
568 RefTypeId** pRefType = (RefTypeId**)varg;
569 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
575 * Get the complete list of reference classes (i.e. all classes except
576 * the primitive types).
578 * Returns a newly-allocated buffer full of RefTypeId values.
580 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
584 dvmHashTableLock(gDvm.loadedClasses);
585 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
586 pRefType = *pClassRefBuf =
587 (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
589 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
590 LOGW("Warning: problem getting class list\n");
591 /* not really expecting this to happen */
593 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
596 dvmHashTableUnlock(gDvm.loadedClasses);
600 * Get the list of reference classes "visible" to the specified class
601 * loader. A class is visible to a class loader if the ClassLoader object
602 * is the defining loader or is listed as an initiating loader.
604 * Returns a newly-allocated buffer full of RefTypeId values.
606 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
607 RefTypeId** pClassRefBuf)
610 int numClasses = 0, maxClasses;
612 classLoader = objectIdToObject(classLoaderId);
613 // I don't think classLoader can be NULL, but the spec doesn't say
615 LOGVV("GetVisibleList: comparing to %p\n", classLoader);
617 dvmHashTableLock(gDvm.loadedClasses);
619 /* over-allocate the return buffer */
620 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
621 *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
624 * Run through the list, looking for matches.
627 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
628 dvmHashIterNext(&iter))
630 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
632 if (clazz->classLoader == classLoader ||
633 dvmLoaderInInitiatingList(clazz, classLoader))
635 LOGVV(" match '%s'\n", clazz->descriptor);
636 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
639 *pNumClasses = numClasses;
641 dvmHashTableUnlock(gDvm.loadedClasses);
645 * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
647 * Our class descriptors are in the correct format, so we just return that.
649 static const char* jniSignature(ClassObject* clazz)
651 return clazz->descriptor;
655 * Get information about a class.
657 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
660 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
661 const char** pSignature)
663 ClassObject* clazz = refTypeIdToClassObject(classId);
665 if (clazz->descriptor[0] == '[') {
666 /* generated array class */
667 *pStatus = CS_VERIFIED | CS_PREPARED;
668 *pTypeTag = TT_ARRAY;
670 if (clazz->status == CLASS_ERROR)
673 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
674 if (dvmIsInterfaceClass(clazz))
675 *pTypeTag = TT_INTERFACE;
677 *pTypeTag = TT_CLASS;
679 if (pSignature != NULL)
680 *pSignature = jniSignature(clazz);
684 * Search the list of loaded classes for a match.
686 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
687 RefTypeId* pRefTypeId)
691 clazz = dvmFindLoadedClass(classDescriptor);
693 *pRefTypeId = classObjectToRefTypeId(clazz);
701 * Get an object's class and "type tag".
703 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
704 RefTypeId* pRefTypeId)
706 Object* obj = objectIdToObject(objectId);
708 if (dvmIsArrayClass(obj->clazz))
709 *pRefTypeTag = TT_ARRAY;
710 else if (dvmIsInterfaceClass(obj->clazz))
711 *pRefTypeTag = TT_INTERFACE;
713 *pRefTypeTag = TT_CLASS;
714 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
718 * Get a class object's "type tag".
720 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
722 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
724 if (dvmIsArrayClass(clazz))
726 else if (dvmIsInterfaceClass(clazz))
733 * Get a class' signature.
735 const char* dvmDbgGetSignature(RefTypeId refTypeId)
739 clazz = refTypeIdToClassObject(refTypeId);
740 assert(clazz != NULL);
742 return jniSignature(clazz);
746 * Get class' source file.
748 * Returns a newly-allocated string.
750 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
754 clazz = refTypeIdToClassObject(refTypeId);
755 assert(clazz != NULL);
757 return clazz->sourceFile;
761 * Get an object's type name. (For log message display only.)
763 const char* dvmDbgGetObjectTypeName(ObjectId objectId)
768 Object* obj = objectIdToObject(objectId);
769 return jniSignature(obj->clazz);
773 * Determine whether or not a tag represents a primitive type.
775 static bool isTagPrimitive(u1 tag)
791 case JT_CLASS_OBJECT:
793 case JT_THREAD_GROUP:
794 case JT_CLASS_LOADER:
797 LOGE("ERROR: unhandled tag '%c'\n", tag);
804 * Determine the best tag type given an object's class.
806 static u1 tagFromClass(ClassObject* clazz)
808 if (dvmIsArrayClass(clazz))
811 if (clazz == gDvm.classJavaLangString) {
813 } else if (dvmIsTheClassClass(clazz)) {
814 return JT_CLASS_OBJECT;
815 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
817 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
818 return JT_THREAD_GROUP;
819 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
820 return JT_CLASS_LOADER;
827 * Return a basic tag value based solely on a type descriptor.
829 * The ASCII value maps directly to the JDWP tag constants, so we don't
830 * need to do much here. This does not return the fancier tags like
833 static u1 basicTagFromDescriptor(const char* descriptor)
835 return descriptor[0];
839 * Objects declared to hold Object might actually hold a more specific
840 * type. The debugger may take a special interest in these (e.g. it
841 * wants to display the contents of Strings), so we want to return an
844 * Null objects are tagged JT_OBJECT.
846 static u1 tagFromObject(const Object* obj)
850 return tagFromClass(obj->clazz);
854 * Determine the tag for an object.
856 * "objectId" may be 0 (i.e. NULL reference).
858 u1 dvmDbgGetObjectTag(ObjectId objectId)
860 return tagFromObject(objectIdToObject(objectId));
864 * Get the widths of the specified JDWP.Tag value.
866 int dvmDbgGetTagWidth(int tag)
884 case JT_THREAD_GROUP:
885 case JT_CLASS_LOADER:
886 case JT_CLASS_OBJECT:
887 return sizeof(ObjectId);
892 LOGE("ERROR: unhandled tag '%c'\n", tag);
900 * Return the length of the specified array.
902 int dvmDbgGetArrayLength(ObjectId arrayId)
904 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
905 assert(dvmIsArray(arrayObj));
906 return arrayObj->length;
910 * Return a tag indicating the general type of elements in the array.
912 u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
914 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
916 ClassObject* arrayClass = arrayObj->obj.clazz;
917 u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
918 if (!isTagPrimitive(tag)) {
919 /* try to refine it */
920 tag = tagFromClass(arrayClass->elementClass);
927 * Copy a series of values with the specified width, changing the byte
928 * ordering to big-endian.
930 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
936 memcpy(out, in, count);
939 for (i = 0; i < count; i++)
940 *(((u2*) out)+i) = get2BE(in + i*2);
943 for (i = 0; i < count; i++)
944 *(((u4*) out)+i) = get4BE(in + i*4);
947 for (i = 0; i < count; i++)
948 *(((u8*) out)+i) = get8BE(in + i*8);
956 * Copy a series of values with the specified width, changing the
957 * byte order from big-endian.
959 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
965 memcpy(out, in, count);
968 for (i = 0; i < count; i++)
969 set2BE(out + i*2, *((u2*)in + i));
972 for (i = 0; i < count; i++)
973 set4BE(out + i*4, *((u4*)in + i));
976 for (i = 0; i < count; i++)
977 set8BE(out + i*8, *((u8*)in + i));
985 * Output a piece of an array to the reply buffer.
987 * Returns "false" if something looks fishy.
989 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
992 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
993 const u1* data = (const u1*)arrayObj->contents;
996 assert(dvmIsArray(arrayObj));
998 if (firstIndex + count > (int)arrayObj->length) {
999 LOGW("Request for index=%d + count=%d excceds length=%d\n",
1000 firstIndex, count, arrayObj->length);
1004 tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
1006 if (isTagPrimitive(tag)) {
1007 int width = dvmDbgGetTagWidth(tag);
1010 outBuf = expandBufAddSpace(pReply, count * width);
1012 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1017 pObjects = (Object**) data;
1018 pObjects += firstIndex;
1020 LOGV(" --> copying %d object IDs\n", count);
1021 //assert(tag == JT_OBJECT); // could be object or "refined" type
1023 for (i = 0; i < count; i++, pObjects++) {
1025 if (*pObjects != NULL)
1026 thisTag = tagFromObject(*pObjects);
1029 expandBufAdd1(pReply, thisTag);
1030 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1038 * Set a range of elements in an array from the data in "buf".
1040 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1043 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1044 u1* data = (u1*)arrayObj->contents;
1047 assert(dvmIsArray(arrayObj));
1049 if (firstIndex + count > (int)arrayObj->length) {
1050 LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
1051 firstIndex, count, arrayObj->length);
1055 tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
1057 if (isTagPrimitive(tag)) {
1058 int width = dvmDbgGetTagWidth(tag);
1060 LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
1062 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1067 pObjects = (Object**) data;
1068 pObjects += firstIndex;
1070 LOGV(" --> setting %d objects", count);
1072 /* should do array type check here */
1073 for (i = 0; i < count; i++) {
1074 ObjectId id = dvmReadObjectId(&buf);
1075 *pObjects++ = objectIdToObject(id);
1083 * Create a new string.
1085 * The only place the reference will be held in the VM is in our registry.
1087 ObjectId dvmDbgCreateString(const char* str)
1089 StringObject* strObj;
1091 strObj = dvmCreateStringFromCstr(str);
1092 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1093 return objectToObjectId((Object*) strObj);
1097 * Allocate a new object of the specified type.
1099 * Add it to the registry to prevent it from being GCed.
1101 ObjectId dvmDbgCreateObject(RefTypeId classId)
1103 ClassObject* clazz = refTypeIdToClassObject(classId);
1104 Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
1105 dvmReleaseTrackedAlloc(newObj, NULL);
1106 return objectToObjectId(newObj);
1110 * Allocate a new array object of the specified type and length. The
1111 * type is the array type, not the element type.
1113 * Add it to the registry to prevent it from being GCed.
1115 ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
1117 ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
1118 Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
1119 dvmReleaseTrackedAlloc(newObj, NULL);
1120 return objectToObjectId(newObj);
1124 * Determine if "instClassId" is an instance of "classId".
1126 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1128 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1129 ClassObject* clazz = refTypeIdToClassObject(classId);
1131 return dvmInstanceof(instClazz, clazz);
1136 * ===========================================================================
1138 * ===========================================================================
1142 * Get the method name from a MethodId.
1144 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1148 meth = methodIdToMethod(refTypeId, id);
1153 * Augment the access flags for synthetic methods and fields by setting
1154 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
1155 * flags not specified by the Java programming language.
1157 static u4 augmentedAccessFlags(u4 accessFlags)
1159 accessFlags &= JAVA_FLAGS_MASK;
1161 if ((accessFlags & ACC_SYNTHETIC) != 0) {
1162 return accessFlags | 0xf0000000;
1169 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1170 * output all fields declared by the class. Inherited fields are
1173 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1176 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
1177 assert(clazz != NULL);
1179 u4 declared = clazz->sfieldCount + clazz->ifieldCount;
1180 expandBufAdd4BE(pReply, declared);
1182 for (int i = 0; i < clazz->sfieldCount; i++) {
1183 Field* field = &clazz->sfields[i].field;
1184 expandBufAddFieldId(pReply, fieldToFieldId(field));
1185 expandBufAddUtf8String(pReply, (const u1*) field->name);
1186 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1188 static const u1 genericSignature[1] = "";
1189 expandBufAddUtf8String(pReply, genericSignature);
1191 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1193 for (int i = 0; i < clazz->ifieldCount; i++) {
1194 Field* field = (Field*)&clazz->ifields[i].field;
1195 expandBufAddFieldId(pReply, fieldToFieldId(field));
1196 expandBufAddUtf8String(pReply, (const u1*) field->name);
1197 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1199 static const u1 genericSignature[1] = "";
1200 expandBufAddUtf8String(pReply, genericSignature);
1202 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1207 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1208 * output all methods declared by the class. Inherited methods are
1211 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1214 DexStringCache stringCache;
1215 static const u1 genericSignature[1] = "";
1221 dexStringCacheInit(&stringCache);
1223 clazz = refTypeIdToClassObject(refTypeId);
1224 assert(clazz != NULL);
1226 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1227 expandBufAdd4BE(pReply, declared);
1229 for (i = 0; i < clazz->directMethodCount; i++) {
1230 meth = &clazz->directMethods[i];
1232 expandBufAddMethodId(pReply, methodToMethodId(meth));
1233 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1235 expandBufAddUtf8String(pReply,
1236 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1240 expandBufAddUtf8String(pReply, genericSignature);
1241 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1243 for (i = 0; i < clazz->virtualMethodCount; i++) {
1244 meth = &clazz->virtualMethods[i];
1246 expandBufAddMethodId(pReply, methodToMethodId(meth));
1247 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1249 expandBufAddUtf8String(pReply,
1250 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1254 expandBufAddUtf8String(pReply, genericSignature);
1255 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1258 dexStringCacheRelease(&stringCache);
1262 * Output all interfaces directly implemented by the class.
1264 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1267 int i, start, count;
1269 clazz = refTypeIdToClassObject(refTypeId);
1270 assert(clazz != NULL);
1272 if (clazz->super == NULL)
1275 start = clazz->super->iftableCount;
1277 count = clazz->iftableCount - start;
1278 expandBufAdd4BE(pReply, count);
1279 for (i = start; i < clazz->iftableCount; i++) {
1280 ClassObject* iface = clazz->iftable[i].clazz;
1281 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1285 struct DebugCallbackContext {
1288 // used by locals table
1292 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1294 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1296 expandBufAdd8BE(pContext->pReply, address);
1297 expandBufAdd4BE(pContext->pReply, lineNum);
1298 pContext->numItems++;
1304 * For Method.LineTable: output the line table.
1306 * Note we operate in Dalvik's 16-bit units rather than bytes.
1308 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1313 DebugCallbackContext context;
1315 memset (&context, 0, sizeof(DebugCallbackContext));
1317 method = methodIdToMethod(refTypeId, methodId);
1318 if (dvmIsNativeMethod(method)) {
1323 end = dvmGetMethodInsnsSize(method);
1326 expandBufAdd8BE(pReply, start);
1327 expandBufAdd8BE(pReply, end);
1329 // Add numLines later
1330 size_t numLinesOffset = expandBufGetLength(pReply);
1331 expandBufAdd4BE(pReply, 0);
1333 context.pReply = pReply;
1335 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1336 dvmGetMethodCode(method),
1337 method->clazz->descriptor,
1338 method->prototype.protoIdx,
1339 method->accessFlags,
1340 lineTablePositionsCb, NULL, &context);
1342 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1346 * Eclipse appears to expect that the "this" reference is in slot zero.
1347 * If it's not, the "variables" display will show two copies of "this",
1348 * possibly because it gets "this" from SF.ThisObject and then displays
1349 * all locals with nonzero slot numbers.
1351 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1352 * SF.GetValues / SF.SetValues we map them back.
1354 static int tweakSlot(int slot, const char* name)
1358 if (strcmp(name, "this") == 0) // only remap "this" ptr
1360 else if (slot == 0) // always remap slot 0
1361 newSlot = kSlot0Sub;
1363 LOGV("untweak: %d to %d\n", slot, newSlot);
1368 * Reverse Eclipse hack.
1370 static int untweakSlot(int slot, const void* framePtr)
1374 if (slot == kSlot0Sub) {
1376 } else if (slot == 0) {
1377 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1378 const Method* method = saveArea->method;
1379 newSlot = method->registersSize - method->insSize;
1382 LOGV("untweak: %d to %d\n", slot, newSlot);
1386 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1387 u4 endAddress, const char *name, const char *descriptor,
1388 const char *signature)
1390 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1392 reg = (u2) tweakSlot(reg, name);
1394 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
1395 pContext->numItems, startAddress, endAddress - startAddress,
1396 name, descriptor, reg);
1398 expandBufAdd8BE(pContext->pReply, startAddress);
1399 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1400 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1401 if (pContext->withGeneric) {
1402 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1404 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1405 expandBufAdd4BE(pContext->pReply, reg);
1407 pContext->numItems++;
1411 * For Method.VariableTable[WithGeneric]: output information about local
1412 * variables for the specified method.
1414 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1415 bool withGeneric, ExpandBuf* pReply)
1418 DebugCallbackContext context;
1420 memset (&context, 0, sizeof(DebugCallbackContext));
1422 method = methodIdToMethod(refTypeId, methodId);
1424 expandBufAdd4BE(pReply, method->insSize);
1426 // Add numLocals later
1427 size_t numLocalsOffset = expandBufGetLength(pReply);
1428 expandBufAdd4BE(pReply, 0);
1430 context.pReply = pReply;
1431 context.withGeneric = withGeneric;
1432 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1433 dvmGetMethodCode(method),
1434 method->clazz->descriptor,
1435 method->prototype.protoIdx,
1436 method->accessFlags,
1437 NULL, variableTableCb, &context);
1439 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1443 * Get the basic tag for an instance field.
1445 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
1447 Object* obj = objectIdToObject(objId);
1448 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1449 const Field* field = fieldIdToField(classId, fieldId);
1450 return basicTagFromDescriptor(field->signature);
1454 * Get the basic tag for a static field.
1456 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
1458 const Field* field = fieldIdToField(refTypeId, fieldId);
1459 return basicTagFromDescriptor(field->signature);
1464 * Copy the value of a static field into the output buffer, preceded
1465 * by an appropriate tag. The tag is based on the value held by the
1466 * field, not the field's type.
1468 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
1470 Object* obj = objectIdToObject(objectId);
1471 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1472 InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
1473 u1 tag = basicTagFromDescriptor(ifield->field.signature);
1475 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1476 Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
1477 tag = tagFromObject(objVal);
1478 expandBufAdd1(pReply, tag);
1479 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1480 LOGV(" --> ifieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
1484 LOGV(" --> ifieldId %x --> tag '%c'\n", fieldId, tag);
1485 expandBufAdd1(pReply, tag);
1489 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1492 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1495 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1498 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1501 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1504 value.f = dvmGetFieldInt(obj, ifield->byteOffset);
1505 expandBufAdd4BE(pReply, value.i);
1508 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1511 value.d = dvmGetFieldInt(obj, ifield->byteOffset);
1512 expandBufAdd8BE(pReply, value.j);
1515 LOGE("ERROR: unhandled field type '%s'\n", ifield->field.signature);
1523 * Set the value of the specified field.
1525 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1528 Object* obj = objectIdToObject(objectId);
1529 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1530 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1532 switch (field->field.signature[0]) {
1535 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1539 dvmSetFieldInt(obj, field->byteOffset, value);
1544 dvmSetFieldInt(obj, field->byteOffset, value);
1549 dvmSetFieldInt(obj, field->byteOffset, value);
1553 assert(width == sizeof(ObjectId));
1554 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1559 dvmSetFieldLong(obj, field->byteOffset, value);
1562 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1569 * Copy the value of a static field into the output buffer, preceded
1570 * by an appropriate tag. The tag is based on the value held by the
1571 * field, not the field's type.
1573 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1576 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1577 u1 tag = basicTagFromDescriptor(sfield->field.signature);
1579 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1580 Object* objVal = dvmGetStaticFieldObject(sfield);
1581 tag = tagFromObject(objVal);
1582 expandBufAdd1(pReply, tag);
1583 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1584 LOGV(" --> sfieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
1588 LOGV(" --> sfieldId %x --> tag '%c'\n", fieldId, tag);
1589 expandBufAdd1(pReply, tag);
1593 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1596 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1599 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1602 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1605 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1608 value.f = dvmGetStaticFieldFloat(sfield);
1609 expandBufAdd4BE(pReply, value.i);
1612 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1615 value.d = dvmGetStaticFieldDouble(sfield);
1616 expandBufAdd8BE(pReply, value.j);
1619 LOGE("ERROR: unhandled field type '%s'\n", sfield->field.signature);
1627 * Set the value of a static field.
1629 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1630 u8 rawValue, int width)
1632 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1638 switch (sfield->field.signature[0]) {
1641 dvmSetStaticFieldBoolean(sfield, value.z);
1645 dvmSetStaticFieldByte(sfield, value.b);
1649 dvmSetStaticFieldShort(sfield, value.s);
1653 dvmSetStaticFieldChar(sfield, value.c);
1657 dvmSetStaticFieldInt(sfield, value.i);
1661 dvmSetStaticFieldFloat(sfield, value.f);
1665 assert(width == sizeof(ObjectId));
1666 objVal = objectIdToObject(rawValue);
1667 dvmSetStaticFieldObject(sfield, objVal);
1671 dvmSetStaticFieldLong(sfield, value.j);
1675 dvmSetStaticFieldDouble(sfield, value.d);
1678 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1685 * Convert a string object to a UTF-8 string.
1687 * Returns a newly-allocated string.
1689 char* dvmDbgStringToUtf8(ObjectId strId)
1691 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1693 return dvmCreateCstrFromString(strObj);
1698 * ===========================================================================
1699 * Thread and ThreadGroup
1700 * ===========================================================================
1704 * Convert a thread object to a Thread ptr.
1706 * This currently requires running through the list of threads and finding
1709 * IMPORTANT: grab gDvm.threadListLock before calling here.
1711 static Thread* threadObjToThread(Object* threadObj)
1715 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1716 if (thread->threadObj == threadObj)
1724 * Get the status and suspend state of a thread.
1726 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1731 bool result = false;
1733 threadObj = objectIdToObject(threadId);
1734 assert(threadObj != NULL);
1736 /* lock the thread list, so the thread doesn't vanish while we work */
1737 dvmLockThreadList(NULL);
1739 thread = threadObjToThread(threadObj);
1743 switch (thread->status) {
1744 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1745 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1746 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1747 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1748 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1749 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1750 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1751 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1752 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1753 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1756 *pThreadStatus = THREAD_ZOMBIE;
1760 if (dvmIsSuspended(thread))
1761 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1763 *pSuspendStatus = 0;
1768 dvmUnlockThreadList();
1773 * Get the thread's suspend count.
1775 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1781 threadObj = objectIdToObject(threadId);
1782 assert(threadObj != NULL);
1784 /* lock the thread list, so the thread doesn't vanish while we work */
1785 dvmLockThreadList(NULL);
1787 thread = threadObjToThread(threadObj);
1791 result = thread->suspendCount;
1794 dvmUnlockThreadList();
1799 * Determine whether or not a thread exists in the VM's thread list.
1801 * Returns "true" if the thread exists.
1803 bool dvmDbgThreadExists(ObjectId threadId)
1809 threadObj = objectIdToObject(threadId);
1810 assert(threadObj != NULL);
1812 /* lock the thread list, so the thread doesn't vanish while we work */
1813 dvmLockThreadList(NULL);
1815 thread = threadObjToThread(threadObj);
1821 dvmUnlockThreadList();
1826 * Determine whether or not a thread is suspended.
1828 * Returns "false" if the thread is running or doesn't exist.
1830 bool dvmDbgIsSuspended(ObjectId threadId)
1834 bool result = false;
1836 threadObj = objectIdToObject(threadId);
1837 assert(threadObj != NULL);
1839 /* lock the thread list, so the thread doesn't vanish while we work */
1840 dvmLockThreadList(NULL);
1842 thread = threadObjToThread(threadObj);
1846 result = dvmIsSuspended(thread);
1849 dvmUnlockThreadList();
1854 * Return the ObjectId for the "system" thread group.
1856 ObjectId dvmDbgGetSystemThreadGroupId()
1858 Object* groupObj = dvmGetSystemThreadGroup();
1859 return objectToObjectId(groupObj);
1863 * Return the ObjectId for the "main" thread group.
1865 ObjectId dvmDbgGetMainThreadGroupId()
1867 Object* groupObj = dvmGetMainThreadGroup();
1868 return objectToObjectId(groupObj);
1872 * Get the name of a thread.
1874 * Returns a newly-allocated string.
1876 char* dvmDbgGetThreadName(ObjectId threadId)
1879 StringObject* nameStr;
1883 threadObj = objectIdToObject(threadId);
1884 assert(threadObj != NULL);
1886 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1887 gDvm.offJavaLangThread_name);
1888 str = dvmCreateCstrFromString(nameStr);
1889 result = (char*) malloc(strlen(str) + 20);
1891 /* lock the thread list, so the thread doesn't vanish while we work */
1892 dvmLockThreadList(NULL);
1893 Thread* thread = threadObjToThread(threadObj);
1895 sprintf(result, "<%d> %s", thread->threadId, str);
1897 sprintf(result, "%s", str);
1898 dvmUnlockThreadList();
1905 * Get a thread's group.
1907 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1912 threadObj = objectIdToObject(threadId);
1913 assert(threadObj != NULL);
1915 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1916 return objectToObjectId(group);
1921 * Get the name of a thread group.
1923 * Returns a newly-allocated string.
1925 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1927 Object* threadGroup;
1928 StringObject* nameStr;
1930 threadGroup = objectIdToObject(threadGroupId);
1931 assert(threadGroup != NULL);
1933 nameStr = (StringObject*)
1934 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1935 return dvmCreateCstrFromString(nameStr);
1939 * Get the parent of a thread group.
1941 * Returns a newly-allocated string.
1943 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1945 Object* threadGroup;
1948 threadGroup = objectIdToObject(threadGroupId);
1949 assert(threadGroup != NULL);
1951 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1952 return objectToObjectId(parent);
1956 * Get the list of threads in the thread group.
1958 * We do this by running through the full list of threads and returning
1959 * the ones that have the ThreadGroup object as their owner.
1961 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1962 * return all threads.
1964 * The caller must free "*ppThreadIds".
1966 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1967 ObjectId** ppThreadIds, u4* pThreadCount)
1969 Object* targetThreadGroup = NULL;
1973 if (threadGroupId != THREAD_GROUP_ALL) {
1974 targetThreadGroup = objectIdToObject(threadGroupId);
1975 assert(targetThreadGroup != NULL);
1978 dvmLockThreadList(NULL);
1980 thread = gDvm.threadList;
1982 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1985 /* Skip over the JDWP support thread. Some debuggers
1986 * get bent out of shape when they can't suspend and
1987 * query all threads, so it's easier if we just don't
1988 * tell them about us.
1990 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1993 /* This thread is currently being created, and isn't ready
1994 * to be seen by the debugger yet.
1996 if (thread->threadObj == NULL)
1999 group = dvmGetFieldObject(thread->threadObj,
2000 gDvm.offJavaLangThread_group);
2001 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2005 *pThreadCount = count;
2008 *ppThreadIds = NULL;
2011 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2013 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2016 /* Skip over the JDWP support thread. Some debuggers
2017 * get bent out of shape when they can't suspend and
2018 * query all threads, so it's easier if we just don't
2019 * tell them about us.
2021 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2024 /* This thread is currently being created, and isn't ready
2025 * to be seen by the debugger yet.
2027 if (thread->threadObj == NULL)
2030 group = dvmGetFieldObject(thread->threadObj,
2031 gDvm.offJavaLangThread_group);
2032 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2034 *ptr++ = objectToObjectId(thread->threadObj);
2042 dvmUnlockThreadList();
2048 * The caller must free "*ppThreadIds".
2050 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2052 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2057 * Count up the #of frames on the thread's stack.
2059 * Returns -1 on failure.
2061 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2067 threadObj = objectIdToObject(threadId);
2069 dvmLockThreadList(NULL);
2070 thread = threadObjToThread(threadObj);
2071 if (thread != NULL) {
2072 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2074 dvmUnlockThreadList();
2080 * Get info for frame N from the specified thread's stack.
2082 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2090 threadObj = objectIdToObject(threadId);
2092 dvmLockThreadList(NULL);
2094 thread = threadObjToThread(threadObj);
2098 framePtr = thread->interpSave.curFrame;
2100 while (framePtr != NULL) {
2101 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2102 const Method* method = saveArea->method;
2104 if (!dvmIsBreakFrame((u4*)framePtr)) {
2106 *pFrameId = frameToFrameId(framePtr);
2107 if (dvmIsInterfaceClass(method->clazz))
2108 pLoc->typeTag = TT_INTERFACE;
2110 pLoc->typeTag = TT_CLASS;
2111 pLoc->classId = classObjectToRefTypeId(method->clazz);
2112 pLoc->methodId = methodToMethodId(method);
2113 if (dvmIsNativeMethod(method))
2116 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2117 dvmUnlockThreadList();
2124 framePtr = saveArea->prevFrame;
2128 dvmUnlockThreadList();
2133 * Get the ThreadId for the current thread.
2135 ObjectId dvmDbgGetThreadSelfId()
2137 Thread* self = dvmThreadSelf();
2138 return objectToObjectId(self->threadObj);
2144 void dvmDbgSuspendVM(bool isEvent)
2146 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2152 void dvmDbgResumeVM()
2154 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2158 * Suspend one thread (not ourselves).
2160 void dvmDbgSuspendThread(ObjectId threadId)
2162 Object* threadObj = objectIdToObject(threadId);
2165 dvmLockThreadList(NULL);
2167 thread = threadObjToThread(threadObj);
2168 if (thread == NULL) {
2169 /* can happen if our ThreadDeath notify crosses in the mail */
2170 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2172 dvmSuspendThread(thread);
2175 dvmUnlockThreadList();
2179 * Resume one thread (not ourselves).
2181 void dvmDbgResumeThread(ObjectId threadId)
2183 Object* threadObj = objectIdToObject(threadId);
2186 dvmLockThreadList(NULL);
2188 thread = threadObjToThread(threadObj);
2189 if (thread == NULL) {
2190 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2192 dvmResumeThread(thread);
2195 dvmUnlockThreadList();
2199 * Suspend ourselves after sending an event to the debugger.
2201 void dvmDbgSuspendSelf()
2203 dvmSuspendSelf(true);
2207 * Get the "this" object for the specified frame.
2209 static Object* getThisObject(const u4* framePtr)
2211 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2212 const Method* method = saveArea->method;
2213 int argOffset = method->registersSize - method->insSize;
2216 if (method == NULL) {
2217 /* this is a "break" frame? */
2222 LOGVV(" Pulling this object for frame at %p\n", framePtr);
2223 LOGVV(" Method='%s' native=%d static=%d this=%p\n",
2224 method->name, dvmIsNativeMethod(method),
2225 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2228 * No "this" pointer for statics. No args on the interp stack for
2229 * native methods invoked directly from the VM.
2231 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2234 thisObj = (Object*) framePtr[argOffset];
2236 if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
2237 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
2238 framePtr, method->clazz->descriptor, method->name);
2246 * Return the "this" object for the specified frame. The thread must be
2249 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2251 const u4* framePtr = frameIdToFrame(frameId);
2254 UNUSED_PARAMETER(threadId);
2256 thisObj = getThisObject(framePtr);
2258 *pThisId = objectToObjectId(thisObj);
2263 * Copy the value of a method argument or local variable into the
2264 * specified buffer. The value will be preceeded with the tag.
2266 * The debugger includes the tags in the request. Object tags may
2267 * be updated with a more refined type.
2269 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2270 u1 tag, u1* buf, int expectedLen)
2272 const u4* framePtr = frameIdToFrame(frameId);
2277 UNUSED_PARAMETER(threadId);
2279 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2283 assert(expectedLen == 1);
2284 intVal = framePtr[slot];
2285 set1(buf+1, intVal != 0);
2288 assert(expectedLen == 1);
2289 intVal = framePtr[slot];
2290 set1(buf+1, intVal);
2294 assert(expectedLen == 2);
2295 intVal = framePtr[slot];
2296 set2BE(buf+1, intVal);
2300 assert(expectedLen == 4);
2301 intVal = framePtr[slot];
2302 set4BE(buf+1, intVal);
2305 assert(expectedLen == sizeof(ObjectId));
2307 /* convert to "ObjectId" */
2308 objVal = (Object*)framePtr[slot];
2309 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2310 LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
2312 dvmAbort(); // DEBUG: make it obvious
2314 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2316 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2320 assert(expectedLen == sizeof(ObjectId));
2322 /* convert to "ObjectId" */
2323 objVal = (Object*)framePtr[slot];
2325 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2326 LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
2328 dvmAbort(); // DEBUG: make it obvious
2331 tag = tagFromObject(objVal);
2332 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2337 assert(expectedLen == 8);
2338 memcpy(&longVal, &framePtr[slot], 8);
2339 set8BE(buf+1, longVal);
2342 LOGE("ERROR: unhandled tag '%c'\n", tag);
2347 /* prepend tag, which may have been updated */
2352 * Copy a new value into an argument or local variable.
2354 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2355 u8 value, int width)
2357 u4* framePtr = frameIdToFrame(frameId);
2359 UNUSED_PARAMETER(threadId);
2361 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2366 framePtr[slot] = (u4)value;
2370 framePtr[slot] = (u4)value;
2375 framePtr[slot] = (u4)value;
2380 framePtr[slot] = (u4)value;
2383 /* The debugger calls VirtualMachine.CreateString to create a new
2384 * string, then uses this to set the object reference, when you
2385 * edit a String object */
2388 assert(width == sizeof(ObjectId));
2389 framePtr[slot] = (u4) objectIdToObject(value);
2394 memcpy(&framePtr[slot], &value, 8);
2397 case JT_CLASS_OBJECT:
2399 case JT_THREAD_GROUP:
2400 case JT_CLASS_LOADER:
2401 /* not expecting these from debugger; fall through to failure */
2403 LOGE("ERROR: unhandled tag '%c'\n", tag);
2411 * ===========================================================================
2412 * Debugger notification
2413 * ===========================================================================
2417 * Tell JDWP that a breakpoint address has been reached.
2419 * "pcOffset" will be -1 for native methods.
2420 * "thisPtr" will be NULL for static methods.
2422 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2423 Object* thisPtr, int eventFlags)
2427 if (dvmIsInterfaceClass(method->clazz))
2428 loc.typeTag = TT_INTERFACE;
2430 loc.typeTag = TT_CLASS;
2431 loc.classId = classObjectToRefTypeId(method->clazz);
2432 loc.methodId = methodToMethodId(method);
2436 * Note we use "NoReg" so we don't keep track of references that are
2437 * never actually sent to the debugger. The "thisPtr" is only used to
2438 * compare against registered events.
2441 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2442 objectToObjectIdNoReg(thisPtr), eventFlags))
2444 classObjectToRefTypeId(method->clazz);
2445 objectToObjectId(thisPtr);
2450 * Tell JDWP that an exception has occurred.
2452 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2453 int catchRelPc, Object* exception)
2455 JdwpLocation throwLoc, catchLoc;
2456 const Method* throwMeth;
2457 const Method* catchMeth;
2459 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2460 if (dvmIsInterfaceClass(throwMeth->clazz))
2461 throwLoc.typeTag = TT_INTERFACE;
2463 throwLoc.typeTag = TT_CLASS;
2464 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2465 throwLoc.methodId = methodToMethodId(throwMeth);
2466 throwLoc.idx = throwRelPc;
2468 if (catchRelPc < 0) {
2469 memset(&catchLoc, 0, sizeof(catchLoc));
2471 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2472 if (dvmIsInterfaceClass(catchMeth->clazz))
2473 catchLoc.typeTag = TT_INTERFACE;
2475 catchLoc.typeTag = TT_CLASS;
2476 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2477 catchLoc.methodId = methodToMethodId(catchMeth);
2478 catchLoc.idx = catchRelPc;
2481 /* need this for InstanceOnly filters */
2482 Object* thisObj = getThisObject((u4*)throwFp);
2485 * Hand the event to the JDWP exception handler. Note we're using the
2486 * "NoReg" objectID on the exception, which is not strictly correct --
2487 * the exception object WILL be passed up to the debugger if the
2488 * debugger is interested in the event. We do this because the current
2489 * implementation of the debugger object registry never throws anything
2490 * away, and some people were experiencing a fatal build up of exception
2491 * objects when dealing with certain libraries.
2493 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2494 objectToObjectIdNoReg(exception),
2495 classObjectToRefTypeId(exception->clazz), &catchLoc,
2496 objectToObjectId(thisObj));
2500 * Tell JDWP and/or DDMS that a thread has started.
2502 void dvmDbgPostThreadStart(Thread* thread)
2504 if (gDvm.debuggerActive) {
2505 dvmJdwpPostThreadChange(gDvm.jdwpState,
2506 objectToObjectId(thread->threadObj), true);
2508 if (gDvm.ddmThreadNotification)
2509 dvmDdmSendThreadNotification(thread, true);
2513 * Tell JDWP and/or DDMS that a thread has gone away.
2515 void dvmDbgPostThreadDeath(Thread* thread)
2517 if (gDvm.debuggerActive) {
2518 dvmJdwpPostThreadChange(gDvm.jdwpState,
2519 objectToObjectId(thread->threadObj), false);
2521 if (gDvm.ddmThreadNotification)
2522 dvmDdmSendThreadNotification(thread, false);
2526 * Tell JDWP that a new class has been prepared.
2528 void dvmDbgPostClassPrepare(ClassObject* clazz)
2530 const char* signature;
2533 if (dvmIsInterfaceClass(clazz))
2538 // TODO - we currently always send both "verified" and "prepared" since
2539 // debuggers seem to like that. There might be some advantage to honesty,
2540 // since the class may not yet be verified.
2541 signature = jniSignature(clazz);
2542 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2543 signature, CS_VERIFIED | CS_PREPARED);
2547 * The JDWP event mechanism has registered an event with a LocationOnly
2548 * mod. Tell the interpreter to call us if we hit the specified
2551 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2553 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2554 assert(!dvmIsNativeMethod(method));
2555 dvmAddBreakAddr(method, pLoc->idx);
2556 return true; /* assume success */
2560 * An event with a LocationOnly mod has been removed.
2562 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2564 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2565 assert(!dvmIsNativeMethod(method));
2566 dvmClearBreakAddr(method, pLoc->idx);
2570 * The JDWP event mechanism has registered a single-step event. Tell
2571 * the interpreter about it.
2573 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
2574 JdwpStepDepth depth)
2578 bool result = false;
2580 threadObj = objectIdToObject(threadId);
2581 assert(threadObj != NULL);
2584 * Get a pointer to the Thread struct for this ID. The pointer will
2585 * be used strictly for comparisons against the current thread pointer
2586 * after the setup is complete, so we can safely release the lock.
2588 dvmLockThreadList(NULL);
2589 thread = threadObjToThread(threadObj);
2591 if (thread == NULL) {
2592 LOGE("Thread for single-step not found\n");
2595 if (!dvmIsSuspended(thread)) {
2596 LOGE("Thread for single-step not suspended\n");
2597 assert(!"non-susp step"); // I want to know if this can happen
2601 assert(dvmIsSuspended(thread));
2602 if (!dvmAddSingleStep(thread, size, depth))
2608 dvmUnlockThreadList();
2613 * A single-step event has been removed.
2615 void dvmDbgUnconfigureStep(ObjectId threadId)
2617 UNUSED_PARAMETER(threadId);
2619 /* right now it's global, so don't need to find Thread */
2620 dvmClearSingleStep(NULL);
2624 * Invoke a method in a thread that has been stopped on a breakpoint or
2625 * other debugger event. (This function is called from the JDWP thread.)
2627 * Note that access control is not enforced, per spec.
2629 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2630 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2631 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2633 Object* threadObj = objectIdToObject(threadId);
2635 dvmLockThreadList(NULL);
2637 Thread* targetThread = threadObjToThread(threadObj);
2638 if (targetThread == NULL) {
2639 dvmUnlockThreadList();
2640 return ERR_INVALID_THREAD; /* thread does not exist */
2642 if (!targetThread->invokeReq.ready) {
2643 dvmUnlockThreadList();
2644 return ERR_INVALID_THREAD; /* thread not stopped by event */
2648 * We currently have a bug where we don't successfully resume the
2649 * target thread if the suspend count is too deep. We're expected to
2650 * require one "resume" for each "suspend", but when asked to execute
2651 * a method we have to resume fully and then re-suspend it back to the
2652 * same level. (The easiest way to cause this is to type "suspend"
2653 * multiple times in jdb.)
2655 * It's unclear what this means when the event specifies "resume all"
2656 * and some threads are suspended more deeply than others. This is
2657 * a rare problem, so for now we just prevent it from hanging forever
2658 * by rejecting the method invocation request. Without this, we will
2659 * be stuck waiting on a suspended thread.
2661 if (targetThread->suspendCount > 1) {
2662 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2663 "for method exec\n",
2664 dvmThreadSelf()->threadId, targetThread->threadId,
2665 targetThread->suspendCount);
2666 dvmUnlockThreadList();
2667 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2671 * TODO: ought to screen the various IDs, and verify that the argument
2675 targetThread->invokeReq.obj = objectIdToObject(objectId);
2676 targetThread->invokeReq.thread = threadObj;
2677 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2678 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2679 targetThread->invokeReq.numArgs = numArgs;
2680 targetThread->invokeReq.argArray = argArray;
2681 targetThread->invokeReq.options = options;
2682 targetThread->invokeReq.invokeNeeded = true;
2685 * This is a bit risky -- if the thread goes away we're sitting high
2686 * and dry -- but we must release this before the dvmResumeAllThreads
2687 * call, and it's unwise to hold it during dvmWaitForSuspend.
2689 dvmUnlockThreadList();
2692 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2693 * so the VM can suspend for a GC if the invoke request causes us to
2694 * run out of memory. It's also a good idea to change it before locking
2695 * the invokeReq mutex, although that should never be held for long.
2697 Thread* self = dvmThreadSelf();
2698 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2700 LOGV(" Transferring control to event thread\n");
2701 dvmLockMutex(&targetThread->invokeReq.lock);
2703 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2704 LOGV(" Resuming all threads\n");
2705 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2707 LOGV(" Resuming event thread only\n");
2708 dvmResumeThread(targetThread);
2712 * Wait for the request to finish executing.
2714 while (targetThread->invokeReq.invokeNeeded) {
2715 pthread_cond_wait(&targetThread->invokeReq.cv,
2716 &targetThread->invokeReq.lock);
2718 dvmUnlockMutex(&targetThread->invokeReq.lock);
2719 LOGV(" Control has returned from event thread\n");
2721 /* wait for thread to re-suspend itself */
2722 dvmWaitForSuspend(targetThread);
2725 * Done waiting, switch back to RUNNING.
2727 dvmChangeStatus(self, oldStatus);
2730 * Suspend the threads. We waited for the target thread to suspend
2731 * itself, so all we need to do is suspend the others.
2733 * The suspendAllThreads() call will double-suspend the event thread,
2734 * so we want to resume the target thread once to keep the books straight.
2736 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2737 LOGV(" Suspending all threads\n");
2738 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2739 LOGV(" Resuming event thread to balance the count\n");
2740 dvmResumeThread(targetThread);
2744 * Set up the result.
2746 *pResultTag = targetThread->invokeReq.resultTag;
2747 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2748 *pResultValue = targetThread->invokeReq.resultValue.j;
2750 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2751 *pResultValue = objectToObjectId(tmpObj);
2753 *pExceptObj = targetThread->invokeReq.exceptObj;
2754 return targetThread->invokeReq.err;
2758 * Return a basic tag value for the return type.
2760 static u1 getReturnTypeBasicTag(const Method* method)
2762 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2763 return basicTagFromDescriptor(descriptor);
2767 * Execute the method described by "*pReq".
2769 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2770 * want to switch to RUNNING while we execute.
2772 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2774 Thread* self = dvmThreadSelf();
2777 ThreadStatus oldStatus;
2780 * We can be called while an exception is pending in the VM. We need
2781 * to preserve that across the method invocation.
2783 oldExcept = dvmGetException(self);
2784 if (oldExcept != NULL) {
2785 dvmAddTrackedAlloc(oldExcept, self);
2786 dvmClearException(self);
2789 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2792 * Translate the method through the vtable, unless we're calling a
2793 * direct method or the debugger wants to suppress it.
2795 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2796 dvmIsDirectMethod(pReq->method))
2798 meth = pReq->method;
2800 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2802 assert(meth != NULL);
2804 assert(sizeof(jvalue) == sizeof(u8));
2807 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2808 LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
2809 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2813 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2814 (jvalue*)pReq->argArray);
2815 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2816 pReq->resultTag = getReturnTypeBasicTag(meth);
2817 if (pReq->exceptObj != 0) {
2818 Object* exc = dvmGetException(self);
2819 LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
2820 exc, exc->clazz->descriptor);
2821 //dvmLogExceptionStackTrace();
2822 dvmClearException(self);
2824 * Nothing should try to use this, but it looks like something is.
2825 * Make it null to be safe.
2827 pReq->resultValue.j = 0; /*0xadadadad;*/
2828 } else if (pReq->resultTag == JT_OBJECT) {
2829 /* if no exception thrown, examine object result more closely */
2830 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2831 if (newTag != pReq->resultTag) {
2832 LOGVV(" JDWP promoted result from %d to %d\n",
2833 pReq->resultTag, newTag);
2834 pReq->resultTag = newTag;
2838 * Register the object. We don't actually need an ObjectId yet,
2839 * but we do need to be sure that the GC won't move or discard the
2840 * object when we switch out of RUNNING. The ObjectId conversion
2841 * will add the object to the "do not touch" list.
2843 * We can't use the "tracked allocation" mechanism here because
2844 * the object is going to be handed off to a different thread.
2846 objectToObjectId((Object*)pReq->resultValue.l);
2849 if (oldExcept != NULL) {
2850 dvmSetException(self, oldExcept);
2851 dvmReleaseTrackedAlloc(oldExcept, self);
2853 dvmChangeStatus(self, oldStatus);
2856 // for dvmAddressSetForLine
2857 struct AddressSetContext {
2858 bool lastAddressValid;
2864 // for dvmAddressSetForLine
2865 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2867 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2869 if (lineNum == pContext->lineNum) {
2870 if (!pContext->lastAddressValid) {
2871 // Everything from this address until the next line change is ours
2872 pContext->lastAddress = address;
2873 pContext->lastAddressValid = true;
2875 // else, If we're already in a valid range for this lineNum,
2876 // just keep going (shouldn't really happen)
2877 } else if (pContext->lastAddressValid) { // and the line number is new
2879 // Add everything from the last entry up until here to the set
2880 for (i = pContext->lastAddress; i < address; i++) {
2881 dvmAddressSetSet(pContext->pSet, i);
2884 pContext->lastAddressValid = false;
2887 // there may be multiple entries for a line
2891 * Build up a set of bytecode addresses associated with a line number
2893 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2896 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2897 u4 insnsSize = dvmGetMethodInsnsSize(method);
2898 AddressSetContext context;
2900 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2901 result->setSize = insnsSize;
2903 memset(&context, 0, sizeof(context));
2904 context.pSet = result;
2905 context.lineNum = line;
2906 context.lastAddressValid = false;
2908 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2909 method->clazz->descriptor,
2910 method->prototype.protoIdx,
2911 method->accessFlags,
2912 addressSetCb, NULL, &context);
2914 // If the line number was the last in the position table...
2915 if (context.lastAddressValid) {
2917 for (i = context.lastAddress; i < insnsSize; i++) {
2918 dvmAddressSetSet(result, i);
2927 * ===========================================================================
2928 * Dalvik Debug Monitor support
2929 * ===========================================================================
2933 * We have received a DDM packet over JDWP. Hand it off to the VM.
2935 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2938 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2942 * First DDM packet has arrived over JDWP. Notify the press.
2944 void dvmDbgDdmConnected()
2950 * JDWP connection has dropped.
2952 void dvmDbgDdmDisconnected()
2954 dvmDdmDisconnected();
2958 * Send up a JDWP event packet with a DDM chunk in it.
2960 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2962 assert(buf != NULL);
2963 struct iovec vec[1] = { {(void*)buf, len} };
2964 dvmDbgDdmSendChunkV(type, vec, 1);
2968 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2969 * concatenated from multiple source buffers.
2971 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2973 if (gDvm.jdwpState == NULL) {
2974 LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)\n",
2979 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);