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 ALOGI("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 ALOGI("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 ALOGD("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 ALOGI("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 ALOGW("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 ALOGE("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 ALOGE("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 ALOGW("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 ALOGW("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)
1269 clazz = refTypeIdToClassObject(refTypeId);
1270 assert(clazz != NULL);
1272 count = clazz->interfaceCount;
1273 expandBufAdd4BE(pReply, count);
1274 for (i = 0; i < count; i++) {
1275 ClassObject* iface = clazz->interfaces[i];
1276 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1280 struct DebugCallbackContext {
1283 // used by locals table
1287 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1289 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1291 expandBufAdd8BE(pContext->pReply, address);
1292 expandBufAdd4BE(pContext->pReply, lineNum);
1293 pContext->numItems++;
1299 * For Method.LineTable: output the line table.
1301 * Note we operate in Dalvik's 16-bit units rather than bytes.
1303 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1308 DebugCallbackContext context;
1310 memset (&context, 0, sizeof(DebugCallbackContext));
1312 method = methodIdToMethod(refTypeId, methodId);
1313 if (dvmIsNativeMethod(method)) {
1318 end = dvmGetMethodInsnsSize(method);
1321 expandBufAdd8BE(pReply, start);
1322 expandBufAdd8BE(pReply, end);
1324 // Add numLines later
1325 size_t numLinesOffset = expandBufGetLength(pReply);
1326 expandBufAdd4BE(pReply, 0);
1328 context.pReply = pReply;
1330 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1331 dvmGetMethodCode(method),
1332 method->clazz->descriptor,
1333 method->prototype.protoIdx,
1334 method->accessFlags,
1335 lineTablePositionsCb, NULL, &context);
1337 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1341 * Eclipse appears to expect that the "this" reference is in slot zero.
1342 * If it's not, the "variables" display will show two copies of "this",
1343 * possibly because it gets "this" from SF.ThisObject and then displays
1344 * all locals with nonzero slot numbers.
1346 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1347 * SF.GetValues / SF.SetValues we map them back.
1349 static int tweakSlot(int slot, const char* name)
1353 if (strcmp(name, "this") == 0) // only remap "this" ptr
1355 else if (slot == 0) // always remap slot 0
1356 newSlot = kSlot0Sub;
1358 ALOGV("untweak: %d to %d", slot, newSlot);
1363 * Reverse Eclipse hack.
1365 static int untweakSlot(int slot, const void* framePtr)
1369 if (slot == kSlot0Sub) {
1371 } else if (slot == 0) {
1372 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1373 const Method* method = saveArea->method;
1374 newSlot = method->registersSize - method->insSize;
1377 ALOGV("untweak: %d to %d", slot, newSlot);
1381 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1382 u4 endAddress, const char *name, const char *descriptor,
1383 const char *signature)
1385 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1387 reg = (u2) tweakSlot(reg, name);
1389 ALOGV(" %2d: %d(%d) '%s' '%s' slot=%d",
1390 pContext->numItems, startAddress, endAddress - startAddress,
1391 name, descriptor, reg);
1393 expandBufAdd8BE(pContext->pReply, startAddress);
1394 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1395 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1396 if (pContext->withGeneric) {
1397 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1399 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1400 expandBufAdd4BE(pContext->pReply, reg);
1402 pContext->numItems++;
1406 * For Method.VariableTable[WithGeneric]: output information about local
1407 * variables for the specified method.
1409 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1410 bool withGeneric, ExpandBuf* pReply)
1413 DebugCallbackContext context;
1415 memset (&context, 0, sizeof(DebugCallbackContext));
1417 method = methodIdToMethod(refTypeId, methodId);
1419 expandBufAdd4BE(pReply, method->insSize);
1421 // Add numLocals later
1422 size_t numLocalsOffset = expandBufGetLength(pReply);
1423 expandBufAdd4BE(pReply, 0);
1425 context.pReply = pReply;
1426 context.withGeneric = withGeneric;
1427 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1428 dvmGetMethodCode(method),
1429 method->clazz->descriptor,
1430 method->prototype.protoIdx,
1431 method->accessFlags,
1432 NULL, variableTableCb, &context);
1434 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1438 * Get the basic tag for an instance field.
1440 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
1442 Object* obj = objectIdToObject(objId);
1443 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1444 const Field* field = fieldIdToField(classId, fieldId);
1445 return basicTagFromDescriptor(field->signature);
1449 * Get the basic tag for a static field.
1451 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
1453 const Field* field = fieldIdToField(refTypeId, fieldId);
1454 return basicTagFromDescriptor(field->signature);
1459 * Copy the value of a static field into the output buffer, preceded
1460 * by an appropriate tag. The tag is based on the value held by the
1461 * field, not the field's type.
1463 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
1465 Object* obj = objectIdToObject(objectId);
1466 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1467 InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
1468 u1 tag = basicTagFromDescriptor(ifield->signature);
1470 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1471 Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
1472 tag = tagFromObject(objVal);
1473 expandBufAdd1(pReply, tag);
1474 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1475 ALOGV(" --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1477 ALOGV(" --> ifieldId %x --> tag '%c'", fieldId, tag);
1478 expandBufAdd1(pReply, tag);
1482 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1485 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1488 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1491 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1495 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1499 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1502 ALOGE("ERROR: unhandled field type '%s'", ifield->signature);
1510 * Set the value of the specified field.
1512 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1515 Object* obj = objectIdToObject(objectId);
1516 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1517 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1519 switch (field->signature[0]) {
1522 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1526 dvmSetFieldInt(obj, field->byteOffset, value);
1531 dvmSetFieldInt(obj, field->byteOffset, value);
1536 dvmSetFieldInt(obj, field->byteOffset, value);
1540 assert(width == sizeof(ObjectId));
1541 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1546 dvmSetFieldLong(obj, field->byteOffset, value);
1549 ALOGE("ERROR: unhandled class type '%s'", field->signature);
1556 * Copy the value of a static field into the output buffer, preceded
1557 * by an appropriate tag. The tag is based on the value held by the
1558 * field, not the field's type.
1560 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1563 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1564 u1 tag = basicTagFromDescriptor(sfield->signature);
1566 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1567 Object* objVal = dvmGetStaticFieldObject(sfield);
1568 tag = tagFromObject(objVal);
1569 expandBufAdd1(pReply, tag);
1570 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1571 ALOGV(" --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1575 ALOGV(" --> sfieldId %x --> tag '%c'", fieldId, tag);
1576 expandBufAdd1(pReply, tag);
1580 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1583 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1586 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1589 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1592 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1595 value.f = dvmGetStaticFieldFloat(sfield);
1596 expandBufAdd4BE(pReply, value.i);
1599 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1602 value.d = dvmGetStaticFieldDouble(sfield);
1603 expandBufAdd8BE(pReply, value.j);
1606 ALOGE("ERROR: unhandled field type '%s'", sfield->signature);
1614 * Set the value of a static field.
1616 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1617 u8 rawValue, int width)
1619 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1625 switch (sfield->signature[0]) {
1628 dvmSetStaticFieldBoolean(sfield, value.z);
1632 dvmSetStaticFieldByte(sfield, value.b);
1636 dvmSetStaticFieldShort(sfield, value.s);
1640 dvmSetStaticFieldChar(sfield, value.c);
1644 dvmSetStaticFieldInt(sfield, value.i);
1648 dvmSetStaticFieldFloat(sfield, value.f);
1652 assert(width == sizeof(ObjectId));
1653 objVal = objectIdToObject(rawValue);
1654 dvmSetStaticFieldObject(sfield, objVal);
1658 dvmSetStaticFieldLong(sfield, value.j);
1662 dvmSetStaticFieldDouble(sfield, value.d);
1665 ALOGE("ERROR: unhandled class type '%s'", sfield->signature);
1672 * Convert a string object to a UTF-8 string.
1674 * Returns a newly-allocated string.
1676 char* dvmDbgStringToUtf8(ObjectId strId)
1678 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1680 return dvmCreateCstrFromString(strObj);
1685 * ===========================================================================
1686 * Thread and ThreadGroup
1687 * ===========================================================================
1691 * Convert a thread object to a Thread ptr.
1693 * This currently requires running through the list of threads and finding
1696 * IMPORTANT: grab gDvm.threadListLock before calling here.
1698 static Thread* threadObjToThread(Object* threadObj)
1702 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1703 if (thread->threadObj == threadObj)
1711 * Get the status and suspend state of a thread.
1713 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1718 bool result = false;
1720 threadObj = objectIdToObject(threadId);
1721 assert(threadObj != NULL);
1723 /* lock the thread list, so the thread doesn't vanish while we work */
1724 dvmLockThreadList(NULL);
1726 thread = threadObjToThread(threadObj);
1730 switch (thread->status) {
1731 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1732 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1733 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1734 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1735 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1736 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1737 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1738 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1739 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1740 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1743 *pThreadStatus = THREAD_ZOMBIE;
1747 if (dvmIsSuspended(thread))
1748 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1750 *pSuspendStatus = 0;
1755 dvmUnlockThreadList();
1760 * Get the thread's suspend count.
1762 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1768 threadObj = objectIdToObject(threadId);
1769 assert(threadObj != NULL);
1771 /* lock the thread list, so the thread doesn't vanish while we work */
1772 dvmLockThreadList(NULL);
1774 thread = threadObjToThread(threadObj);
1778 result = thread->suspendCount;
1781 dvmUnlockThreadList();
1786 * Determine whether or not a thread exists in the VM's thread list.
1788 * Returns "true" if the thread exists.
1790 bool dvmDbgThreadExists(ObjectId threadId)
1796 threadObj = objectIdToObject(threadId);
1797 assert(threadObj != NULL);
1799 /* lock the thread list, so the thread doesn't vanish while we work */
1800 dvmLockThreadList(NULL);
1802 thread = threadObjToThread(threadObj);
1808 dvmUnlockThreadList();
1813 * Determine whether or not a thread is suspended.
1815 * Returns "false" if the thread is running or doesn't exist.
1817 bool dvmDbgIsSuspended(ObjectId threadId)
1821 bool result = false;
1823 threadObj = objectIdToObject(threadId);
1824 assert(threadObj != NULL);
1826 /* lock the thread list, so the thread doesn't vanish while we work */
1827 dvmLockThreadList(NULL);
1829 thread = threadObjToThread(threadObj);
1833 result = dvmIsSuspended(thread);
1836 dvmUnlockThreadList();
1841 * Return the ObjectId for the "system" thread group.
1843 ObjectId dvmDbgGetSystemThreadGroupId()
1845 Object* groupObj = dvmGetSystemThreadGroup();
1846 return objectToObjectId(groupObj);
1850 * Return the ObjectId for the "main" thread group.
1852 ObjectId dvmDbgGetMainThreadGroupId()
1854 Object* groupObj = dvmGetMainThreadGroup();
1855 return objectToObjectId(groupObj);
1859 * Get the name of a thread.
1861 * Returns a newly-allocated string.
1863 char* dvmDbgGetThreadName(ObjectId threadId)
1866 StringObject* nameStr;
1870 threadObj = objectIdToObject(threadId);
1871 assert(threadObj != NULL);
1873 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1874 gDvm.offJavaLangThread_name);
1875 str = dvmCreateCstrFromString(nameStr);
1876 result = (char*) malloc(strlen(str) + 20);
1878 /* lock the thread list, so the thread doesn't vanish while we work */
1879 dvmLockThreadList(NULL);
1880 Thread* thread = threadObjToThread(threadObj);
1882 sprintf(result, "<%d> %s", thread->threadId, str);
1884 sprintf(result, "%s", str);
1885 dvmUnlockThreadList();
1892 * Get a thread's group.
1894 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1899 threadObj = objectIdToObject(threadId);
1900 assert(threadObj != NULL);
1902 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1903 return objectToObjectId(group);
1908 * Get the name of a thread group.
1910 * Returns a newly-allocated string.
1912 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1914 Object* threadGroup;
1915 StringObject* nameStr;
1917 threadGroup = objectIdToObject(threadGroupId);
1918 assert(threadGroup != NULL);
1920 nameStr = (StringObject*)
1921 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1922 return dvmCreateCstrFromString(nameStr);
1926 * Get the parent of a thread group.
1928 * Returns a newly-allocated string.
1930 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1932 Object* threadGroup;
1935 threadGroup = objectIdToObject(threadGroupId);
1936 assert(threadGroup != NULL);
1938 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1939 return objectToObjectId(parent);
1943 * Get the list of threads in the thread group.
1945 * We do this by running through the full list of threads and returning
1946 * the ones that have the ThreadGroup object as their owner.
1948 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1949 * return all threads.
1951 * The caller must free "*ppThreadIds".
1953 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1954 ObjectId** ppThreadIds, u4* pThreadCount)
1956 Object* targetThreadGroup = NULL;
1960 if (threadGroupId != THREAD_GROUP_ALL) {
1961 targetThreadGroup = objectIdToObject(threadGroupId);
1962 assert(targetThreadGroup != NULL);
1965 dvmLockThreadList(NULL);
1967 thread = gDvm.threadList;
1969 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1972 /* Skip over the JDWP support thread. Some debuggers
1973 * get bent out of shape when they can't suspend and
1974 * query all threads, so it's easier if we just don't
1975 * tell them about us.
1977 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1980 /* This thread is currently being created, and isn't ready
1981 * to be seen by the debugger yet.
1983 if (thread->threadObj == NULL)
1986 group = dvmGetFieldObject(thread->threadObj,
1987 gDvm.offJavaLangThread_group);
1988 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
1992 *pThreadCount = count;
1995 *ppThreadIds = NULL;
1998 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2000 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2003 /* Skip over the JDWP support thread. Some debuggers
2004 * get bent out of shape when they can't suspend and
2005 * query all threads, so it's easier if we just don't
2006 * tell them about us.
2008 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2011 /* This thread is currently being created, and isn't ready
2012 * to be seen by the debugger yet.
2014 if (thread->threadObj == NULL)
2017 group = dvmGetFieldObject(thread->threadObj,
2018 gDvm.offJavaLangThread_group);
2019 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2021 *ptr++ = objectToObjectId(thread->threadObj);
2029 dvmUnlockThreadList();
2035 * The caller must free "*ppThreadIds".
2037 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2039 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2044 * Count up the #of frames on the thread's stack.
2046 * Returns -1 on failure.
2048 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2054 threadObj = objectIdToObject(threadId);
2056 dvmLockThreadList(NULL);
2057 thread = threadObjToThread(threadObj);
2058 if (thread != NULL) {
2059 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2061 dvmUnlockThreadList();
2067 * Get info for frame N from the specified thread's stack.
2069 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2077 threadObj = objectIdToObject(threadId);
2079 dvmLockThreadList(NULL);
2081 thread = threadObjToThread(threadObj);
2085 framePtr = thread->interpSave.curFrame;
2087 while (framePtr != NULL) {
2088 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2089 const Method* method = saveArea->method;
2091 if (!dvmIsBreakFrame((u4*)framePtr)) {
2093 *pFrameId = frameToFrameId(framePtr);
2094 if (dvmIsInterfaceClass(method->clazz))
2095 pLoc->typeTag = TT_INTERFACE;
2097 pLoc->typeTag = TT_CLASS;
2098 pLoc->classId = classObjectToRefTypeId(method->clazz);
2099 pLoc->methodId = methodToMethodId(method);
2100 if (dvmIsNativeMethod(method))
2103 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2104 dvmUnlockThreadList();
2111 framePtr = saveArea->prevFrame;
2115 dvmUnlockThreadList();
2120 * Get the ThreadId for the current thread.
2122 ObjectId dvmDbgGetThreadSelfId()
2124 Thread* self = dvmThreadSelf();
2125 return objectToObjectId(self->threadObj);
2131 void dvmDbgSuspendVM(bool isEvent)
2133 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2139 void dvmDbgResumeVM()
2141 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2145 * Suspend one thread (not ourselves).
2147 void dvmDbgSuspendThread(ObjectId threadId)
2149 Object* threadObj = objectIdToObject(threadId);
2152 dvmLockThreadList(NULL);
2154 thread = threadObjToThread(threadObj);
2155 if (thread == NULL) {
2156 /* can happen if our ThreadDeath notify crosses in the mail */
2157 ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2159 dvmSuspendThread(thread);
2162 dvmUnlockThreadList();
2166 * Resume one thread (not ourselves).
2168 void dvmDbgResumeThread(ObjectId threadId)
2170 Object* threadObj = objectIdToObject(threadId);
2173 dvmLockThreadList(NULL);
2175 thread = threadObjToThread(threadObj);
2176 if (thread == NULL) {
2177 ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2179 dvmResumeThread(thread);
2182 dvmUnlockThreadList();
2186 * Suspend ourselves after sending an event to the debugger.
2188 void dvmDbgSuspendSelf()
2190 dvmSuspendSelf(true);
2194 * Get the "this" object for the specified frame.
2196 static Object* getThisObject(const u4* framePtr)
2198 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2199 const Method* method = saveArea->method;
2200 int argOffset = method->registersSize - method->insSize;
2203 if (method == NULL) {
2204 /* this is a "break" frame? */
2209 LOGVV(" Pulling this object for frame at %p", framePtr);
2210 LOGVV(" Method='%s' native=%d static=%d this=%p",
2211 method->name, dvmIsNativeMethod(method),
2212 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2215 * No "this" pointer for statics. No args on the interp stack for
2216 * native methods invoked directly from the VM.
2218 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2221 thisObj = (Object*) framePtr[argOffset];
2223 if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
2224 ALOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
2225 framePtr, method->clazz->descriptor, method->name);
2233 * Return the "this" object for the specified frame. The thread must be
2236 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2238 const u4* framePtr = frameIdToFrame(frameId);
2241 UNUSED_PARAMETER(threadId);
2243 thisObj = getThisObject(framePtr);
2245 *pThisId = objectToObjectId(thisObj);
2250 * Copy the value of a method argument or local variable into the
2251 * specified buffer. The value will be preceeded with the tag.
2253 * The debugger includes the tags in the request. Object tags may
2254 * be updated with a more refined type.
2256 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2257 u1 tag, u1* buf, int expectedLen)
2259 const u4* framePtr = frameIdToFrame(frameId);
2264 UNUSED_PARAMETER(threadId);
2266 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2270 assert(expectedLen == 1);
2271 intVal = framePtr[slot];
2272 set1(buf+1, intVal != 0);
2275 assert(expectedLen == 1);
2276 intVal = framePtr[slot];
2277 set1(buf+1, intVal);
2281 assert(expectedLen == 2);
2282 intVal = framePtr[slot];
2283 set2BE(buf+1, intVal);
2287 assert(expectedLen == 4);
2288 intVal = framePtr[slot];
2289 set4BE(buf+1, intVal);
2292 assert(expectedLen == sizeof(ObjectId));
2294 /* convert to "ObjectId" */
2295 objVal = (Object*)framePtr[slot];
2296 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2297 ALOGW("JDWP: slot %d expected to hold array, %p invalid",
2299 dvmAbort(); // DEBUG: make it obvious
2301 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2303 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2307 assert(expectedLen == sizeof(ObjectId));
2309 /* convert to "ObjectId" */
2310 objVal = (Object*)framePtr[slot];
2312 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2313 ALOGW("JDWP: slot %d expected to hold object, %p invalid",
2315 dvmAbort(); // DEBUG: make it obvious
2318 tag = tagFromObject(objVal);
2319 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2324 assert(expectedLen == 8);
2325 memcpy(&longVal, &framePtr[slot], 8);
2326 set8BE(buf+1, longVal);
2329 ALOGE("ERROR: unhandled tag '%c'", tag);
2334 /* prepend tag, which may have been updated */
2339 * Copy a new value into an argument or local variable.
2341 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2342 u8 value, int width)
2344 u4* framePtr = frameIdToFrame(frameId);
2346 UNUSED_PARAMETER(threadId);
2348 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2353 framePtr[slot] = (u4)value;
2357 framePtr[slot] = (u4)value;
2362 framePtr[slot] = (u4)value;
2367 framePtr[slot] = (u4)value;
2370 /* The debugger calls VirtualMachine.CreateString to create a new
2371 * string, then uses this to set the object reference, when you
2372 * edit a String object */
2375 assert(width == sizeof(ObjectId));
2376 framePtr[slot] = (u4) objectIdToObject(value);
2381 memcpy(&framePtr[slot], &value, 8);
2384 case JT_CLASS_OBJECT:
2386 case JT_THREAD_GROUP:
2387 case JT_CLASS_LOADER:
2388 /* not expecting these from debugger; fall through to failure */
2390 ALOGE("ERROR: unhandled tag '%c'", tag);
2398 * ===========================================================================
2399 * Debugger notification
2400 * ===========================================================================
2404 * Tell JDWP that a breakpoint address has been reached.
2406 * "pcOffset" will be -1 for native methods.
2407 * "thisPtr" will be NULL for static methods.
2409 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2410 Object* thisPtr, int eventFlags)
2414 if (dvmIsInterfaceClass(method->clazz))
2415 loc.typeTag = TT_INTERFACE;
2417 loc.typeTag = TT_CLASS;
2418 loc.classId = classObjectToRefTypeId(method->clazz);
2419 loc.methodId = methodToMethodId(method);
2423 * Note we use "NoReg" so we don't keep track of references that are
2424 * never actually sent to the debugger. The "thisPtr" is only used to
2425 * compare against registered events.
2428 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2429 objectToObjectIdNoReg(thisPtr), eventFlags))
2431 classObjectToRefTypeId(method->clazz);
2432 objectToObjectId(thisPtr);
2437 * Tell JDWP that an exception has occurred.
2439 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2440 int catchRelPc, Object* exception)
2442 JdwpLocation throwLoc, catchLoc;
2443 const Method* throwMeth;
2444 const Method* catchMeth;
2446 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2447 if (dvmIsInterfaceClass(throwMeth->clazz))
2448 throwLoc.typeTag = TT_INTERFACE;
2450 throwLoc.typeTag = TT_CLASS;
2451 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2452 throwLoc.methodId = methodToMethodId(throwMeth);
2453 throwLoc.idx = throwRelPc;
2455 if (catchRelPc < 0) {
2456 memset(&catchLoc, 0, sizeof(catchLoc));
2458 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2459 if (dvmIsInterfaceClass(catchMeth->clazz))
2460 catchLoc.typeTag = TT_INTERFACE;
2462 catchLoc.typeTag = TT_CLASS;
2463 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2464 catchLoc.methodId = methodToMethodId(catchMeth);
2465 catchLoc.idx = catchRelPc;
2468 /* need this for InstanceOnly filters */
2469 Object* thisObj = getThisObject((u4*)throwFp);
2472 * Hand the event to the JDWP exception handler. Note we're using the
2473 * "NoReg" objectID on the exception, which is not strictly correct --
2474 * the exception object WILL be passed up to the debugger if the
2475 * debugger is interested in the event. We do this because the current
2476 * implementation of the debugger object registry never throws anything
2477 * away, and some people were experiencing a fatal build up of exception
2478 * objects when dealing with certain libraries.
2480 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2481 objectToObjectIdNoReg(exception),
2482 classObjectToRefTypeId(exception->clazz), &catchLoc,
2483 objectToObjectId(thisObj));
2487 * Tell JDWP and/or DDMS that a thread has started.
2489 void dvmDbgPostThreadStart(Thread* thread)
2491 if (gDvm.debuggerActive) {
2492 dvmJdwpPostThreadChange(gDvm.jdwpState,
2493 objectToObjectId(thread->threadObj), true);
2495 if (gDvm.ddmThreadNotification)
2496 dvmDdmSendThreadNotification(thread, true);
2500 * Tell JDWP and/or DDMS that a thread has gone away.
2502 void dvmDbgPostThreadDeath(Thread* thread)
2504 if (gDvm.debuggerActive) {
2505 dvmJdwpPostThreadChange(gDvm.jdwpState,
2506 objectToObjectId(thread->threadObj), false);
2508 if (gDvm.ddmThreadNotification)
2509 dvmDdmSendThreadNotification(thread, false);
2513 * Tell JDWP that a new class has been prepared.
2515 void dvmDbgPostClassPrepare(ClassObject* clazz)
2517 const char* signature;
2520 if (dvmIsInterfaceClass(clazz))
2525 // TODO - we currently always send both "verified" and "prepared" since
2526 // debuggers seem to like that. There might be some advantage to honesty,
2527 // since the class may not yet be verified.
2528 signature = jniSignature(clazz);
2529 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2530 signature, CS_VERIFIED | CS_PREPARED);
2534 * The JDWP event mechanism has registered an event with a LocationOnly
2535 * mod. Tell the interpreter to call us if we hit the specified
2538 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2540 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2541 assert(!dvmIsNativeMethod(method));
2542 dvmAddBreakAddr(method, pLoc->idx);
2543 return true; /* assume success */
2547 * An event with a LocationOnly mod has been removed.
2549 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2551 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2552 assert(!dvmIsNativeMethod(method));
2553 dvmClearBreakAddr(method, pLoc->idx);
2557 * The JDWP event mechanism has registered a single-step event. Tell
2558 * the interpreter about it.
2560 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
2561 JdwpStepDepth depth)
2565 bool result = false;
2567 threadObj = objectIdToObject(threadId);
2568 assert(threadObj != NULL);
2571 * Get a pointer to the Thread struct for this ID. The pointer will
2572 * be used strictly for comparisons against the current thread pointer
2573 * after the setup is complete, so we can safely release the lock.
2575 dvmLockThreadList(NULL);
2576 thread = threadObjToThread(threadObj);
2578 if (thread == NULL) {
2579 ALOGE("Thread for single-step not found");
2582 if (!dvmIsSuspended(thread)) {
2583 ALOGE("Thread for single-step not suspended");
2584 assert(!"non-susp step"); // I want to know if this can happen
2588 assert(dvmIsSuspended(thread));
2589 if (!dvmAddSingleStep(thread, size, depth))
2595 dvmUnlockThreadList();
2600 * A single-step event has been removed.
2602 void dvmDbgUnconfigureStep(ObjectId threadId)
2604 UNUSED_PARAMETER(threadId);
2606 /* right now it's global, so don't need to find Thread */
2607 dvmClearSingleStep(NULL);
2611 * Invoke a method in a thread that has been stopped on a breakpoint or
2612 * other debugger event. (This function is called from the JDWP thread.)
2614 * Note that access control is not enforced, per spec.
2616 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2617 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2618 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2620 Object* threadObj = objectIdToObject(threadId);
2622 dvmLockThreadList(NULL);
2624 Thread* targetThread = threadObjToThread(threadObj);
2625 if (targetThread == NULL) {
2626 dvmUnlockThreadList();
2627 return ERR_INVALID_THREAD; /* thread does not exist */
2629 if (!targetThread->invokeReq.ready) {
2630 dvmUnlockThreadList();
2631 return ERR_INVALID_THREAD; /* thread not stopped by event */
2635 * We currently have a bug where we don't successfully resume the
2636 * target thread if the suspend count is too deep. We're expected to
2637 * require one "resume" for each "suspend", but when asked to execute
2638 * a method we have to resume fully and then re-suspend it back to the
2639 * same level. (The easiest way to cause this is to type "suspend"
2640 * multiple times in jdb.)
2642 * It's unclear what this means when the event specifies "resume all"
2643 * and some threads are suspended more deeply than others. This is
2644 * a rare problem, so for now we just prevent it from hanging forever
2645 * by rejecting the method invocation request. Without this, we will
2646 * be stuck waiting on a suspended thread.
2648 if (targetThread->suspendCount > 1) {
2649 ALOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2651 dvmThreadSelf()->threadId, targetThread->threadId,
2652 targetThread->suspendCount);
2653 dvmUnlockThreadList();
2654 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2658 * TODO: ought to screen the various IDs, and verify that the argument
2662 targetThread->invokeReq.obj = objectIdToObject(objectId);
2663 targetThread->invokeReq.thread = threadObj;
2664 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2665 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2666 targetThread->invokeReq.numArgs = numArgs;
2667 targetThread->invokeReq.argArray = argArray;
2668 targetThread->invokeReq.options = options;
2669 targetThread->invokeReq.invokeNeeded = true;
2672 * This is a bit risky -- if the thread goes away we're sitting high
2673 * and dry -- but we must release this before the dvmResumeAllThreads
2674 * call, and it's unwise to hold it during dvmWaitForSuspend.
2676 dvmUnlockThreadList();
2679 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2680 * so the VM can suspend for a GC if the invoke request causes us to
2681 * run out of memory. It's also a good idea to change it before locking
2682 * the invokeReq mutex, although that should never be held for long.
2684 Thread* self = dvmThreadSelf();
2685 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2687 ALOGV(" Transferring control to event thread");
2688 dvmLockMutex(&targetThread->invokeReq.lock);
2690 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2691 ALOGV(" Resuming all threads");
2692 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2694 ALOGV(" Resuming event thread only");
2695 dvmResumeThread(targetThread);
2699 * Wait for the request to finish executing.
2701 while (targetThread->invokeReq.invokeNeeded) {
2702 pthread_cond_wait(&targetThread->invokeReq.cv,
2703 &targetThread->invokeReq.lock);
2705 dvmUnlockMutex(&targetThread->invokeReq.lock);
2706 ALOGV(" Control has returned from event thread");
2708 /* wait for thread to re-suspend itself */
2709 dvmWaitForSuspend(targetThread);
2712 * Done waiting, switch back to RUNNING.
2714 dvmChangeStatus(self, oldStatus);
2717 * Suspend the threads. We waited for the target thread to suspend
2718 * itself, so all we need to do is suspend the others.
2720 * The suspendAllThreads() call will double-suspend the event thread,
2721 * so we want to resume the target thread once to keep the books straight.
2723 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2724 ALOGV(" Suspending all threads");
2725 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2726 ALOGV(" Resuming event thread to balance the count");
2727 dvmResumeThread(targetThread);
2731 * Set up the result.
2733 *pResultTag = targetThread->invokeReq.resultTag;
2734 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2735 *pResultValue = targetThread->invokeReq.resultValue.j;
2737 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2738 *pResultValue = objectToObjectId(tmpObj);
2740 *pExceptObj = targetThread->invokeReq.exceptObj;
2741 return targetThread->invokeReq.err;
2745 * Return a basic tag value for the return type.
2747 static u1 getReturnTypeBasicTag(const Method* method)
2749 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2750 return basicTagFromDescriptor(descriptor);
2754 * Execute the method described by "*pReq".
2756 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2757 * want to switch to RUNNING while we execute.
2759 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2761 Thread* self = dvmThreadSelf();
2764 ThreadStatus oldStatus;
2767 * We can be called while an exception is pending in the VM. We need
2768 * to preserve that across the method invocation.
2770 oldExcept = dvmGetException(self);
2771 if (oldExcept != NULL) {
2772 dvmAddTrackedAlloc(oldExcept, self);
2773 dvmClearException(self);
2776 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2779 * Translate the method through the vtable, unless we're calling a
2780 * direct method or the debugger wants to suppress it.
2782 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2783 dvmIsDirectMethod(pReq->method))
2785 meth = pReq->method;
2787 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2789 assert(meth != NULL);
2791 assert(sizeof(jvalue) == sizeof(u8));
2794 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2795 ALOGV("JDWP invoking method %p/%p %s.%s:%s",
2796 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2800 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2801 (jvalue*)pReq->argArray);
2802 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2803 pReq->resultTag = getReturnTypeBasicTag(meth);
2804 if (pReq->exceptObj != 0) {
2805 Object* exc = dvmGetException(self);
2806 ALOGD(" JDWP invocation returning with exceptObj=%p (%s)",
2807 exc, exc->clazz->descriptor);
2808 //dvmLogExceptionStackTrace();
2809 dvmClearException(self);
2811 * Nothing should try to use this, but it looks like something is.
2812 * Make it null to be safe.
2814 pReq->resultValue.j = 0; /*0xadadadad;*/
2815 } else if (pReq->resultTag == JT_OBJECT) {
2816 /* if no exception thrown, examine object result more closely */
2817 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2818 if (newTag != pReq->resultTag) {
2819 LOGVV(" JDWP promoted result from %d to %d",
2820 pReq->resultTag, newTag);
2821 pReq->resultTag = newTag;
2825 * Register the object. We don't actually need an ObjectId yet,
2826 * but we do need to be sure that the GC won't move or discard the
2827 * object when we switch out of RUNNING. The ObjectId conversion
2828 * will add the object to the "do not touch" list.
2830 * We can't use the "tracked allocation" mechanism here because
2831 * the object is going to be handed off to a different thread.
2833 objectToObjectId((Object*)pReq->resultValue.l);
2836 if (oldExcept != NULL) {
2837 dvmSetException(self, oldExcept);
2838 dvmReleaseTrackedAlloc(oldExcept, self);
2840 dvmChangeStatus(self, oldStatus);
2843 // for dvmAddressSetForLine
2844 struct AddressSetContext {
2845 bool lastAddressValid;
2851 // for dvmAddressSetForLine
2852 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2854 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2856 if (lineNum == pContext->lineNum) {
2857 if (!pContext->lastAddressValid) {
2858 // Everything from this address until the next line change is ours
2859 pContext->lastAddress = address;
2860 pContext->lastAddressValid = true;
2862 // else, If we're already in a valid range for this lineNum,
2863 // just keep going (shouldn't really happen)
2864 } else if (pContext->lastAddressValid) { // and the line number is new
2866 // Add everything from the last entry up until here to the set
2867 for (i = pContext->lastAddress; i < address; i++) {
2868 dvmAddressSetSet(pContext->pSet, i);
2871 pContext->lastAddressValid = false;
2874 // there may be multiple entries for a line
2878 * Build up a set of bytecode addresses associated with a line number
2880 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2883 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2884 u4 insnsSize = dvmGetMethodInsnsSize(method);
2885 AddressSetContext context;
2887 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2888 result->setSize = insnsSize;
2890 memset(&context, 0, sizeof(context));
2891 context.pSet = result;
2892 context.lineNum = line;
2893 context.lastAddressValid = false;
2895 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2896 method->clazz->descriptor,
2897 method->prototype.protoIdx,
2898 method->accessFlags,
2899 addressSetCb, NULL, &context);
2901 // If the line number was the last in the position table...
2902 if (context.lastAddressValid) {
2904 for (i = context.lastAddress; i < insnsSize; i++) {
2905 dvmAddressSetSet(result, i);
2914 * ===========================================================================
2915 * Dalvik Debug Monitor support
2916 * ===========================================================================
2920 * We have received a DDM packet over JDWP. Hand it off to the VM.
2922 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2925 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2929 * First DDM packet has arrived over JDWP. Notify the press.
2931 void dvmDbgDdmConnected()
2937 * JDWP connection has dropped.
2939 void dvmDbgDdmDisconnected()
2941 dvmDdmDisconnected();
2945 * Send up a JDWP event packet with a DDM chunk in it.
2947 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2949 assert(buf != NULL);
2950 struct iovec vec[1] = { {(void*)buf, len} };
2951 dvmDbgDdmSendChunkV(type, vec, 1);
2955 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2956 * concatenated from multiple source buffers.
2958 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2960 if (gDvm.jdwpState == NULL) {
2961 ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
2966 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);