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",
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 ALOGV("+++ registering %p (%s)", 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 ALOGV("JDWP has attached");
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");
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",
429 dvmHashTableNumEntries(gDvm.dbgRegistry));
431 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
432 // LOGVV("%4d: 0x%llx", 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", 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");
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", 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'", 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'", 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'", 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->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",
1000 firstIndex, count, arrayObj->length);
1004 tag = basicTagFromDescriptor(arrayObj->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 ALOGV(" --> copying %d object IDs", 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",
1051 firstIndex, count, arrayObj->length);
1055 tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
1057 if (isTagPrimitive(tag)) {
1058 int width = dvmDbgGetTagWidth(tag);
1060 ALOGV(" --> setting %d '%c' width=%d", count, tag, width);
1062 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1067 pObjects = (Object**) data;
1068 pObjects += firstIndex;
1070 ALOGV(" --> 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];
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 = &clazz->ifields[i];
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 ALOGV("untweak: %d to %d", 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 ALOGV("untweak: %d to %d", 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 ALOGV(" %2d: %d(%d) '%s' '%s' slot=%d",
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->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 ALOGV(" --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1482 ALOGV(" --> ifieldId %x --> tag '%c'", fieldId, tag);
1483 expandBufAdd1(pReply, tag);
1487 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1490 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1493 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1496 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1500 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1504 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1507 LOGE("ERROR: unhandled field type '%s'", ifield->signature);
1515 * Set the value of the specified field.
1517 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1520 Object* obj = objectIdToObject(objectId);
1521 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1522 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1524 switch (field->signature[0]) {
1527 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1531 dvmSetFieldInt(obj, field->byteOffset, value);
1536 dvmSetFieldInt(obj, field->byteOffset, value);
1541 dvmSetFieldInt(obj, field->byteOffset, value);
1545 assert(width == sizeof(ObjectId));
1546 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1551 dvmSetFieldLong(obj, field->byteOffset, value);
1554 LOGE("ERROR: unhandled class type '%s'", field->signature);
1561 * Copy the value of a static field into the output buffer, preceded
1562 * by an appropriate tag. The tag is based on the value held by the
1563 * field, not the field's type.
1565 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1568 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1569 u1 tag = basicTagFromDescriptor(sfield->signature);
1571 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1572 Object* objVal = dvmGetStaticFieldObject(sfield);
1573 tag = tagFromObject(objVal);
1574 expandBufAdd1(pReply, tag);
1575 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1576 ALOGV(" --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1580 ALOGV(" --> sfieldId %x --> tag '%c'", fieldId, tag);
1581 expandBufAdd1(pReply, tag);
1585 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1588 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1591 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1594 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1597 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1600 value.f = dvmGetStaticFieldFloat(sfield);
1601 expandBufAdd4BE(pReply, value.i);
1604 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1607 value.d = dvmGetStaticFieldDouble(sfield);
1608 expandBufAdd8BE(pReply, value.j);
1611 LOGE("ERROR: unhandled field type '%s'", sfield->signature);
1619 * Set the value of a static field.
1621 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1622 u8 rawValue, int width)
1624 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1630 switch (sfield->signature[0]) {
1633 dvmSetStaticFieldBoolean(sfield, value.z);
1637 dvmSetStaticFieldByte(sfield, value.b);
1641 dvmSetStaticFieldShort(sfield, value.s);
1645 dvmSetStaticFieldChar(sfield, value.c);
1649 dvmSetStaticFieldInt(sfield, value.i);
1653 dvmSetStaticFieldFloat(sfield, value.f);
1657 assert(width == sizeof(ObjectId));
1658 objVal = objectIdToObject(rawValue);
1659 dvmSetStaticFieldObject(sfield, objVal);
1663 dvmSetStaticFieldLong(sfield, value.j);
1667 dvmSetStaticFieldDouble(sfield, value.d);
1670 LOGE("ERROR: unhandled class type '%s'", sfield->signature);
1677 * Convert a string object to a UTF-8 string.
1679 * Returns a newly-allocated string.
1681 char* dvmDbgStringToUtf8(ObjectId strId)
1683 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1685 return dvmCreateCstrFromString(strObj);
1690 * ===========================================================================
1691 * Thread and ThreadGroup
1692 * ===========================================================================
1696 * Convert a thread object to a Thread ptr.
1698 * This currently requires running through the list of threads and finding
1701 * IMPORTANT: grab gDvm.threadListLock before calling here.
1703 static Thread* threadObjToThread(Object* threadObj)
1707 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1708 if (thread->threadObj == threadObj)
1716 * Get the status and suspend state of a thread.
1718 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1723 bool result = false;
1725 threadObj = objectIdToObject(threadId);
1726 assert(threadObj != NULL);
1728 /* lock the thread list, so the thread doesn't vanish while we work */
1729 dvmLockThreadList(NULL);
1731 thread = threadObjToThread(threadObj);
1735 switch (thread->status) {
1736 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1737 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1738 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1739 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1740 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1741 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1742 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1743 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1744 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1745 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1748 *pThreadStatus = THREAD_ZOMBIE;
1752 if (dvmIsSuspended(thread))
1753 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1755 *pSuspendStatus = 0;
1760 dvmUnlockThreadList();
1765 * Get the thread's suspend count.
1767 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1773 threadObj = objectIdToObject(threadId);
1774 assert(threadObj != NULL);
1776 /* lock the thread list, so the thread doesn't vanish while we work */
1777 dvmLockThreadList(NULL);
1779 thread = threadObjToThread(threadObj);
1783 result = thread->suspendCount;
1786 dvmUnlockThreadList();
1791 * Determine whether or not a thread exists in the VM's thread list.
1793 * Returns "true" if the thread exists.
1795 bool dvmDbgThreadExists(ObjectId threadId)
1801 threadObj = objectIdToObject(threadId);
1802 assert(threadObj != NULL);
1804 /* lock the thread list, so the thread doesn't vanish while we work */
1805 dvmLockThreadList(NULL);
1807 thread = threadObjToThread(threadObj);
1813 dvmUnlockThreadList();
1818 * Determine whether or not a thread is suspended.
1820 * Returns "false" if the thread is running or doesn't exist.
1822 bool dvmDbgIsSuspended(ObjectId threadId)
1826 bool result = false;
1828 threadObj = objectIdToObject(threadId);
1829 assert(threadObj != NULL);
1831 /* lock the thread list, so the thread doesn't vanish while we work */
1832 dvmLockThreadList(NULL);
1834 thread = threadObjToThread(threadObj);
1838 result = dvmIsSuspended(thread);
1841 dvmUnlockThreadList();
1846 * Return the ObjectId for the "system" thread group.
1848 ObjectId dvmDbgGetSystemThreadGroupId()
1850 Object* groupObj = dvmGetSystemThreadGroup();
1851 return objectToObjectId(groupObj);
1855 * Return the ObjectId for the "main" thread group.
1857 ObjectId dvmDbgGetMainThreadGroupId()
1859 Object* groupObj = dvmGetMainThreadGroup();
1860 return objectToObjectId(groupObj);
1864 * Get the name of a thread.
1866 * Returns a newly-allocated string.
1868 char* dvmDbgGetThreadName(ObjectId threadId)
1871 StringObject* nameStr;
1875 threadObj = objectIdToObject(threadId);
1876 assert(threadObj != NULL);
1878 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1879 gDvm.offJavaLangThread_name);
1880 str = dvmCreateCstrFromString(nameStr);
1881 result = (char*) malloc(strlen(str) + 20);
1883 /* lock the thread list, so the thread doesn't vanish while we work */
1884 dvmLockThreadList(NULL);
1885 Thread* thread = threadObjToThread(threadObj);
1887 sprintf(result, "<%d> %s", thread->threadId, str);
1889 sprintf(result, "%s", str);
1890 dvmUnlockThreadList();
1897 * Get a thread's group.
1899 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1904 threadObj = objectIdToObject(threadId);
1905 assert(threadObj != NULL);
1907 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1908 return objectToObjectId(group);
1913 * Get the name of a thread group.
1915 * Returns a newly-allocated string.
1917 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1919 Object* threadGroup;
1920 StringObject* nameStr;
1922 threadGroup = objectIdToObject(threadGroupId);
1923 assert(threadGroup != NULL);
1925 nameStr = (StringObject*)
1926 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1927 return dvmCreateCstrFromString(nameStr);
1931 * Get the parent of a thread group.
1933 * Returns a newly-allocated string.
1935 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1937 Object* threadGroup;
1940 threadGroup = objectIdToObject(threadGroupId);
1941 assert(threadGroup != NULL);
1943 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1944 return objectToObjectId(parent);
1948 * Get the list of threads in the thread group.
1950 * We do this by running through the full list of threads and returning
1951 * the ones that have the ThreadGroup object as their owner.
1953 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1954 * return all threads.
1956 * The caller must free "*ppThreadIds".
1958 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1959 ObjectId** ppThreadIds, u4* pThreadCount)
1961 Object* targetThreadGroup = NULL;
1965 if (threadGroupId != THREAD_GROUP_ALL) {
1966 targetThreadGroup = objectIdToObject(threadGroupId);
1967 assert(targetThreadGroup != NULL);
1970 dvmLockThreadList(NULL);
1972 thread = gDvm.threadList;
1974 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1977 /* Skip over the JDWP support thread. Some debuggers
1978 * get bent out of shape when they can't suspend and
1979 * query all threads, so it's easier if we just don't
1980 * tell them about us.
1982 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1985 /* This thread is currently being created, and isn't ready
1986 * to be seen by the debugger yet.
1988 if (thread->threadObj == NULL)
1991 group = dvmGetFieldObject(thread->threadObj,
1992 gDvm.offJavaLangThread_group);
1993 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
1997 *pThreadCount = count;
2000 *ppThreadIds = NULL;
2003 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2005 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2008 /* Skip over the JDWP support thread. Some debuggers
2009 * get bent out of shape when they can't suspend and
2010 * query all threads, so it's easier if we just don't
2011 * tell them about us.
2013 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2016 /* This thread is currently being created, and isn't ready
2017 * to be seen by the debugger yet.
2019 if (thread->threadObj == NULL)
2022 group = dvmGetFieldObject(thread->threadObj,
2023 gDvm.offJavaLangThread_group);
2024 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2026 *ptr++ = objectToObjectId(thread->threadObj);
2034 dvmUnlockThreadList();
2040 * The caller must free "*ppThreadIds".
2042 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2044 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2049 * Count up the #of frames on the thread's stack.
2051 * Returns -1 on failure.
2053 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2059 threadObj = objectIdToObject(threadId);
2061 dvmLockThreadList(NULL);
2062 thread = threadObjToThread(threadObj);
2063 if (thread != NULL) {
2064 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2066 dvmUnlockThreadList();
2072 * Get info for frame N from the specified thread's stack.
2074 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2082 threadObj = objectIdToObject(threadId);
2084 dvmLockThreadList(NULL);
2086 thread = threadObjToThread(threadObj);
2090 framePtr = thread->interpSave.curFrame;
2092 while (framePtr != NULL) {
2093 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2094 const Method* method = saveArea->method;
2096 if (!dvmIsBreakFrame((u4*)framePtr)) {
2098 *pFrameId = frameToFrameId(framePtr);
2099 if (dvmIsInterfaceClass(method->clazz))
2100 pLoc->typeTag = TT_INTERFACE;
2102 pLoc->typeTag = TT_CLASS;
2103 pLoc->classId = classObjectToRefTypeId(method->clazz);
2104 pLoc->methodId = methodToMethodId(method);
2105 if (dvmIsNativeMethod(method))
2108 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2109 dvmUnlockThreadList();
2116 framePtr = saveArea->prevFrame;
2120 dvmUnlockThreadList();
2125 * Get the ThreadId for the current thread.
2127 ObjectId dvmDbgGetThreadSelfId()
2129 Thread* self = dvmThreadSelf();
2130 return objectToObjectId(self->threadObj);
2136 void dvmDbgSuspendVM(bool isEvent)
2138 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2144 void dvmDbgResumeVM()
2146 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2150 * Suspend one thread (not ourselves).
2152 void dvmDbgSuspendThread(ObjectId threadId)
2154 Object* threadObj = objectIdToObject(threadId);
2157 dvmLockThreadList(NULL);
2159 thread = threadObjToThread(threadObj);
2160 if (thread == NULL) {
2161 /* can happen if our ThreadDeath notify crosses in the mail */
2162 LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2164 dvmSuspendThread(thread);
2167 dvmUnlockThreadList();
2171 * Resume one thread (not ourselves).
2173 void dvmDbgResumeThread(ObjectId threadId)
2175 Object* threadObj = objectIdToObject(threadId);
2178 dvmLockThreadList(NULL);
2180 thread = threadObjToThread(threadObj);
2181 if (thread == NULL) {
2182 LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2184 dvmResumeThread(thread);
2187 dvmUnlockThreadList();
2191 * Suspend ourselves after sending an event to the debugger.
2193 void dvmDbgSuspendSelf()
2195 dvmSuspendSelf(true);
2199 * Get the "this" object for the specified frame.
2201 static Object* getThisObject(const u4* framePtr)
2203 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2204 const Method* method = saveArea->method;
2205 int argOffset = method->registersSize - method->insSize;
2208 if (method == NULL) {
2209 /* this is a "break" frame? */
2214 LOGVV(" Pulling this object for frame at %p", framePtr);
2215 LOGVV(" Method='%s' native=%d static=%d this=%p",
2216 method->name, dvmIsNativeMethod(method),
2217 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2220 * No "this" pointer for statics. No args on the interp stack for
2221 * native methods invoked directly from the VM.
2223 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2226 thisObj = (Object*) framePtr[argOffset];
2228 if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
2229 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
2230 framePtr, method->clazz->descriptor, method->name);
2238 * Return the "this" object for the specified frame. The thread must be
2241 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2243 const u4* framePtr = frameIdToFrame(frameId);
2246 UNUSED_PARAMETER(threadId);
2248 thisObj = getThisObject(framePtr);
2250 *pThisId = objectToObjectId(thisObj);
2255 * Copy the value of a method argument or local variable into the
2256 * specified buffer. The value will be preceeded with the tag.
2258 * The debugger includes the tags in the request. Object tags may
2259 * be updated with a more refined type.
2261 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2262 u1 tag, u1* buf, int expectedLen)
2264 const u4* framePtr = frameIdToFrame(frameId);
2269 UNUSED_PARAMETER(threadId);
2271 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2275 assert(expectedLen == 1);
2276 intVal = framePtr[slot];
2277 set1(buf+1, intVal != 0);
2280 assert(expectedLen == 1);
2281 intVal = framePtr[slot];
2282 set1(buf+1, intVal);
2286 assert(expectedLen == 2);
2287 intVal = framePtr[slot];
2288 set2BE(buf+1, intVal);
2292 assert(expectedLen == 4);
2293 intVal = framePtr[slot];
2294 set4BE(buf+1, intVal);
2297 assert(expectedLen == sizeof(ObjectId));
2299 /* convert to "ObjectId" */
2300 objVal = (Object*)framePtr[slot];
2301 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2302 LOGW("JDWP: slot %d expected to hold array, %p invalid",
2304 dvmAbort(); // DEBUG: make it obvious
2306 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2308 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2312 assert(expectedLen == sizeof(ObjectId));
2314 /* convert to "ObjectId" */
2315 objVal = (Object*)framePtr[slot];
2317 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2318 LOGW("JDWP: slot %d expected to hold object, %p invalid",
2320 dvmAbort(); // DEBUG: make it obvious
2323 tag = tagFromObject(objVal);
2324 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2329 assert(expectedLen == 8);
2330 memcpy(&longVal, &framePtr[slot], 8);
2331 set8BE(buf+1, longVal);
2334 LOGE("ERROR: unhandled tag '%c'", tag);
2339 /* prepend tag, which may have been updated */
2344 * Copy a new value into an argument or local variable.
2346 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2347 u8 value, int width)
2349 u4* framePtr = frameIdToFrame(frameId);
2351 UNUSED_PARAMETER(threadId);
2353 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2358 framePtr[slot] = (u4)value;
2362 framePtr[slot] = (u4)value;
2367 framePtr[slot] = (u4)value;
2372 framePtr[slot] = (u4)value;
2375 /* The debugger calls VirtualMachine.CreateString to create a new
2376 * string, then uses this to set the object reference, when you
2377 * edit a String object */
2380 assert(width == sizeof(ObjectId));
2381 framePtr[slot] = (u4) objectIdToObject(value);
2386 memcpy(&framePtr[slot], &value, 8);
2389 case JT_CLASS_OBJECT:
2391 case JT_THREAD_GROUP:
2392 case JT_CLASS_LOADER:
2393 /* not expecting these from debugger; fall through to failure */
2395 LOGE("ERROR: unhandled tag '%c'", tag);
2403 * ===========================================================================
2404 * Debugger notification
2405 * ===========================================================================
2409 * Tell JDWP that a breakpoint address has been reached.
2411 * "pcOffset" will be -1 for native methods.
2412 * "thisPtr" will be NULL for static methods.
2414 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2415 Object* thisPtr, int eventFlags)
2419 if (dvmIsInterfaceClass(method->clazz))
2420 loc.typeTag = TT_INTERFACE;
2422 loc.typeTag = TT_CLASS;
2423 loc.classId = classObjectToRefTypeId(method->clazz);
2424 loc.methodId = methodToMethodId(method);
2428 * Note we use "NoReg" so we don't keep track of references that are
2429 * never actually sent to the debugger. The "thisPtr" is only used to
2430 * compare against registered events.
2433 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2434 objectToObjectIdNoReg(thisPtr), eventFlags))
2436 classObjectToRefTypeId(method->clazz);
2437 objectToObjectId(thisPtr);
2442 * Tell JDWP that an exception has occurred.
2444 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2445 int catchRelPc, Object* exception)
2447 JdwpLocation throwLoc, catchLoc;
2448 const Method* throwMeth;
2449 const Method* catchMeth;
2451 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2452 if (dvmIsInterfaceClass(throwMeth->clazz))
2453 throwLoc.typeTag = TT_INTERFACE;
2455 throwLoc.typeTag = TT_CLASS;
2456 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2457 throwLoc.methodId = methodToMethodId(throwMeth);
2458 throwLoc.idx = throwRelPc;
2460 if (catchRelPc < 0) {
2461 memset(&catchLoc, 0, sizeof(catchLoc));
2463 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2464 if (dvmIsInterfaceClass(catchMeth->clazz))
2465 catchLoc.typeTag = TT_INTERFACE;
2467 catchLoc.typeTag = TT_CLASS;
2468 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2469 catchLoc.methodId = methodToMethodId(catchMeth);
2470 catchLoc.idx = catchRelPc;
2473 /* need this for InstanceOnly filters */
2474 Object* thisObj = getThisObject((u4*)throwFp);
2477 * Hand the event to the JDWP exception handler. Note we're using the
2478 * "NoReg" objectID on the exception, which is not strictly correct --
2479 * the exception object WILL be passed up to the debugger if the
2480 * debugger is interested in the event. We do this because the current
2481 * implementation of the debugger object registry never throws anything
2482 * away, and some people were experiencing a fatal build up of exception
2483 * objects when dealing with certain libraries.
2485 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2486 objectToObjectIdNoReg(exception),
2487 classObjectToRefTypeId(exception->clazz), &catchLoc,
2488 objectToObjectId(thisObj));
2492 * Tell JDWP and/or DDMS that a thread has started.
2494 void dvmDbgPostThreadStart(Thread* thread)
2496 if (gDvm.debuggerActive) {
2497 dvmJdwpPostThreadChange(gDvm.jdwpState,
2498 objectToObjectId(thread->threadObj), true);
2500 if (gDvm.ddmThreadNotification)
2501 dvmDdmSendThreadNotification(thread, true);
2505 * Tell JDWP and/or DDMS that a thread has gone away.
2507 void dvmDbgPostThreadDeath(Thread* thread)
2509 if (gDvm.debuggerActive) {
2510 dvmJdwpPostThreadChange(gDvm.jdwpState,
2511 objectToObjectId(thread->threadObj), false);
2513 if (gDvm.ddmThreadNotification)
2514 dvmDdmSendThreadNotification(thread, false);
2518 * Tell JDWP that a new class has been prepared.
2520 void dvmDbgPostClassPrepare(ClassObject* clazz)
2522 const char* signature;
2525 if (dvmIsInterfaceClass(clazz))
2530 // TODO - we currently always send both "verified" and "prepared" since
2531 // debuggers seem to like that. There might be some advantage to honesty,
2532 // since the class may not yet be verified.
2533 signature = jniSignature(clazz);
2534 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2535 signature, CS_VERIFIED | CS_PREPARED);
2539 * The JDWP event mechanism has registered an event with a LocationOnly
2540 * mod. Tell the interpreter to call us if we hit the specified
2543 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2545 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2546 assert(!dvmIsNativeMethod(method));
2547 dvmAddBreakAddr(method, pLoc->idx);
2548 return true; /* assume success */
2552 * An event with a LocationOnly mod has been removed.
2554 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2556 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2557 assert(!dvmIsNativeMethod(method));
2558 dvmClearBreakAddr(method, pLoc->idx);
2562 * The JDWP event mechanism has registered a single-step event. Tell
2563 * the interpreter about it.
2565 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
2566 JdwpStepDepth depth)
2570 bool result = false;
2572 threadObj = objectIdToObject(threadId);
2573 assert(threadObj != NULL);
2576 * Get a pointer to the Thread struct for this ID. The pointer will
2577 * be used strictly for comparisons against the current thread pointer
2578 * after the setup is complete, so we can safely release the lock.
2580 dvmLockThreadList(NULL);
2581 thread = threadObjToThread(threadObj);
2583 if (thread == NULL) {
2584 LOGE("Thread for single-step not found");
2587 if (!dvmIsSuspended(thread)) {
2588 LOGE("Thread for single-step not suspended");
2589 assert(!"non-susp step"); // I want to know if this can happen
2593 assert(dvmIsSuspended(thread));
2594 if (!dvmAddSingleStep(thread, size, depth))
2600 dvmUnlockThreadList();
2605 * A single-step event has been removed.
2607 void dvmDbgUnconfigureStep(ObjectId threadId)
2609 UNUSED_PARAMETER(threadId);
2611 /* right now it's global, so don't need to find Thread */
2612 dvmClearSingleStep(NULL);
2616 * Invoke a method in a thread that has been stopped on a breakpoint or
2617 * other debugger event. (This function is called from the JDWP thread.)
2619 * Note that access control is not enforced, per spec.
2621 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2622 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2623 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2625 Object* threadObj = objectIdToObject(threadId);
2627 dvmLockThreadList(NULL);
2629 Thread* targetThread = threadObjToThread(threadObj);
2630 if (targetThread == NULL) {
2631 dvmUnlockThreadList();
2632 return ERR_INVALID_THREAD; /* thread does not exist */
2634 if (!targetThread->invokeReq.ready) {
2635 dvmUnlockThreadList();
2636 return ERR_INVALID_THREAD; /* thread not stopped by event */
2640 * We currently have a bug where we don't successfully resume the
2641 * target thread if the suspend count is too deep. We're expected to
2642 * require one "resume" for each "suspend", but when asked to execute
2643 * a method we have to resume fully and then re-suspend it back to the
2644 * same level. (The easiest way to cause this is to type "suspend"
2645 * multiple times in jdb.)
2647 * It's unclear what this means when the event specifies "resume all"
2648 * and some threads are suspended more deeply than others. This is
2649 * a rare problem, so for now we just prevent it from hanging forever
2650 * by rejecting the method invocation request. Without this, we will
2651 * be stuck waiting on a suspended thread.
2653 if (targetThread->suspendCount > 1) {
2654 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2656 dvmThreadSelf()->threadId, targetThread->threadId,
2657 targetThread->suspendCount);
2658 dvmUnlockThreadList();
2659 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2663 * TODO: ought to screen the various IDs, and verify that the argument
2667 targetThread->invokeReq.obj = objectIdToObject(objectId);
2668 targetThread->invokeReq.thread = threadObj;
2669 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2670 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2671 targetThread->invokeReq.numArgs = numArgs;
2672 targetThread->invokeReq.argArray = argArray;
2673 targetThread->invokeReq.options = options;
2674 targetThread->invokeReq.invokeNeeded = true;
2677 * This is a bit risky -- if the thread goes away we're sitting high
2678 * and dry -- but we must release this before the dvmResumeAllThreads
2679 * call, and it's unwise to hold it during dvmWaitForSuspend.
2681 dvmUnlockThreadList();
2684 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2685 * so the VM can suspend for a GC if the invoke request causes us to
2686 * run out of memory. It's also a good idea to change it before locking
2687 * the invokeReq mutex, although that should never be held for long.
2689 Thread* self = dvmThreadSelf();
2690 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2692 ALOGV(" Transferring control to event thread");
2693 dvmLockMutex(&targetThread->invokeReq.lock);
2695 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2696 ALOGV(" Resuming all threads");
2697 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2699 ALOGV(" Resuming event thread only");
2700 dvmResumeThread(targetThread);
2704 * Wait for the request to finish executing.
2706 while (targetThread->invokeReq.invokeNeeded) {
2707 pthread_cond_wait(&targetThread->invokeReq.cv,
2708 &targetThread->invokeReq.lock);
2710 dvmUnlockMutex(&targetThread->invokeReq.lock);
2711 ALOGV(" Control has returned from event thread");
2713 /* wait for thread to re-suspend itself */
2714 dvmWaitForSuspend(targetThread);
2717 * Done waiting, switch back to RUNNING.
2719 dvmChangeStatus(self, oldStatus);
2722 * Suspend the threads. We waited for the target thread to suspend
2723 * itself, so all we need to do is suspend the others.
2725 * The suspendAllThreads() call will double-suspend the event thread,
2726 * so we want to resume the target thread once to keep the books straight.
2728 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2729 ALOGV(" Suspending all threads");
2730 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2731 ALOGV(" Resuming event thread to balance the count");
2732 dvmResumeThread(targetThread);
2736 * Set up the result.
2738 *pResultTag = targetThread->invokeReq.resultTag;
2739 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2740 *pResultValue = targetThread->invokeReq.resultValue.j;
2742 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2743 *pResultValue = objectToObjectId(tmpObj);
2745 *pExceptObj = targetThread->invokeReq.exceptObj;
2746 return targetThread->invokeReq.err;
2750 * Return a basic tag value for the return type.
2752 static u1 getReturnTypeBasicTag(const Method* method)
2754 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2755 return basicTagFromDescriptor(descriptor);
2759 * Execute the method described by "*pReq".
2761 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2762 * want to switch to RUNNING while we execute.
2764 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2766 Thread* self = dvmThreadSelf();
2769 ThreadStatus oldStatus;
2772 * We can be called while an exception is pending in the VM. We need
2773 * to preserve that across the method invocation.
2775 oldExcept = dvmGetException(self);
2776 if (oldExcept != NULL) {
2777 dvmAddTrackedAlloc(oldExcept, self);
2778 dvmClearException(self);
2781 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2784 * Translate the method through the vtable, unless we're calling a
2785 * direct method or the debugger wants to suppress it.
2787 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2788 dvmIsDirectMethod(pReq->method))
2790 meth = pReq->method;
2792 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2794 assert(meth != NULL);
2796 assert(sizeof(jvalue) == sizeof(u8));
2799 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2800 ALOGV("JDWP invoking method %p/%p %s.%s:%s",
2801 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2805 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2806 (jvalue*)pReq->argArray);
2807 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2808 pReq->resultTag = getReturnTypeBasicTag(meth);
2809 if (pReq->exceptObj != 0) {
2810 Object* exc = dvmGetException(self);
2811 LOGD(" JDWP invocation returning with exceptObj=%p (%s)",
2812 exc, exc->clazz->descriptor);
2813 //dvmLogExceptionStackTrace();
2814 dvmClearException(self);
2816 * Nothing should try to use this, but it looks like something is.
2817 * Make it null to be safe.
2819 pReq->resultValue.j = 0; /*0xadadadad;*/
2820 } else if (pReq->resultTag == JT_OBJECT) {
2821 /* if no exception thrown, examine object result more closely */
2822 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2823 if (newTag != pReq->resultTag) {
2824 LOGVV(" JDWP promoted result from %d to %d",
2825 pReq->resultTag, newTag);
2826 pReq->resultTag = newTag;
2830 * Register the object. We don't actually need an ObjectId yet,
2831 * but we do need to be sure that the GC won't move or discard the
2832 * object when we switch out of RUNNING. The ObjectId conversion
2833 * will add the object to the "do not touch" list.
2835 * We can't use the "tracked allocation" mechanism here because
2836 * the object is going to be handed off to a different thread.
2838 objectToObjectId((Object*)pReq->resultValue.l);
2841 if (oldExcept != NULL) {
2842 dvmSetException(self, oldExcept);
2843 dvmReleaseTrackedAlloc(oldExcept, self);
2845 dvmChangeStatus(self, oldStatus);
2848 // for dvmAddressSetForLine
2849 struct AddressSetContext {
2850 bool lastAddressValid;
2856 // for dvmAddressSetForLine
2857 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2859 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2861 if (lineNum == pContext->lineNum) {
2862 if (!pContext->lastAddressValid) {
2863 // Everything from this address until the next line change is ours
2864 pContext->lastAddress = address;
2865 pContext->lastAddressValid = true;
2867 // else, If we're already in a valid range for this lineNum,
2868 // just keep going (shouldn't really happen)
2869 } else if (pContext->lastAddressValid) { // and the line number is new
2871 // Add everything from the last entry up until here to the set
2872 for (i = pContext->lastAddress; i < address; i++) {
2873 dvmAddressSetSet(pContext->pSet, i);
2876 pContext->lastAddressValid = false;
2879 // there may be multiple entries for a line
2883 * Build up a set of bytecode addresses associated with a line number
2885 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2888 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2889 u4 insnsSize = dvmGetMethodInsnsSize(method);
2890 AddressSetContext context;
2892 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2893 result->setSize = insnsSize;
2895 memset(&context, 0, sizeof(context));
2896 context.pSet = result;
2897 context.lineNum = line;
2898 context.lastAddressValid = false;
2900 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2901 method->clazz->descriptor,
2902 method->prototype.protoIdx,
2903 method->accessFlags,
2904 addressSetCb, NULL, &context);
2906 // If the line number was the last in the position table...
2907 if (context.lastAddressValid) {
2909 for (i = context.lastAddress; i < insnsSize; i++) {
2910 dvmAddressSetSet(result, i);
2919 * ===========================================================================
2920 * Dalvik Debug Monitor support
2921 * ===========================================================================
2925 * We have received a DDM packet over JDWP. Hand it off to the VM.
2927 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2930 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2934 * First DDM packet has arrived over JDWP. Notify the press.
2936 void dvmDbgDdmConnected()
2942 * JDWP connection has dropped.
2944 void dvmDbgDdmDisconnected()
2946 dvmDdmDisconnected();
2950 * Send up a JDWP event packet with a DDM chunk in it.
2952 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2954 assert(buf != NULL);
2955 struct iovec vec[1] = { {(void*)buf, len} };
2956 dvmDbgDdmSendChunkV(type, vec, 1);
2960 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2961 * concatenated from multiple source buffers.
2963 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2965 if (gDvm.jdwpState == NULL) {
2966 ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
2971 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);